mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-03-06 14:00:39 +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_X11)
|
||||
mark_as_advanced(SDL_XINPUT)
|
||||
|
||||
mark_as_advanced(SDL2_DIR)
|
||||
add_subdirectory(sdl ${TORQUE_LIB_TARG_DIRECTORY}/sdl2 EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory(nativeFileDialogs ${TORQUE_LIB_TARG_DIRECTORY}/nfd EXCLUDE_FROM_ALL)
|
||||
|
|
@ -199,64 +199,59 @@ add_subdirectory(glad ${TORQUE_LIB_TARG_DIRECTORY}/glad EXCLUDE_FROM_ALL)
|
|||
if(TORQUE_SFX_OPENAL)
|
||||
advanced_option(TORQUE_OGGVORBIS "Enable OGG Vorbis" ON)
|
||||
advanced_option(ALSOFT_EAX "Enable legacy EAX extensions" ${WIN32})
|
||||
advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" ON)
|
||||
advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" ON)
|
||||
advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" OFF)
|
||||
advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" OFF)
|
||||
advanced_option(ALSOFT_UPDATE_BUILD_VERSION "Update git build version info" ON)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_OBOE)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_PIPEWIRE)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_RTKIT)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SSE3)
|
||||
#Hide some unnecessary fields as advanced
|
||||
mark_as_advanced(ALSOFT_INSTALL_AMBDEC_PRESETS)
|
||||
mark_as_advanced(ALSOFT_BACKEND_DSOUND)
|
||||
mark_as_advanced(ALSOFT_BACKEND_MMDEVAPI)
|
||||
mark_as_advanced(ALSOFT_BACKEND_COREAUDIO)
|
||||
mark_as_advanced(ALSOFT_BACKEND_DSOUND)
|
||||
mark_as_advanced(ALSOFT_BACKEND_JACK)
|
||||
mark_as_advanced(ALSOFT_BACKEND_OBOE)
|
||||
mark_as_advanced(ALSOFT_BACKEND_OPENSL)
|
||||
mark_as_advanced(ALSOFT_BACKEND_PIPEWIRE)
|
||||
mark_as_advanced(ALSOFT_BACKEND_PORTAUDIO)
|
||||
mark_as_advanced(ALSOFT_BACKEND_PULSEAUDIO)
|
||||
mark_as_advanced(ALSOFT_BACKEND_SDL2)
|
||||
mark_as_advanced(ALSOFT_BACKEND_WASAPI)
|
||||
mark_as_advanced(ALSOFT_BACKEND_WAVE)
|
||||
mark_as_advanced(ALSOFT_BACKEND_WINMM)
|
||||
mark_as_advanced(ALSOFT_INSTALL_CONFIG)
|
||||
mark_as_advanced(ALSOFT_BUILD_ROUTER)
|
||||
mark_as_advanced(ALSOFT_CPUEXT_NEON)
|
||||
mark_as_advanced(ALSOFT_CPUEXT_SSE)
|
||||
mark_as_advanced(ALSOFT_CPUEXT_SSE2)
|
||||
mark_as_advanced(ALSOFT_CPUEXT_SSE3)
|
||||
mark_as_advanced(ALSOFT_CPUEXT_SSE4_1)
|
||||
mark_as_advanced(ALSOFT_DLOPEN)
|
||||
mark_as_advanced(ALSOFT_EMBED_HRTF_DATA)
|
||||
mark_as_advanced(ALSOFT_EXAMPLES)
|
||||
mark_as_advanced(ALSOFT_INSTALL_HRTF_DATA)
|
||||
mark_as_advanced(ALSOFT_EAX)
|
||||
advanced_option(ALSOFT_EMBED_HRTF_DATA "Embed the HRTF data files (increases library footprint)" OFF)
|
||||
advanced_option(ALSOFT_EXAMPLES "Build example programs" OFF)
|
||||
mark_as_advanced(ALSOFT_INSTALL)
|
||||
advanced_option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" OFF)
|
||||
advanced_option(ALSOFT_INSTALL_HRTF_DATA "Install HRTF data files" OFF)
|
||||
advanced_option(ALSOFT_INSTALL_AMBDEC_PRESETS "Install AmbDec preset files" OFF)
|
||||
advanced_option(ALSOFT_INSTALL_EXAMPLES "Install example programs (alplay, alstream, ...)" OFF)
|
||||
advanced_option(ALSOFT_INSTALL_UTILS "Install utility programs (openal-info, alsoft-config, ...)" OFF)
|
||||
mark_as_advanced(ALSOFT_UPDATE_BUILD_VERSION)
|
||||
mark_as_advanced(ALSOFT_NO_CONFIG_UTIL)
|
||||
mark_as_advanced(ALSOFT_NO_UID_DEFS)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_ALSA)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_COREAUDIO)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_DSOUND)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_JACK)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_MMDEVAPI)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_NEON)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_OBOE)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_OPENSL)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_OSS)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_PIPEWIRE)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_PORTAUDIO)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_PULSEAUDIO)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_QSA)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SNDIO)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SOLARIS)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SDL2)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SSE)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SSE2)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SSE3)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SSE4_1)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_WASAPI)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_WINMM)
|
||||
mark_as_advanced(ALSOFT_SEARCH_INSTALL_DATADIR)
|
||||
mark_as_advanced(ALSOFT_TESTS)
|
||||
mark_as_advanced(ALSOFT_UTILS)
|
||||
mark_as_advanced(ALSOFT_WERROR)
|
||||
mark_as_advanced(COREAUDIO_FRAMEWORK)
|
||||
mark_as_advanced(CMAKE_DEBUG_POSTFIX)
|
||||
mark_as_advanced(FORCE_STATIC_VCRT)
|
||||
mark_as_advanced(ALSOFT_BACKEND_WASAPI)
|
||||
mark_as_advanced(ALSOFT_BUILD_ROUTER)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_SDL2)
|
||||
mark_as_advanced(ALSOFT_REQUIRE_WASAPI)
|
||||
mark_as_advanced(ALSOFT_BACKEND_COREAUDIO)
|
||||
mark_as_advanced(ALSOFT_OSX_FRAMEWORK)
|
||||
mark_as_advanced(ALSOFT_STATIC_LIBGCC)
|
||||
mark_as_advanced(ALSOFT_STATIC_STDCXX)
|
||||
mark_as_advanced(AUDIOTOOLBOX_LIBRARY)
|
||||
mark_as_advanced(AUDIOUNIT_INCLUDE_DIR)
|
||||
endif()
|
||||
|
||||
advanced_option(INSTALL_DOCS "" OFF)
|
||||
|
|
@ -273,9 +268,33 @@ add_subdirectory(flac ${TORQUE_LIB_TARG_DIRECTORY}/flac EXCLUDE_FROM_ALL)
|
|||
advanced_option(INSTALL_DOCS "" OFF)
|
||||
advanced_option(OPUS_INSTALL_PKG_CONFIG_MODULE "" OFF)
|
||||
advanced_option(OPUS_INSTALL_CMAKE_CONFIG_MODULE "" OFF)
|
||||
mark_as_advanced(OPUS_ASSERTIONS)
|
||||
mark_as_advanced(OPUS_BUILD_PROGRAMS)
|
||||
mark_as_advanced(OPUS_BUILD_SHARED_LIBRARY)
|
||||
mark_as_advanced(OPUS_BUILD_TESTING)
|
||||
mark_as_advanced(OPUS_CHECK_ASM)
|
||||
mark_as_advanced(OPUS_CUSTOM_MODES)
|
||||
mark_as_advanced(OPUS_DISABLE_INTRINSICS)
|
||||
mark_as_advanced(OPUS_ENABLE_FLOAT_API)
|
||||
mark_as_advanced(OPUS_FIXED_POINT)
|
||||
mark_as_advanced(OPUS_FLOAT_APPROX)
|
||||
mark_as_advanced(OPUS_FUZZING)
|
||||
mark_as_advanced(OPUS_HARDENING)
|
||||
mark_as_advanced(OPUS_STACK_PROTECTOR)
|
||||
mark_as_advanced(OPUS_USE_ALLOCA)
|
||||
mark_as_advanced(OPUS_X86_MAY_HAVE_AVX)
|
||||
mark_as_advanced(OPUS_X86_MAY_HAVE_SSE)
|
||||
mark_as_advanced(OPUS_X86_MAY_HAVE_SSE2)
|
||||
mark_as_advanced(OPUS_X86_MAY_HAVE_SSE4_1)
|
||||
mark_as_advanced(OPUS_X86_PRESUME_AVX)
|
||||
mark_as_advanced(OPUS_X86_PRESUME_SSE)
|
||||
mark_as_advanced(OPUS_X86_PRESUME_SSE2)
|
||||
mark_as_advanced(OPUS_X86_PRESUME_SSE4_1)
|
||||
add_subdirectory(opus ${TORQUE_LIB_TARG_DIRECTORY}/opus EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(libtheora ${TORQUE_LIB_TARG_DIRECTORY}/libtheora EXCLUDE_FROM_ALL)
|
||||
|
||||
mark_as_advanced(PULSEAUDIO_INCLUDE_DIR)
|
||||
mark_as_advanced(PULSEAUDIO_LIBRARY)
|
||||
advanced_option(BUILD_PROGRAMS "" OFF)
|
||||
advanced_option(BUILD_EXAMPLES "" OFF)
|
||||
advanced_option(ENABLE_CPACK "" OFF)
|
||||
|
|
@ -288,4 +307,29 @@ add_subdirectory(libsndfile ${TORQUE_LIB_TARG_DIRECTORY}/libsndfile EXCLUDE_FROM
|
|||
if(TORQUE_TESTING)
|
||||
add_subdirectory(gtest ${TORQUE_LIB_TARG_DIRECTORY}/gtest EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
#endif()
|
||||
#endif()
|
||||
|
||||
#misc randoms
|
||||
mark_as_advanced(WINDRES)
|
||||
mark_as_advanced(AUDIOUNIT_INCLUDE_DIR)
|
||||
mark_as_advanced(CCACHE_BINARY)
|
||||
mark_as_advanced(COREAUDIO_FRAMEWORK)
|
||||
mark_as_advanced(FORCE_STATIC_VCRT)
|
||||
mark_as_advanced(HAVE_GIT)
|
||||
mark_as_advanced(LIBRARY_SUFFIX)
|
||||
mark_as_advanced(USE_STATIC_CRT)
|
||||
|
||||
mark_as_advanced(BUILD_CXXLIBS)
|
||||
mark_as_advanced(BUILD_DOCS)
|
||||
mark_as_advanced(BUILD_REGTEST)
|
||||
mark_as_advanced(BUILD_UTILS)
|
||||
|
||||
mark_as_advanced(ENABLE_64_BIT_WORDS)
|
||||
mark_as_advanced(ENABLE_BOW_DOCS)
|
||||
mark_as_advanced(ENABLE_EXTERNAL_LIBS)
|
||||
|
||||
mark_as_advanced(WITH_ASM)
|
||||
mark_as_advanced(WITH_AVX)
|
||||
mark_as_advanced(WITH_FORTIFY_SOURCE)
|
||||
mark_as_advanced(WITH_OGG)
|
||||
mark_as_advanced(WITH_STACK_PROTECTOR)
|
||||
|
|
|
|||
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
|
||||
|
||||
on: [push]
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
@ -14,6 +14,7 @@ jobs:
|
|||
name: "Win32-Release",
|
||||
os: windows-latest,
|
||||
cmake_opts: "-A Win32 \
|
||||
-DALSOFT_TESTS=ON \
|
||||
-DALSOFT_BUILD_ROUTER=ON \
|
||||
-DALSOFT_REQUIRE_WINMM=ON \
|
||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||
|
|
@ -24,6 +25,7 @@ jobs:
|
|||
name: "Win32-Debug",
|
||||
os: windows-latest,
|
||||
cmake_opts: "-A Win32 \
|
||||
-DALSOFT_TESTS=ON \
|
||||
-DALSOFT_BUILD_ROUTER=ON \
|
||||
-DALSOFT_REQUIRE_WINMM=ON \
|
||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||
|
|
@ -34,6 +36,7 @@ jobs:
|
|||
name: "Win64-Release",
|
||||
os: windows-latest,
|
||||
cmake_opts: "-A x64 \
|
||||
-DALSOFT_TESTS=ON \
|
||||
-DALSOFT_BUILD_ROUTER=ON \
|
||||
-DALSOFT_REQUIRE_WINMM=ON \
|
||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||
|
|
@ -44,16 +47,42 @@ jobs:
|
|||
name: "Win64-Debug",
|
||||
os: windows-latest,
|
||||
cmake_opts: "-A x64 \
|
||||
-DALSOFT_TESTS=ON \
|
||||
-DALSOFT_BUILD_ROUTER=ON \
|
||||
-DALSOFT_REQUIRE_WINMM=ON \
|
||||
-DALSOFT_REQUIRE_DSOUND=ON \
|
||||
-DALSOFT_REQUIRE_WASAPI=ON",
|
||||
build_type: "Debug"
|
||||
}
|
||||
- {
|
||||
name: "Win64-UWP",
|
||||
os: windows-latest,
|
||||
cmake_opts: "-A x64 \
|
||||
-DALSOFT_TESTS=OFF \
|
||||
-DCMAKE_SYSTEM_NAME=WindowsStore \
|
||||
\"-DCMAKE_SYSTEM_VERSION=10.0\" \
|
||||
-DALSOFT_BUILD_ROUTER=ON \
|
||||
-DALSOFT_REQUIRE_WASAPI=ON",
|
||||
build_type: "Release"
|
||||
}
|
||||
- {
|
||||
name: "macOS-Release",
|
||||
os: macos-latest,
|
||||
cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON",
|
||||
cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON \
|
||||
-DALSOFT_TESTS=ON",
|
||||
build_type: "Release"
|
||||
}
|
||||
- {
|
||||
name: "iOS-Release",
|
||||
os: macos-latest,
|
||||
cmake_opts: "-GXcode \
|
||||
-DCMAKE_SYSTEM_NAME=iOS \
|
||||
-DALSOFT_REQUIRE_COREAUDIO=ON \
|
||||
-DALSOFT_UTILS=OFF \
|
||||
-DALSOFT_EXAMPLES=OFF \
|
||||
-DALSOFT_TESTS=OFF \
|
||||
-DALSOFT_INSTALL=OFF \
|
||||
\"-DCMAKE_OSX_ARCHITECTURES=arm64\"",
|
||||
build_type: "Release"
|
||||
}
|
||||
- {
|
||||
|
|
@ -65,7 +94,8 @@ jobs:
|
|||
-DALSOFT_REQUIRE_PORTAUDIO=ON \
|
||||
-DALSOFT_REQUIRE_PULSEAUDIO=ON \
|
||||
-DALSOFT_REQUIRE_JACK=ON \
|
||||
-DALSOFT_REQUIRE_PIPEWIRE=ON",
|
||||
-DALSOFT_REQUIRE_PIPEWIRE=ON \
|
||||
-DALSOFT_TESTS=ON",
|
||||
deps_cmdline: "sudo apt update && sudo apt-get install -qq \
|
||||
libpulse-dev \
|
||||
portaudio19-dev \
|
||||
|
|
@ -78,7 +108,7 @@ jobs:
|
|||
}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
|
|
@ -97,6 +127,12 @@ jobs:
|
|||
run: |
|
||||
cmake --build build --config ${{matrix.config.build_type}}
|
||||
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: |
|
||||
cd build
|
||||
ctest
|
||||
|
||||
- name: Create Archive
|
||||
if: ${{ matrix.config.os == 'windows-latest' }}
|
||||
shell: bash
|
||||
|
|
@ -109,7 +145,7 @@ jobs:
|
|||
|
||||
- name: Upload Archive
|
||||
# Upload package as an artifact of this workflow.
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
if: ${{ matrix.config.os == 'windows-latest' }}
|
||||
with:
|
||||
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"
|
||||
|
||||
- name: Upload makemhr artifact
|
||||
uses: actions/upload-artifact@v3.1.1
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
with:
|
||||
name: makemhr
|
||||
path: "Artifacts/"
|
||||
|
|
|
|||
1
Engine/lib/openal-soft/.gitignore
vendored
1
Engine/lib/openal-soft/.gitignore
vendored
|
|
@ -1,6 +1,7 @@
|
|||
build*/
|
||||
winbuild
|
||||
win64build
|
||||
.vs/
|
||||
|
||||
## kdevelop
|
||||
*.kdev4
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# CMake build file list for OpenAL
|
||||
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
enable_testing()
|
||||
|
||||
if(APPLE)
|
||||
# The workaround for try_compile failing with code signing
|
||||
|
|
@ -28,11 +29,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
|||
FORCE)
|
||||
endif()
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||
set(ALSOFT_UWP TRUE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||
|
||||
if(COMMAND CMAKE_POLICY)
|
||||
cmake_policy(SET CMP0003 NEW)
|
||||
cmake_policy(SET CMP0005 NEW)
|
||||
|
|
@ -76,8 +76,8 @@ if(NOT CMAKE_DEBUG_POSTFIX)
|
|||
endif()
|
||||
|
||||
set(DEFAULT_TARGET_PROPS
|
||||
# Require C++14.
|
||||
CXX_STANDARD 14
|
||||
# Require C++17.
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED TRUE
|
||||
# Prefer C11, but support C99 and earlier when possible.
|
||||
C_STANDARD 11)
|
||||
|
|
@ -109,6 +109,7 @@ option(ALSOFT_UTILS "Build utility programs" ON)
|
|||
option(ALSOFT_NO_CONFIG_UTIL "Disable building the alsoft-config utility" OFF)
|
||||
|
||||
option(ALSOFT_EXAMPLES "Build example programs" ON)
|
||||
option(ALSOFT_TESTS "Build test programs" OFF)
|
||||
|
||||
option(ALSOFT_INSTALL "Install main library" ON)
|
||||
option(ALSOFT_INSTALL_CONFIG "Install alsoft.conf sample configuration file" ON)
|
||||
|
|
@ -148,6 +149,8 @@ set(CPP_DEFS ) # C pre-processor, not C++
|
|||
set(INC_PATHS )
|
||||
set(C_FLAGS )
|
||||
set(LINKER_FLAGS )
|
||||
set(LINKER_FLAGS_DEBUG )
|
||||
set(LINKER_FLAGS_RELEASE )
|
||||
set(EXTRA_LIBS )
|
||||
|
||||
if(WIN32)
|
||||
|
|
@ -165,7 +168,7 @@ elseif(APPLE)
|
|||
endif()
|
||||
|
||||
|
||||
# QNX's gcc do not uses /usr/include and /usr/lib pathes by default
|
||||
# QNX's gcc do not uses /usr/include and /usr/lib paths by default
|
||||
if("${CMAKE_C_PLATFORM_ID}" STREQUAL "QNX")
|
||||
set(INC_PATHS ${INC_PATHS} /usr/include)
|
||||
set(LINKER_FLAGS ${LINKER_FLAGS} -L/usr/lib)
|
||||
|
|
@ -186,29 +189,18 @@ set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0)
|
|||
set(EXPORT_DECL "")
|
||||
|
||||
|
||||
if(NOT WIN32)
|
||||
# Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions
|
||||
check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT)
|
||||
if(NOT HAVE_POSIX_MEMALIGN_DEFAULT)
|
||||
set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600")
|
||||
check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_POSIX)
|
||||
if(NOT HAVE_POSIX_MEMALIGN_POSIX)
|
||||
set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS})
|
||||
else()
|
||||
set(CPP_DEFS ${CPP_DEFS} _POSIX_C_SOURCE=200112L _XOPEN_SOURCE=600)
|
||||
endif()
|
||||
endif()
|
||||
unset(OLD_REQUIRED_FLAGS)
|
||||
endif()
|
||||
|
||||
# C99 has restrict, but C++ does not, so we can only utilize __restrict.
|
||||
check_cxx_source_compiles("int *__restrict foo;
|
||||
int main() { return 0; }" HAVE___RESTRICT)
|
||||
if(HAVE___RESTRICT)
|
||||
set(CPP_DEFS ${CPP_DEFS} RESTRICT=__restrict)
|
||||
else()
|
||||
set(CPP_DEFS ${CPP_DEFS} "RESTRICT=")
|
||||
# Some systems erroneously require the __STDC_FORMAT_MACROS macro to be defined
|
||||
# to get the fixed-width integer type formatter macros.
|
||||
check_cxx_source_compiles("#include <cinttypes>
|
||||
#include <cstdio>
|
||||
int main()
|
||||
{
|
||||
int64_t i64{};
|
||||
std::printf(\"%\" PRId64, i64);
|
||||
}"
|
||||
HAVE_STDC_FORMAT_MACROS)
|
||||
if(NOT HAVE_STDC_FORMAT_MACROS)
|
||||
set(CPP_DEFS ${CPP_DEFS} __STDC_FORMAT_MACROS)
|
||||
endif()
|
||||
|
||||
# Some systems may need libatomic for atomic functions to work
|
||||
|
|
@ -278,6 +270,11 @@ else()
|
|||
endif()
|
||||
endif()
|
||||
|
||||
check_cxx_compiler_flag(-Wno-interference-size HAVE_WNO_INTERFERENCE_SIZE)
|
||||
if(HAVE_WNO_INTERFERENCE_SIZE)
|
||||
set(C_FLAGS ${C_FLAGS} $<$<COMPILE_LANGUAGE:CXX>:-Wno-interference-size>)
|
||||
endif()
|
||||
|
||||
if(ALSOFT_WERROR)
|
||||
set(C_FLAGS ${C_FLAGS} -Werror)
|
||||
endif()
|
||||
|
|
@ -312,7 +309,7 @@ else()
|
|||
option(ALSOFT_STATIC_STDCXX "Static link libstdc++" OFF)
|
||||
if(ALSOFT_STATIC_STDCXX)
|
||||
set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state")
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-static-libstdc++")
|
||||
check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBSTDCXX_SWITCH)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
|
||||
unset(OLD_REQUIRED_LIBRARIES)
|
||||
|
|
@ -320,14 +317,14 @@ else()
|
|||
if(NOT HAVE_STATIC_LIBSTDCXX_SWITCH)
|
||||
message(FATAL_ERROR "Cannot static link libstdc++")
|
||||
endif()
|
||||
set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state")
|
||||
set(LINKER_FLAGS ${LINKER_FLAGS} "-static-libstdc++")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
option(ALSOFT_STATIC_WINPTHREAD "Static link libwinpthread" OFF)
|
||||
if(ALSOFT_STATIC_WINPTHREAD)
|
||||
set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state")
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state")
|
||||
check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBWINPTHREAD_SWITCH)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES})
|
||||
unset(OLD_REQUIRED_LIBRARIES)
|
||||
|
|
@ -335,38 +332,38 @@ else()
|
|||
if(NOT HAVE_STATIC_LIBWINPTHREAD_SWITCH)
|
||||
message(FATAL_ERROR "Cannot static link libwinpthread")
|
||||
endif()
|
||||
set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state")
|
||||
set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Set visibility/export options if available
|
||||
if(WIN32)
|
||||
if(NOT LIBTYPE STREQUAL "STATIC")
|
||||
if(NOT LIBTYPE STREQUAL "STATIC")
|
||||
if(WIN32)
|
||||
set(EXPORT_DECL "__declspec(dllexport)")
|
||||
endif()
|
||||
else()
|
||||
set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||
# Yes GCC, really don't accept visibility modes you don't support
|
||||
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror")
|
||||
|
||||
check_c_source_compiles("int foo() __attribute__((visibility(\"protected\")));
|
||||
int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY)
|
||||
if(HAVE_GCC_PROTECTED_VISIBILITY)
|
||||
if(NOT LIBTYPE STREQUAL "STATIC")
|
||||
set(EXPORT_DECL "__attribute__((visibility(\"protected\")))")
|
||||
endif()
|
||||
else()
|
||||
check_c_source_compiles("int foo() __attribute__((visibility(\"default\")));
|
||||
int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY)
|
||||
if(HAVE_GCC_DEFAULT_VISIBILITY)
|
||||
if(NOT LIBTYPE STREQUAL "STATIC")
|
||||
set(OLD_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
|
||||
# Yes GCC, really don't accept visibility modes you don't support
|
||||
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS} -Wattributes -Werror")
|
||||
|
||||
check_c_source_compiles("int foo() __attribute__((visibility(\"protected\")));
|
||||
int main() {return 0;}" HAVE_GCC_PROTECTED_VISIBILITY)
|
||||
if(HAVE_GCC_PROTECTED_VISIBILITY)
|
||||
set(EXPORT_DECL "__attribute__((visibility(\"protected\")))")
|
||||
else()
|
||||
check_c_source_compiles("int foo() __attribute__((visibility(\"default\")));
|
||||
int main() {return 0;}" HAVE_GCC_DEFAULT_VISIBILITY)
|
||||
if(HAVE_GCC_DEFAULT_VISIBILITY)
|
||||
set(EXPORT_DECL "__attribute__((visibility(\"default\")))")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(HAVE_GCC_PROTECTED_VISIBILITY OR HAVE_GCC_DEFAULT_VISIBILITY)
|
||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||
endif()
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
|
||||
set(CMAKE_REQUIRED_FLAGS "${OLD_REQUIRED_FLAGS}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
|
|
@ -403,7 +400,7 @@ if(ALSOFT_CPUEXT_SSE AND HAVE_XMMINTRIN_H)
|
|||
set(HAVE_SSE 1)
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE)
|
||||
message(FATAL_ERROR "Failed to enabled required SSE CPU extensions")
|
||||
message(FATAL_ERROR "Failed to enable required SSE CPU extensions")
|
||||
endif()
|
||||
|
||||
option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON)
|
||||
|
|
@ -448,10 +445,11 @@ if(ALSOFT_CPUEXT_NEON AND HAVE_ARM_NEON_H)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON)
|
||||
message(FATAL_ERROR "Failed to enabled required ARM NEON CPU extensions")
|
||||
message(FATAL_ERROR "Failed to enable required ARM NEON CPU extensions")
|
||||
endif()
|
||||
|
||||
|
||||
set(ALSOFT_FORCE_ALIGN )
|
||||
set(SSE_FLAGS )
|
||||
set(FPMATH_SET "0")
|
||||
if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
|
||||
|
|
@ -476,6 +474,7 @@ if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
|
|||
# OSs don't guarantee this on 32-bit, so externally-callable
|
||||
# functions need to ensure an aligned stack.
|
||||
set(EXPORT_DECL "${EXPORT_DECL}__attribute__((force_align_arg_pointer))")
|
||||
set(ALSOFT_FORCE_ALIGN "__attribute__((force_align_arg_pointer))")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -493,13 +492,9 @@ if(HAVE_SSE2)
|
|||
endif()
|
||||
|
||||
|
||||
check_include_file(malloc.h HAVE_MALLOC_H)
|
||||
check_include_file(cpuid.h HAVE_CPUID_H)
|
||||
check_include_file(intrin.h HAVE_INTRIN_H)
|
||||
check_include_file(guiddef.h HAVE_GUIDDEF_H)
|
||||
if(NOT HAVE_GUIDDEF_H)
|
||||
check_include_file(initguid.h HAVE_INITGUID_H)
|
||||
endif()
|
||||
|
||||
# Some systems need libm for some math functions to work
|
||||
set(MATH_LIB )
|
||||
|
|
@ -517,7 +512,7 @@ if(HAVE_LIBRT)
|
|||
set(RT_LIB rt)
|
||||
endif()
|
||||
|
||||
# Check for the dlopen API (for dynamicly loading backend libs)
|
||||
# Check for the dlopen API (for dynamically loading backend libs)
|
||||
if(ALSOFT_DLOPEN)
|
||||
check_include_file(dlfcn.h HAVE_DLFCN_H)
|
||||
check_library_exists(dl dlopen "" HAVE_LIBDL)
|
||||
|
|
@ -546,8 +541,6 @@ if(HAVE_INTRIN_H)
|
|||
}" HAVE_CPUID_INTRINSIC)
|
||||
endif()
|
||||
|
||||
check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN)
|
||||
check_symbol_exists(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC)
|
||||
check_symbol_exists(proc_pidpath libproc.h HAVE_PROC_PIDPATH)
|
||||
|
||||
if(NOT WIN32)
|
||||
|
|
@ -582,34 +575,36 @@ if(NOT WIN32)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
check_symbol_exists(getopt unistd.h HAVE_GETOPT)
|
||||
|
||||
|
||||
# Common sources used by both the OpenAL implementation library, the OpenAL
|
||||
# router, and certain tools and examples.
|
||||
set(COMMON_OBJS
|
||||
common/alassert.cpp
|
||||
common/alassert.h
|
||||
common/albit.h
|
||||
common/albyte.h
|
||||
common/alcomplex.cpp
|
||||
common/alcomplex.h
|
||||
common/aldeque.h
|
||||
common/alfstream.cpp
|
||||
common/alfstream.h
|
||||
common/almalloc.cpp
|
||||
common/almalloc.h
|
||||
common/alnumbers.h
|
||||
common/alnumeric.h
|
||||
common/aloptional.h
|
||||
common/alsem.cpp
|
||||
common/alsem.h
|
||||
common/alspan.h
|
||||
common/alstring.cpp
|
||||
common/alstring.h
|
||||
common/althrd_setname.cpp
|
||||
common/althrd_setname.h
|
||||
common/althreads.h
|
||||
common/altraits.h
|
||||
common/atomic.h
|
||||
common/comptr.h
|
||||
common/dynload.cpp
|
||||
common/dynload.h
|
||||
common/flexarray.h
|
||||
common/intrusive_ptr.h
|
||||
common/opthelpers.h
|
||||
common/pffft.cpp
|
||||
common/pffft.h
|
||||
common/phase_shifter.h
|
||||
common/polyphase_resampler.cpp
|
||||
common/polyphase_resampler.h
|
||||
|
|
@ -618,8 +613,6 @@ set(COMMON_OBJS
|
|||
common/ringbuffer.h
|
||||
common/strutils.cpp
|
||||
common/strutils.h
|
||||
common/threads.cpp
|
||||
common/threads.h
|
||||
common/vecmat.h
|
||||
common/vector.h)
|
||||
|
||||
|
|
@ -680,6 +673,8 @@ set(CORE_OBJS
|
|||
core/mixer.cpp
|
||||
core/mixer.h
|
||||
core/resampler_limits.h
|
||||
core/storage_formats.cpp
|
||||
core/storage_formats.h
|
||||
core/uhjfilter.cpp
|
||||
core/uhjfilter.h
|
||||
core/uiddefs.cpp
|
||||
|
|
@ -726,7 +721,7 @@ if(NOT WIN32)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT)
|
||||
message(FATAL_ERROR "Failed to enabled required RTKit support")
|
||||
message(FATAL_ERROR "Failed to enable required RTKit support")
|
||||
endif()
|
||||
|
||||
# Default mixers, always available
|
||||
|
|
@ -742,6 +737,9 @@ set(OPENAL_OBJS
|
|||
al/auxeffectslot.h
|
||||
al/buffer.cpp
|
||||
al/buffer.h
|
||||
al/debug.cpp
|
||||
al/debug.h
|
||||
al/direct_defs.h
|
||||
al/effect.cpp
|
||||
al/effect.h
|
||||
al/effects/autowah.cpp
|
||||
|
|
@ -761,6 +759,7 @@ set(OPENAL_OBJS
|
|||
al/effects/reverb.cpp
|
||||
al/effects/vmorpher.cpp
|
||||
al/error.cpp
|
||||
al/error.h
|
||||
al/event.cpp
|
||||
al/event.h
|
||||
al/extension.cpp
|
||||
|
|
@ -798,6 +797,9 @@ set(ALC_OBJS
|
|||
alc/effects/pshifter.cpp
|
||||
alc/effects/reverb.cpp
|
||||
alc/effects/vmorpher.cpp
|
||||
alc/events.cpp
|
||||
alc/events.h
|
||||
alc/export_list.h
|
||||
alc/inprogext.h
|
||||
alc/panning.cpp)
|
||||
|
||||
|
|
@ -815,7 +817,6 @@ if(ALSOFT_EAX)
|
|||
al/eax/fx_slot_index.h
|
||||
al/eax/fx_slots.cpp
|
||||
al/eax/fx_slots.h
|
||||
al/eax/globals.cpp
|
||||
al/eax/globals.h
|
||||
al/eax/utils.cpp
|
||||
al/eax/utils.h
|
||||
|
|
@ -899,7 +900,7 @@ if(ALSOFT_BACKEND_PIPEWIRE AND PkgConfig_FOUND)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_PIPEWIRE AND NOT HAVE_PIPEWIRE)
|
||||
message(FATAL_ERROR "Failed to enabled required PipeWire backend")
|
||||
message(FATAL_ERROR "Failed to enable required PipeWire backend")
|
||||
endif()
|
||||
|
||||
# Check PulseAudio backend
|
||||
|
|
@ -916,7 +917,7 @@ if(ALSOFT_BACKEND_PULSEAUDIO)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO)
|
||||
message(FATAL_ERROR "Failed to enabled required PulseAudio backend")
|
||||
message(FATAL_ERROR "Failed to enable required PulseAudio backend")
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
|
|
@ -963,66 +964,72 @@ if(NOT WIN32)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# Check SndIO backend
|
||||
option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON)
|
||||
# Check SndIO backend (disabled by default on non-BSDs)
|
||||
if(BSD)
|
||||
option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON)
|
||||
else()
|
||||
option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" OFF)
|
||||
endif()
|
||||
option(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF)
|
||||
if(ALSOFT_BACKEND_SNDIO)
|
||||
find_package(SoundIO)
|
||||
if(SOUNDIO_FOUND)
|
||||
find_package(SndIO)
|
||||
if(SNDIO_FOUND)
|
||||
set(HAVE_SNDIO 1)
|
||||
set(BACKENDS "${BACKENDS} SndIO (linked),")
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.h)
|
||||
set(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS})
|
||||
set(INC_PATHS ${INC_PATHS} ${SOUNDIO_INCLUDE_DIRS})
|
||||
set(EXTRA_LIBS ${SNDIO_LIBRARIES} ${EXTRA_LIBS})
|
||||
set(INC_PATHS ${INC_PATHS} ${SNDIO_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA)
|
||||
message(FATAL_ERROR "Failed to enabled required ALSA backend")
|
||||
message(FATAL_ERROR "Failed to enable required ALSA backend")
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS)
|
||||
message(FATAL_ERROR "Failed to enabled required OSS backend")
|
||||
message(FATAL_ERROR "Failed to enable required OSS backend")
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS)
|
||||
message(FATAL_ERROR "Failed to enabled required Solaris backend")
|
||||
message(FATAL_ERROR "Failed to enable required Solaris backend")
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO)
|
||||
message(FATAL_ERROR "Failed to enabled required SndIO backend")
|
||||
message(FATAL_ERROR "Failed to enable required SndIO backend")
|
||||
endif()
|
||||
|
||||
# Check Windows-only backends
|
||||
if(WIN32)
|
||||
# Check MMSystem backend
|
||||
option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON)
|
||||
option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF)
|
||||
if(ALSOFT_BACKEND_WINMM)
|
||||
set(HAVE_WINMM 1)
|
||||
set(BACKENDS "${BACKENDS} WinMM,")
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h)
|
||||
# There doesn't seem to be good way to search for winmm.lib for MSVC.
|
||||
# find_library doesn't find it without being told to look in a specific
|
||||
# place in the WindowsSDK, but it links anyway. If there ends up being
|
||||
# Windows targets without this, another means to detect it is needed.
|
||||
set(EXTRA_LIBS winmm ${EXTRA_LIBS})
|
||||
endif()
|
||||
|
||||
# Check DSound backend
|
||||
option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON)
|
||||
option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF)
|
||||
if(ALSOFT_BACKEND_DSOUND)
|
||||
check_include_file(dsound.h HAVE_DSOUND_H)
|
||||
if(DXSDK_DIR)
|
||||
find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h"
|
||||
PATHS "${DXSDK_DIR}" PATH_SUFFIXES include
|
||||
DOC "The DirectSound include directory")
|
||||
if (NOT ALSOFT_UWP)
|
||||
# Check MMSystem backend
|
||||
option(ALSOFT_BACKEND_WINMM "Enable Windows Multimedia backend" ON)
|
||||
option(ALSOFT_REQUIRE_WINMM "Require Windows Multimedia backend" OFF)
|
||||
if(ALSOFT_BACKEND_WINMM)
|
||||
set(HAVE_WINMM 1)
|
||||
set(BACKENDS "${BACKENDS} WinMM,")
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/winmm.cpp alc/backends/winmm.h)
|
||||
# There doesn't seem to be good way to search for winmm.lib for MSVC.
|
||||
# find_library doesn't find it without being told to look in a specific
|
||||
# place in the WindowsSDK, but it links anyway. If there ends up being
|
||||
# Windows targets without this, another means to detect it is needed.
|
||||
set(EXTRA_LIBS winmm ${EXTRA_LIBS})
|
||||
endif()
|
||||
if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR)
|
||||
set(HAVE_DSOUND 1)
|
||||
set(BACKENDS "${BACKENDS} DirectSound,")
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h)
|
||||
|
||||
if(NOT HAVE_DSOUND_H)
|
||||
set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR})
|
||||
# Check DSound backend
|
||||
option(ALSOFT_BACKEND_DSOUND "Enable DirectSound backend" ON)
|
||||
option(ALSOFT_REQUIRE_DSOUND "Require DirectSound backend" OFF)
|
||||
if(ALSOFT_BACKEND_DSOUND)
|
||||
check_include_file(dsound.h HAVE_DSOUND_H)
|
||||
if(DXSDK_DIR)
|
||||
find_path(DSOUND_INCLUDE_DIR NAMES "dsound.h"
|
||||
PATHS "${DXSDK_DIR}" PATH_SUFFIXES include
|
||||
DOC "The DirectSound include directory")
|
||||
endif()
|
||||
if(HAVE_DSOUND_H OR DSOUND_INCLUDE_DIR)
|
||||
set(HAVE_DSOUND 1)
|
||||
set(BACKENDS "${BACKENDS} DirectSound,")
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/dsound.cpp alc/backends/dsound.h)
|
||||
|
||||
if(NOT HAVE_DSOUND_H)
|
||||
set(INC_PATHS ${INC_PATHS} ${DSOUND_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
@ -1040,13 +1047,13 @@ if(WIN32)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM)
|
||||
message(FATAL_ERROR "Failed to enabled required WinMM backend")
|
||||
message(FATAL_ERROR "Failed to enable required WinMM backend")
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND)
|
||||
message(FATAL_ERROR "Failed to enabled required DSound backend")
|
||||
message(FATAL_ERROR "Failed to enable required DSound backend")
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI)
|
||||
message(FATAL_ERROR "Failed to enabled required WASAPI backend")
|
||||
message(FATAL_ERROR "Failed to enable required WASAPI backend")
|
||||
endif()
|
||||
|
||||
# Check JACK backend
|
||||
|
|
@ -1063,7 +1070,7 @@ if(ALSOFT_BACKEND_JACK)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK)
|
||||
message(FATAL_ERROR "Failed to enabled required JACK backend")
|
||||
message(FATAL_ERROR "Failed to enable required JACK backend")
|
||||
endif()
|
||||
|
||||
# Check CoreAudio backend
|
||||
|
|
@ -1098,7 +1105,7 @@ if(ALSOFT_BACKEND_COREAUDIO)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO)
|
||||
message(FATAL_ERROR "Failed to enabled required CoreAudio backend")
|
||||
message(FATAL_ERROR "Failed to enable required CoreAudio backend")
|
||||
endif()
|
||||
|
||||
# Check for Oboe (Android) backend
|
||||
|
|
@ -1130,7 +1137,7 @@ if(ALSOFT_BACKEND_OBOE)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE)
|
||||
message(FATAL_ERROR "Failed to enabled required Oboe backend")
|
||||
message(FATAL_ERROR "Failed to enable required Oboe backend")
|
||||
endif()
|
||||
|
||||
# Check for OpenSL (Android) backend
|
||||
|
|
@ -1142,11 +1149,12 @@ if(ALSOFT_BACKEND_OPENSL)
|
|||
set(HAVE_OPENSL 1)
|
||||
set(ALC_OBJS ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h)
|
||||
set(BACKENDS "${BACKENDS} OpenSL,")
|
||||
set(EXTRA_LIBS "OpenSL::OpenSLES" ${EXTRA_LIBS})
|
||||
set(EXTRA_LIBS ${OPENSL_LIBRARIES} ${EXTRA_LIBS})
|
||||
set(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL)
|
||||
message(FATAL_ERROR "Failed to enabled required OpenSL backend")
|
||||
message(FATAL_ERROR "Failed to enable required OpenSL backend")
|
||||
endif()
|
||||
|
||||
# Check PortAudio backend
|
||||
|
|
@ -1163,7 +1171,7 @@ if(ALSOFT_BACKEND_PORTAUDIO)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO)
|
||||
message(FATAL_ERROR "Failed to enabled required PortAudio backend")
|
||||
message(FATAL_ERROR "Failed to enable required PortAudio backend")
|
||||
endif()
|
||||
|
||||
# Check for SDL2 backend
|
||||
|
|
@ -1181,7 +1189,7 @@ if(ALSOFT_BACKEND_SDL2)
|
|||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND)
|
||||
message(FATAL_ERROR "Failed to enabled required SDL2 backend")
|
||||
message(FATAL_ERROR "Failed to enable required SDL2 backend")
|
||||
endif()
|
||||
|
||||
# Optionally enable the Wave Writer backend
|
||||
|
|
@ -1309,11 +1317,12 @@ configure_file(
|
|||
@ONLY)
|
||||
|
||||
|
||||
add_library(common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
|
||||
target_include_directories(common PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include)
|
||||
target_compile_definitions(common PRIVATE ${CPP_DEFS})
|
||||
target_compile_options(common PRIVATE ${C_FLAGS})
|
||||
set_target_properties(common PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE)
|
||||
add_library(alcommon STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS})
|
||||
target_include_directories(alcommon PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include
|
||||
PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_definitions(alcommon PRIVATE ${CPP_DEFS})
|
||||
target_compile_options(alcommon PRIVATE ${C_FLAGS})
|
||||
set_target_properties(alcommon PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE)
|
||||
|
||||
|
||||
unset(HAS_ROUTER)
|
||||
|
|
@ -1348,7 +1357,7 @@ else()
|
|||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}"
|
||||
"AL_API=${EXPORT_DECL}" ${CPP_DEFS})
|
||||
target_compile_options(OpenAL PRIVATE ${C_FLAGS})
|
||||
target_link_libraries(OpenAL PRIVATE common ${LINKER_FLAGS})
|
||||
target_link_libraries(OpenAL PRIVATE alcommon ${LINKER_FLAGS})
|
||||
target_include_directories(OpenAL
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${OpenAL_SOURCE_DIR}/include>
|
||||
|
|
@ -1379,13 +1388,31 @@ else()
|
|||
if(WIN32)
|
||||
set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "")
|
||||
endif()
|
||||
target_link_libraries(${IMPL_TARGET} PRIVATE common ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
|
||||
target_link_libraries(${IMPL_TARGET} PRIVATE alcommon ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB})
|
||||
|
||||
if(ALSOFT_UWP)
|
||||
set(ALSOFT_CPPWINRT_VERSION "2.0.230706.1" CACHE STRING "The soft-oal default cppwinrt version")
|
||||
|
||||
find_program(NUGET_EXE NAMES nuget)
|
||||
if(NOT NUGET_EXE)
|
||||
message("NUGET.EXE not found.")
|
||||
message(FATAL_ERROR "Please install this executable, and run CMake again.")
|
||||
endif()
|
||||
|
||||
exec_program(${NUGET_EXE}
|
||||
ARGS install "Microsoft.Windows.CppWinRT" -Version ${ALSOFT_CPPWINRT_VERSION} -ExcludeVersion -OutputDirectory "\"${CMAKE_BINARY_DIR}/packages\"")
|
||||
|
||||
set_target_properties(${IMPL_TARGET} PROPERTIES
|
||||
VS_PROJECT_IMPORT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.props
|
||||
)
|
||||
target_link_libraries(${IMPL_TARGET} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.targets)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
# FIXME: This doesn't put a dependency on the version script. Changing
|
||||
# the version script will not cause a relink as it should.
|
||||
set_property(TARGET ${IMPL_TARGET} APPEND_STRING PROPERTY
|
||||
LINK_FLAGS " -Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version")
|
||||
target_link_options(${IMPL_TARGET} PRIVATE
|
||||
"-Wl,--version-script=${OpenAL_SOURCE_DIR}/libopenal.version")
|
||||
endif()
|
||||
|
||||
if(APPLE AND ALSOFT_OSX_FRAMEWORK)
|
||||
|
|
@ -1443,8 +1470,8 @@ set_target_properties(${IMPL_TARGET} PROPERTIES ${DEFAULT_TARGET_PROPS}
|
|||
SOVERSION ${LIB_MAJOR_VERSION}
|
||||
)
|
||||
target_compile_definitions(${IMPL_TARGET}
|
||||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}"
|
||||
${CPP_DEFS})
|
||||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES $<$<BOOL:${ALSOFT_EAX}>:ALSOFT_EAX>
|
||||
"ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" ${CPP_DEFS})
|
||||
target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS})
|
||||
|
||||
if(TARGET build_version)
|
||||
|
|
@ -1462,8 +1489,7 @@ if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC"
|
|||
message(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation")
|
||||
endif()
|
||||
else()
|
||||
set_property(TARGET OpenAL APPEND_STRING PROPERTY
|
||||
LINK_FLAGS " -Wl,--output-def,OpenAL32.def")
|
||||
target_link_options(OpenAL PRIVATE "-Wl,--output-def,${PROJECT_BINARY_DIR}/OpenAL32.def")
|
||||
add_custom_command(TARGET OpenAL POST_BUILD
|
||||
COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def
|
||||
COMMAND "${CMAKE_DLLTOOL}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll
|
||||
|
|
@ -1494,7 +1520,10 @@ if(FPMATH_SET)
|
|||
message(STATUS "Building with SSE${FPMATH_SET} codegen")
|
||||
message(STATUS "")
|
||||
endif()
|
||||
|
||||
if(ALSOFT_UWP)
|
||||
message(STATUS "Building with UWP support")
|
||||
message(STATUS "")
|
||||
endif()
|
||||
if(ALSOFT_EAX)
|
||||
message(STATUS "Building with legacy EAX extension support")
|
||||
message(STATUS "")
|
||||
|
|
@ -1580,7 +1609,7 @@ if(ALSOFT_UTILS)
|
|||
target_include_directories(uhjdecoder
|
||||
PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_options(uhjdecoder PRIVATE ${C_FLAGS})
|
||||
target_link_libraries(uhjdecoder PUBLIC common
|
||||
target_link_libraries(uhjdecoder PUBLIC alcommon
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
||||
set_target_properties(uhjdecoder PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
|
|
@ -1589,7 +1618,7 @@ if(ALSOFT_UTILS)
|
|||
target_include_directories(uhjencoder
|
||||
PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_options(uhjencoder PRIVATE ${C_FLAGS})
|
||||
target_link_libraries(uhjencoder PUBLIC common
|
||||
target_link_libraries(uhjencoder PUBLIC alcommon
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
||||
set_target_properties(uhjencoder PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
endif()
|
||||
|
|
@ -1602,7 +1631,7 @@ if(ALSOFT_UTILS)
|
|||
target_compile_definitions(sofa-support PRIVATE ${CPP_DEFS})
|
||||
target_include_directories(sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_options(sofa-support PRIVATE ${C_FLAGS})
|
||||
target_link_libraries(sofa-support PUBLIC common MySOFA::MySOFA PRIVATE ${LINKER_FLAGS})
|
||||
target_link_libraries(sofa-support PUBLIC alcommon MySOFA::MySOFA PRIVATE ${LINKER_FLAGS})
|
||||
set_target_properties(sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
set(MAKEMHR_SRCS
|
||||
|
|
@ -1612,9 +1641,6 @@ if(ALSOFT_UTILS)
|
|||
utils/makemhr/loadsofa.h
|
||||
utils/makemhr/makemhr.cpp
|
||||
utils/makemhr/makemhr.h)
|
||||
if(NOT HAVE_GETOPT)
|
||||
set(MAKEMHR_SRCS ${MAKEMHR_SRCS} utils/getopt.c utils/getopt.h)
|
||||
endif()
|
||||
add_executable(makemhr ${MAKEMHR_SRCS})
|
||||
target_compile_definitions(makemhr PRIVATE ${CPP_DEFS})
|
||||
target_include_directories(makemhr
|
||||
|
|
@ -1644,22 +1670,22 @@ endif()
|
|||
|
||||
|
||||
# Add a static library with common functions used by multiple example targets
|
||||
add_library(ex-common STATIC EXCLUDE_FROM_ALL
|
||||
add_library(al-excommon STATIC EXCLUDE_FROM_ALL
|
||||
examples/common/alhelpers.c
|
||||
examples/common/alhelpers.h)
|
||||
target_compile_definitions(ex-common PUBLIC ${CPP_DEFS})
|
||||
target_include_directories(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_options(ex-common PUBLIC ${C_FLAGS})
|
||||
target_link_libraries(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB})
|
||||
set_target_properties(ex-common PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
target_compile_definitions(al-excommon PUBLIC ${CPP_DEFS})
|
||||
target_include_directories(al-excommon PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||
target_compile_options(al-excommon PUBLIC ${C_FLAGS})
|
||||
target_link_libraries(al-excommon PUBLIC OpenAL PRIVATE ${RT_LIB})
|
||||
set_target_properties(al-excommon PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
if(ALSOFT_EXAMPLES)
|
||||
add_executable(altonegen examples/altonegen.c)
|
||||
target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} ex-common ${UNICODE_FLAG})
|
||||
target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} al-excommon ${UNICODE_FLAG})
|
||||
set_target_properties(altonegen PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alrecord examples/alrecord.c)
|
||||
target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} ex-common ${UNICODE_FLAG})
|
||||
target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} al-excommon ${UNICODE_FLAG})
|
||||
set_target_properties(alrecord PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
if(ALSOFT_INSTALL_EXAMPLES)
|
||||
|
|
@ -1670,48 +1696,53 @@ if(ALSOFT_EXAMPLES)
|
|||
|
||||
if(SNDFILE_FOUND)
|
||||
add_executable(alplay examples/alplay.c)
|
||||
target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
||||
target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||
${UNICODE_FLAG})
|
||||
set_target_properties(alplay PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alstream examples/alstream.c)
|
||||
target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
||||
target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||
${UNICODE_FLAG})
|
||||
set_target_properties(alstream PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alreverb examples/alreverb.c)
|
||||
target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
||||
target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||
${UNICODE_FLAG})
|
||||
set_target_properties(alreverb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(almultireverb examples/almultireverb.c)
|
||||
target_link_libraries(almultireverb
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG})
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG})
|
||||
set_target_properties(almultireverb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(allatency examples/allatency.c)
|
||||
target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
||||
target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||
${UNICODE_FLAG})
|
||||
set_target_properties(allatency PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alhrtf examples/alhrtf.c)
|
||||
target_link_libraries(alhrtf
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common ${MATH_LIB} ${UNICODE_FLAG})
|
||||
PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG})
|
||||
set_target_properties(alhrtf PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alstreamcb examples/alstreamcb.cpp)
|
||||
target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile ex-common
|
||||
target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||
${UNICODE_FLAG})
|
||||
set_target_properties(alstreamcb PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alconvolve examples/alconvolve.c)
|
||||
target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} common SndFile::SndFile ex-common
|
||||
add_executable(aldirect examples/aldirect.cpp)
|
||||
target_link_libraries(aldirect PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon
|
||||
${UNICODE_FLAG})
|
||||
set_target_properties(aldirect PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
add_executable(alconvolve examples/alconvolve.c)
|
||||
target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} alcommon SndFile::SndFile
|
||||
al-excommon ${UNICODE_FLAG})
|
||||
set_target_properties(alconvolve PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
if(ALSOFT_INSTALL_EXAMPLES)
|
||||
set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency
|
||||
alhrtf)
|
||||
alhrtf aldirect)
|
||||
endif()
|
||||
|
||||
message(STATUS "Building SndFile example programs")
|
||||
|
|
@ -1720,7 +1751,7 @@ if(ALSOFT_EXAMPLES)
|
|||
if(SDL2_FOUND)
|
||||
add_executable(alloopback examples/alloopback.c)
|
||||
target_link_libraries(alloopback
|
||||
PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ex-common ${MATH_LIB})
|
||||
PRIVATE ${LINKER_FLAGS} SDL2::SDL2 al-excommon ${MATH_LIB})
|
||||
set_target_properties(alloopback PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
if(ALSOFT_INSTALL_EXAMPLES)
|
||||
|
|
@ -1757,7 +1788,7 @@ if(ALSOFT_EXAMPLES)
|
|||
add_executable(alffplay examples/alffplay.cpp)
|
||||
target_include_directories(alffplay PRIVATE ${FFMPEG_INCLUDE_DIRS})
|
||||
target_link_libraries(alffplay
|
||||
PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} ex-common)
|
||||
PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} al-excommon)
|
||||
set_target_properties(alffplay PROPERTIES ${DEFAULT_TARGET_PROPS})
|
||||
|
||||
if(ALSOFT_INSTALL_EXAMPLES)
|
||||
|
|
@ -1769,6 +1800,10 @@ if(ALSOFT_EXAMPLES)
|
|||
message(STATUS "")
|
||||
endif()
|
||||
|
||||
if (ALSOFT_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if(EXTRA_INSTALLS)
|
||||
install(TARGETS ${EXTRA_INSTALLS}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
|
|
|
|||
38
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")
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,33 @@ as application-agnostic behavior of the library. See alsoftrc.sample for
|
|||
available settings.
|
||||
|
||||
|
||||
Language Bindings
|
||||
-----------------
|
||||
|
||||
As a C API, OpenAL Soft can be used directly by any language that can use
|
||||
functions with C linkage. For languages that can't directly use C-style
|
||||
headers, bindings may be developed to allow code written in that language to
|
||||
call into the library. Some bindings for some languages are listed here.
|
||||
|
||||
C# Bindings:
|
||||
* [OpenTK](https://opentk.net/) includes low-level C# bindings for the OpenAL
|
||||
API, including some extensions. It also includes utility libraries for math and
|
||||
linear algebra, which can be useful for 3D calculations.
|
||||
|
||||
Java Bindings:
|
||||
* [JOAL](https://jogamp.org/joal/www/), part of the JogAmp project, includes
|
||||
Java bindings for the OpenAL API, usable with OpenAL Soft. It also includes a
|
||||
higher level Sound3D Toolkit API and utility functions to make easier use of
|
||||
OpenAL features and capabilities.
|
||||
|
||||
Python Bindings:
|
||||
* [PyOpenAL](https://pypi.org/project/PyOpenAL/). Also includes methods to play
|
||||
wave files and, with PyOgg, also Vorbis, Opus, and FLAC.
|
||||
|
||||
Other bindings for these and other languages also exist. This list will grow as
|
||||
more bindings are found.
|
||||
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,23 +1,24 @@
|
|||
#ifndef AL_AUXEFFECTSLOT_H
|
||||
#define AL_AUXEFFECTSLOT_H
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/device.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "core/effectslot.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <memory>
|
||||
#include "eax/api.h"
|
||||
#include "eax/call.h"
|
||||
#include "eax/effect.h"
|
||||
#include "eax/exception.h"
|
||||
|
|
@ -26,8 +27,6 @@
|
|||
#endif // ALSOFT_EAX
|
||||
|
||||
struct ALbuffer;
|
||||
struct ALeffect;
|
||||
struct WetBuffer;
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
class EaxFxSlotException : public EaxException {
|
||||
|
|
@ -38,30 +37,30 @@ public:
|
|||
};
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
enum class SlotState : ALenum {
|
||||
Initial = AL_INITIAL,
|
||||
Playing = AL_PLAYING,
|
||||
Stopped = AL_STOPPED,
|
||||
enum class SlotState : bool {
|
||||
Initial, Playing,
|
||||
};
|
||||
|
||||
struct ALeffectslot {
|
||||
ALuint EffectId{};
|
||||
float Gain{1.0f};
|
||||
bool AuxSendAuto{true};
|
||||
ALeffectslot *Target{nullptr};
|
||||
ALbuffer *Buffer{nullptr};
|
||||
|
||||
struct {
|
||||
struct EffectData {
|
||||
EffectSlotType Type{EffectSlotType::None};
|
||||
EffectProps Props{};
|
||||
|
||||
al::intrusive_ptr<EffectState> State;
|
||||
} Effect;
|
||||
};
|
||||
EffectData Effect;
|
||||
|
||||
bool mPropsDirty{true};
|
||||
|
||||
SlotState mState{SlotState::Initial};
|
||||
|
||||
RefCount ref{0u};
|
||||
std::atomic<ALuint> ref{0u};
|
||||
|
||||
EffectSlot *mSlot{nullptr};
|
||||
|
||||
|
|
@ -73,23 +72,23 @@ struct ALeffectslot {
|
|||
ALeffectslot& operator=(const ALeffectslot&) = delete;
|
||||
~ALeffectslot();
|
||||
|
||||
ALenum initEffect(ALenum effectType, const EffectProps &effectProps, ALCcontext *context);
|
||||
void updateProps(ALCcontext *context);
|
||||
ALenum initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps,
|
||||
ALCcontext *context);
|
||||
void updateProps(ALCcontext *context) const;
|
||||
|
||||
/* This can be new'd for the context's default effect slot. */
|
||||
DEF_NEWDEL(ALeffectslot)
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
public:
|
||||
void eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index);
|
||||
|
||||
EaxFxSlotIndexValue eax_get_index() const noexcept { return eax_fx_slot_index_; }
|
||||
const EAX50FXSLOTPROPERTIES& eax_get_eax_fx_slot() const noexcept
|
||||
[[nodiscard]] auto eax_get_index() const noexcept -> EaxFxSlotIndexValue { return eax_fx_slot_index_; }
|
||||
[[nodiscard]] auto eax_get_eax_fx_slot() const noexcept -> const EAX50FXSLOTPROPERTIES&
|
||||
{ return eax_; }
|
||||
|
||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||
bool eax_dispatch(const EaxCall& call)
|
||||
[[nodiscard]] auto eax_dispatch(const EaxCall& call) -> bool
|
||||
{ return call.is_get() ? eax_get(call) : eax_set(call); }
|
||||
|
||||
void eax_commit();
|
||||
|
|
@ -193,6 +192,17 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
struct Eax5FlagsValidator {
|
||||
void operator()(unsigned long ulFlags) const
|
||||
{
|
||||
EaxRangeValidator{}(
|
||||
"Flags",
|
||||
ulFlags,
|
||||
0UL,
|
||||
~EAX50FXSLOTFLAGS_RESERVED);
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax5OcclusionValidator {
|
||||
void operator()(long lOcclusion) const
|
||||
{
|
||||
|
|
@ -215,21 +225,13 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
struct Eax5FlagsValidator {
|
||||
void operator()(unsigned long ulFlags) const
|
||||
{
|
||||
EaxRangeValidator{}(
|
||||
"Flags",
|
||||
ulFlags,
|
||||
0UL,
|
||||
~EAX50FXSLOTFLAGS_RESERVED);
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax5AllValidator {
|
||||
void operator()(const EAX50FXSLOTPROPERTIES& all) const
|
||||
{
|
||||
Eax4AllValidator{}(static_cast<const EAX40FXSLOTPROPERTIES&>(all));
|
||||
Eax4GuidLoadEffectValidator{}(all.guidLoadEffect);
|
||||
Eax4VolumeValidator{}(all.lVolume);
|
||||
Eax4LockValidator{}(all.lLock);
|
||||
Eax5FlagsValidator{}(all.ulFlags);
|
||||
Eax5OcclusionValidator{}(all.lOcclusion);
|
||||
Eax5OcclusionLfRatioValidator{}(all.flOcclusionLFRatio);
|
||||
}
|
||||
|
|
@ -277,14 +279,14 @@ private:
|
|||
dst = src;
|
||||
}
|
||||
|
||||
constexpr bool eax4_fx_slot_is_legacy() const noexcept
|
||||
[[nodiscard]] constexpr auto eax4_fx_slot_is_legacy() const noexcept -> bool
|
||||
{ return eax_fx_slot_index_ < 2; }
|
||||
|
||||
void eax4_fx_slot_ensure_unlocked() const;
|
||||
|
||||
static ALenum eax_get_efx_effect_type(const GUID& guid);
|
||||
const GUID& eax_get_eax_default_effect_guid() const noexcept;
|
||||
long eax_get_eax_default_lock() const noexcept;
|
||||
[[nodiscard]] static auto eax_get_efx_effect_type(const GUID& guid) -> ALenum;
|
||||
[[nodiscard]] auto eax_get_eax_default_effect_guid() const noexcept -> const GUID&;
|
||||
[[nodiscard]] auto eax_get_eax_default_lock() const noexcept -> long;
|
||||
|
||||
void eax4_fx_slot_set_defaults(Eax4Props& props) noexcept;
|
||||
void eax5_fx_slot_set_defaults(Eax5Props& props) noexcept;
|
||||
|
|
@ -293,8 +295,8 @@ private:
|
|||
void eax_fx_slot_set_current_defaults();
|
||||
void eax_fx_slot_set_defaults();
|
||||
|
||||
void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) const;
|
||||
void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props) const;
|
||||
static void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props);
|
||||
static void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props);
|
||||
void eax_fx_slot_get(const EaxCall& call) const;
|
||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||
bool eax_get(const EaxCall& call);
|
||||
|
|
@ -307,7 +309,7 @@ private:
|
|||
void eax4_fx_slot_set_all(const EaxCall& call);
|
||||
void eax5_fx_slot_set_all(const EaxCall& call);
|
||||
|
||||
bool eax_fx_slot_should_update_sources() const noexcept;
|
||||
[[nodiscard]] auto eax_fx_slot_should_update_sources() const noexcept -> bool;
|
||||
|
||||
// Returns `true` if all sources should be updated, or `false` otherwise.
|
||||
bool eax4_fx_slot_set(const EaxCall& call);
|
||||
|
|
@ -365,4 +367,20 @@ EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context);
|
|||
void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot);
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
struct EffectSlotSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALeffectslot,64>*> EffectSlots{nullptr};
|
||||
|
||||
EffectSlotSubList() noexcept = default;
|
||||
EffectSlotSubList(const EffectSlotSubList&) = delete;
|
||||
EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
|
||||
: FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
|
||||
~EffectSlotSubList();
|
||||
|
||||
EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
|
||||
EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,20 +1,23 @@
|
|||
#ifndef AL_BUFFER_H
|
||||
#define AL_BUFFER_H
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/buffer_storage.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "eax/x_ram.h"
|
||||
|
||||
enum class EaxStorage : uint8_t {
|
||||
Automatic,
|
||||
Accessible,
|
||||
|
|
@ -26,7 +29,7 @@ enum class EaxStorage : uint8_t {
|
|||
struct ALbuffer : public BufferStorage {
|
||||
ALbitfieldSOFT Access{0u};
|
||||
|
||||
al::vector<al::byte,16> mDataStorage;
|
||||
al::vector<std::byte,16> mDataStorage;
|
||||
|
||||
ALuint OriginalSize{0};
|
||||
|
||||
|
|
@ -42,12 +45,14 @@ struct ALbuffer : public BufferStorage {
|
|||
ALuint mLoopEnd{0u};
|
||||
|
||||
/* Number of times buffer was attached to a source (deletion can only occur when 0) */
|
||||
RefCount ref{0u};
|
||||
std::atomic<ALuint> ref{0u};
|
||||
|
||||
/* Self ID */
|
||||
ALuint id{0};
|
||||
|
||||
DISABLE_ALLOC()
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
DISABLE_ALLOC
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
EaxStorage eax_x_ram_mode{EaxStorage::Automatic};
|
||||
|
|
@ -55,4 +60,19 @@ struct ALbuffer : public BufferStorage {
|
|||
#endif // ALSOFT_EAX
|
||||
};
|
||||
|
||||
struct BufferSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALbuffer,64>*> Buffers{nullptr};
|
||||
|
||||
BufferSubList() noexcept = default;
|
||||
BufferSubList(const BufferSubList&) = delete;
|
||||
BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; }
|
||||
~BufferSubList();
|
||||
|
||||
BufferSubList& operator=(const BufferSubList&) = delete;
|
||||
BufferSubList& operator=(BufferSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
618
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 <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#ifdef _WIN32
|
||||
#include <guiddef.h>
|
||||
#endif
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
|
||||
#ifndef GUID_DEFINED
|
||||
#define GUID_DEFINED
|
||||
typedef struct _GUID {
|
||||
#ifndef _WIN32
|
||||
using GUID = struct _GUID { /* NOLINT(*-reserved-identifier) */
|
||||
std::uint32_t Data1;
|
||||
std::uint16_t Data2;
|
||||
std::uint16_t Data3;
|
||||
std::uint8_t Data4[8];
|
||||
} GUID;
|
||||
std::array<std::uint8_t,8> Data4;
|
||||
};
|
||||
|
||||
#ifndef _SYS_GUID_OPERATOR_EQ_
|
||||
#define _SYS_GUID_OPERATOR_EQ_
|
||||
inline bool operator==(const GUID& lhs, const GUID& rhs) noexcept
|
||||
{ return std::memcmp(&lhs, &rhs, sizeof(GUID)) == 0; }
|
||||
|
||||
inline bool operator!=(const GUID& lhs, const GUID& rhs) noexcept
|
||||
{ return !(lhs == rhs); }
|
||||
#endif // _SYS_GUID_OPERATOR_EQ_
|
||||
#endif // GUID_DEFINED
|
||||
#endif // _WIN32
|
||||
|
||||
#define DECL_EQOP(T, ...) \
|
||||
[[nodiscard]] auto get_members() const noexcept { return std::forward_as_tuple(__VA_ARGS__); } \
|
||||
[[nodiscard]] friend bool operator==(const T &lhs, const T &rhs) noexcept \
|
||||
{ return lhs.get_members() == rhs.get_members(); } \
|
||||
[[nodiscard]] friend bool operator!=(const T &lhs, const T &rhs) noexcept \
|
||||
{ return !(lhs == rhs); }
|
||||
|
||||
extern const GUID DSPROPSETID_EAX_ReverbProperties;
|
||||
|
||||
|
|
@ -276,11 +281,15 @@ struct EAXVECTOR {
|
|||
float x;
|
||||
float y;
|
||||
float z;
|
||||
[[nodiscard]]
|
||||
auto get_members() const noexcept { return std::forward_as_tuple(x, y, z); }
|
||||
}; // EAXVECTOR
|
||||
|
||||
[[nodiscard]]
|
||||
inline bool operator==(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
|
||||
{ return std::memcmp(&lhs, &rhs, sizeof(EAXVECTOR)) == 0; }
|
||||
{ return lhs.get_members() == rhs.get_members(); }
|
||||
|
||||
[[nodiscard]]
|
||||
inline bool operator!=(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept
|
||||
{ return !(lhs == rhs); }
|
||||
|
||||
|
|
@ -361,6 +370,7 @@ constexpr auto EAXCONTEXT_MINMACROFXFACTOR = 0.0F;
|
|||
constexpr auto EAXCONTEXT_MAXMACROFXFACTOR = 1.0F;
|
||||
constexpr auto EAXCONTEXT_DEFAULTMACROFXFACTOR = 0.0F;
|
||||
|
||||
constexpr auto EAXCONTEXT_DEFAULTLASTERROR = EAX_OK;
|
||||
|
||||
extern const GUID EAXPROPERTYID_EAX40_FXSlot0;
|
||||
extern const GUID EAXPROPERTYID_EAX50_FXSlot0;
|
||||
|
|
@ -613,7 +623,7 @@ struct EAX30SOURCEPROPERTIES {
|
|||
float flOcclusionLFRatio; // occlusion low-frequency level re. main control
|
||||
float flOcclusionRoomRatio; // relative occlusion control for room effect
|
||||
float flOcclusionDirectRatio; // relative occlusion control for direct path
|
||||
long lExclusion; // main exlusion control (attenuation at high frequencies)
|
||||
long lExclusion; // main exclusion control (attenuation at high frequencies)
|
||||
float flExclusionLFRatio; // exclusion low-frequency level re. main control
|
||||
long lOutsideVolumeHF; // outside sound cone level at high frequencies
|
||||
float flDopplerFactor; // like DS3D flDopplerFactor but per source
|
||||
|
|
@ -653,11 +663,11 @@ struct EAXSPEAKERLEVELPROPERTIES {
|
|||
}; // EAXSPEAKERLEVELPROPERTIES
|
||||
|
||||
struct EAX40ACTIVEFXSLOTS {
|
||||
GUID guidActiveFXSlots[EAX40_MAX_ACTIVE_FXSLOTS];
|
||||
std::array<GUID,EAX40_MAX_ACTIVE_FXSLOTS> guidActiveFXSlots;
|
||||
}; // EAX40ACTIVEFXSLOTS
|
||||
|
||||
struct EAX50ACTIVEFXSLOTS {
|
||||
GUID guidActiveFXSlots[EAX50_MAX_ACTIVE_FXSLOTS];
|
||||
std::array<GUID,EAX50_MAX_ACTIVE_FXSLOTS> guidActiveFXSlots;
|
||||
}; // EAX50ACTIVEFXSLOTS
|
||||
|
||||
// Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property.
|
||||
|
|
@ -836,6 +846,11 @@ struct EAXREVERBPROPERTIES {
|
|||
float flLFReference; // reference low frequency
|
||||
float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect
|
||||
unsigned long ulFlags; // modifies the behavior of properties
|
||||
DECL_EQOP(EAXREVERBPROPERTIES, ulEnvironment, flEnvironmentSize, flEnvironmentDiffusion, lRoom,
|
||||
lRoomHF, lRoomLF, flDecayTime, flDecayHFRatio, flDecayLFRatio, lReflections,
|
||||
flReflectionsDelay, vReflectionsPan, lReverb, flReverbDelay, vReverbPan, flEchoTime,
|
||||
flEchoDepth, flModulationTime, flModulationDepth, flAirAbsorptionHF, flHFReference,
|
||||
flLFReference, flRoomRolloffFactor, ulFlags)
|
||||
}; // EAXREVERBPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -965,6 +980,7 @@ enum EAXAGCCOMPRESSOR_PROPERTY : unsigned int {
|
|||
|
||||
struct EAXAGCCOMPRESSORPROPERTIES {
|
||||
unsigned long ulOnOff; // Switch Compressor on or off
|
||||
DECL_EQOP(EAXAGCCOMPRESSORPROPERTIES, ulOnOff)
|
||||
}; // EAXAGCCOMPRESSORPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -991,6 +1007,7 @@ struct EAXAUTOWAHPROPERTIES {
|
|||
float flReleaseTime; // Release time (seconds)
|
||||
long lResonance; // Resonance (mB)
|
||||
long lPeakLevel; // Peak level (mB)
|
||||
DECL_EQOP(EAXAUTOWAHPROPERTIES, flAttackTime, flReleaseTime, lResonance, lPeakLevel)
|
||||
}; // EAXAUTOWAHPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1038,6 +1055,7 @@ struct EAXCHORUSPROPERTIES {
|
|||
float flDepth; // Depth (0 to 1)
|
||||
float flFeedback; // Feedback (-1 to 1)
|
||||
float flDelay; // Delay (seconds)
|
||||
DECL_EQOP(EAXCHORUSPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay)
|
||||
}; // EAXCHORUSPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1086,6 +1104,7 @@ struct EAXDISTORTIONPROPERTIES {
|
|||
float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz)
|
||||
float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz)
|
||||
float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz)
|
||||
DECL_EQOP(EAXDISTORTIONPROPERTIES, flEdge, lGain, flLowPassCutOff, flEQCenter, flEQBandwidth)
|
||||
}; // EAXDISTORTIONPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1130,6 +1149,7 @@ struct EAXECHOPROPERTIES {
|
|||
float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1)
|
||||
float flFeedback; // Controls the duration of echo repetition (0 to 1)
|
||||
float flSpread; // Controls the left-right spread of the echoes
|
||||
DECL_EQOP(EAXECHOPROPERTIES, flDelay, flLRDelay, flDamping, flFeedback, flSpread)
|
||||
}; // EAXECHOPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1184,6 +1204,8 @@ struct EAXEQUALIZERPROPERTIES {
|
|||
float flMid2Width; // (octaves)
|
||||
long lHighGain; // (mB)
|
||||
float flHighCutOff; // (Hz)
|
||||
DECL_EQOP(EAXEQUALIZERPROPERTIES, lLowGain, flLowCutOff, lMid1Gain, flMid1Center, flMid1Width,
|
||||
lMid2Gain, flMid2Center, flMid2Width, lHighGain, flHighCutOff)
|
||||
}; // EAXEQUALIZERPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1255,6 +1277,7 @@ struct EAXFLANGERPROPERTIES {
|
|||
float flDepth; // Depth (0 to 1)
|
||||
float flFeedback; // Feedback (0 to 1)
|
||||
float flDelay; // Delay (seconds)
|
||||
DECL_EQOP(EAXFLANGERPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay)
|
||||
}; // EAXFLANGERPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1305,6 +1328,7 @@ struct EAXFREQUENCYSHIFTERPROPERTIES {
|
|||
float flFrequency; // (Hz)
|
||||
unsigned long ulLeftDirection; // see enum above
|
||||
unsigned long ulRightDirection; // see enum above
|
||||
DECL_EQOP(EAXFREQUENCYSHIFTERPROPERTIES, flFrequency, ulLeftDirection, ulRightDirection)
|
||||
}; // EAXFREQUENCYSHIFTERPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1383,6 +1407,8 @@ struct EAXVOCALMORPHERPROPERTIES {
|
|||
long lPhonemeBCoarseTuning; // (semitones)
|
||||
unsigned long ulWaveform; // Waveform selector - see enum above
|
||||
float flRate; // (Hz)
|
||||
DECL_EQOP(EAXVOCALMORPHERPROPERTIES, ulPhonemeA, lPhonemeACoarseTuning, ulPhonemeB,
|
||||
lPhonemeBCoarseTuning, ulWaveform, flRate)
|
||||
}; // EAXVOCALMORPHERPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1425,6 +1451,7 @@ enum EAXPITCHSHIFTER_PROPERTY : unsigned int {
|
|||
struct EAXPITCHSHIFTERPROPERTIES {
|
||||
long lCoarseTune; // Amount of pitch shift (semitones)
|
||||
long lFineTune; // Amount of pitch shift (cents)
|
||||
DECL_EQOP(EAXPITCHSHIFTERPROPERTIES, lCoarseTune, lFineTune)
|
||||
}; // EAXPITCHSHIFTERPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1460,6 +1487,7 @@ struct EAXRINGMODULATORPROPERTIES {
|
|||
float flFrequency; // Frequency of modulation (Hz)
|
||||
float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz)
|
||||
unsigned long ulWaveform; // Waveform selector - see enum above
|
||||
DECL_EQOP(EAXRINGMODULATORPROPERTIES, flFrequency, flHighPassCutOff, ulWaveform)
|
||||
}; // EAXRINGMODULATORPROPERTIES
|
||||
|
||||
|
||||
|
|
@ -1490,4 +1518,5 @@ using LPEAXGET = ALenum(AL_APIENTRY*)(
|
|||
ALvoid* property_buffer,
|
||||
ALuint property_size);
|
||||
|
||||
#undef DECL_EQOP
|
||||
#endif // !EAX_API_INCLUDED
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ EaxCall::EaxCall(
|
|||
ALuint property_source_id,
|
||||
ALvoid* property_buffer,
|
||||
ALuint property_size)
|
||||
: mCallType{type}, mVersion{0}, mPropertySetId{EaxCallPropertySetId::none}
|
||||
, mIsDeferred{(property_id & deferred_flag) != 0}
|
||||
: mCallType{type}, mIsDeferred{(property_id & deferred_flag) != 0}
|
||||
, mPropertyId{property_id & ~deferred_flag}, mPropertySourceId{property_source_id}
|
||||
, mPropertyBuffer{property_buffer}, mPropertyBufferSize{property_size}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,16 +31,16 @@ public:
|
|||
ALvoid* property_buffer,
|
||||
ALuint property_size);
|
||||
|
||||
bool is_get() const noexcept { return mCallType == EaxCallType::get; }
|
||||
bool is_deferred() const noexcept { return mIsDeferred; }
|
||||
int get_version() const noexcept { return mVersion; }
|
||||
EaxCallPropertySetId get_property_set_id() const noexcept { return mPropertySetId; }
|
||||
ALuint get_property_id() const noexcept { return mPropertyId; }
|
||||
ALuint get_property_al_name() const noexcept { return mPropertySourceId; }
|
||||
EaxFxSlotIndex get_fx_slot_index() const noexcept { return mFxSlotIndex; }
|
||||
[[nodiscard]] auto is_get() const noexcept -> bool { return mCallType == EaxCallType::get; }
|
||||
[[nodiscard]] auto is_deferred() const noexcept -> bool { return mIsDeferred; }
|
||||
[[nodiscard]] auto get_version() const noexcept -> int { return mVersion; }
|
||||
[[nodiscard]] auto get_property_set_id() const noexcept -> EaxCallPropertySetId { return mPropertySetId; }
|
||||
[[nodiscard]] auto get_property_id() const noexcept -> ALuint { return mPropertyId; }
|
||||
[[nodiscard]] auto get_property_al_name() const noexcept -> ALuint { return mPropertySourceId; }
|
||||
[[nodiscard]] auto get_fx_slot_index() const noexcept -> EaxFxSlotIndex { return mFxSlotIndex; }
|
||||
|
||||
template<typename TException, typename TValue>
|
||||
TValue& get_value() const
|
||||
[[nodiscard]] auto get_value() const -> TValue&
|
||||
{
|
||||
if(mPropertyBufferSize < sizeof(TValue))
|
||||
fail_too_small();
|
||||
|
|
@ -49,32 +49,32 @@ public:
|
|||
}
|
||||
|
||||
template<typename TValue>
|
||||
al::span<TValue> get_values(size_t max_count) const
|
||||
[[nodiscard]] auto get_values(size_t max_count) const -> al::span<TValue>
|
||||
{
|
||||
if(max_count == 0 || mPropertyBufferSize < sizeof(TValue))
|
||||
fail_too_small();
|
||||
|
||||
const auto count = minz(mPropertyBufferSize / sizeof(TValue), max_count);
|
||||
return al::as_span(static_cast<TValue*>(mPropertyBuffer), count);
|
||||
const auto count = std::min(mPropertyBufferSize/sizeof(TValue), max_count);
|
||||
return {static_cast<TValue*>(mPropertyBuffer), count};
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
al::span<TValue> get_values() const
|
||||
[[nodiscard]] auto get_values() const -> al::span<TValue>
|
||||
{
|
||||
return get_values<TValue>(~size_t{});
|
||||
return get_values<TValue>(~0_uz);
|
||||
}
|
||||
|
||||
template<typename TException, typename TValue>
|
||||
void set_value(const TValue& value) const
|
||||
auto set_value(const TValue& value) const -> void
|
||||
{
|
||||
get_value<TException, TValue>() = value;
|
||||
}
|
||||
|
||||
private:
|
||||
const EaxCallType mCallType;
|
||||
int mVersion;
|
||||
EaxFxSlotIndex mFxSlotIndex;
|
||||
EaxCallPropertySetId mPropertySetId;
|
||||
int mVersion{};
|
||||
EaxFxSlotIndex mFxSlotIndex{};
|
||||
EaxCallPropertySetId mPropertySetId{EaxCallPropertySetId::none};
|
||||
bool mIsDeferred;
|
||||
|
||||
const ALuint mPropertyId;
|
||||
|
|
|
|||
|
|
@ -4,60 +4,56 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
|
||||
#include "alnumeric.h"
|
||||
#include "AL/al.h"
|
||||
#include "AL/alext.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "call.h"
|
||||
|
||||
struct EaxEffectErrorMessages
|
||||
{
|
||||
struct EaxEffectErrorMessages {
|
||||
static constexpr auto unknown_property_id() noexcept { return "Unknown property id."; }
|
||||
static constexpr auto unknown_version() noexcept { return "Unknown version."; }
|
||||
}; // EaxEffectErrorMessages
|
||||
|
||||
/* TODO: Use std::variant (C++17). */
|
||||
enum class EaxEffectType {
|
||||
None, Reverb, Chorus, Autowah, Compressor, Distortion, Echo, Equalizer, Flanger,
|
||||
FrequencyShifter, Modulator, PitchShifter, VocalMorpher
|
||||
};
|
||||
struct EaxEffectProps {
|
||||
EaxEffectType mType;
|
||||
union {
|
||||
EAXREVERBPROPERTIES mReverb;
|
||||
EAXCHORUSPROPERTIES mChorus;
|
||||
EAXAUTOWAHPROPERTIES mAutowah;
|
||||
EAXAGCCOMPRESSORPROPERTIES mCompressor;
|
||||
EAXDISTORTIONPROPERTIES mDistortion;
|
||||
EAXECHOPROPERTIES mEcho;
|
||||
EAXEQUALIZERPROPERTIES mEqualizer;
|
||||
EAXFLANGERPROPERTIES mFlanger;
|
||||
EAXFREQUENCYSHIFTERPROPERTIES mFrequencyShifter;
|
||||
EAXRINGMODULATORPROPERTIES mModulator;
|
||||
EAXPITCHSHIFTERPROPERTIES mPitchShifter;
|
||||
EAXVOCALMORPHERPROPERTIES mVocalMorpher;
|
||||
};
|
||||
};
|
||||
using EaxEffectProps = std::variant<std::monostate,
|
||||
EAXREVERBPROPERTIES,
|
||||
EAXCHORUSPROPERTIES,
|
||||
EAXAUTOWAHPROPERTIES,
|
||||
EAXAGCCOMPRESSORPROPERTIES,
|
||||
EAXDISTORTIONPROPERTIES,
|
||||
EAXECHOPROPERTIES,
|
||||
EAXEQUALIZERPROPERTIES,
|
||||
EAXFLANGERPROPERTIES,
|
||||
EAXFREQUENCYSHIFTERPROPERTIES,
|
||||
EAXRINGMODULATORPROPERTIES,
|
||||
EAXPITCHSHIFTERPROPERTIES,
|
||||
EAXVOCALMORPHERPROPERTIES>;
|
||||
|
||||
template<typename... Ts>
|
||||
struct overloaded : Ts... { using Ts::operator()...; };
|
||||
|
||||
template<typename... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props)
|
||||
{
|
||||
switch(props.mType)
|
||||
{
|
||||
case EaxEffectType::None: break;
|
||||
case EaxEffectType::Reverb: return AL_EFFECT_EAXREVERB;
|
||||
case EaxEffectType::Chorus: return AL_EFFECT_CHORUS;
|
||||
case EaxEffectType::Autowah: return AL_EFFECT_AUTOWAH;
|
||||
case EaxEffectType::Compressor: return AL_EFFECT_COMPRESSOR;
|
||||
case EaxEffectType::Distortion: return AL_EFFECT_DISTORTION;
|
||||
case EaxEffectType::Echo: return AL_EFFECT_ECHO;
|
||||
case EaxEffectType::Equalizer: return AL_EFFECT_EQUALIZER;
|
||||
case EaxEffectType::Flanger: return AL_EFFECT_FLANGER;
|
||||
case EaxEffectType::FrequencyShifter: return AL_EFFECT_FREQUENCY_SHIFTER;
|
||||
case EaxEffectType::Modulator: return AL_EFFECT_RING_MODULATOR;
|
||||
case EaxEffectType::PitchShifter: return AL_EFFECT_PITCH_SHIFTER;
|
||||
case EaxEffectType::VocalMorpher: return AL_EFFECT_VOCAL_MORPHER;
|
||||
}
|
||||
return AL_EFFECT_NULL;
|
||||
return std::visit(overloaded{
|
||||
[](const std::monostate&) noexcept { return AL_EFFECT_NULL; },
|
||||
[](const EAXREVERBPROPERTIES&) noexcept { return AL_EFFECT_EAXREVERB; },
|
||||
[](const EAXCHORUSPROPERTIES&) noexcept { return AL_EFFECT_CHORUS; },
|
||||
[](const EAXAUTOWAHPROPERTIES&) noexcept { return AL_EFFECT_AUTOWAH; },
|
||||
[](const EAXAGCCOMPRESSORPROPERTIES&) noexcept { return AL_EFFECT_COMPRESSOR; },
|
||||
[](const EAXDISTORTIONPROPERTIES&) noexcept { return AL_EFFECT_DISTORTION; },
|
||||
[](const EAXECHOPROPERTIES&) noexcept { return AL_EFFECT_ECHO; },
|
||||
[](const EAXEQUALIZERPROPERTIES&) noexcept { return AL_EFFECT_EQUALIZER; },
|
||||
[](const EAXFLANGERPROPERTIES&) noexcept { return AL_EFFECT_FLANGER; },
|
||||
[](const EAXFREQUENCYSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_FREQUENCY_SHIFTER; },
|
||||
[](const EAXRINGMODULATORPROPERTIES&) noexcept { return AL_EFFECT_RING_MODULATOR; },
|
||||
[](const EAXPITCHSHIFTERPROPERTIES&) noexcept { return AL_EFFECT_PITCH_SHIFTER; },
|
||||
[](const EAXVOCALMORPHERPROPERTIES&) noexcept { return AL_EFFECT_VOCAL_MORPHER; }
|
||||
}, props);
|
||||
}
|
||||
|
||||
struct EaxReverbCommitter {
|
||||
|
|
@ -105,7 +101,6 @@ struct EaxReverbCommitter {
|
|||
bool commit(const EAX_REVERBPROPERTIES &props);
|
||||
bool commit(const EAX20LISTENERPROPERTIES &props);
|
||||
bool commit(const EAXREVERBPROPERTIES &props);
|
||||
bool commit(const EaxEffectProps &props);
|
||||
|
||||
static void SetDefaults(EAX_REVERBPROPERTIES &props);
|
||||
static void SetDefaults(EAX20LISTENERPROPERTIES &props);
|
||||
|
|
@ -115,16 +110,13 @@ struct EaxReverbCommitter {
|
|||
static void Get(const EaxCall &call, const EAX_REVERBPROPERTIES &props);
|
||||
static void Get(const EaxCall &call, const EAX20LISTENERPROPERTIES &props);
|
||||
static void Get(const EaxCall &call, const EAXREVERBPROPERTIES &props);
|
||||
static void Get(const EaxCall &call, const EaxEffectProps &props);
|
||||
|
||||
static void Set(const EaxCall &call, EAX_REVERBPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXREVERBPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EaxEffectProps &props);
|
||||
|
||||
static void translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept;
|
||||
static void translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept;
|
||||
static void translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept;
|
||||
static void translate(const EAX_REVERBPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept;
|
||||
static void translate(const EAX20LISTENERPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
|
@ -149,51 +141,137 @@ struct EaxCommitter {
|
|||
[[noreturn]] static void fail(const char *message);
|
||||
[[noreturn]] static void fail_unknown_property_id()
|
||||
{ fail(EaxEffectErrorMessages::unknown_property_id()); }
|
||||
|
||||
bool commit(const EaxEffectProps &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EaxEffectProps &props);
|
||||
static void Set(const EaxCall &call, EaxEffectProps &props);
|
||||
};
|
||||
|
||||
struct EaxAutowahCommitter : public EaxCommitter<EaxAutowahCommitter> {
|
||||
using EaxCommitter<EaxAutowahCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXAUTOWAHPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props);
|
||||
};
|
||||
struct EaxChorusCommitter : public EaxCommitter<EaxChorusCommitter> {
|
||||
using EaxCommitter<EaxChorusCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXCHORUSPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXCHORUSPROPERTIES &props);
|
||||
};
|
||||
struct EaxCompressorCommitter : public EaxCommitter<EaxCompressorCommitter> {
|
||||
using EaxCommitter<EaxCompressorCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXAGCCOMPRESSORPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props);
|
||||
};
|
||||
struct EaxDistortionCommitter : public EaxCommitter<EaxDistortionCommitter> {
|
||||
using EaxCommitter<EaxDistortionCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXDISTORTIONPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props);
|
||||
};
|
||||
struct EaxEchoCommitter : public EaxCommitter<EaxEchoCommitter> {
|
||||
using EaxCommitter<EaxEchoCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXECHOPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXECHOPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXECHOPROPERTIES &props);
|
||||
};
|
||||
struct EaxEqualizerCommitter : public EaxCommitter<EaxEqualizerCommitter> {
|
||||
using EaxCommitter<EaxEqualizerCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXEQUALIZERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props);
|
||||
};
|
||||
struct EaxFlangerCommitter : public EaxCommitter<EaxFlangerCommitter> {
|
||||
using EaxCommitter<EaxFlangerCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXFLANGERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXFLANGERPROPERTIES &props);
|
||||
};
|
||||
struct EaxFrequencyShifterCommitter : public EaxCommitter<EaxFrequencyShifterCommitter> {
|
||||
using EaxCommitter<EaxFrequencyShifterCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXFREQUENCYSHIFTERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props);
|
||||
};
|
||||
struct EaxModulatorCommitter : public EaxCommitter<EaxModulatorCommitter> {
|
||||
using EaxCommitter<EaxModulatorCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXRINGMODULATORPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props);
|
||||
};
|
||||
struct EaxPitchShifterCommitter : public EaxCommitter<EaxPitchShifterCommitter> {
|
||||
using EaxCommitter<EaxPitchShifterCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXPITCHSHIFTERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props);
|
||||
};
|
||||
struct EaxVocalMorpherCommitter : public EaxCommitter<EaxVocalMorpherCommitter> {
|
||||
using EaxCommitter<EaxVocalMorpherCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const EAXVOCALMORPHERPROPERTIES &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props);
|
||||
static void Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props);
|
||||
};
|
||||
struct EaxNullCommitter : public EaxCommitter<EaxNullCommitter> {
|
||||
using EaxCommitter<EaxNullCommitter>::EaxCommitter;
|
||||
|
||||
bool commit(const std::monostate &props);
|
||||
|
||||
static void SetDefaults(EaxEffectProps &props);
|
||||
static void Get(const EaxCall &call, const std::monostate &props);
|
||||
static void Set(const EaxCall &call, std::monostate &props);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct CommitterFromProps { };
|
||||
|
||||
template<> struct CommitterFromProps<std::monostate> { using type = EaxNullCommitter; };
|
||||
template<> struct CommitterFromProps<EAXREVERBPROPERTIES> { using type = EaxReverbCommitter; };
|
||||
template<> struct CommitterFromProps<EAXCHORUSPROPERTIES> { using type = EaxChorusCommitter; };
|
||||
template<> struct CommitterFromProps<EAXAGCCOMPRESSORPROPERTIES> { using type = EaxCompressorCommitter; };
|
||||
template<> struct CommitterFromProps<EAXAUTOWAHPROPERTIES> { using type = EaxAutowahCommitter; };
|
||||
template<> struct CommitterFromProps<EAXDISTORTIONPROPERTIES> { using type = EaxDistortionCommitter; };
|
||||
template<> struct CommitterFromProps<EAXECHOPROPERTIES> { using type = EaxEchoCommitter; };
|
||||
template<> struct CommitterFromProps<EAXEQUALIZERPROPERTIES> { using type = EaxEqualizerCommitter; };
|
||||
template<> struct CommitterFromProps<EAXFLANGERPROPERTIES> { using type = EaxFlangerCommitter; };
|
||||
template<> struct CommitterFromProps<EAXFREQUENCYSHIFTERPROPERTIES> { using type = EaxFrequencyShifterCommitter; };
|
||||
template<> struct CommitterFromProps<EAXRINGMODULATORPROPERTIES> { using type = EaxModulatorCommitter; };
|
||||
template<> struct CommitterFromProps<EAXPITCHSHIFTERPROPERTIES> { using type = EaxPitchShifterCommitter; };
|
||||
template<> struct CommitterFromProps<EAXVOCALMORPHERPROPERTIES> { using type = EaxVocalMorpherCommitter; };
|
||||
|
||||
template<typename T>
|
||||
using CommitterFor = typename CommitterFromProps<std::remove_cv_t<std::remove_reference_t<T>>>::type;
|
||||
|
||||
|
||||
class EaxEffect {
|
||||
public:
|
||||
|
|
@ -238,51 +316,39 @@ public:
|
|||
State4 state5_{};
|
||||
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
void call_set_defaults(Args&& ...args)
|
||||
{ return T::SetDefaults(std::forward<Args>(args)...); }
|
||||
|
||||
void call_set_defaults(const ALenum altype, EaxEffectProps &props)
|
||||
static void call_set_defaults(const ALenum altype, EaxEffectProps &props)
|
||||
{
|
||||
if(altype == AL_EFFECT_EAXREVERB)
|
||||
return call_set_defaults<EaxReverbCommitter>(props);
|
||||
if(altype == AL_EFFECT_CHORUS)
|
||||
return call_set_defaults<EaxChorusCommitter>(props);
|
||||
if(altype == AL_EFFECT_AUTOWAH)
|
||||
return call_set_defaults<EaxAutowahCommitter>(props);
|
||||
if(altype == AL_EFFECT_COMPRESSOR)
|
||||
return call_set_defaults<EaxCompressorCommitter>(props);
|
||||
if(altype == AL_EFFECT_DISTORTION)
|
||||
return call_set_defaults<EaxDistortionCommitter>(props);
|
||||
if(altype == AL_EFFECT_ECHO)
|
||||
return call_set_defaults<EaxEchoCommitter>(props);
|
||||
if(altype == AL_EFFECT_EQUALIZER)
|
||||
return call_set_defaults<EaxEqualizerCommitter>(props);
|
||||
if(altype == AL_EFFECT_FLANGER)
|
||||
return call_set_defaults<EaxFlangerCommitter>(props);
|
||||
if(altype == AL_EFFECT_FREQUENCY_SHIFTER)
|
||||
return call_set_defaults<EaxFrequencyShifterCommitter>(props);
|
||||
if(altype == AL_EFFECT_RING_MODULATOR)
|
||||
return call_set_defaults<EaxModulatorCommitter>(props);
|
||||
if(altype == AL_EFFECT_PITCH_SHIFTER)
|
||||
return call_set_defaults<EaxPitchShifterCommitter>(props);
|
||||
if(altype == AL_EFFECT_VOCAL_MORPHER)
|
||||
return call_set_defaults<EaxVocalMorpherCommitter>(props);
|
||||
return call_set_defaults<EaxNullCommitter>(props);
|
||||
switch(altype)
|
||||
{
|
||||
case AL_EFFECT_EAXREVERB: return EaxReverbCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_CHORUS: return EaxChorusCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_AUTOWAH: return EaxAutowahCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_COMPRESSOR: return EaxCompressorCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_DISTORTION: return EaxDistortionCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_ECHO: return EaxEchoCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_EQUALIZER: return EaxEqualizerCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_FLANGER: return EaxFlangerCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_FREQUENCY_SHIFTER: return EaxFrequencyShifterCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_RING_MODULATOR: return EaxModulatorCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_PITCH_SHIFTER: return EaxPitchShifterCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_VOCAL_MORPHER: return EaxVocalMorpherCommitter::SetDefaults(props);
|
||||
case AL_EFFECT_NULL: break;
|
||||
}
|
||||
return EaxNullCommitter::SetDefaults(props);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void init()
|
||||
{
|
||||
call_set_defaults<EaxReverbCommitter>(state1_.d);
|
||||
EaxReverbCommitter::SetDefaults(state1_.d);
|
||||
state1_.i = state1_.d;
|
||||
call_set_defaults<EaxReverbCommitter>(state2_.d);
|
||||
EaxReverbCommitter::SetDefaults(state2_.d);
|
||||
state2_.i = state2_.d;
|
||||
call_set_defaults<EaxReverbCommitter>(state3_.d);
|
||||
EaxReverbCommitter::SetDefaults(state3_.d);
|
||||
state3_.i = state3_.d;
|
||||
call_set_defaults<T>(state4_.d);
|
||||
T::SetDefaults(state4_.d);
|
||||
state4_.i = state4_.d;
|
||||
call_set_defaults<T>(state5_.d);
|
||||
T::SetDefaults(state5_.d);
|
||||
state5_.i = state5_.d;
|
||||
}
|
||||
|
||||
|
|
@ -290,9 +356,9 @@ public:
|
|||
{
|
||||
switch(eax_version)
|
||||
{
|
||||
case 1: call_set_defaults<EaxReverbCommitter>(state1_.d); break;
|
||||
case 2: call_set_defaults<EaxReverbCommitter>(state2_.d); break;
|
||||
case 3: call_set_defaults<EaxReverbCommitter>(state3_.d); break;
|
||||
case 1: EaxReverbCommitter::SetDefaults(state1_.d); break;
|
||||
case 2: EaxReverbCommitter::SetDefaults(state2_.d); break;
|
||||
case 3: EaxReverbCommitter::SetDefaults(state3_.d); break;
|
||||
case 4: call_set_defaults(altype, state4_.d); break;
|
||||
case 5: call_set_defaults(altype, state5_.d); break;
|
||||
}
|
||||
|
|
@ -300,47 +366,20 @@ public:
|
|||
}
|
||||
|
||||
|
||||
#define EAXCALL(T, Callable, ...) \
|
||||
if(T == EaxEffectType::Reverb) \
|
||||
return Callable<EaxReverbCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Chorus) \
|
||||
return Callable<EaxChorusCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Autowah) \
|
||||
return Callable<EaxAutowahCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Compressor) \
|
||||
return Callable<EaxCompressorCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Distortion) \
|
||||
return Callable<EaxDistortionCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Echo) \
|
||||
return Callable<EaxEchoCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Equalizer) \
|
||||
return Callable<EaxEqualizerCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Flanger) \
|
||||
return Callable<EaxFlangerCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::FrequencyShifter) \
|
||||
return Callable<EaxFrequencyShifterCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::Modulator) \
|
||||
return Callable<EaxModulatorCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::PitchShifter) \
|
||||
return Callable<EaxPitchShifterCommitter>(__VA_ARGS__); \
|
||||
if(T == EaxEffectType::VocalMorpher) \
|
||||
return Callable<EaxVocalMorpherCommitter>(__VA_ARGS__); \
|
||||
return Callable<EaxNullCommitter>(__VA_ARGS__)
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
static void call_set(Args&& ...args)
|
||||
{ return T::Set(std::forward<Args>(args)...); }
|
||||
|
||||
static void call_set(const EaxCall &call, EaxEffectProps &props)
|
||||
{ EAXCALL(props.mType, call_set, call, props); }
|
||||
{
|
||||
return std::visit([&](auto &arg)
|
||||
{ return CommitterFor<decltype(arg)>::Set(call, arg); },
|
||||
props);
|
||||
}
|
||||
|
||||
void set(const EaxCall &call)
|
||||
{
|
||||
switch(call.get_version())
|
||||
{
|
||||
case 1: call_set<EaxReverbCommitter>(call, state1_.d); break;
|
||||
case 2: call_set<EaxReverbCommitter>(call, state2_.d); break;
|
||||
case 3: call_set<EaxReverbCommitter>(call, state3_.d); break;
|
||||
case 1: EaxReverbCommitter::Set(call, state1_.d); break;
|
||||
case 2: EaxReverbCommitter::Set(call, state2_.d); break;
|
||||
case 3: EaxReverbCommitter::Set(call, state3_.d); break;
|
||||
case 4: call_set(call, state4_.d); break;
|
||||
case 5: call_set(call, state5_.d); break;
|
||||
}
|
||||
|
|
@ -348,32 +387,32 @@ public:
|
|||
}
|
||||
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
static void call_get(Args&& ...args)
|
||||
{ return T::Get(std::forward<Args>(args)...); }
|
||||
|
||||
static void call_get(const EaxCall &call, const EaxEffectProps &props)
|
||||
{ EAXCALL(props.mType, call_get, call, props); }
|
||||
{
|
||||
return std::visit([&](auto &arg)
|
||||
{ return CommitterFor<decltype(arg)>::Get(call, arg); },
|
||||
props);
|
||||
}
|
||||
|
||||
void get(const EaxCall &call)
|
||||
void get(const EaxCall &call) const
|
||||
{
|
||||
switch(call.get_version())
|
||||
{
|
||||
case 1: call_get<EaxReverbCommitter>(call, state1_.d); break;
|
||||
case 2: call_get<EaxReverbCommitter>(call, state2_.d); break;
|
||||
case 3: call_get<EaxReverbCommitter>(call, state3_.d); break;
|
||||
case 1: EaxReverbCommitter::Get(call, state1_.d); break;
|
||||
case 2: EaxReverbCommitter::Get(call, state2_.d); break;
|
||||
case 3: EaxReverbCommitter::Get(call, state3_.d); break;
|
||||
case 4: call_get(call, state4_.d); break;
|
||||
case 5: call_get(call, state5_.d); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T, typename ...Args>
|
||||
bool call_commit(Args&& ...args)
|
||||
{ return T{props_, al_effect_props_}.commit(std::forward<Args>(args)...); }
|
||||
|
||||
bool call_commit(const EaxEffectProps &props)
|
||||
{ EAXCALL(props.mType, call_commit, props); }
|
||||
{
|
||||
return std::visit([&](auto &arg)
|
||||
{ return CommitterFor<decltype(arg)>{props_, al_effect_props_}.commit(arg); },
|
||||
props);
|
||||
}
|
||||
|
||||
bool commit(int eax_version)
|
||||
{
|
||||
|
|
@ -388,15 +427,15 @@ public:
|
|||
{
|
||||
case 1:
|
||||
state1_.i = state1_.d;
|
||||
ret |= call_commit<EaxReverbCommitter>(state1_.d);
|
||||
ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state1_.d);
|
||||
break;
|
||||
case 2:
|
||||
state2_.i = state2_.d;
|
||||
ret |= call_commit<EaxReverbCommitter>(state2_.d);
|
||||
ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state2_.d);
|
||||
break;
|
||||
case 3:
|
||||
state3_.i = state3_.d;
|
||||
ret |= call_commit<EaxReverbCommitter>(state3_.d);
|
||||
ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state3_.d);
|
||||
break;
|
||||
case 4:
|
||||
state4_.i = state4_.d;
|
||||
|
|
|
|||
|
|
@ -6,54 +6,27 @@
|
|||
#include <string>
|
||||
|
||||
|
||||
EaxException::EaxException(const char *context, const char *message)
|
||||
EaxException::EaxException(std::string_view context, std::string_view message)
|
||||
: std::runtime_error{make_message(context, message)}
|
||||
{
|
||||
}
|
||||
EaxException::~EaxException() = default;
|
||||
|
||||
|
||||
std::string EaxException::make_message(const char *context, const char *message)
|
||||
std::string EaxException::make_message(std::string_view context, std::string_view message)
|
||||
{
|
||||
const auto context_size = (context ? std::string::traits_type::length(context) : 0);
|
||||
const auto has_contex = (context_size > 0);
|
||||
|
||||
const auto message_size = (message ? std::string::traits_type::length(message) : 0);
|
||||
const auto has_message = (message_size > 0);
|
||||
|
||||
if (!has_contex && !has_message)
|
||||
{
|
||||
return std::string{};
|
||||
}
|
||||
|
||||
static constexpr char left_prefix[] = "[";
|
||||
const auto left_prefix_size = std::string::traits_type::length(left_prefix);
|
||||
|
||||
static constexpr char right_prefix[] = "] ";
|
||||
const auto right_prefix_size = std::string::traits_type::length(right_prefix);
|
||||
|
||||
const auto what_size =
|
||||
(
|
||||
has_contex ?
|
||||
left_prefix_size + context_size + right_prefix_size :
|
||||
0) +
|
||||
message_size +
|
||||
1;
|
||||
|
||||
auto what = std::string{};
|
||||
what.reserve(what_size);
|
||||
if(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.append(context, context_size);
|
||||
what.append(right_prefix, right_prefix_size);
|
||||
}
|
||||
|
||||
if (has_message)
|
||||
{
|
||||
what.append(message, message_size);
|
||||
what += "[";
|
||||
what += context;
|
||||
what += "] ";
|
||||
}
|
||||
what += message;
|
||||
|
||||
return what;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,23 @@
|
|||
#ifndef EAX_EXCEPTION_INCLUDED
|
||||
#define EAX_EXCEPTION_INCLUDED
|
||||
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
|
||||
class EaxException : public std::runtime_error {
|
||||
static std::string make_message(const char *context, const char *message);
|
||||
static std::string make_message(std::string_view context, std::string_view message);
|
||||
|
||||
public:
|
||||
EaxException(const char *context, const char *message);
|
||||
EaxException() = delete;
|
||||
EaxException(const EaxException&) = default;
|
||||
EaxException(EaxException&&) = default;
|
||||
EaxException(std::string_view context, std::string_view message);
|
||||
~EaxException() override;
|
||||
}; // EaxException
|
||||
|
||||
auto operator=(const EaxException&) -> EaxException& = default;
|
||||
auto operator=(EaxException&&) -> EaxException& = default;
|
||||
};
|
||||
|
||||
#endif // !EAX_EXCEPTION_INCLUDED
|
||||
#endif /* EAX_EXCEPTION_INCLUDED */
|
||||
|
|
|
|||
|
|
@ -3,17 +3,16 @@
|
|||
|
||||
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
|
||||
#include "aloptional.h"
|
||||
#include "api.h"
|
||||
|
||||
|
||||
using EaxFxSlotIndexValue = std::size_t;
|
||||
|
||||
class EaxFxSlotIndex : public al::optional<EaxFxSlotIndexValue>
|
||||
{
|
||||
class EaxFxSlotIndex : public std::optional<EaxFxSlotIndexValue> {
|
||||
public:
|
||||
using al::optional<EaxFxSlotIndexValue>::optional;
|
||||
using std::optional<EaxFxSlotIndexValue>::optional;
|
||||
|
||||
EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; }
|
||||
EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; }
|
||||
|
|
|
|||
|
|
@ -6,13 +6,10 @@
|
|||
|
||||
#include "al/auxeffectslot.h"
|
||||
|
||||
#include "api.h"
|
||||
#include "call.h"
|
||||
#include "fx_slot_index.h"
|
||||
|
||||
|
||||
class EaxFxSlots
|
||||
{
|
||||
class EaxFxSlots {
|
||||
public:
|
||||
void initialize(ALCcontext& al_context);
|
||||
|
||||
|
|
@ -25,11 +22,9 @@ public:
|
|||
}
|
||||
|
||||
|
||||
const ALeffectslot& get(
|
||||
EaxFxSlotIndex index) const;
|
||||
[[nodiscard]] auto get(EaxFxSlotIndex index) const -> const ALeffectslot&;
|
||||
|
||||
ALeffectslot& get(
|
||||
EaxFxSlotIndex index);
|
||||
[[nodiscard]] auto get(EaxFxSlotIndex index) -> ALeffectslot&;
|
||||
|
||||
private:
|
||||
using Items = std::array<EaxAlEffectSlotUPtr, EAX_MAX_FXSLOTS>;
|
||||
|
|
@ -39,8 +34,7 @@ private:
|
|||
|
||||
|
||||
[[noreturn]]
|
||||
static void fail(
|
||||
const char* message);
|
||||
static void fail(const char* message);
|
||||
|
||||
void initialize_fx_slots(ALCcontext& al_context);
|
||||
}; // EaxFxSlots
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#define EAX_GLOBALS_INCLUDED
|
||||
|
||||
inline bool eax_g_is_enabled{true};
|
||||
|
||||
extern bool eax_g_is_enabled;
|
||||
|
||||
|
||||
extern const char eax1_ext_name[];
|
||||
extern const char eax2_ext_name[];
|
||||
extern const char eax3_ext_name[];
|
||||
extern const char eax4_ext_name[];
|
||||
extern const char eax5_ext_name[];
|
||||
|
||||
extern const char eax_x_ram_ext_name[];
|
||||
|
||||
extern const char eax_eax_set_func_name[];
|
||||
extern const char eax_eax_get_func_name[];
|
||||
|
||||
extern const char eax_eax_set_buffer_mode_func_name[];
|
||||
extern const char eax_eax_get_buffer_mode_func_name[];
|
||||
|
||||
#endif // !EAX_GLOBALS_INCLUDED
|
||||
#endif /* EAX_GLOBALS_INCLUDED */
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@
|
|||
#include <cassert>
|
||||
#include <exception>
|
||||
|
||||
#include "alstring.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
|
||||
void eax_log_exception(const char *message) noexcept
|
||||
void eax_log_exception(std::string_view message) noexcept
|
||||
{
|
||||
const auto exception_ptr = std::current_exception();
|
||||
assert(exception_ptr);
|
||||
|
|
@ -17,10 +18,9 @@ void eax_log_exception(const char *message) noexcept
|
|||
std::rethrow_exception(exception_ptr);
|
||||
}
|
||||
catch(const std::exception& ex) {
|
||||
const auto ex_message = ex.what();
|
||||
ERR("%s %s\n", message ? message : "", ex_message);
|
||||
ERR("%.*s %s\n", al::sizei(message), message.data(), ex.what());
|
||||
}
|
||||
catch(...) {
|
||||
ERR("%s %s\n", message ? message : "", "Generic exception.");
|
||||
ERR("%.*s %s\n", al::sizei(message), message.data(), "Generic exception.");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@
|
|||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include "opthelpers.h"
|
||||
|
||||
using EaxDirtyFlags = unsigned int;
|
||||
|
||||
struct EaxAlLowPassParam {
|
||||
|
|
@ -13,16 +16,13 @@ struct EaxAlLowPassParam {
|
|||
float gain_hf;
|
||||
};
|
||||
|
||||
void eax_log_exception(const char *message) noexcept;
|
||||
void eax_log_exception(std::string_view message) noexcept;
|
||||
|
||||
template<typename TException, typename TValue>
|
||||
void eax_validate_range(
|
||||
const char* value_name,
|
||||
const TValue& value,
|
||||
const TValue& min_value,
|
||||
void eax_validate_range(std::string_view value_name, const TValue& value, const TValue& min_value,
|
||||
const TValue& max_value)
|
||||
{
|
||||
if (value >= min_value && value <= max_value)
|
||||
if(value >= min_value && value <= max_value) LIKELY
|
||||
return;
|
||||
|
||||
const auto message =
|
||||
|
|
|
|||
|
|
@ -24,15 +24,7 @@ constexpr auto AL_STORAGE_AUTOMATIC_NAME = "AL_STORAGE_AUTOMATIC";
|
|||
constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE";
|
||||
constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE";
|
||||
|
||||
|
||||
ALboolean AL_APIENTRY EAXSetBufferMode(
|
||||
ALsizei n,
|
||||
const ALuint* buffers,
|
||||
ALint value);
|
||||
|
||||
ALenum AL_APIENTRY EAXGetBufferMode(
|
||||
ALuint buffer,
|
||||
ALint* pReserved);
|
||||
|
||||
ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint *buffers, ALint value) noexcept;
|
||||
ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint *pReserved) noexcept;
|
||||
|
||||
#endif // !EAX_X_RAM_INCLUDED
|
||||
|
|
|
|||
|
|
@ -28,9 +28,13 @@
|
|||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
|
@ -38,26 +42,23 @@
|
|||
#include "AL/efx-presets.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "al/effects/effects.h"
|
||||
#include "albit.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "alstring.h"
|
||||
#include "core/except.h"
|
||||
#include "core/logging.h"
|
||||
#include "direct_defs.h"
|
||||
#include "error.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "opthelpers.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
|
||||
#include "eax/exception.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
const EffectList gEffectList[16]{
|
||||
const std::array<EffectList,16> gEffectList{{
|
||||
{ "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB },
|
||||
{ "reverb", REVERB_EFFECT, AL_EFFECT_REVERB },
|
||||
{ "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH },
|
||||
|
|
@ -73,95 +74,74 @@ const EffectList gEffectList[16]{
|
|||
{ "vmorpher", VMORPHER_EFFECT, AL_EFFECT_VOCAL_MORPHER },
|
||||
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT },
|
||||
{ "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_DIALOGUE },
|
||||
{ "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_REVERB_SOFT },
|
||||
};
|
||||
{ "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_SOFT },
|
||||
}};
|
||||
|
||||
bool DisabledEffects[MAX_EFFECTS];
|
||||
|
||||
|
||||
effect_exception::effect_exception(ALenum code, const char *msg, ...) : mErrorCode{code}
|
||||
{
|
||||
std::va_list args;
|
||||
va_start(args, msg);
|
||||
setMessage(msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
effect_exception::~effect_exception() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
struct EffectPropsItem {
|
||||
ALenum Type;
|
||||
const EffectProps &DefaultProps;
|
||||
const EffectVtable &Vtable;
|
||||
};
|
||||
constexpr EffectPropsItem EffectPropsList[] = {
|
||||
{ AL_EFFECT_NULL, NullEffectProps, NullEffectVtable },
|
||||
{ AL_EFFECT_EAXREVERB, ReverbEffectProps, ReverbEffectVtable },
|
||||
{ AL_EFFECT_REVERB, StdReverbEffectProps, StdReverbEffectVtable },
|
||||
{ AL_EFFECT_AUTOWAH, AutowahEffectProps, AutowahEffectVtable },
|
||||
{ AL_EFFECT_CHORUS, ChorusEffectProps, ChorusEffectVtable },
|
||||
{ AL_EFFECT_COMPRESSOR, CompressorEffectProps, CompressorEffectVtable },
|
||||
{ AL_EFFECT_DISTORTION, DistortionEffectProps, DistortionEffectVtable },
|
||||
{ AL_EFFECT_ECHO, EchoEffectProps, EchoEffectVtable },
|
||||
{ AL_EFFECT_EQUALIZER, EqualizerEffectProps, EqualizerEffectVtable },
|
||||
{ AL_EFFECT_FLANGER, FlangerEffectProps, FlangerEffectVtable },
|
||||
{ AL_EFFECT_FREQUENCY_SHIFTER, FshifterEffectProps, FshifterEffectVtable },
|
||||
{ AL_EFFECT_RING_MODULATOR, ModulatorEffectProps, ModulatorEffectVtable },
|
||||
{ AL_EFFECT_PITCH_SHIFTER, PshifterEffectProps, PshifterEffectVtable },
|
||||
{ AL_EFFECT_VOCAL_MORPHER, VmorpherEffectProps, VmorpherEffectVtable },
|
||||
{ AL_EFFECT_DEDICATED_DIALOGUE, DedicatedEffectProps, DedicatedEffectVtable },
|
||||
{ AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedEffectProps, DedicatedEffectVtable },
|
||||
{ AL_EFFECT_CONVOLUTION_REVERB_SOFT, ConvolutionEffectProps, ConvolutionEffectVtable },
|
||||
};
|
||||
using SubListAllocator = al::allocator<std::array<ALeffect,64>>;
|
||||
|
||||
|
||||
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)
|
||||
constexpr auto GetDefaultProps(ALenum type) noexcept -> const EffectProps&
|
||||
{
|
||||
auto iter = std::find_if(std::begin(EffectPropsList), std::end(EffectPropsList),
|
||||
[type](const EffectPropsItem &item) noexcept -> bool
|
||||
{ return item.Type == type; });
|
||||
return (iter != std::end(EffectPropsList)) ? al::to_address(iter) : nullptr;
|
||||
switch(type)
|
||||
{
|
||||
case AL_EFFECT_NULL: return NullEffectProps;
|
||||
case AL_EFFECT_EAXREVERB: return ReverbEffectProps;
|
||||
case AL_EFFECT_REVERB: return StdReverbEffectProps;
|
||||
case AL_EFFECT_AUTOWAH: return AutowahEffectProps;
|
||||
case AL_EFFECT_CHORUS: return ChorusEffectProps;
|
||||
case AL_EFFECT_COMPRESSOR: return CompressorEffectProps;
|
||||
case AL_EFFECT_DISTORTION: return DistortionEffectProps;
|
||||
case AL_EFFECT_ECHO: return EchoEffectProps;
|
||||
case AL_EFFECT_EQUALIZER: return EqualizerEffectProps;
|
||||
case AL_EFFECT_FLANGER: return FlangerEffectProps;
|
||||
case AL_EFFECT_FREQUENCY_SHIFTER: return FshifterEffectProps;
|
||||
case AL_EFFECT_RING_MODULATOR: return ModulatorEffectProps;
|
||||
case AL_EFFECT_PITCH_SHIFTER: return PshifterEffectProps;
|
||||
case AL_EFFECT_VOCAL_MORPHER: return VmorpherEffectProps;
|
||||
case AL_EFFECT_DEDICATED_DIALOGUE: return DedicatedDialogEffectProps;
|
||||
case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return DedicatedLfeEffectProps;
|
||||
case AL_EFFECT_CONVOLUTION_SOFT: return ConvolutionEffectProps;
|
||||
}
|
||||
return NullEffectProps;
|
||||
}
|
||||
|
||||
void InitEffectParams(ALeffect *effect, ALenum type)
|
||||
void InitEffectParams(ALeffect *effect, ALenum type) noexcept
|
||||
{
|
||||
const EffectPropsItem *item{getEffectPropsItemByType(type)};
|
||||
if(item)
|
||||
switch(type)
|
||||
{
|
||||
effect->Props = item->DefaultProps;
|
||||
effect->vtab = &item->Vtable;
|
||||
}
|
||||
else
|
||||
{
|
||||
effect->Props = EffectProps{};
|
||||
effect->vtab = &NullEffectVtable;
|
||||
case AL_EFFECT_NULL: effect->PropsVariant.emplace<NullEffectHandler>(); break;
|
||||
case AL_EFFECT_EAXREVERB: effect->PropsVariant.emplace<ReverbEffectHandler>(); break;
|
||||
case AL_EFFECT_REVERB: effect->PropsVariant.emplace<StdReverbEffectHandler>(); break;
|
||||
case AL_EFFECT_AUTOWAH: effect->PropsVariant.emplace<AutowahEffectHandler>(); break;
|
||||
case AL_EFFECT_CHORUS: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
|
||||
case AL_EFFECT_COMPRESSOR: effect->PropsVariant.emplace<CompressorEffectHandler>(); break;
|
||||
case AL_EFFECT_DISTORTION: effect->PropsVariant.emplace<DistortionEffectHandler>(); break;
|
||||
case AL_EFFECT_ECHO: effect->PropsVariant.emplace<EchoEffectHandler>(); break;
|
||||
case AL_EFFECT_EQUALIZER: effect->PropsVariant.emplace<EqualizerEffectHandler>(); break;
|
||||
case AL_EFFECT_FLANGER: effect->PropsVariant.emplace<ChorusEffectHandler>(); break;
|
||||
case AL_EFFECT_FREQUENCY_SHIFTER: effect->PropsVariant.emplace<FshifterEffectHandler>(); break;
|
||||
case AL_EFFECT_RING_MODULATOR: effect->PropsVariant.emplace<ModulatorEffectHandler>(); break;
|
||||
case AL_EFFECT_PITCH_SHIFTER: effect->PropsVariant.emplace<PshifterEffectHandler>(); break;
|
||||
case AL_EFFECT_VOCAL_MORPHER: effect->PropsVariant.emplace<VmorpherEffectHandler>(); break;
|
||||
case AL_EFFECT_DEDICATED_DIALOGUE:
|
||||
effect->PropsVariant.emplace<DedicatedDialogEffectHandler>();
|
||||
break;
|
||||
case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT:
|
||||
effect->PropsVariant.emplace<DedicatedLfeEffectHandler>();
|
||||
break;
|
||||
case AL_EFFECT_CONVOLUTION_SOFT:
|
||||
effect->PropsVariant.emplace<ConvolutionEffectHandler>();
|
||||
break;
|
||||
}
|
||||
effect->Props = GetDefaultProps(type);
|
||||
effect->type = type;
|
||||
}
|
||||
|
||||
bool EnsureEffects(ALCdevice *device, size_t needed)
|
||||
{
|
||||
size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), size_t{0},
|
||||
auto EnsureEffects(ALCdevice *device, size_t needed) noexcept -> bool
|
||||
try {
|
||||
size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), 0_uz,
|
||||
[](size_t cur, const EffectSubList &sublist) noexcept -> size_t
|
||||
{ return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
|
||||
|
||||
|
|
@ -170,21 +150,19 @@ bool EnsureEffects(ALCdevice *device, size_t needed)
|
|||
if(device->EffectList.size() >= 1<<25) UNLIKELY
|
||||
return false;
|
||||
|
||||
device->EffectList.emplace_back();
|
||||
auto sublist = device->EffectList.end() - 1;
|
||||
sublist->FreeMask = ~0_u64;
|
||||
sublist->Effects = static_cast<ALeffect*>(al_calloc(alignof(ALeffect), sizeof(ALeffect)*64));
|
||||
if(!sublist->Effects) UNLIKELY
|
||||
{
|
||||
device->EffectList.pop_back();
|
||||
return false;
|
||||
}
|
||||
count += 64;
|
||||
EffectSubList sublist{};
|
||||
sublist.FreeMask = ~0_u64;
|
||||
sublist.Effects = SubListAllocator{}.allocate(1);
|
||||
device->EffectList.emplace_back(std::move(sublist));
|
||||
count += std::tuple_size_v<SubListAllocator::value_type>;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch(...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ALeffect *AllocEffect(ALCdevice *device)
|
||||
ALeffect *AllocEffect(ALCdevice *device) noexcept
|
||||
{
|
||||
auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(),
|
||||
[](const EffectSubList &entry) noexcept -> bool
|
||||
|
|
@ -193,7 +171,7 @@ ALeffect *AllocEffect(ALCdevice *device)
|
|||
auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
|
||||
ASSUME(slidx < 64);
|
||||
|
||||
ALeffect *effect{al::construct_at(sublist->Effects + slidx)};
|
||||
ALeffect *effect{al::construct_at(al::to_address(sublist->Effects->begin() + slidx))};
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
|
||||
/* Add 1 to avoid effect ID 0. */
|
||||
|
|
@ -206,16 +184,18 @@ ALeffect *AllocEffect(ALCdevice *device)
|
|||
|
||||
void FreeEffect(ALCdevice *device, ALeffect *effect)
|
||||
{
|
||||
device->mEffectNames.erase(effect->id);
|
||||
|
||||
const ALuint id{effect->id - 1};
|
||||
const size_t lidx{id >> 6};
|
||||
const ALuint slidx{id & 0x3f};
|
||||
|
||||
al::destroy_at(effect);
|
||||
std::destroy_at(effect);
|
||||
|
||||
device->EffectList[lidx].FreeMask |= 1_u64 << slidx;
|
||||
}
|
||||
|
||||
inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
|
||||
inline auto LookupEffect(ALCdevice *device, ALuint id) noexcept -> ALeffect*
|
||||
{
|
||||
const size_t lidx{(id-1) >> 6};
|
||||
const ALuint slidx{(id-1) & 0x3f};
|
||||
|
|
@ -225,320 +205,294 @@ inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
|
|||
EffectSubList &sublist = device->EffectList[lidx];
|
||||
if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
|
||||
return nullptr;
|
||||
return sublist.Effects + slidx;
|
||||
return al::to_address(sublist.Effects->begin() + slidx);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
if(n < 0) UNLIKELY
|
||||
context->setError(AL_INVALID_VALUE, "Generating %d effects", n);
|
||||
AL_API DECL_FUNC2(void, alGenEffects, ALsizei,n, ALuint*,effects)
|
||||
FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept
|
||||
try {
|
||||
if(n < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Generating %d effects", n};
|
||||
if(n <= 0) UNLIKELY return;
|
||||
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
if(!EnsureEffects(device, static_cast<ALuint>(n)))
|
||||
{
|
||||
context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n, (n==1)?"":"s");
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
if(n == 1) LIKELY
|
||||
{
|
||||
/* Special handling for the easy and normal case. */
|
||||
ALeffect *effect{AllocEffect(device)};
|
||||
effects[0] = effect->id;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Store the allocated buffer IDs in a separate local list, to avoid
|
||||
* modifying the user storage in case of failure.
|
||||
*/
|
||||
al::vector<ALuint> ids;
|
||||
ids.reserve(static_cast<ALuint>(n));
|
||||
do {
|
||||
ALeffect *effect{AllocEffect(device)};
|
||||
ids.emplace_back(effect->id);
|
||||
} while(--n);
|
||||
std::copy(ids.cbegin(), ids.cend(), effects);
|
||||
}
|
||||
const al::span eids{effects, static_cast<ALuint>(n)};
|
||||
if(!EnsureEffects(device, eids.size()))
|
||||
throw al::context_error{AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n,
|
||||
(n == 1) ? "" : "s"};
|
||||
|
||||
std::generate(eids.begin(), eids.end(), [device]{ return AllocEffect(device)->id; });
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
if(n < 0) UNLIKELY
|
||||
context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
|
||||
AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei,n, const ALuint*,effects)
|
||||
FORCE_ALIGN void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n,
|
||||
const ALuint *effects) noexcept
|
||||
try {
|
||||
if(n < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Deleting %d effects", n};
|
||||
if(n <= 0) UNLIKELY return;
|
||||
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
/* First try to find any effects that are invalid. */
|
||||
auto validate_effect = [device](const ALuint eid) -> bool
|
||||
{ return !eid || LookupEffect(device, eid) != nullptr; };
|
||||
|
||||
const ALuint *effects_end = effects + n;
|
||||
auto inveffect = std::find_if_not(effects, effects_end, validate_effect);
|
||||
if(inveffect != effects_end) UNLIKELY
|
||||
{
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", *inveffect);
|
||||
return;
|
||||
}
|
||||
const al::span eids{effects, static_cast<ALuint>(n)};
|
||||
auto inveffect = std::find_if_not(eids.begin(), eids.end(), validate_effect);
|
||||
if(inveffect != eids.end())
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", *inveffect};
|
||||
|
||||
/* All good. Delete non-0 effect IDs. */
|
||||
auto delete_effect = [device](ALuint eid) -> void
|
||||
{
|
||||
ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr};
|
||||
if(effect) FreeEffect(device, effect);
|
||||
if(ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr})
|
||||
FreeEffect(device, effect);
|
||||
};
|
||||
std::for_each(effects, effects_end, delete_effect);
|
||||
std::for_each(eids.begin(), eids.end(), delete_effect);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
|
||||
START_API_FUNC
|
||||
AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint,effect)
|
||||
FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(context) LIKELY
|
||||
{
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
if(!effect || LookupEffect(device, effect))
|
||||
return AL_TRUE;
|
||||
}
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
if(!effect || LookupEffect(device, effect))
|
||||
return AL_TRUE;
|
||||
return AL_FALSE;
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alEffecti, ALuint,effect, ALenum,param, ALint,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALint value) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else if(param == AL_EFFECT_TYPE)
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
bool isOk{value == AL_EFFECT_NULL};
|
||||
if(!isOk)
|
||||
case AL_EFFECT_TYPE:
|
||||
if(value != AL_EFFECT_NULL)
|
||||
{
|
||||
for(const EffectList &effectitem : gEffectList)
|
||||
{
|
||||
if(value == effectitem.val && !DisabledEffects[effectitem.type])
|
||||
{
|
||||
isOk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto check_effect = [value](const EffectList &item) -> bool
|
||||
{ return value == item.val && !DisabledEffects.test(item.type); };
|
||||
if(!std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect))
|
||||
throw al::context_error{AL_INVALID_VALUE, "Effect type 0x%04x not supported",
|
||||
value};
|
||||
}
|
||||
|
||||
if(isOk)
|
||||
InitEffectParams(aleffect, value);
|
||||
else
|
||||
context->setError(AL_INVALID_VALUE, "Effect type 0x%04x not supported", value);
|
||||
}
|
||||
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]);
|
||||
InitEffectParams(aleffect, value);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,value](auto &arg)
|
||||
{
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.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()};
|
||||
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)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,value](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_setParamiv(aleffect, param, values);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.SetParamf(std::get<PropType>(aleffect->Props), param, value);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY 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()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,values](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_setParamf(aleffect, param, value);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.SetParamfv(std::get<PropType>(aleffect->Props), param, values);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
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());
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alGetEffecti, ALuint,effect, ALenum,param, ALint*,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALint *value) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else if(param == AL_EFFECT_TYPE)
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_EFFECT_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;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,value](auto &arg)
|
||||
{
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.GetParami(std::get<PropType>(aleffect->Props), param, value);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
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()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,values](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_getParamiv(aleffect, param, values);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.GetParamiv(std::get<PropType>(aleffect->Props), param, values);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alGetEffectf, ALuint,effect, ALenum,param, ALfloat*,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALfloat *value) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,value](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_getParamf(aleffect, param, value);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.GetParamf(std::get<PropType>(aleffect->Props), param, value);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC3(void, alGetEffectfv, ALuint,effect, ALenum,param, ALfloat*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
|
||||
ALfloat *values) noexcept
|
||||
try {
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
if(!aleffect) UNLIKELY
|
||||
context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect);
|
||||
else try
|
||||
if(!aleffect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", effect};
|
||||
|
||||
/* Call the appropriate handler */
|
||||
std::visit([aleffect,param,values](auto &arg)
|
||||
{
|
||||
/* Call the appropriate handler */
|
||||
ALeffect_getParamfv(aleffect, param, values);
|
||||
}
|
||||
catch(effect_exception &e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
using Type = std::remove_cv_t<std::remove_reference_t<decltype(arg)>>;
|
||||
using PropType = typename Type::prop_type;
|
||||
return arg.GetParamfv(std::get<PropType>(aleffect->Props), param, values);
|
||||
}, aleffect->PropsVariant);
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
void InitEffect(ALeffect *effect)
|
||||
|
|
@ -546,26 +500,43 @@ void InitEffect(ALeffect *effect)
|
|||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
}
|
||||
|
||||
void ALeffect::SetName(ALCcontext* context, ALuint id, std::string_view name)
|
||||
{
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> effectlock{device->EffectLock};
|
||||
|
||||
auto effect = LookupEffect(device, id);
|
||||
if(!effect)
|
||||
throw al::context_error{AL_INVALID_NAME, "Invalid effect ID %u", id};
|
||||
|
||||
device->mEffectNames.insert_or_assign(id, name);
|
||||
}
|
||||
|
||||
|
||||
EffectSubList::~EffectSubList()
|
||||
{
|
||||
if(!Effects)
|
||||
return;
|
||||
|
||||
uint64_t usemask{~FreeMask};
|
||||
while(usemask)
|
||||
{
|
||||
const int idx{al::countr_zero(usemask)};
|
||||
al::destroy_at(Effects+idx);
|
||||
std::destroy_at(al::to_address(Effects->begin()+idx));
|
||||
usemask &= ~(1_u64 << idx);
|
||||
}
|
||||
FreeMask = ~usemask;
|
||||
al_free(Effects);
|
||||
SubListAllocator{}.deallocate(Effects, 1);
|
||||
Effects = nullptr;
|
||||
}
|
||||
|
||||
|
||||
#define DECL(x) { #x, EFX_REVERB_PRESET_##x }
|
||||
static const struct {
|
||||
const char name[32];
|
||||
struct EffectPreset {
|
||||
const char name[32]; /* NOLINT(*-avoid-c-arrays) */
|
||||
EFXEAXREVERBPROPERTIES props;
|
||||
} reverblist[] = {
|
||||
};
|
||||
#define DECL(x) EffectPreset{#x, EFX_REVERB_PRESET_##x}
|
||||
static constexpr std::array reverblist{
|
||||
DECL(GENERIC),
|
||||
DECL(PADDEDCELL),
|
||||
DECL(ROOM),
|
||||
|
|
@ -695,61 +666,62 @@ static const struct {
|
|||
};
|
||||
#undef DECL
|
||||
|
||||
void LoadReverbPreset(const char *name, ALeffect *effect)
|
||||
void LoadReverbPreset(const std::string_view name, ALeffect *effect)
|
||||
{
|
||||
if(al::strcasecmp(name, "NONE") == 0)
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
if(al::case_compare(name, "NONE"sv) == 0)
|
||||
{
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
TRACE("Loading reverb '%s'\n", "NONE");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!DisabledEffects[EAXREVERB_EFFECT])
|
||||
if(!DisabledEffects.test(EAXREVERB_EFFECT))
|
||||
InitEffectParams(effect, AL_EFFECT_EAXREVERB);
|
||||
else if(!DisabledEffects[REVERB_EFFECT])
|
||||
else if(!DisabledEffects.test(REVERB_EFFECT))
|
||||
InitEffectParams(effect, AL_EFFECT_REVERB);
|
||||
else
|
||||
InitEffectParams(effect, AL_EFFECT_NULL);
|
||||
for(const auto &reverbitem : reverblist)
|
||||
{
|
||||
const EFXEAXREVERBPROPERTIES *props;
|
||||
|
||||
if(al::strcasecmp(name, reverbitem.name) != 0)
|
||||
if(al::case_compare(name, std::data(reverbitem.name)) != 0)
|
||||
continue;
|
||||
|
||||
TRACE("Loading reverb '%s'\n", reverbitem.name);
|
||||
props = &reverbitem.props;
|
||||
effect->Props.Reverb.Density = props->flDensity;
|
||||
effect->Props.Reverb.Diffusion = props->flDiffusion;
|
||||
effect->Props.Reverb.Gain = props->flGain;
|
||||
effect->Props.Reverb.GainHF = props->flGainHF;
|
||||
effect->Props.Reverb.GainLF = props->flGainLF;
|
||||
effect->Props.Reverb.DecayTime = props->flDecayTime;
|
||||
effect->Props.Reverb.DecayHFRatio = props->flDecayHFRatio;
|
||||
effect->Props.Reverb.DecayLFRatio = props->flDecayLFRatio;
|
||||
effect->Props.Reverb.ReflectionsGain = props->flReflectionsGain;
|
||||
effect->Props.Reverb.ReflectionsDelay = props->flReflectionsDelay;
|
||||
effect->Props.Reverb.ReflectionsPan[0] = props->flReflectionsPan[0];
|
||||
effect->Props.Reverb.ReflectionsPan[1] = props->flReflectionsPan[1];
|
||||
effect->Props.Reverb.ReflectionsPan[2] = props->flReflectionsPan[2];
|
||||
effect->Props.Reverb.LateReverbGain = props->flLateReverbGain;
|
||||
effect->Props.Reverb.LateReverbDelay = props->flLateReverbDelay;
|
||||
effect->Props.Reverb.LateReverbPan[0] = props->flLateReverbPan[0];
|
||||
effect->Props.Reverb.LateReverbPan[1] = props->flLateReverbPan[1];
|
||||
effect->Props.Reverb.LateReverbPan[2] = props->flLateReverbPan[2];
|
||||
effect->Props.Reverb.EchoTime = props->flEchoTime;
|
||||
effect->Props.Reverb.EchoDepth = props->flEchoDepth;
|
||||
effect->Props.Reverb.ModulationTime = props->flModulationTime;
|
||||
effect->Props.Reverb.ModulationDepth = props->flModulationDepth;
|
||||
effect->Props.Reverb.AirAbsorptionGainHF = props->flAirAbsorptionGainHF;
|
||||
effect->Props.Reverb.HFReference = props->flHFReference;
|
||||
effect->Props.Reverb.LFReference = props->flLFReference;
|
||||
effect->Props.Reverb.RoomRolloffFactor = props->flRoomRolloffFactor;
|
||||
effect->Props.Reverb.DecayHFLimit = props->iDecayHFLimit ? AL_TRUE : AL_FALSE;
|
||||
TRACE("Loading reverb '%s'\n", std::data(reverbitem.name));
|
||||
const auto &props = reverbitem.props;
|
||||
auto &dst = std::get<ReverbProps>(effect->Props);
|
||||
dst.Density = props.flDensity;
|
||||
dst.Diffusion = props.flDiffusion;
|
||||
dst.Gain = props.flGain;
|
||||
dst.GainHF = props.flGainHF;
|
||||
dst.GainLF = props.flGainLF;
|
||||
dst.DecayTime = props.flDecayTime;
|
||||
dst.DecayHFRatio = props.flDecayHFRatio;
|
||||
dst.DecayLFRatio = props.flDecayLFRatio;
|
||||
dst.ReflectionsGain = props.flReflectionsGain;
|
||||
dst.ReflectionsDelay = props.flReflectionsDelay;
|
||||
dst.ReflectionsPan[0] = props.flReflectionsPan[0];
|
||||
dst.ReflectionsPan[1] = props.flReflectionsPan[1];
|
||||
dst.ReflectionsPan[2] = props.flReflectionsPan[2];
|
||||
dst.LateReverbGain = props.flLateReverbGain;
|
||||
dst.LateReverbDelay = props.flLateReverbDelay;
|
||||
dst.LateReverbPan[0] = props.flLateReverbPan[0];
|
||||
dst.LateReverbPan[1] = props.flLateReverbPan[1];
|
||||
dst.LateReverbPan[2] = props.flLateReverbPan[2];
|
||||
dst.EchoTime = props.flEchoTime;
|
||||
dst.EchoDepth = props.flEchoDepth;
|
||||
dst.ModulationTime = props.flModulationTime;
|
||||
dst.ModulationDepth = props.flModulationDepth;
|
||||
dst.AirAbsorptionGainHF = props.flAirAbsorptionGainHF;
|
||||
dst.HFReference = props.flHFReference;
|
||||
dst.LFReference = props.flLFReference;
|
||||
dst.RoomRolloffFactor = props.flRoomRolloffFactor;
|
||||
dst.DecayHFLimit = props.iDecayHFLimit ? AL_TRUE : AL_FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
WARN("Reverb preset '%s' not found\n", name);
|
||||
WARN("Reverb preset '%.*s' not found\n", al::sizei(name), name.data());
|
||||
}
|
||||
|
||||
bool IsValidEffectType(ALenum type) noexcept
|
||||
|
|
@ -757,10 +729,7 @@ bool IsValidEffectType(ALenum type) noexcept
|
|||
if(type == AL_EFFECT_NULL)
|
||||
return true;
|
||||
|
||||
for(const auto &effect_item : gEffectList)
|
||||
{
|
||||
if(type == effect_item.val && !DisabledEffects[effect_item.type])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
auto check_effect = [type](const EffectList &item) noexcept -> bool
|
||||
{ return type == item.val && !DisabledEffects.test(item.type); };
|
||||
return std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,20 @@
|
|||
#ifndef AL_EFFECT_H
|
||||
#define AL_EFFECT_H
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "al/effects/effects.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "effects/effects.h"
|
||||
|
||||
|
||||
enum {
|
||||
|
|
@ -27,36 +36,55 @@ enum {
|
|||
|
||||
MAX_EFFECTS
|
||||
};
|
||||
extern bool DisabledEffects[MAX_EFFECTS];
|
||||
|
||||
extern float ReverbBoost;
|
||||
inline std::bitset<MAX_EFFECTS> DisabledEffects;
|
||||
|
||||
struct EffectList {
|
||||
const char name[16];
|
||||
int type;
|
||||
const char name[16]; /* NOLINT(*-avoid-c-arrays) */
|
||||
ALuint type;
|
||||
ALenum val;
|
||||
};
|
||||
extern const EffectList gEffectList[16];
|
||||
extern const std::array<EffectList,16> gEffectList;
|
||||
|
||||
using EffectHandlerVariant = std::variant<NullEffectHandler,ReverbEffectHandler,
|
||||
StdReverbEffectHandler,AutowahEffectHandler,ChorusEffectHandler,CompressorEffectHandler,
|
||||
DistortionEffectHandler,EchoEffectHandler,EqualizerEffectHandler,FlangerEffectHandler,
|
||||
FshifterEffectHandler,ModulatorEffectHandler,PshifterEffectHandler,VmorpherEffectHandler,
|
||||
DedicatedDialogEffectHandler,DedicatedLfeEffectHandler,ConvolutionEffectHandler>;
|
||||
|
||||
struct ALeffect {
|
||||
// Effect type (AL_EFFECT_NULL, ...)
|
||||
ALenum type{AL_EFFECT_NULL};
|
||||
|
||||
EffectHandlerVariant PropsVariant;
|
||||
EffectProps Props{};
|
||||
|
||||
const EffectVtable *vtab{nullptr};
|
||||
|
||||
/* Self ID */
|
||||
ALuint id{0u};
|
||||
|
||||
DISABLE_ALLOC()
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
DISABLE_ALLOC
|
||||
};
|
||||
|
||||
void InitEffect(ALeffect *effect);
|
||||
|
||||
void LoadReverbPreset(const char *name, ALeffect *effect);
|
||||
void LoadReverbPreset(const std::string_view name, ALeffect *effect);
|
||||
|
||||
bool IsValidEffectType(ALenum type) noexcept;
|
||||
|
||||
struct EffectSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALeffect,64>*> Effects{nullptr}; /* 64 */
|
||||
|
||||
EffectSubList() noexcept = default;
|
||||
EffectSubList(const EffectSubList&) = delete;
|
||||
EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; }
|
||||
~EffectSubList();
|
||||
|
||||
EffectSubList& operator=(const EffectSubList&) = delete;
|
||||
EffectSubList& operator=(EffectSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -20,100 +21,87 @@
|
|||
|
||||
namespace {
|
||||
|
||||
void Autowah_setParamf(EffectProps *props, ALenum param, float val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
AutowahProps props{};
|
||||
props.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
|
||||
props.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
|
||||
props.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
|
||||
props.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps AutowahEffectProps{genDefaultProps()};
|
||||
|
||||
void AutowahEffectHandler::SetParami(AutowahProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
||||
void AutowahEffectHandler::SetParamiv(AutowahProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
void AutowahEffectHandler::SetParamf(AutowahProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"};
|
||||
props->Autowah.AttackTime = val;
|
||||
props.AttackTime = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RELEASE_TIME:
|
||||
if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"};
|
||||
props->Autowah.ReleaseTime = val;
|
||||
props.ReleaseTime = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RESONANCE:
|
||||
if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"};
|
||||
props->Autowah.Resonance = val;
|
||||
props.Resonance = val;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_PEAK_GAIN:
|
||||
if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"};
|
||||
props->Autowah.PeakGain = val;
|
||||
props.PeakGain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Autowah_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Autowah_setParamf(props, param, vals[0]); }
|
||||
void AutowahEffectHandler::SetParamfv(AutowahProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Autowah_setParami(EffectProps*, ALenum param, int)
|
||||
void AutowahEffectHandler::GetParami(const AutowahProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
||||
void Autowah_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void AutowahEffectHandler::GetParamiv(const AutowahProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
void Autowah_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void AutowahEffectHandler::GetParamf(const AutowahProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_AUTOWAH_ATTACK_TIME:
|
||||
*val = props->Autowah.AttackTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RELEASE_TIME:
|
||||
*val = props->Autowah.ReleaseTime;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_RESONANCE:
|
||||
*val = props->Autowah.Resonance;
|
||||
break;
|
||||
|
||||
case AL_AUTOWAH_PEAK_GAIN:
|
||||
*val = props->Autowah.PeakGain;
|
||||
break;
|
||||
case AL_AUTOWAH_ATTACK_TIME: *val = props.AttackTime; break;
|
||||
case AL_AUTOWAH_RELEASE_TIME: *val = props.ReleaseTime; break;
|
||||
case AL_AUTOWAH_RESONANCE: *val = props.Resonance; break;
|
||||
case AL_AUTOWAH_PEAK_GAIN: *val = props.PeakGain; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
|
||||
}
|
||||
|
||||
}
|
||||
void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Autowah_getParamf(props, param, vals); }
|
||||
|
||||
void Autowah_getParami(const EffectProps*, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
|
||||
void Autowah_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
|
||||
props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
|
||||
props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
|
||||
props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Autowah);
|
||||
|
||||
const EffectProps AutowahEffectProps{genDefaultProps()};
|
||||
void AutowahEffectHandler::GetParamfv(const AutowahProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -189,62 +177,62 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool AutowahCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxAutowahCommitter::commit(const EAXAUTOWAHPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType
|
||||
&& mEaxProps.mAutowah.flAttackTime == props.mAutowah.flAttackTime
|
||||
&& mEaxProps.mAutowah.flReleaseTime == props.mAutowah.flReleaseTime
|
||||
&& mEaxProps.mAutowah.lResonance == props.mAutowah.lResonance
|
||||
&& mEaxProps.mAutowah.lPeakLevel == props.mAutowah.lPeakLevel)
|
||||
if(auto *cur = std::get_if<EAXAUTOWAHPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
||||
mAlProps.Autowah.AttackTime = props.mAutowah.flAttackTime;
|
||||
mAlProps.Autowah.ReleaseTime = props.mAutowah.flReleaseTime;
|
||||
mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast<float>(props.mAutowah.lResonance));
|
||||
mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast<float>(props.mAutowah.lPeakLevel));
|
||||
mAlProps = [&]{
|
||||
AutowahProps ret{};
|
||||
ret.AttackTime = props.flAttackTime;
|
||||
ret.ReleaseTime = props.flReleaseTime;
|
||||
ret.Resonance = level_mb_to_gain(static_cast<float>(props.lResonance));
|
||||
ret.PeakGain = level_mb_to_gain(static_cast<float>(props.lPeakLevel));
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void AutowahCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxAutowahCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::Autowah;
|
||||
props.mAutowah.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
|
||||
props.mAutowah.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
|
||||
props.mAutowah.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
|
||||
props.mAutowah.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
|
||||
static constexpr EAXAUTOWAHPROPERTIES defprops{[]
|
||||
{
|
||||
EAXAUTOWAHPROPERTIES ret{};
|
||||
ret.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
|
||||
ret.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
|
||||
ret.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
|
||||
ret.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxAutowahCommitter::Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXAUTOWAH_NONE: break;
|
||||
case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props.mAutowah); break;
|
||||
case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.mAutowah.flAttackTime); break;
|
||||
case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.mAutowah.flReleaseTime); break;
|
||||
case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.mAutowah.lResonance); break;
|
||||
case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.mAutowah.lPeakLevel); break;
|
||||
case EAXAUTOWAH_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXAUTOWAH_ATTACKTIME: call.set_value<Exception>(props.flAttackTime); break;
|
||||
case EAXAUTOWAH_RELEASETIME: call.set_value<Exception>(props.flReleaseTime); break;
|
||||
case EAXAUTOWAH_RESONANCE: call.set_value<Exception>(props.lResonance); break;
|
||||
case EAXAUTOWAH_PEAKLEVEL: call.set_value<Exception>(props.lPeakLevel); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxAutowahCommitter::Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXAUTOWAH_NONE: break;
|
||||
case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props.mAutowah); break;
|
||||
case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.mAutowah.flAttackTime); break;
|
||||
case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.mAutowah.flReleaseTime); break;
|
||||
case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.mAutowah.lResonance); break;
|
||||
case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.mAutowah.lPeakLevel); break;
|
||||
case EAXAUTOWAH_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXAUTOWAH_ATTACKTIME: defer<AttackTimeValidator>(call, props.flAttackTime); break;
|
||||
case EAXAUTOWAH_RELEASETIME: defer<ReleaseTimeValidator>(call, props.flReleaseTime); break;
|
||||
case EAXAUTOWAH_RESONANCE: defer<ResonanceValidator>(call, props.lResonance); break;
|
||||
case EAXAUTOWAH_PEAKLEVEL: defer<PeakLevelValidator>(call, props.lPeakLevel); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "core/logging.h"
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -27,16 +25,16 @@ static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too sm
|
|||
static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
|
||||
static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
|
||||
|
||||
inline al::optional<ChorusWaveform> WaveformFromEnum(ALenum type)
|
||||
constexpr std::optional<ChorusWaveform> WaveformFromEnum(ALenum type) noexcept
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM_SINUSOID: return ChorusWaveform::Sinusoid;
|
||||
case AL_CHORUS_WAVEFORM_TRIANGLE: return ChorusWaveform::Triangle;
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
inline ALenum EnumFromWaveform(ChorusWaveform type)
|
||||
constexpr ALenum EnumFromWaveform(ChorusWaveform type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
|
|
@ -46,13 +44,41 @@ inline ALenum EnumFromWaveform(ChorusWaveform type)
|
|||
throw std::runtime_error{"Invalid chorus waveform: "+std::to_string(static_cast<int>(type))};
|
||||
}
|
||||
|
||||
void Chorus_setParami(EffectProps *props, ALenum param, int val)
|
||||
constexpr EffectProps genDefaultChorusProps() noexcept
|
||||
{
|
||||
ChorusProps props{};
|
||||
props.Waveform = WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM).value();
|
||||
props.Phase = AL_CHORUS_DEFAULT_PHASE;
|
||||
props.Rate = AL_CHORUS_DEFAULT_RATE;
|
||||
props.Depth = AL_CHORUS_DEFAULT_DEPTH;
|
||||
props.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
|
||||
props.Delay = AL_CHORUS_DEFAULT_DELAY;
|
||||
return props;
|
||||
}
|
||||
|
||||
constexpr EffectProps genDefaultFlangerProps() noexcept
|
||||
{
|
||||
ChorusProps props{};
|
||||
props.Waveform = WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM).value();
|
||||
props.Phase = AL_FLANGER_DEFAULT_PHASE;
|
||||
props.Rate = AL_FLANGER_DEFAULT_RATE;
|
||||
props.Depth = AL_FLANGER_DEFAULT_DEPTH;
|
||||
props.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
|
||||
props.Delay = AL_FLANGER_DEFAULT_DELAY;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps ChorusEffectProps{genDefaultChorusProps()};
|
||||
|
||||
void ChorusEffectHandler::SetParami(ChorusProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM:
|
||||
if(auto formopt = WaveformFromEnum(val))
|
||||
props->Chorus.Waveform = *formopt;
|
||||
props.Waveform = *formopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform: 0x%04x", val};
|
||||
break;
|
||||
|
|
@ -60,115 +86,89 @@ void Chorus_setParami(EffectProps *props, ALenum param, int val)
|
|||
case AL_CHORUS_PHASE:
|
||||
if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range: %d", val};
|
||||
props->Chorus.Phase = val;
|
||||
props.Phase = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Chorus_setParami(props, param, vals[0]); }
|
||||
void Chorus_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void ChorusEffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
void ChorusEffectHandler::SetParamf(ChorusProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_RATE:
|
||||
if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Chorus rate out of range: %f", val};
|
||||
props->Chorus.Rate = val;
|
||||
props.Rate = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DEPTH:
|
||||
if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Chorus depth out of range: %f", val};
|
||||
props->Chorus.Depth = val;
|
||||
props.Depth = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_FEEDBACK:
|
||||
if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Chorus feedback out of range: %f", val};
|
||||
props->Chorus.Feedback = val;
|
||||
props.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DELAY:
|
||||
if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range: %f", val};
|
||||
props->Chorus.Delay = val;
|
||||
props.Delay = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Chorus_setParamf(props, param, vals[0]); }
|
||||
void ChorusEffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Chorus_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void ChorusEffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_WAVEFORM:
|
||||
*val = EnumFromWaveform(props->Chorus.Waveform);
|
||||
break;
|
||||
|
||||
case AL_CHORUS_PHASE:
|
||||
*val = props->Chorus.Phase;
|
||||
break;
|
||||
case AL_CHORUS_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||
case AL_CHORUS_PHASE: *val = props.Phase; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Chorus_getParami(props, param, vals); }
|
||||
void Chorus_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void ChorusEffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
void ChorusEffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_CHORUS_RATE:
|
||||
*val = props->Chorus.Rate;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DEPTH:
|
||||
*val = props->Chorus.Depth;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_FEEDBACK:
|
||||
*val = props->Chorus.Feedback;
|
||||
break;
|
||||
|
||||
case AL_CHORUS_DELAY:
|
||||
*val = props->Chorus.Delay;
|
||||
break;
|
||||
case AL_CHORUS_RATE: *val = props.Rate; break;
|
||||
case AL_CHORUS_DEPTH: *val = props.Depth; break;
|
||||
case AL_CHORUS_FEEDBACK: *val = props.Feedback; break;
|
||||
case AL_CHORUS_DELAY: *val = props.Delay; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Chorus_getParamf(props, param, vals); }
|
||||
|
||||
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 ChorusEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
|
||||
void Flanger_setParami(EffectProps *props, ALenum param, int val)
|
||||
const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
|
||||
|
||||
void FlangerEffectHandler::SetParami(ChorusProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM:
|
||||
if(auto formopt = WaveformFromEnum(val))
|
||||
props->Chorus.Waveform = *formopt;
|
||||
props.Waveform = *formopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform: 0x%04x", val};
|
||||
break;
|
||||
|
|
@ -176,127 +176,87 @@ void Flanger_setParami(EffectProps *props, ALenum param, int val)
|
|||
case AL_FLANGER_PHASE:
|
||||
if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range: %d", val};
|
||||
props->Chorus.Phase = val;
|
||||
props.Phase = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Flanger_setParami(props, param, vals[0]); }
|
||||
void Flanger_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void FlangerEffectHandler::SetParamiv(ChorusProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
void FlangerEffectHandler::SetParamf(ChorusProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_RATE:
|
||||
if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Flanger rate out of range: %f", val};
|
||||
props->Chorus.Rate = val;
|
||||
props.Rate = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DEPTH:
|
||||
if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Flanger depth out of range: %f", val};
|
||||
props->Chorus.Depth = val;
|
||||
props.Depth = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_FEEDBACK:
|
||||
if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Flanger feedback out of range: %f", val};
|
||||
props->Chorus.Feedback = val;
|
||||
props.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DELAY:
|
||||
if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range: %f", val};
|
||||
props->Chorus.Delay = val;
|
||||
props.Delay = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Flanger_setParamf(props, param, vals[0]); }
|
||||
void FlangerEffectHandler::SetParamfv(ChorusProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Flanger_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void FlangerEffectHandler::GetParami(const ChorusProps &props, ALenum param, int *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_WAVEFORM:
|
||||
*val = EnumFromWaveform(props->Chorus.Waveform);
|
||||
break;
|
||||
|
||||
case AL_FLANGER_PHASE:
|
||||
*val = props->Chorus.Phase;
|
||||
break;
|
||||
case AL_FLANGER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||
case AL_FLANGER_PHASE: *val = props.Phase; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Flanger_getParami(props, param, vals); }
|
||||
void Flanger_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void FlangerEffectHandler::GetParamiv(const ChorusProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
void FlangerEffectHandler::GetParamf(const ChorusProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FLANGER_RATE:
|
||||
*val = props->Chorus.Rate;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DEPTH:
|
||||
*val = props->Chorus.Depth;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_FEEDBACK:
|
||||
*val = props->Chorus.Feedback;
|
||||
break;
|
||||
|
||||
case AL_FLANGER_DELAY:
|
||||
*val = props->Chorus.Delay;
|
||||
break;
|
||||
case AL_FLANGER_RATE: *val = props.Rate; break;
|
||||
case AL_FLANGER_DEPTH: *val = props.Depth; break;
|
||||
case AL_FLANGER_FEEDBACK: *val = props.Feedback; break;
|
||||
case AL_FLANGER_DELAY: *val = props.Delay; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Flanger_getParamf(props, param, vals); }
|
||||
|
||||
EffectProps genDefaultFlangerProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Chorus.Waveform = *WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM);
|
||||
props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE;
|
||||
props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE;
|
||||
props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH;
|
||||
props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
|
||||
props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Chorus);
|
||||
|
||||
const EffectProps ChorusEffectProps{genDefaultChorusProps()};
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Flanger);
|
||||
|
||||
const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
|
||||
void FlangerEffectHandler::GetParamfv(const ChorusProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
||||
struct EaxChorusTraits {
|
||||
using Props = EAXCHORUSPROPERTIES;
|
||||
using EaxProps = EAXCHORUSPROPERTIES;
|
||||
using Committer = EaxChorusCommitter;
|
||||
static constexpr auto Field = &EaxEffectProps::mChorus;
|
||||
|
||||
static constexpr auto eax_effect_type() { return EaxEffectType::Chorus; }
|
||||
static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; }
|
||||
|
||||
static constexpr auto eax_none_param_id() { return EAXCHORUS_NONE; }
|
||||
|
|
@ -359,11 +319,9 @@ struct EaxChorusTraits {
|
|||
}; // EaxChorusTraits
|
||||
|
||||
struct EaxFlangerTraits {
|
||||
using Props = EAXFLANGERPROPERTIES;
|
||||
using EaxProps = EAXFLANGERPROPERTIES;
|
||||
using Committer = EaxFlangerCommitter;
|
||||
static constexpr auto Field = &EaxEffectProps::mFlanger;
|
||||
|
||||
static constexpr auto eax_effect_type() { return EaxEffectType::Flanger; }
|
||||
static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; }
|
||||
|
||||
static constexpr auto eax_none_param_id() { return EAXFLANGER_NONE; }
|
||||
|
|
@ -428,11 +386,10 @@ struct EaxFlangerTraits {
|
|||
template<typename TTraits>
|
||||
struct ChorusFlangerEffect {
|
||||
using Traits = TTraits;
|
||||
using EaxProps = typename Traits::EaxProps;
|
||||
using Committer = typename Traits::Committer;
|
||||
using Exception = typename Committer::Exception;
|
||||
|
||||
static constexpr auto Field = Traits::Field;
|
||||
|
||||
struct WaveformValidator {
|
||||
void operator()(unsigned long ulWaveform) const
|
||||
{
|
||||
|
|
@ -500,7 +457,7 @@ struct ChorusFlangerEffect {
|
|||
}; // DelayValidator
|
||||
|
||||
struct AllValidator {
|
||||
void operator()(const typename Traits::Props& all) const
|
||||
void operator()(const EaxProps& all) const
|
||||
{
|
||||
WaveformValidator{}(all.ulWaveform);
|
||||
PhaseValidator{}(all.lPhase);
|
||||
|
|
@ -514,8 +471,7 @@ struct ChorusFlangerEffect {
|
|||
public:
|
||||
static void SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
auto&& all = props.*Field;
|
||||
props.mType = Traits::eax_effect_type();
|
||||
auto&& all = props.emplace<EaxProps>();
|
||||
all.ulWaveform = Traits::eax_default_waveform();
|
||||
all.lPhase = Traits::eax_default_phase();
|
||||
all.flRate = Traits::eax_default_rate();
|
||||
|
|
@ -525,109 +481,83 @@ public:
|
|||
}
|
||||
|
||||
|
||||
static void Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
static void Get(const EaxCall &call, const EaxProps &all)
|
||||
{
|
||||
auto&& all = props.*Field;
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case Traits::eax_none_param_id():
|
||||
break;
|
||||
|
||||
case Traits::eax_allparameters_param_id():
|
||||
call.template set_value<Exception>(all);
|
||||
break;
|
||||
|
||||
case Traits::eax_waveform_param_id():
|
||||
call.template set_value<Exception>(all.ulWaveform);
|
||||
break;
|
||||
|
||||
case Traits::eax_phase_param_id():
|
||||
call.template set_value<Exception>(all.lPhase);
|
||||
break;
|
||||
|
||||
case Traits::eax_rate_param_id():
|
||||
call.template set_value<Exception>(all.flRate);
|
||||
break;
|
||||
|
||||
case Traits::eax_depth_param_id():
|
||||
call.template set_value<Exception>(all.flDepth);
|
||||
break;
|
||||
|
||||
case Traits::eax_feedback_param_id():
|
||||
call.template set_value<Exception>(all.flFeedback);
|
||||
break;
|
||||
|
||||
case Traits::eax_delay_param_id():
|
||||
call.template set_value<Exception>(all.flDelay);
|
||||
break;
|
||||
|
||||
default:
|
||||
Committer::fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
static void Set(const EaxCall &call, EaxEffectProps &props)
|
||||
static void Set(const EaxCall &call, EaxProps &all)
|
||||
{
|
||||
auto&& all = props.*Field;
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case Traits::eax_none_param_id():
|
||||
break;
|
||||
|
||||
case Traits::eax_allparameters_param_id():
|
||||
Committer::template defer<AllValidator>(call, all);
|
||||
break;
|
||||
|
||||
case Traits::eax_waveform_param_id():
|
||||
Committer::template defer<WaveformValidator>(call, all.ulWaveform);
|
||||
break;
|
||||
|
||||
case Traits::eax_phase_param_id():
|
||||
Committer::template defer<PhaseValidator>(call, all.lPhase);
|
||||
break;
|
||||
|
||||
case Traits::eax_rate_param_id():
|
||||
Committer::template defer<RateValidator>(call, all.flRate);
|
||||
break;
|
||||
|
||||
case Traits::eax_depth_param_id():
|
||||
Committer::template defer<DepthValidator>(call, all.flDepth);
|
||||
break;
|
||||
|
||||
case Traits::eax_feedback_param_id():
|
||||
Committer::template defer<FeedbackValidator>(call, all.flFeedback);
|
||||
break;
|
||||
|
||||
case Traits::eax_delay_param_id():
|
||||
Committer::template defer<DelayValidator>(call, all.flDelay);
|
||||
break;
|
||||
|
||||
default:
|
||||
Committer::fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
static bool Commit(const EaxEffectProps &props, EaxEffectProps &props_, EffectProps &al_props_)
|
||||
static bool Commit(const EaxProps &props, EaxEffectProps &props_, ChorusProps &al_props_)
|
||||
{
|
||||
if(props.mType == props_.mType)
|
||||
{
|
||||
auto&& src = props_.*Field;
|
||||
auto&& dst = props.*Field;
|
||||
if(dst.ulWaveform == src.ulWaveform && dst.lPhase == src.lPhase
|
||||
&& dst.flRate == src.flRate && dst.flDepth == src.flDepth
|
||||
&& dst.flFeedback == src.flFeedback && dst.flDelay == src.flDelay)
|
||||
return false;
|
||||
}
|
||||
if(auto *cur = std::get_if<EaxProps>(&props_); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
props_ = props;
|
||||
auto&& dst = props.*Field;
|
||||
|
||||
al_props_.Chorus.Waveform = Traits::eax_waveform(dst.ulWaveform);
|
||||
al_props_.Chorus.Phase = static_cast<int>(dst.lPhase);
|
||||
al_props_.Chorus.Rate = dst.flRate;
|
||||
al_props_.Chorus.Depth = dst.flDepth;
|
||||
al_props_.Chorus.Feedback = dst.flFeedback;
|
||||
al_props_.Chorus.Delay = dst.flDelay;
|
||||
al_props_.Waveform = Traits::eax_waveform(props.ulWaveform);
|
||||
al_props_.Phase = static_cast<int>(props.lPhase);
|
||||
al_props_.Rate = props.flRate;
|
||||
al_props_.Depth = props.flDepth;
|
||||
al_props_.Feedback = props.flFeedback;
|
||||
al_props_.Delay = props.flDelay;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -652,29 +582,25 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool ChorusCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxChorusCommitter::commit(const EAXCHORUSPROPERTIES &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
||||
return Committer::Commit(props, mEaxProps, mAlProps);
|
||||
return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
|
||||
}
|
||||
|
||||
template<>
|
||||
void ChorusCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxChorusCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
||||
Committer::SetDefaults(props);
|
||||
}
|
||||
|
||||
template<>
|
||||
void ChorusCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxChorusCommitter::Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
||||
Committer::Get(call, props);
|
||||
}
|
||||
|
||||
template<>
|
||||
void ChorusCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxChorusCommitter::Set(const EaxCall &call, EAXCHORUSPROPERTIES &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxChorusTraits>;
|
||||
Committer::Set(call, props);
|
||||
|
|
@ -693,29 +619,25 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool FlangerCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxFlangerCommitter::commit(const EAXFLANGERPROPERTIES &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
||||
return Committer::Commit(props, mEaxProps, mAlProps);
|
||||
return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
|
||||
}
|
||||
|
||||
template<>
|
||||
void FlangerCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxFlangerCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
||||
Committer::SetDefaults(props);
|
||||
}
|
||||
|
||||
template<>
|
||||
void FlangerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxFlangerCommitter::Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
||||
Committer::Get(call, props);
|
||||
}
|
||||
|
||||
template<>
|
||||
void FlangerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxFlangerCommitter::Set(const EaxCall &call, EAXFLANGERPROPERTIES &props)
|
||||
{
|
||||
using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
|
||||
Committer::Set(call, props);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -16,14 +17,25 @@
|
|||
|
||||
namespace {
|
||||
|
||||
void Compressor_setParami(EffectProps *props, ALenum param, int val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
CompressorProps props{};
|
||||
props.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps CompressorEffectProps{genDefaultProps()};
|
||||
|
||||
void CompressorEffectHandler::SetParami(CompressorProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_COMPRESSOR_ONOFF:
|
||||
if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Compressor state out of range"};
|
||||
props->Compressor.OnOff = (val != AL_FALSE);
|
||||
props.OnOff = (val != AL_FALSE);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -31,51 +43,36 @@ void Compressor_setParami(EffectProps *props, ALenum param, int val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Compressor_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Compressor_setParami(props, param, vals[0]); }
|
||||
void Compressor_setParamf(EffectProps*, ALenum param, float)
|
||||
void CompressorEffectHandler::SetParamiv(CompressorProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
void CompressorEffectHandler::SetParamf(CompressorProps&, ALenum param, float)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
|
||||
void Compressor_setParamfv(EffectProps*, ALenum param, const float*)
|
||||
void CompressorEffectHandler::SetParamfv(CompressorProps&, ALenum param, const float*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
void Compressor_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void CompressorEffectHandler::GetParami(const CompressorProps &props, ALenum param, int *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_COMPRESSOR_ONOFF:
|
||||
*val = props->Compressor.OnOff;
|
||||
break;
|
||||
|
||||
case AL_COMPRESSOR_ONOFF: *val = props.OnOff; break;
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Compressor_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Compressor_getParami(props, param, vals); }
|
||||
void Compressor_getParamf(const EffectProps*, ALenum param, float*)
|
||||
void CompressorEffectHandler::GetParamiv(const CompressorProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
void CompressorEffectHandler::GetParamf(const CompressorProps&, ALenum param, float*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; }
|
||||
void Compressor_getParamfv(const EffectProps*, ALenum param, float*)
|
||||
void CompressorEffectHandler::GetParamfv(const CompressorProps&, ALenum param, float*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Compressor);
|
||||
|
||||
const EffectProps CompressorEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -115,46 +112,40 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool CompressorCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxCompressorCommitter::commit(const EAXAGCCOMPRESSORPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType
|
||||
&& props.mCompressor.ulOnOff == mEaxProps.mCompressor.ulOnOff)
|
||||
if(auto *cur = std::get_if<EAXAGCCOMPRESSORPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
mAlProps = CompressorProps{props.ulOnOff != 0};
|
||||
|
||||
mAlProps.Compressor.OnOff = (props.mCompressor.ulOnOff != 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void CompressorCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxCompressorCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::Compressor;
|
||||
props.mCompressor.ulOnOff = EAXAGCCOMPRESSOR_DEFAULTONOFF;
|
||||
props = EAXAGCCOMPRESSORPROPERTIES{EAXAGCCOMPRESSOR_DEFAULTONOFF};
|
||||
}
|
||||
|
||||
template<>
|
||||
void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxCompressorCommitter::Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXAGCCOMPRESSOR_NONE: break;
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props.mCompressor); break;
|
||||
case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.mCompressor.ulOnOff); break;
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXAGCCOMPRESSOR_ONOFF: call.set_value<Exception>(props.ulOnOff); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxCompressorCommitter::Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXAGCCOMPRESSOR_NONE: break;
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props.mCompressor); break;
|
||||
case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.mCompressor.ulOnOff); break;
|
||||
case EAXAGCCOMPRESSOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXAGCCOMPRESSOR_ONOFF: defer<OnOffValidator>(call, props.ulOnOff); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,93 +1,117 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include <algorithm>
|
||||
#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"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void Convolution_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
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{};
|
||||
ConvolutionProps props{};
|
||||
props.OrientAt = {0.0f, 0.0f, -1.0f};
|
||||
props.OrientUp = {0.0f, 1.0f, 0.0f};
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Convolution);
|
||||
|
||||
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 {
|
||||
|
||||
void Dedicated_setParami(EffectProps*, ALenum param, int)
|
||||
constexpr EffectProps genDefaultDialogProps() noexcept
|
||||
{
|
||||
DedicatedProps props{};
|
||||
props.Target = DedicatedProps::Dialog;
|
||||
props.Gain = 1.0f;
|
||||
return props;
|
||||
}
|
||||
|
||||
constexpr EffectProps genDefaultLfeProps() noexcept
|
||||
{
|
||||
DedicatedProps props{};
|
||||
props.Target = DedicatedProps::Lfe;
|
||||
props.Gain = 1.0f;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps DedicatedDialogEffectProps{genDefaultDialogProps()};
|
||||
|
||||
void DedicatedDialogEffectHandler::SetParami(DedicatedProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||
void Dedicated_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void DedicatedDialogEffectHandler::SetParamiv(DedicatedProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Dedicated_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void DedicatedDialogEffectHandler::SetParamf(DedicatedProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN:
|
||||
if(!(val >= 0.0f && std::isfinite(val)))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
|
||||
props->Dedicated.Gain = val;
|
||||
props.Gain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Dedicated_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Dedicated_setParamf(props, param, vals[0]); }
|
||||
void DedicatedDialogEffectHandler::SetParamfv(DedicatedProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Dedicated_getParami(const EffectProps*, ALenum param, int*)
|
||||
void DedicatedDialogEffectHandler::GetParami(const DedicatedProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||
void Dedicated_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void DedicatedDialogEffectHandler::GetParamiv(const DedicatedProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Dedicated_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void DedicatedDialogEffectHandler::GetParamf(const DedicatedProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN: *val = props.Gain; break;
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void DedicatedDialogEffectHandler::GetParamfv(const DedicatedProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
|
||||
const EffectProps DedicatedLfeEffectProps{genDefaultLfeProps()};
|
||||
|
||||
void DedicatedLfeEffectHandler::SetParami(DedicatedProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||
void DedicatedLfeEffectHandler::SetParamiv(DedicatedProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void DedicatedLfeEffectHandler::SetParamf(DedicatedProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN:
|
||||
*val = props->Dedicated.Gain;
|
||||
if(!(val >= 0.0f && std::isfinite(val)))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"};
|
||||
props.Gain = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Dedicated_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Dedicated_getParamf(props, param, vals); }
|
||||
void DedicatedLfeEffectHandler::SetParamfv(DedicatedProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
void DedicatedLfeEffectHandler::GetParami(const DedicatedProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; }
|
||||
void DedicatedLfeEffectHandler::GetParamiv(const DedicatedProps&, ALenum param, int*)
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Dedicated.Gain = 1.0f;
|
||||
return props;
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Dedicated);
|
||||
|
||||
const EffectProps DedicatedEffectProps{genDefaultProps()};
|
||||
void DedicatedLfeEffectHandler::GetParamf(const DedicatedProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DEDICATED_GAIN: *val = props.Gain; break;
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void DedicatedLfeEffectHandler::GetParamfv(const DedicatedProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -16,108 +17,93 @@
|
|||
|
||||
namespace {
|
||||
|
||||
void Distortion_setParami(EffectProps*, ALenum param, int)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
DistortionProps props{};
|
||||
props.Edge = AL_DISTORTION_DEFAULT_EDGE;
|
||||
props.Gain = AL_DISTORTION_DEFAULT_GAIN;
|
||||
props.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
|
||||
props.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
|
||||
props.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps DistortionEffectProps{genDefaultProps()};
|
||||
|
||||
void DistortionEffectHandler::SetParami(DistortionProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
|
||||
void Distortion_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void DistortionEffectHandler::SetParamiv(DistortionProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Distortion_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void DistortionEffectHandler::SetParamf(DistortionProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DISTORTION_EDGE:
|
||||
if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion edge out of range"};
|
||||
props->Distortion.Edge = val;
|
||||
props.Edge = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_GAIN:
|
||||
if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion gain out of range"};
|
||||
props->Distortion.Gain = val;
|
||||
props.Gain = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF:
|
||||
if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion low-pass cutoff out of range"};
|
||||
props->Distortion.LowpassCutoff = val;
|
||||
props.LowpassCutoff = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQCENTER:
|
||||
if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion EQ center out of range"};
|
||||
props->Distortion.EQCenter = val;
|
||||
props.EQCenter = val;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQBANDWIDTH:
|
||||
if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"};
|
||||
props->Distortion.EQBandwidth = val;
|
||||
props.EQBandwidth = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Distortion_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Distortion_setParamf(props, param, vals[0]); }
|
||||
void DistortionEffectHandler::SetParamfv(DistortionProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Distortion_getParami(const EffectProps*, ALenum param, int*)
|
||||
void DistortionEffectHandler::GetParami(const DistortionProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
|
||||
void Distortion_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void DistortionEffectHandler::GetParamiv(const DistortionProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Distortion_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void DistortionEffectHandler::GetParamf(const DistortionProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_DISTORTION_EDGE:
|
||||
*val = props->Distortion.Edge;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_GAIN:
|
||||
*val = props->Distortion.Gain;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF:
|
||||
*val = props->Distortion.LowpassCutoff;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQCENTER:
|
||||
*val = props->Distortion.EQCenter;
|
||||
break;
|
||||
|
||||
case AL_DISTORTION_EQBANDWIDTH:
|
||||
*val = props->Distortion.EQBandwidth;
|
||||
break;
|
||||
case AL_DISTORTION_EDGE: *val = props.Edge; break;
|
||||
case AL_DISTORTION_GAIN: *val = props.Gain; break;
|
||||
case AL_DISTORTION_LOWPASS_CUTOFF: *val = props.LowpassCutoff; break;
|
||||
case AL_DISTORTION_EQCENTER: *val = props.EQCenter; break;
|
||||
case AL_DISTORTION_EQBANDWIDTH: *val = props.EQBandwidth; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Distortion_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Distortion_getParamf(props, param, vals); }
|
||||
void DistortionEffectHandler::GetParamfv(const DistortionProps &props, ALenum param, float *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
|
||||
namespace {
|
||||
|
|
@ -204,66 +190,66 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool DistortionCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxDistortionCommitter::commit(const EAXDISTORTIONPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType && mEaxProps.mDistortion.flEdge == props.mDistortion.flEdge
|
||||
&& mEaxProps.mDistortion.lGain == props.mDistortion.lGain
|
||||
&& mEaxProps.mDistortion.flLowPassCutOff == props.mDistortion.flLowPassCutOff
|
||||
&& mEaxProps.mDistortion.flEQCenter == props.mDistortion.flEQCenter
|
||||
&& mEaxProps.mDistortion.flEQBandwidth == props.mDistortion.flEQBandwidth)
|
||||
if(auto *cur = std::get_if<EAXDISTORTIONPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
||||
mAlProps.Distortion.Edge = props.mDistortion.flEdge;
|
||||
mAlProps.Distortion.Gain = level_mb_to_gain(static_cast<float>(props.mDistortion.lGain));
|
||||
mAlProps.Distortion.LowpassCutoff = props.mDistortion.flLowPassCutOff;
|
||||
mAlProps.Distortion.EQCenter = props.mDistortion.flEQCenter;
|
||||
mAlProps.Distortion.EQBandwidth = props.mDistortion.flEdge;
|
||||
mAlProps = [&]{
|
||||
DistortionProps ret{};
|
||||
ret.Edge = props.flEdge;
|
||||
ret.Gain = level_mb_to_gain(static_cast<float>(props.lGain));
|
||||
ret.LowpassCutoff = props.flLowPassCutOff;
|
||||
ret.EQCenter = props.flEQCenter;
|
||||
ret.EQBandwidth = props.flEdge;
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void DistortionCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxDistortionCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::Distortion;
|
||||
props.mDistortion.flEdge = EAXDISTORTION_DEFAULTEDGE;
|
||||
props.mDistortion.lGain = EAXDISTORTION_DEFAULTGAIN;
|
||||
props.mDistortion.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
|
||||
props.mDistortion.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
|
||||
props.mDistortion.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
|
||||
static constexpr EAXDISTORTIONPROPERTIES defprops{[]
|
||||
{
|
||||
EAXDISTORTIONPROPERTIES ret{};
|
||||
ret.flEdge = EAXDISTORTION_DEFAULTEDGE;
|
||||
ret.lGain = EAXDISTORTION_DEFAULTGAIN;
|
||||
ret.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
|
||||
ret.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
|
||||
ret.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxDistortionCommitter::Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXDISTORTION_NONE: break;
|
||||
case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props.mDistortion); break;
|
||||
case EAXDISTORTION_EDGE: call.set_value<Exception>(props.mDistortion.flEdge); break;
|
||||
case EAXDISTORTION_GAIN: call.set_value<Exception>(props.mDistortion.lGain); break;
|
||||
case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.mDistortion.flLowPassCutOff); break;
|
||||
case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.mDistortion.flEQCenter); break;
|
||||
case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.mDistortion.flEQBandwidth); break;
|
||||
case EAXDISTORTION_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXDISTORTION_EDGE: call.set_value<Exception>(props.flEdge); break;
|
||||
case EAXDISTORTION_GAIN: call.set_value<Exception>(props.lGain); break;
|
||||
case EAXDISTORTION_LOWPASSCUTOFF: call.set_value<Exception>(props.flLowPassCutOff); break;
|
||||
case EAXDISTORTION_EQCENTER: call.set_value<Exception>(props.flEQCenter); break;
|
||||
case EAXDISTORTION_EQBANDWIDTH: call.set_value<Exception>(props.flEQBandwidth); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxDistortionCommitter::Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXDISTORTION_NONE: break;
|
||||
case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props.mDistortion); break;
|
||||
case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.mDistortion.flEdge); break;
|
||||
case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.mDistortion.lGain); break;
|
||||
case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.mDistortion.flLowPassCutOff); break;
|
||||
case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.mDistortion.flEQCenter); break;
|
||||
case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.mDistortion.flEQBandwidth); break;
|
||||
case EAXDISTORTION_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXDISTORTION_EDGE: defer<EdgeValidator>(call, props.flEdge); break;
|
||||
case EAXDISTORTION_GAIN: defer<GainValidator>(call, props.lGain); break;
|
||||
case EAXDISTORTION_LOWPASSCUTOFF: defer<LowPassCutOffValidator>(call, props.flLowPassCutOff); break;
|
||||
case EAXDISTORTION_EQCENTER: defer<EqCenterValidator>(call, props.flEQCenter); break;
|
||||
case EAXDISTORTION_EQBANDWIDTH: defer<EqBandwidthValidator>(call, props.flEQBandwidth); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -19,102 +20,87 @@ namespace {
|
|||
static_assert(EchoMaxDelay >= AL_ECHO_MAX_DELAY, "Echo max delay too short");
|
||||
static_assert(EchoMaxLRDelay >= AL_ECHO_MAX_LRDELAY, "Echo max left-right delay too short");
|
||||
|
||||
void Echo_setParami(EffectProps*, ALenum param, int)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EchoProps props{};
|
||||
props.Delay = AL_ECHO_DEFAULT_DELAY;
|
||||
props.LRDelay = AL_ECHO_DEFAULT_LRDELAY;
|
||||
props.Damping = AL_ECHO_DEFAULT_DAMPING;
|
||||
props.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
|
||||
props.Spread = AL_ECHO_DEFAULT_SPREAD;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps EchoEffectProps{genDefaultProps()};
|
||||
|
||||
void EchoEffectHandler::SetParami(EchoProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
|
||||
void Echo_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void EchoEffectHandler::SetParamiv(EchoProps&, ALenum param, const int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
|
||||
void Echo_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void EchoEffectHandler::SetParamf(EchoProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_ECHO_DELAY:
|
||||
if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Echo delay out of range"};
|
||||
props->Echo.Delay = val;
|
||||
props.Delay = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_LRDELAY:
|
||||
if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Echo LR delay out of range"};
|
||||
props->Echo.LRDelay = val;
|
||||
props.LRDelay = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_DAMPING:
|
||||
if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Echo damping out of range"};
|
||||
props->Echo.Damping = val;
|
||||
props.Damping = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_FEEDBACK:
|
||||
if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Echo feedback out of range"};
|
||||
props->Echo.Feedback = val;
|
||||
props.Feedback = val;
|
||||
break;
|
||||
|
||||
case AL_ECHO_SPREAD:
|
||||
if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"};
|
||||
props->Echo.Spread = val;
|
||||
props.Spread = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Echo_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Echo_setParamf(props, param, vals[0]); }
|
||||
void EchoEffectHandler::SetParamfv(EchoProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Echo_getParami(const EffectProps*, ALenum param, int*)
|
||||
void EchoEffectHandler::GetParami(const EchoProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
|
||||
void Echo_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void EchoEffectHandler::GetParamiv(const EchoProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
|
||||
void Echo_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void EchoEffectHandler::GetParamf(const EchoProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_ECHO_DELAY:
|
||||
*val = props->Echo.Delay;
|
||||
break;
|
||||
|
||||
case AL_ECHO_LRDELAY:
|
||||
*val = props->Echo.LRDelay;
|
||||
break;
|
||||
|
||||
case AL_ECHO_DAMPING:
|
||||
*val = props->Echo.Damping;
|
||||
break;
|
||||
|
||||
case AL_ECHO_FEEDBACK:
|
||||
*val = props->Echo.Feedback;
|
||||
break;
|
||||
|
||||
case AL_ECHO_SPREAD:
|
||||
*val = props->Echo.Spread;
|
||||
break;
|
||||
case AL_ECHO_DELAY: *val = props.Delay; break;
|
||||
case AL_ECHO_LRDELAY: *val = props.LRDelay; break;
|
||||
case AL_ECHO_DAMPING: *val = props.Damping; break;
|
||||
case AL_ECHO_FEEDBACK: *val = props.Feedback; break;
|
||||
case AL_ECHO_SPREAD: *val = props.Spread; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Echo_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Echo_getParamf(props, param, vals); }
|
||||
void EchoEffectHandler::GetParamfv(const EchoProps &props, ALenum param, float *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
|
||||
namespace {
|
||||
|
|
@ -201,66 +187,66 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool EchoCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxEchoCommitter::commit(const EAXECHOPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType && mEaxProps.mEcho.flDelay == props.mEcho.flDelay
|
||||
&& mEaxProps.mEcho.flLRDelay == props.mEcho.flLRDelay
|
||||
&& mEaxProps.mEcho.flDamping == props.mEcho.flDamping
|
||||
&& mEaxProps.mEcho.flFeedback == props.mEcho.flFeedback
|
||||
&& mEaxProps.mEcho.flSpread == props.mEcho.flSpread)
|
||||
if(auto *cur = std::get_if<EAXECHOPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
||||
mAlProps.Echo.Delay = props.mEcho.flDelay;
|
||||
mAlProps.Echo.LRDelay = props.mEcho.flLRDelay;
|
||||
mAlProps.Echo.Damping = props.mEcho.flDamping;
|
||||
mAlProps.Echo.Feedback = props.mEcho.flFeedback;
|
||||
mAlProps.Echo.Spread = props.mEcho.flSpread;
|
||||
mAlProps = [&]{
|
||||
EchoProps ret{};
|
||||
ret.Delay = props.flDelay;
|
||||
ret.LRDelay = props.flLRDelay;
|
||||
ret.Damping = props.flDamping;
|
||||
ret.Feedback = props.flFeedback;
|
||||
ret.Spread = props.flSpread;
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void EchoCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxEchoCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::Echo;
|
||||
props.mEcho.flDelay = EAXECHO_DEFAULTDELAY;
|
||||
props.mEcho.flLRDelay = EAXECHO_DEFAULTLRDELAY;
|
||||
props.mEcho.flDamping = EAXECHO_DEFAULTDAMPING;
|
||||
props.mEcho.flFeedback = EAXECHO_DEFAULTFEEDBACK;
|
||||
props.mEcho.flSpread = EAXECHO_DEFAULTSPREAD;
|
||||
static constexpr EAXECHOPROPERTIES defprops{[]
|
||||
{
|
||||
EAXECHOPROPERTIES ret{};
|
||||
ret.flDelay = EAXECHO_DEFAULTDELAY;
|
||||
ret.flLRDelay = EAXECHO_DEFAULTLRDELAY;
|
||||
ret.flDamping = EAXECHO_DEFAULTDAMPING;
|
||||
ret.flFeedback = EAXECHO_DEFAULTFEEDBACK;
|
||||
ret.flSpread = EAXECHO_DEFAULTSPREAD;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxEchoCommitter::Get(const EaxCall &call, const EAXECHOPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXECHO_NONE: break;
|
||||
case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props.mEcho); break;
|
||||
case EAXECHO_DELAY: call.set_value<Exception>(props.mEcho.flDelay); break;
|
||||
case EAXECHO_LRDELAY: call.set_value<Exception>(props.mEcho.flLRDelay); break;
|
||||
case EAXECHO_DAMPING: call.set_value<Exception>(props.mEcho.flDamping); break;
|
||||
case EAXECHO_FEEDBACK: call.set_value<Exception>(props.mEcho.flFeedback); break;
|
||||
case EAXECHO_SPREAD: call.set_value<Exception>(props.mEcho.flSpread); break;
|
||||
case EAXECHO_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXECHO_DELAY: call.set_value<Exception>(props.flDelay); break;
|
||||
case EAXECHO_LRDELAY: call.set_value<Exception>(props.flLRDelay); break;
|
||||
case EAXECHO_DAMPING: call.set_value<Exception>(props.flDamping); break;
|
||||
case EAXECHO_FEEDBACK: call.set_value<Exception>(props.flFeedback); break;
|
||||
case EAXECHO_SPREAD: call.set_value<Exception>(props.flSpread); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxEchoCommitter::Set(const EaxCall &call, EAXECHOPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXECHO_NONE: break;
|
||||
case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props.mEcho); break;
|
||||
case EAXECHO_DELAY: defer<DelayValidator>(call, props.mEcho.flDelay); break;
|
||||
case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.mEcho.flLRDelay); break;
|
||||
case EAXECHO_DAMPING: defer<DampingValidator>(call, props.mEcho.flDamping); break;
|
||||
case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.mEcho.flFeedback); break;
|
||||
case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.mEcho.flSpread); break;
|
||||
case EAXECHO_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXECHO_DELAY: defer<DelayValidator>(call, props.flDelay); break;
|
||||
case EAXECHO_LRDELAY: defer<LrDelayValidator>(call, props.flLRDelay); break;
|
||||
case EAXECHO_DAMPING: defer<DampingValidator>(call, props.flDamping); break;
|
||||
case EAXECHO_FEEDBACK: defer<FeedbackValidator>(call, props.flFeedback); break;
|
||||
case EAXECHO_SPREAD: defer<SpreadValidator>(call, props.flSpread); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
#include "config.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
|
||||
#include <cassert>
|
||||
#include "AL/efx.h"
|
||||
#include "effects.h"
|
||||
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
|
|||
|
|
@ -1,52 +1,47 @@
|
|||
#ifndef AL_EFFECTS_EFFECTS_H
|
||||
#define AL_EFFECTS_EFFECTS_H
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "AL/al.h"
|
||||
|
||||
#include "core/except.h"
|
||||
#include "al/error.h"
|
||||
#include "core/effects/base.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "al/eax/effect.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
union EffectProps;
|
||||
|
||||
|
||||
class effect_exception final : public al::base_exception {
|
||||
ALenum mErrorCode;
|
||||
|
||||
public:
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
[[gnu::format(gnu_printf, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
effect_exception(ALenum code, const char *msg, ...);
|
||||
~effect_exception() override;
|
||||
|
||||
ALenum errorCode() const noexcept { return mErrorCode; }
|
||||
#define DECL_HANDLER(N, T) \
|
||||
struct N { \
|
||||
using prop_type = T; \
|
||||
\
|
||||
static void SetParami(prop_type &props, ALenum param, int val); \
|
||||
static void SetParamiv(prop_type &props, ALenum param, const int *vals); \
|
||||
static void SetParamf(prop_type &props, ALenum param, float val); \
|
||||
static void SetParamfv(prop_type &props, ALenum param, const float *vals);\
|
||||
static void GetParami(const prop_type &props, ALenum param, int *val); \
|
||||
static void GetParamiv(const prop_type &props, ALenum param, int *vals); \
|
||||
static void GetParamf(const prop_type &props, ALenum param, float *val); \
|
||||
static void GetParamfv(const prop_type &props, ALenum param, float *vals);\
|
||||
};
|
||||
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 {
|
||||
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, \
|
||||
}
|
||||
using effect_exception = al::context_error;
|
||||
|
||||
|
||||
/* Default properties for the given effect types. */
|
||||
|
|
@ -64,25 +59,8 @@ extern const EffectProps FshifterEffectProps;
|
|||
extern const EffectProps ModulatorEffectProps;
|
||||
extern const EffectProps PshifterEffectProps;
|
||||
extern const EffectProps VmorpherEffectProps;
|
||||
extern const EffectProps DedicatedEffectProps;
|
||||
extern const EffectProps DedicatedDialogEffectProps;
|
||||
extern const EffectProps DedicatedLfeEffectProps;
|
||||
extern const EffectProps ConvolutionEffectProps;
|
||||
|
||||
/* Vtables to get/set properties for the given effect types. */
|
||||
extern const EffectVtable NullEffectVtable;
|
||||
extern const EffectVtable ReverbEffectVtable;
|
||||
extern const EffectVtable StdReverbEffectVtable;
|
||||
extern const EffectVtable AutowahEffectVtable;
|
||||
extern const EffectVtable ChorusEffectVtable;
|
||||
extern const EffectVtable CompressorEffectVtable;
|
||||
extern const EffectVtable DistortionEffectVtable;
|
||||
extern const EffectVtable EchoEffectVtable;
|
||||
extern const EffectVtable EqualizerEffectVtable;
|
||||
extern const EffectVtable FlangerEffectVtable;
|
||||
extern const EffectVtable FshifterEffectVtable;
|
||||
extern const EffectVtable ModulatorEffectVtable;
|
||||
extern const EffectVtable PshifterEffectVtable;
|
||||
extern const EffectVtable VmorpherEffectVtable;
|
||||
extern const EffectVtable DedicatedEffectVtable;
|
||||
extern const EffectVtable ConvolutionEffectVtable;
|
||||
|
||||
#endif /* AL_EFFECTS_EFFECTS_H */
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -16,163 +17,133 @@
|
|||
|
||||
namespace {
|
||||
|
||||
void Equalizer_setParami(EffectProps*, ALenum param, int)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EqualizerProps props{};
|
||||
props.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
|
||||
props.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
|
||||
props.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
|
||||
props.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
|
||||
props.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
|
||||
props.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
|
||||
props.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
|
||||
props.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
|
||||
props.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
|
||||
props.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps EqualizerEffectProps{genDefaultProps()};
|
||||
|
||||
void EqualizerEffectHandler::SetParami(EqualizerProps&, ALenum param, int)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
|
||||
void Equalizer_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void EqualizerEffectHandler::SetParamiv(EqualizerProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Equalizer_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void EqualizerEffectHandler::SetParamf(EqualizerProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_EQUALIZER_LOW_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band gain out of range"};
|
||||
props->Equalizer.LowGain = val;
|
||||
props.LowGain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_LOW_CUTOFF:
|
||||
if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band cutoff out of range"};
|
||||
props->Equalizer.LowCutoff = val;
|
||||
props.LowCutoff = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band gain out of range"};
|
||||
props->Equalizer.Mid1Gain = val;
|
||||
props.Mid1Gain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_CENTER:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band center out of range"};
|
||||
props->Equalizer.Mid1Center = val;
|
||||
props.Mid1Center = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_WIDTH:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band width out of range"};
|
||||
props->Equalizer.Mid1Width = val;
|
||||
props.Mid1Width = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band gain out of range"};
|
||||
props->Equalizer.Mid2Gain = val;
|
||||
props.Mid2Gain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_CENTER:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band center out of range"};
|
||||
props->Equalizer.Mid2Center = val;
|
||||
props.Mid2Center = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_WIDTH:
|
||||
if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band width out of range"};
|
||||
props->Equalizer.Mid2Width = val;
|
||||
props.Mid2Width = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_GAIN:
|
||||
if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band gain out of range"};
|
||||
props->Equalizer.HighGain = val;
|
||||
props.HighGain = val;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_CUTOFF:
|
||||
if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band cutoff out of range"};
|
||||
props->Equalizer.HighCutoff = val;
|
||||
props.HighCutoff = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Equalizer_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Equalizer_setParamf(props, param, vals[0]); }
|
||||
void EqualizerEffectHandler::SetParamfv(EqualizerProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Equalizer_getParami(const EffectProps*, ALenum param, int*)
|
||||
void EqualizerEffectHandler::GetParami(const EqualizerProps&, ALenum param, int*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; }
|
||||
void Equalizer_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void EqualizerEffectHandler::GetParamiv(const EqualizerProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Equalizer_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void EqualizerEffectHandler::GetParamf(const EqualizerProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_EQUALIZER_LOW_GAIN:
|
||||
*val = props->Equalizer.LowGain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_LOW_CUTOFF:
|
||||
*val = props->Equalizer.LowCutoff;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_GAIN:
|
||||
*val = props->Equalizer.Mid1Gain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_CENTER:
|
||||
*val = props->Equalizer.Mid1Center;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID1_WIDTH:
|
||||
*val = props->Equalizer.Mid1Width;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_GAIN:
|
||||
*val = props->Equalizer.Mid2Gain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_CENTER:
|
||||
*val = props->Equalizer.Mid2Center;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_MID2_WIDTH:
|
||||
*val = props->Equalizer.Mid2Width;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_GAIN:
|
||||
*val = props->Equalizer.HighGain;
|
||||
break;
|
||||
|
||||
case AL_EQUALIZER_HIGH_CUTOFF:
|
||||
*val = props->Equalizer.HighCutoff;
|
||||
break;
|
||||
case AL_EQUALIZER_LOW_GAIN: *val = props.LowGain; break;
|
||||
case AL_EQUALIZER_LOW_CUTOFF: *val = props.LowCutoff; break;
|
||||
case AL_EQUALIZER_MID1_GAIN: *val = props.Mid1Gain; break;
|
||||
case AL_EQUALIZER_MID1_CENTER: *val = props.Mid1Center; break;
|
||||
case AL_EQUALIZER_MID1_WIDTH: *val = props.Mid1Width; break;
|
||||
case AL_EQUALIZER_MID2_GAIN: *val = props.Mid2Gain; break;
|
||||
case AL_EQUALIZER_MID2_CENTER: *val = props.Mid2Center; break;
|
||||
case AL_EQUALIZER_MID2_WIDTH: *val = props.Mid2Width; break;
|
||||
case AL_EQUALIZER_HIGH_GAIN: *val = props.HighGain; break;
|
||||
case AL_EQUALIZER_HIGH_CUTOFF: *val = props.HighCutoff; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Equalizer_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Equalizer_getParamf(props, param, vals); }
|
||||
void EqualizerEffectHandler::GetParamfv(const EqualizerProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF;
|
||||
props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN;
|
||||
props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER;
|
||||
props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN;
|
||||
props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH;
|
||||
props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER;
|
||||
props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN;
|
||||
props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH;
|
||||
props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF;
|
||||
props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Equalizer);
|
||||
|
||||
const EffectProps EqualizerEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -319,91 +290,86 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool EqualizerCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxEqualizerCommitter::commit(const EAXEQUALIZERPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType && mEaxProps.mEqualizer.lLowGain == props.mEqualizer.lLowGain
|
||||
&& mEaxProps.mEqualizer.flLowCutOff == props.mEqualizer.flLowCutOff
|
||||
&& mEaxProps.mEqualizer.lMid1Gain == props.mEqualizer.lMid1Gain
|
||||
&& mEaxProps.mEqualizer.flMid1Center == props.mEqualizer.flMid1Center
|
||||
&& mEaxProps.mEqualizer.flMid1Width == props.mEqualizer.flMid1Width
|
||||
&& mEaxProps.mEqualizer.lMid2Gain == props.mEqualizer.lMid2Gain
|
||||
&& mEaxProps.mEqualizer.flMid2Center == props.mEqualizer.flMid2Center
|
||||
&& mEaxProps.mEqualizer.flMid2Width == props.mEqualizer.flMid2Width
|
||||
&& mEaxProps.mEqualizer.lHighGain == props.mEqualizer.lHighGain
|
||||
&& mEaxProps.mEqualizer.flHighCutOff == props.mEqualizer.flHighCutOff)
|
||||
if(auto *cur = std::get_if<EAXEQUALIZERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
||||
mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lLowGain));
|
||||
mAlProps.Equalizer.LowCutoff = props.mEqualizer.flLowCutOff;
|
||||
mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid1Gain));
|
||||
mAlProps.Equalizer.Mid1Center = props.mEqualizer.flMid1Center;
|
||||
mAlProps.Equalizer.Mid1Width = props.mEqualizer.flMid1Width;
|
||||
mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lMid2Gain));
|
||||
mAlProps.Equalizer.Mid2Center = props.mEqualizer.flMid2Center;
|
||||
mAlProps.Equalizer.Mid2Width = props.mEqualizer.flMid2Width;
|
||||
mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast<float>(props.mEqualizer.lHighGain));
|
||||
mAlProps.Equalizer.HighCutoff = props.mEqualizer.flHighCutOff;
|
||||
mAlProps = [&]{
|
||||
EqualizerProps ret{};
|
||||
ret.LowGain = level_mb_to_gain(static_cast<float>(props.lLowGain));
|
||||
ret.LowCutoff = props.flLowCutOff;
|
||||
ret.Mid1Gain = level_mb_to_gain(static_cast<float>(props.lMid1Gain));
|
||||
ret.Mid1Center = props.flMid1Center;
|
||||
ret.Mid1Width = props.flMid1Width;
|
||||
ret.Mid2Gain = level_mb_to_gain(static_cast<float>(props.lMid2Gain));
|
||||
ret.Mid2Center = props.flMid2Center;
|
||||
ret.Mid2Width = props.flMid2Width;
|
||||
ret.HighGain = level_mb_to_gain(static_cast<float>(props.lHighGain));
|
||||
ret.HighCutoff = props.flHighCutOff;
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void EqualizerCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxEqualizerCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::Equalizer;
|
||||
props.mEqualizer.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
|
||||
props.mEqualizer.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
|
||||
props.mEqualizer.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
|
||||
props.mEqualizer.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
|
||||
props.mEqualizer.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
|
||||
props.mEqualizer.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
|
||||
props.mEqualizer.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
|
||||
props.mEqualizer.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
|
||||
props.mEqualizer.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
|
||||
props.mEqualizer.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
|
||||
static constexpr EAXEQUALIZERPROPERTIES defprops{[]
|
||||
{
|
||||
EAXEQUALIZERPROPERTIES ret{};
|
||||
ret.lLowGain = EAXEQUALIZER_DEFAULTLOWGAIN;
|
||||
ret.flLowCutOff = EAXEQUALIZER_DEFAULTLOWCUTOFF;
|
||||
ret.lMid1Gain = EAXEQUALIZER_DEFAULTMID1GAIN;
|
||||
ret.flMid1Center = EAXEQUALIZER_DEFAULTMID1CENTER;
|
||||
ret.flMid1Width = EAXEQUALIZER_DEFAULTMID1WIDTH;
|
||||
ret.lMid2Gain = EAXEQUALIZER_DEFAULTMID2GAIN;
|
||||
ret.flMid2Center = EAXEQUALIZER_DEFAULTMID2CENTER;
|
||||
ret.flMid2Width = EAXEQUALIZER_DEFAULTMID2WIDTH;
|
||||
ret.lHighGain = EAXEQUALIZER_DEFAULTHIGHGAIN;
|
||||
ret.flHighCutOff = EAXEQUALIZER_DEFAULTHIGHCUTOFF;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxEqualizerCommitter::Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXEQUALIZER_NONE: break;
|
||||
case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props.mEqualizer); break;
|
||||
case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.mEqualizer.lLowGain); break;
|
||||
case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.mEqualizer.flLowCutOff); break;
|
||||
case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.mEqualizer.lMid1Gain); break;
|
||||
case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.mEqualizer.flMid1Center); break;
|
||||
case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.mEqualizer.flMid1Width); break;
|
||||
case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.mEqualizer.lMid2Gain); break;
|
||||
case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.mEqualizer.flMid2Center); break;
|
||||
case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.mEqualizer.flMid2Width); break;
|
||||
case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.mEqualizer.lHighGain); break;
|
||||
case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.mEqualizer.flHighCutOff); break;
|
||||
case EAXEQUALIZER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXEQUALIZER_LOWGAIN: call.set_value<Exception>(props.lLowGain); break;
|
||||
case EAXEQUALIZER_LOWCUTOFF: call.set_value<Exception>(props.flLowCutOff); break;
|
||||
case EAXEQUALIZER_MID1GAIN: call.set_value<Exception>(props.lMid1Gain); break;
|
||||
case EAXEQUALIZER_MID1CENTER: call.set_value<Exception>(props.flMid1Center); break;
|
||||
case EAXEQUALIZER_MID1WIDTH: call.set_value<Exception>(props.flMid1Width); break;
|
||||
case EAXEQUALIZER_MID2GAIN: call.set_value<Exception>(props.lMid2Gain); break;
|
||||
case EAXEQUALIZER_MID2CENTER: call.set_value<Exception>(props.flMid2Center); break;
|
||||
case EAXEQUALIZER_MID2WIDTH: call.set_value<Exception>(props.flMid2Width); break;
|
||||
case EAXEQUALIZER_HIGHGAIN: call.set_value<Exception>(props.lHighGain); break;
|
||||
case EAXEQUALIZER_HIGHCUTOFF: call.set_value<Exception>(props.flHighCutOff); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxEqualizerCommitter::Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXEQUALIZER_NONE: break;
|
||||
case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props.mEqualizer); break;
|
||||
case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.mEqualizer.lLowGain); break;
|
||||
case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.mEqualizer.flLowCutOff); break;
|
||||
case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.mEqualizer.lMid1Gain); break;
|
||||
case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.mEqualizer.flMid1Center); break;
|
||||
case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.mEqualizer.flMid1Width); break;
|
||||
case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.mEqualizer.lMid2Gain); break;
|
||||
case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.mEqualizer.flMid2Center); break;
|
||||
case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.mEqualizer.flMid2Width); break;
|
||||
case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.mEqualizer.lHighGain); break;
|
||||
case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.mEqualizer.flHighCutOff); break;
|
||||
case EAXEQUALIZER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXEQUALIZER_LOWGAIN: defer<LowGainValidator>(call, props.lLowGain); break;
|
||||
case EAXEQUALIZER_LOWCUTOFF: defer<LowCutOffValidator>(call, props.flLowCutOff); break;
|
||||
case EAXEQUALIZER_MID1GAIN: defer<Mid1GainValidator>(call, props.lMid1Gain); break;
|
||||
case EAXEQUALIZER_MID1CENTER: defer<Mid1CenterValidator>(call, props.flMid1Center); break;
|
||||
case EAXEQUALIZER_MID1WIDTH: defer<Mid1WidthValidator>(call, props.flMid1Width); break;
|
||||
case EAXEQUALIZER_MID2GAIN: defer<Mid2GainValidator>(call, props.lMid2Gain); break;
|
||||
case EAXEQUALIZER_MID2CENTER: defer<Mid2CenterValidator>(call, props.flMid2Center); break;
|
||||
case EAXEQUALIZER_MID2WIDTH: defer<Mid2WidthValidator>(call, props.flMid2Width); break;
|
||||
case EAXEQUALIZER_HIGHGAIN: defer<HighGainValidator>(call, props.lHighGain); break;
|
||||
case EAXEQUALIZER_HIGHCUTOFF: defer<HighCutOffValidator>(call, props.flHighCutOff); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -20,7 +21,7 @@
|
|||
|
||||
namespace {
|
||||
|
||||
al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
|
||||
constexpr std::optional<FShifterDirection> DirectionFromEmum(ALenum value) noexcept
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
|
|
@ -28,9 +29,9 @@ al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
|
|||
case AL_FREQUENCY_SHIFTER_DIRECTION_UP: return FShifterDirection::Up;
|
||||
case AL_FREQUENCY_SHIFTER_DIRECTION_OFF: return FShifterDirection::Off;
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
ALenum EnumFromDirection(FShifterDirection dir)
|
||||
constexpr ALenum EnumFromDirection(FShifterDirection dir)
|
||||
{
|
||||
switch(dir)
|
||||
{
|
||||
|
|
@ -41,31 +42,26 @@ ALenum EnumFromDirection(FShifterDirection dir)
|
|||
throw std::runtime_error{"Invalid direction: "+std::to_string(static_cast<int>(dir))};
|
||||
}
|
||||
|
||||
void Fshifter_setParamf(EffectProps *props, ALenum param, float val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_FREQUENCY:
|
||||
if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"};
|
||||
props->Fshifter.Frequency = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
|
||||
param};
|
||||
}
|
||||
FshifterProps props{};
|
||||
props.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
|
||||
props.LeftDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION).value();
|
||||
props.RightDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION).value();
|
||||
return props;
|
||||
}
|
||||
void Fshifter_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Fshifter_setParamf(props, param, vals[0]); }
|
||||
|
||||
void Fshifter_setParami(EffectProps *props, ALenum param, int val)
|
||||
} // namespace
|
||||
|
||||
const EffectProps FshifterEffectProps{genDefaultProps()};
|
||||
|
||||
void FshifterEffectHandler::SetParami(FshifterProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
||||
if(auto diropt = DirectionFromEmum(val))
|
||||
props->Fshifter.LeftDirection = *diropt;
|
||||
props.LeftDirection = *diropt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE,
|
||||
"Unsupported frequency shifter left direction: 0x%04x", val};
|
||||
|
|
@ -73,7 +69,7 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val)
|
|||
|
||||
case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
|
||||
if(auto diropt = DirectionFromEmum(val))
|
||||
props->Fshifter.RightDirection = *diropt;
|
||||
props.RightDirection = *diropt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE,
|
||||
"Unsupported frequency shifter right direction: 0x%04x", val};
|
||||
|
|
@ -84,33 +80,17 @@ void Fshifter_setParami(EffectProps *props, ALenum param, int val)
|
|||
"Invalid frequency shifter integer property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Fshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Fshifter_setParami(props, param, vals[0]); }
|
||||
void FshifterEffectHandler::SetParamiv(FshifterProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
|
||||
void Fshifter_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
{
|
||||
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)
|
||||
void FshifterEffectHandler::SetParamf(FshifterProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
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;
|
||||
|
||||
default:
|
||||
|
|
@ -118,23 +98,44 @@ void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Fshifter_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Fshifter_getParamf(props, param, vals); }
|
||||
void FshifterEffectHandler::SetParamfv(FshifterProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
void FshifterEffectHandler::GetParami(const FshifterProps &props, ALenum param, int *val)
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
|
||||
props.Fshifter.LeftDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION);
|
||||
props.Fshifter.RightDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION);
|
||||
return props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
|
||||
*val = EnumFromDirection(props.LeftDirection);
|
||||
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
|
||||
namespace {
|
||||
|
|
@ -197,13 +198,9 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxFrequencyShifterCommitter::commit(const EAXFREQUENCYSHIFTERPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType
|
||||
&& mEaxProps.mFrequencyShifter.flFrequency == props.mFrequencyShifter.flFrequency
|
||||
&& mEaxProps.mFrequencyShifter.ulLeftDirection == props.mFrequencyShifter.ulLeftDirection
|
||||
&& mEaxProps.mFrequencyShifter.ulRightDirection == props.mFrequencyShifter.ulRightDirection)
|
||||
if(auto *cur = std::get_if<EAXFREQUENCYSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
|
@ -217,46 +214,52 @@ bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
|
|||
return FShifterDirection::Off;
|
||||
};
|
||||
|
||||
mAlProps.Fshifter.Frequency = props.mFrequencyShifter.flFrequency;
|
||||
mAlProps.Fshifter.LeftDirection = get_direction(props.mFrequencyShifter.ulLeftDirection);
|
||||
mAlProps.Fshifter.RightDirection = get_direction(props.mFrequencyShifter.ulRightDirection);
|
||||
mAlProps = [&]{
|
||||
FshifterProps ret{};
|
||||
ret.Frequency = props.flFrequency;
|
||||
ret.LeftDirection = get_direction(props.ulLeftDirection);
|
||||
ret.RightDirection = get_direction(props.ulRightDirection);
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxFrequencyShifterCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::FrequencyShifter;
|
||||
props.mFrequencyShifter.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
|
||||
props.mFrequencyShifter.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
|
||||
props.mFrequencyShifter.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
|
||||
static constexpr EAXFREQUENCYSHIFTERPROPERTIES defprops{[]
|
||||
{
|
||||
EAXFREQUENCYSHIFTERPROPERTIES ret{};
|
||||
ret.flFrequency = EAXFREQUENCYSHIFTER_DEFAULTFREQUENCY;
|
||||
ret.ulLeftDirection = EAXFREQUENCYSHIFTER_DEFAULTLEFTDIRECTION;
|
||||
ret.ulRightDirection = EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxFrequencyShifterCommitter::Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXFREQUENCYSHIFTER_NONE: break;
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mFrequencyShifter); break;
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.mFrequencyShifter.flFrequency); break;
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulLeftDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.mFrequencyShifter.ulRightDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: call.set_value<Exception>(props.ulLeftDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: call.set_value<Exception>(props.ulRightDirection); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxFrequencyShifterCommitter::Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXFREQUENCYSHIFTER_NONE: break;
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mFrequencyShifter); break;
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.mFrequencyShifter.flFrequency); break;
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.mFrequencyShifter.ulLeftDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.mFrequencyShifter.ulRightDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXFREQUENCYSHIFTER_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
|
||||
case EAXFREQUENCYSHIFTER_LEFTDIRECTION: defer<LeftDirectionValidator>(call, props.ulLeftDirection); break;
|
||||
case EAXFREQUENCYSHIFTER_RIGHTDIRECTION: defer<RightDirectionValidator>(call, props.ulRightDirection); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -20,7 +21,7 @@
|
|||
|
||||
namespace {
|
||||
|
||||
al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
|
||||
constexpr std::optional<ModulatorWaveform> WaveformFromEmum(ALenum value) noexcept
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
|
|
@ -28,9 +29,9 @@ al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
|
|||
case AL_RING_MODULATOR_SAWTOOTH: return ModulatorWaveform::Sawtooth;
|
||||
case AL_RING_MODULATOR_SQUARE: return ModulatorWaveform::Square;
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
ALenum EnumFromWaveform(ModulatorWaveform type)
|
||||
constexpr ALenum EnumFromWaveform(ModulatorWaveform type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
|
|
@ -42,40 +43,31 @@ ALenum EnumFromWaveform(ModulatorWaveform type)
|
|||
std::to_string(static_cast<int>(type))};
|
||||
}
|
||||
|
||||
void Modulator_setParamf(EffectProps *props, ALenum param, float val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
|
||||
props->Modulator.Frequency = val;
|
||||
break;
|
||||
|
||||
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};
|
||||
}
|
||||
ModulatorProps props{};
|
||||
props.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
|
||||
props.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
|
||||
props.Waveform = WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM).value();
|
||||
return props;
|
||||
}
|
||||
void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Modulator_setParamf(props, param, vals[0]); }
|
||||
void Modulator_setParami(EffectProps *props, ALenum param, int val)
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps ModulatorEffectProps{genDefaultProps()};
|
||||
|
||||
void ModulatorEffectHandler::SetParami(ModulatorProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
Modulator_setParamf(props, param, static_cast<float>(val));
|
||||
SetParamf(props, param, static_cast<float>(val));
|
||||
break;
|
||||
|
||||
case AL_RING_MODULATOR_WAVEFORM:
|
||||
if(auto formopt = WaveformFromEmum(val))
|
||||
props->Modulator.Waveform = *formopt;
|
||||
props.Waveform = *formopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val};
|
||||
break;
|
||||
|
|
@ -85,62 +77,61 @@ void Modulator_setParami(EffectProps *props, ALenum param, int val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Modulator_setParami(props, param, vals[0]); }
|
||||
void ModulatorEffectHandler::SetParamiv(ModulatorProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
|
||||
void Modulator_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void ModulatorEffectHandler::SetParamf(ModulatorProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY:
|
||||
*val = static_cast<int>(props->Modulator.Frequency);
|
||||
break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
|
||||
*val = static_cast<int>(props->Modulator.HighPassCutoff);
|
||||
break;
|
||||
case AL_RING_MODULATOR_WAVEFORM:
|
||||
*val = EnumFromWaveform(props->Modulator.Waveform);
|
||||
if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
|
||||
props.Frequency = val;
|
||||
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:
|
||||
*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;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
|
||||
}
|
||||
}
|
||||
void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Modulator_getParamf(props, param, vals); }
|
||||
void ModulatorEffectHandler::SetParamfv(ModulatorProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
void ModulatorEffectHandler::GetParami(const ModulatorProps &props, ALenum param, int *val)
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
|
||||
props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
|
||||
props.Modulator.Waveform = *WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM);
|
||||
return props;
|
||||
switch(param)
|
||||
{
|
||||
case AL_RING_MODULATOR_FREQUENCY: *val = static_cast<int>(props.Frequency); break;
|
||||
case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = static_cast<int>(props.HighPassCutoff); break;
|
||||
case AL_RING_MODULATOR_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void 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
|
||||
namespace {
|
||||
|
|
@ -203,13 +194,9 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool ModulatorCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxModulatorCommitter::commit(const EAXRINGMODULATORPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType
|
||||
&& mEaxProps.mModulator.flFrequency == props.mModulator.flFrequency
|
||||
&& mEaxProps.mModulator.flHighPassCutOff == props.mModulator.flHighPassCutOff
|
||||
&& mEaxProps.mModulator.ulWaveform == props.mModulator.ulWaveform)
|
||||
if(auto *cur = std::get_if<EAXRINGMODULATORPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
|
@ -225,46 +212,52 @@ bool ModulatorCommitter::commit(const EaxEffectProps &props)
|
|||
return ModulatorWaveform::Sinusoid;
|
||||
};
|
||||
|
||||
mAlProps.Modulator.Frequency = props.mModulator.flFrequency;
|
||||
mAlProps.Modulator.HighPassCutoff = props.mModulator.flHighPassCutOff;
|
||||
mAlProps.Modulator.Waveform = get_waveform(props.mModulator.ulWaveform);
|
||||
mAlProps = [&]{
|
||||
ModulatorProps ret{};
|
||||
ret.Frequency = props.flFrequency;
|
||||
ret.HighPassCutoff = props.flHighPassCutOff;
|
||||
ret.Waveform = get_waveform(props.ulWaveform);
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void ModulatorCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxModulatorCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::Modulator;
|
||||
props.mModulator.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
|
||||
props.mModulator.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
|
||||
props.mModulator.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
|
||||
static constexpr EAXRINGMODULATORPROPERTIES defprops{[]
|
||||
{
|
||||
EAXRINGMODULATORPROPERTIES ret{};
|
||||
ret.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
|
||||
ret.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
|
||||
ret.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxModulatorCommitter::Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXRINGMODULATOR_NONE: break;
|
||||
case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props.mModulator); break;
|
||||
case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.mModulator.flFrequency); break;
|
||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.mModulator.flHighPassCutOff); break;
|
||||
case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.mModulator.ulWaveform); break;
|
||||
case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
|
||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.flHighPassCutOff); break;
|
||||
case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxModulatorCommitter::Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props)
|
||||
{
|
||||
switch (call.get_property_id())
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXRINGMODULATOR_NONE: break;
|
||||
case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props.mModulator); break;
|
||||
case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.mModulator.flFrequency); break;
|
||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.mModulator.flHighPassCutOff); break;
|
||||
case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.mModulator.ulWaveform); break;
|
||||
case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
|
||||
case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.flHighPassCutOff); break;
|
||||
case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,94 +8,92 @@
|
|||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
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;
|
||||
return std::monostate{};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Null);
|
||||
|
||||
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
|
||||
namespace {
|
||||
|
|
@ -117,30 +115,26 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool NullCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxNullCommitter::commit(const std::monostate &props)
|
||||
{
|
||||
const bool ret{props.mType != mEaxProps.mType};
|
||||
const bool ret{std::holds_alternative<std::monostate>(mEaxProps)};
|
||||
mEaxProps = props;
|
||||
mAlProps = std::monostate{};
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<>
|
||||
void NullCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxNullCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props = EaxEffectProps{};
|
||||
props.mType = EaxEffectType::None;
|
||||
props = std::monostate{};
|
||||
}
|
||||
|
||||
template<>
|
||||
void NullCommitter::Get(const EaxCall &call, const EaxEffectProps&)
|
||||
void EaxNullCommitter::Get(const EaxCall &call, const std::monostate&)
|
||||
{
|
||||
if(call.get_property_id() != 0)
|
||||
fail_unknown_property_id();
|
||||
}
|
||||
|
||||
template<>
|
||||
void NullCommitter::Set(const EaxCall &call, EaxEffectProps&)
|
||||
void EaxNullCommitter::Set(const EaxCall &call, std::monostate&)
|
||||
{
|
||||
if(call.get_property_id() != 0)
|
||||
fail_unknown_property_id();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -16,28 +17,32 @@
|
|||
|
||||
namespace {
|
||||
|
||||
void Pshifter_setParamf(EffectProps*, ALenum param, float)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
||||
void Pshifter_setParamfv(EffectProps*, ALenum param, const float*)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
|
||||
param};
|
||||
PshifterProps props{};
|
||||
props.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
|
||||
props.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
|
||||
return props;
|
||||
}
|
||||
|
||||
void Pshifter_setParami(EffectProps *props, ALenum param, int val)
|
||||
} // namespace
|
||||
|
||||
const EffectProps PshifterEffectProps{genDefaultProps()};
|
||||
|
||||
void PshifterEffectHandler::SetParami(PshifterProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE:
|
||||
if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Pitch shifter coarse tune out of range"};
|
||||
props->Pshifter.CoarseTune = val;
|
||||
props.CoarseTune = val;
|
||||
break;
|
||||
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE:
|
||||
if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Pitch shifter fine tune out of range"};
|
||||
props->Pshifter.FineTune = val;
|
||||
props.FineTune = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -45,49 +50,40 @@ void Pshifter_setParami(EffectProps *props, ALenum param, int val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Pshifter_setParamiv(EffectProps *props, ALenum param, const int *vals)
|
||||
{ Pshifter_setParami(props, param, vals[0]); }
|
||||
void PshifterEffectHandler::SetParamiv(PshifterProps &props, ALenum param, const int *vals)
|
||||
{ SetParami(props, param, *vals); }
|
||||
|
||||
void Pshifter_getParami(const EffectProps *props, ALenum param, int *val)
|
||||
void PshifterEffectHandler::SetParamf(PshifterProps&, ALenum param, float)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
||||
void PshifterEffectHandler::SetParamfv(PshifterProps&, ALenum param, const float*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
void PshifterEffectHandler::GetParami(const PshifterProps &props, ALenum param, int *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE:
|
||||
*val = props->Pshifter.CoarseTune;
|
||||
break;
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE:
|
||||
*val = props->Pshifter.FineTune;
|
||||
break;
|
||||
case AL_PITCH_SHIFTER_COARSE_TUNE: *val = props.CoarseTune; break;
|
||||
case AL_PITCH_SHIFTER_FINE_TUNE: *val = props.FineTune; break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Pshifter_getParamiv(const EffectProps *props, ALenum param, int *vals)
|
||||
{ Pshifter_getParami(props, param, vals); }
|
||||
void PshifterEffectHandler::GetParamiv(const PshifterProps &props, ALenum param, int *vals)
|
||||
{ GetParami(props, param, vals); }
|
||||
|
||||
void Pshifter_getParamf(const EffectProps*, ALenum param, float*)
|
||||
void PshifterEffectHandler::GetParamf(const PshifterProps&, ALenum param, float*)
|
||||
{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; }
|
||||
void Pshifter_getParamfv(const EffectProps*, ALenum param, float*)
|
||||
void PshifterEffectHandler::GetParamfv(const PshifterProps&, ALenum param, float*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x",
|
||||
param};
|
||||
}
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE;
|
||||
props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE;
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Pshifter);
|
||||
|
||||
const EffectProps PshifterEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -138,52 +134,48 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool PitchShifterCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxPitchShifterCommitter::commit(const EAXPITCHSHIFTERPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType
|
||||
&& mEaxProps.mPitchShifter.lCoarseTune == props.mPitchShifter.lCoarseTune
|
||||
&& mEaxProps.mPitchShifter.lFineTune == props.mPitchShifter.lFineTune)
|
||||
if(auto *cur = std::get_if<EAXPITCHSHIFTERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
||||
mAlProps.Pshifter.CoarseTune = static_cast<int>(mEaxProps.mPitchShifter.lCoarseTune);
|
||||
mAlProps.Pshifter.FineTune = static_cast<int>(mEaxProps.mPitchShifter.lFineTune);
|
||||
mAlProps = [&]{
|
||||
PshifterProps ret{};
|
||||
ret.CoarseTune = static_cast<int>(props.lCoarseTune);
|
||||
ret.FineTune = static_cast<int>(props.lFineTune);
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void PitchShifterCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxPitchShifterCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::PitchShifter;
|
||||
props.mPitchShifter.lCoarseTune = EAXPITCHSHIFTER_DEFAULTCOARSETUNE;
|
||||
props.mPitchShifter.lFineTune = EAXPITCHSHIFTER_DEFAULTFINETUNE;
|
||||
props = EAXPITCHSHIFTERPROPERTIES{EAXPITCHSHIFTER_DEFAULTCOARSETUNE,
|
||||
EAXPITCHSHIFTER_DEFAULTFINETUNE};
|
||||
}
|
||||
|
||||
template<>
|
||||
void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxPitchShifterCommitter::Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXPITCHSHIFTER_NONE: break;
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props.mPitchShifter); break;
|
||||
case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.mPitchShifter.lCoarseTune); break;
|
||||
case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.mPitchShifter.lFineTune); break;
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXPITCHSHIFTER_COARSETUNE: call.set_value<Exception>(props.lCoarseTune); break;
|
||||
case EAXPITCHSHIFTER_FINETUNE: call.set_value<Exception>(props.lFineTune); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxPitchShifterCommitter::Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXPITCHSHIFTER_NONE: break;
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props.mPitchShifter); break;
|
||||
case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.mPitchShifter.lCoarseTune); break;
|
||||
case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.mPitchShifter.lFineTune); break;
|
||||
case EAXPITCHSHIFTER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXPITCHSHIFTER_COARSETUNE: defer<CoarseTuneValidator>(call, props.lCoarseTune); break;
|
||||
case EAXPITCHSHIFTER_FINETUNE: defer<FineTuneValidator>(call, props.lFineTune); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,18 +1,18 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "effects.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include <cassert>
|
||||
#include "alnumeric.h"
|
||||
#include "al/eax/effect.h"
|
||||
#include "al/eax/exception.h"
|
||||
#include "al/eax/utils.h"
|
||||
#endif // ALSOFT_EAX
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
namespace {
|
||||
|
||||
al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
|
||||
constexpr std::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val) noexcept
|
||||
{
|
||||
#define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x: \
|
||||
return VMorpherPhenome::x
|
||||
|
|
@ -57,10 +57,10 @@ al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
|
|||
HANDLE_PHENOME(V);
|
||||
HANDLE_PHENOME(Z);
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
#undef HANDLE_PHENOME
|
||||
}
|
||||
ALenum EnumFromPhenome(VMorpherPhenome phenome)
|
||||
constexpr ALenum EnumFromPhenome(VMorpherPhenome phenome)
|
||||
{
|
||||
#define HANDLE_PHENOME(x) case VMorpherPhenome::x: return AL_VOCAL_MORPHER_PHONEME_ ## x
|
||||
switch(phenome)
|
||||
|
|
@ -100,7 +100,7 @@ ALenum EnumFromPhenome(VMorpherPhenome phenome)
|
|||
#undef HANDLE_PHENOME
|
||||
}
|
||||
|
||||
al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
|
||||
constexpr std::optional<VMorpherWaveform> WaveformFromEmum(ALenum value) noexcept
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
|
|
@ -108,9 +108,9 @@ al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
|
|||
case AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE: return VMorpherWaveform::Triangle;
|
||||
case AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH: return VMorpherWaveform::Sawtooth;
|
||||
}
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
ALenum EnumFromWaveform(VMorpherWaveform type)
|
||||
constexpr ALenum EnumFromWaveform(VMorpherWaveform type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
|
|
@ -122,13 +122,29 @@ ALenum EnumFromWaveform(VMorpherWaveform type)
|
|||
std::to_string(static_cast<int>(type))};
|
||||
}
|
||||
|
||||
void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
||||
constexpr EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
VmorpherProps props{};
|
||||
props.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE;
|
||||
props.PhonemeA = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA).value();
|
||||
props.PhonemeB = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB).value();
|
||||
props.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
|
||||
props.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
|
||||
props.Waveform = WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM).value();
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const EffectProps VmorpherEffectProps{genDefaultProps()};
|
||||
|
||||
void VmorpherEffectHandler::SetParami(VmorpherProps &props, ALenum param, int val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_PHONEMEA:
|
||||
if(auto phenomeopt = PhenomeFromEnum(val))
|
||||
props->Vmorpher.PhonemeA = *phenomeopt;
|
||||
props.PhonemeA = *phenomeopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range: 0x%04x", val};
|
||||
break;
|
||||
|
|
@ -136,12 +152,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
|||
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
|
||||
if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a coarse tuning out of range"};
|
||||
props->Vmorpher.PhonemeACoarseTuning = val;
|
||||
props.PhonemeACoarseTuning = val;
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_PHONEMEB:
|
||||
if(auto phenomeopt = PhenomeFromEnum(val))
|
||||
props->Vmorpher.PhonemeB = *phenomeopt;
|
||||
props.PhonemeB = *phenomeopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range: 0x%04x", val};
|
||||
break;
|
||||
|
|
@ -149,12 +165,12 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
|||
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
|
||||
if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b coarse tuning out of range"};
|
||||
props->Vmorpher.PhonemeBCoarseTuning = val;
|
||||
props.PhonemeBCoarseTuning = val;
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_WAVEFORM:
|
||||
if(auto formopt = WaveformFromEmum(val))
|
||||
props->Vmorpher.Waveform = *formopt;
|
||||
props.Waveform = *formopt;
|
||||
else
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range: 0x%04x", val};
|
||||
break;
|
||||
|
|
@ -164,19 +180,19 @@ void Vmorpher_setParami(EffectProps *props, ALenum param, int val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Vmorpher_setParamiv(EffectProps*, ALenum param, const int*)
|
||||
void VmorpherEffectHandler::SetParamiv(VmorpherProps&, ALenum param, const int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Vmorpher_setParamf(EffectProps *props, ALenum param, float val)
|
||||
void VmorpherEffectHandler::SetParamf(VmorpherProps &props, ALenum param, float val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_RATE:
|
||||
if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_RATE))
|
||||
throw effect_exception{AL_INVALID_VALUE, "Vocal morpher rate out of range"};
|
||||
props->Vmorpher.Rate = val;
|
||||
props.Rate = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -184,49 +200,35 @@ void Vmorpher_setParamf(EffectProps *props, ALenum param, float val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Vmorpher_setParamfv(EffectProps *props, ALenum param, const float *vals)
|
||||
{ Vmorpher_setParamf(props, param, vals[0]); }
|
||||
void VmorpherEffectHandler::SetParamfv(VmorpherProps &props, ALenum param, const float *vals)
|
||||
{ SetParamf(props, param, *vals); }
|
||||
|
||||
void Vmorpher_getParami(const EffectProps *props, ALenum param, int* val)
|
||||
void VmorpherEffectHandler::GetParami(const VmorpherProps &props, ALenum param, int* val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_PHONEMEA:
|
||||
*val = EnumFromPhenome(props->Vmorpher.PhonemeA);
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING:
|
||||
*val = props->Vmorpher.PhonemeACoarseTuning;
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_PHONEMEB:
|
||||
*val = EnumFromPhenome(props->Vmorpher.PhonemeB);
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING:
|
||||
*val = props->Vmorpher.PhonemeBCoarseTuning;
|
||||
break;
|
||||
|
||||
case AL_VOCAL_MORPHER_WAVEFORM:
|
||||
*val = EnumFromWaveform(props->Vmorpher.Waveform);
|
||||
break;
|
||||
case AL_VOCAL_MORPHER_PHONEMEA: *val = EnumFromPhenome(props.PhonemeA); break;
|
||||
case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: *val = props.PhonemeACoarseTuning; break;
|
||||
case AL_VOCAL_MORPHER_PHONEMEB: *val = EnumFromPhenome(props.PhonemeB); break;
|
||||
case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: *val = props.PhonemeBCoarseTuning; break;
|
||||
case AL_VOCAL_MORPHER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); break;
|
||||
|
||||
default:
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x",
|
||||
param};
|
||||
}
|
||||
}
|
||||
void Vmorpher_getParamiv(const EffectProps*, ALenum param, int*)
|
||||
void VmorpherEffectHandler::GetParamiv(const VmorpherProps&, ALenum param, int*)
|
||||
{
|
||||
throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x",
|
||||
param};
|
||||
}
|
||||
void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val)
|
||||
void VmorpherEffectHandler::GetParamf(const VmorpherProps &props, ALenum param, float *val)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_VOCAL_MORPHER_RATE:
|
||||
*val = props->Vmorpher.Rate;
|
||||
*val = props.Rate;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -234,26 +236,9 @@ void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val)
|
|||
param};
|
||||
}
|
||||
}
|
||||
void Vmorpher_getParamfv(const EffectProps *props, ALenum param, float *vals)
|
||||
{ Vmorpher_getParamf(props, param, vals); }
|
||||
void VmorpherEffectHandler::GetParamfv(const VmorpherProps &props, ALenum param, float *vals)
|
||||
{ GetParamf(props, param, vals); }
|
||||
|
||||
EffectProps genDefaultProps() noexcept
|
||||
{
|
||||
EffectProps props{};
|
||||
props.Vmorpher.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE;
|
||||
props.Vmorpher.PhonemeA = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA);
|
||||
props.Vmorpher.PhonemeB = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB);
|
||||
props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING;
|
||||
props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING;
|
||||
props.Vmorpher.Waveform = *WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM);
|
||||
return props;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DEFINE_ALEFFECT_VTABLE(Vmorpher);
|
||||
|
||||
const EffectProps VmorpherEffectProps{genDefaultProps()};
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
namespace {
|
||||
|
|
@ -352,16 +337,9 @@ template<>
|
|||
throw Exception{message};
|
||||
}
|
||||
|
||||
template<>
|
||||
bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
|
||||
bool EaxVocalMorpherCommitter::commit(const EAXVOCALMORPHERPROPERTIES &props)
|
||||
{
|
||||
if(props.mType == mEaxProps.mType
|
||||
&& mEaxProps.mVocalMorpher.ulPhonemeA == props.mVocalMorpher.ulPhonemeA
|
||||
&& mEaxProps.mVocalMorpher.lPhonemeACoarseTuning == props.mVocalMorpher.lPhonemeACoarseTuning
|
||||
&& mEaxProps.mVocalMorpher.ulPhonemeB == props.mVocalMorpher.ulPhonemeB
|
||||
&& mEaxProps.mVocalMorpher.lPhonemeBCoarseTuning == props.mVocalMorpher.lPhonemeBCoarseTuning
|
||||
&& mEaxProps.mVocalMorpher.ulWaveform == props.mVocalMorpher.ulWaveform
|
||||
&& mEaxProps.mVocalMorpher.flRate == props.mVocalMorpher.flRate)
|
||||
if(auto *cur = std::get_if<EAXVOCALMORPHERPROPERTIES>(&mEaxProps); cur && *cur == props)
|
||||
return false;
|
||||
|
||||
mEaxProps = props;
|
||||
|
|
@ -413,107 +391,65 @@ bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
|
|||
return VMorpherWaveform::Sinusoid;
|
||||
};
|
||||
|
||||
mAlProps.Vmorpher.PhonemeA = get_phoneme(props.mVocalMorpher.ulPhonemeA);
|
||||
mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeACoarseTuning);
|
||||
mAlProps.Vmorpher.PhonemeB = get_phoneme(props.mVocalMorpher.ulPhonemeB);
|
||||
mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast<int>(props.mVocalMorpher.lPhonemeBCoarseTuning);
|
||||
mAlProps.Vmorpher.Waveform = get_waveform(props.mVocalMorpher.ulWaveform);
|
||||
mAlProps.Vmorpher.Rate = props.mVocalMorpher.flRate;
|
||||
mAlProps = [&]{
|
||||
VmorpherProps ret{};
|
||||
ret.PhonemeA = get_phoneme(props.ulPhonemeA);
|
||||
ret.PhonemeACoarseTuning = static_cast<int>(props.lPhonemeACoarseTuning);
|
||||
ret.PhonemeB = get_phoneme(props.ulPhonemeB);
|
||||
ret.PhonemeBCoarseTuning = static_cast<int>(props.lPhonemeBCoarseTuning);
|
||||
ret.Waveform = get_waveform(props.ulWaveform);
|
||||
ret.Rate = props.flRate;
|
||||
return ret;
|
||||
}();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props)
|
||||
void EaxVocalMorpherCommitter::SetDefaults(EaxEffectProps &props)
|
||||
{
|
||||
props.mType = EaxEffectType::VocalMorpher;
|
||||
props.mVocalMorpher.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
|
||||
props.mVocalMorpher.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
|
||||
props.mVocalMorpher.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
|
||||
props.mVocalMorpher.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
|
||||
props.mVocalMorpher.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
|
||||
props.mVocalMorpher.flRate = EAXVOCALMORPHER_DEFAULTRATE;
|
||||
static constexpr EAXVOCALMORPHERPROPERTIES defprops{[]
|
||||
{
|
||||
EAXVOCALMORPHERPROPERTIES ret{};
|
||||
ret.ulPhonemeA = EAXVOCALMORPHER_DEFAULTPHONEMEA;
|
||||
ret.lPhonemeACoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEACOARSETUNING;
|
||||
ret.ulPhonemeB = EAXVOCALMORPHER_DEFAULTPHONEMEB;
|
||||
ret.lPhonemeBCoarseTuning = EAXVOCALMORPHER_DEFAULTPHONEMEBCOARSETUNING;
|
||||
ret.ulWaveform = EAXVOCALMORPHER_DEFAULTWAVEFORM;
|
||||
ret.flRate = EAXVOCALMORPHER_DEFAULTRATE;
|
||||
return ret;
|
||||
}()};
|
||||
props = defprops;
|
||||
}
|
||||
|
||||
template<>
|
||||
void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
|
||||
void EaxVocalMorpherCommitter::Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXVOCALMORPHER_NONE:
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_ALLPARAMETERS:
|
||||
call.set_value<Exception>(props.mVocalMorpher);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEA:
|
||||
call.set_value<Exception>(props.mVocalMorpher.ulPhonemeA);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
|
||||
call.set_value<Exception>(props.mVocalMorpher.lPhonemeACoarseTuning);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEB:
|
||||
call.set_value<Exception>(props.mVocalMorpher.ulPhonemeB);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
|
||||
call.set_value<Exception>(props.mVocalMorpher.lPhonemeBCoarseTuning);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_WAVEFORM:
|
||||
call.set_value<Exception>(props.mVocalMorpher.ulWaveform);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_RATE:
|
||||
call.set_value<Exception>(props.mVocalMorpher.flRate);
|
||||
break;
|
||||
|
||||
default:
|
||||
fail_unknown_property_id();
|
||||
case EAXVOCALMORPHER_NONE: break;
|
||||
case EAXVOCALMORPHER_ALLPARAMETERS: call.set_value<Exception>(props); break;
|
||||
case EAXVOCALMORPHER_PHONEMEA: call.set_value<Exception>(props.ulPhonemeA); break;
|
||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING: call.set_value<Exception>(props.lPhonemeACoarseTuning); break;
|
||||
case EAXVOCALMORPHER_PHONEMEB: call.set_value<Exception>(props.ulPhonemeB); break;
|
||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: call.set_value<Exception>(props.lPhonemeBCoarseTuning); break;
|
||||
case EAXVOCALMORPHER_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
|
||||
case EAXVOCALMORPHER_RATE: call.set_value<Exception>(props.flRate); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props)
|
||||
void EaxVocalMorpherCommitter::Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props)
|
||||
{
|
||||
switch(call.get_property_id())
|
||||
{
|
||||
case EAXVOCALMORPHER_NONE:
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_ALLPARAMETERS:
|
||||
defer<AllValidator>(call, props.mVocalMorpher);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEA:
|
||||
defer<PhonemeAValidator>(call, props.mVocalMorpher.ulPhonemeA);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
|
||||
defer<PhonemeACoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeACoarseTuning);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEB:
|
||||
defer<PhonemeBValidator>(call, props.mVocalMorpher.ulPhonemeB);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
|
||||
defer<PhonemeBCoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeBCoarseTuning);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_WAVEFORM:
|
||||
defer<WaveformValidator>(call, props.mVocalMorpher.ulWaveform);
|
||||
break;
|
||||
|
||||
case EAXVOCALMORPHER_RATE:
|
||||
defer<RateValidator>(call, props.mVocalMorpher.flRate);
|
||||
break;
|
||||
|
||||
default:
|
||||
fail_unknown_property_id();
|
||||
case EAXVOCALMORPHER_NONE: break;
|
||||
case EAXVOCALMORPHER_ALLPARAMETERS: defer<AllValidator>(call, props); break;
|
||||
case EAXVOCALMORPHER_PHONEMEA: defer<PhonemeAValidator>(call, props.ulPhonemeA); break;
|
||||
case EAXVOCALMORPHER_PHONEMEACOARSETUNING: defer<PhonemeACoarseTuningValidator>(call, props.lPhonemeACoarseTuning); break;
|
||||
case EAXVOCALMORPHER_PHONEMEB: defer<PhonemeBValidator>(call, props.ulPhonemeB); break;
|
||||
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: defer<PhonemeBCoarseTuningValidator>(call, props.lPhonemeBCoarseTuning); break;
|
||||
case EAXVOCALMORPHER_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
|
||||
case EAXVOCALMORPHER_RATE: defer<RateValidator>(call, props.flRate); break;
|
||||
default: fail_unknown_property_id();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
|
@ -29,27 +31,45 @@
|
|||
#include <csignal>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "al/debug.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "alc/context.h"
|
||||
#include "almalloc.h"
|
||||
#include "core/except.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "core/logging.h"
|
||||
#include "opthelpers.h"
|
||||
#include "vector.h"
|
||||
#include "strutils.h"
|
||||
|
||||
|
||||
bool TrapALError{false};
|
||||
namespace al {
|
||||
context_error::context_error(ALenum code, const char *msg, ...) : mErrorCode{code}
|
||||
{
|
||||
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
||||
std::va_list args;
|
||||
va_start(args, msg);
|
||||
setMessage(msg, args);
|
||||
va_end(args);
|
||||
/* NOLINTEND(*-array-to-pointer-decay) */
|
||||
}
|
||||
context_error::~context_error() = default;
|
||||
} /* namespace al */
|
||||
|
||||
void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
||||
{
|
||||
auto message = al::vector<char>(256);
|
||||
auto message = std::vector<char>(256);
|
||||
|
||||
va_list args, args2;
|
||||
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
||||
std::va_list args, args2;
|
||||
va_start(args, msg);
|
||||
va_copy(args2, args);
|
||||
int msglen{std::vsnprintf(message.data(), message.size(), msg, args)};
|
||||
|
|
@ -60,9 +80,15 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
|||
}
|
||||
va_end(args2);
|
||||
va_end(args);
|
||||
/* NOLINTEND(*-array-to-pointer-decay) */
|
||||
|
||||
if(msglen >= 0) msg = message.data();
|
||||
else msg = "<internal error constructing message>";
|
||||
if(msglen >= 0)
|
||||
msg = message.data();
|
||||
else
|
||||
{
|
||||
msg = "<internal error constructing message>";
|
||||
msglen = static_cast<int>(strlen(msg));
|
||||
}
|
||||
|
||||
WARN("Error generated on context %p, code 0x%04x, \"%s\"\n",
|
||||
decltype(std::declval<void*>()){this}, errorCode, msg);
|
||||
|
|
@ -77,30 +103,55 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
|
|||
#endif
|
||||
}
|
||||
|
||||
ALenum curerr{AL_NO_ERROR};
|
||||
mLastError.compare_exchange_strong(curerr, errorCode);
|
||||
if(mLastThreadError.get() == AL_NO_ERROR)
|
||||
mLastThreadError.set(errorCode);
|
||||
|
||||
debugMessage(DebugSource::API, DebugType::Error, 0, DebugSeverity::High,
|
||||
{msg, static_cast<uint>(msglen)});
|
||||
}
|
||||
|
||||
AL_API ALenum AL_APIENTRY alGetError(void)
|
||||
START_API_FUNC
|
||||
/* Special-case alGetError since it (potentially) raises a debug signal and
|
||||
* returns a non-default value for a null context.
|
||||
*/
|
||||
AL_API auto AL_APIENTRY alGetError() noexcept -> ALenum
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY
|
||||
{
|
||||
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;
|
||||
}
|
||||
if(auto context = GetContextRef()) LIKELY
|
||||
return alGetErrorDirect(context.get());
|
||||
|
||||
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 <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <bitset>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alsem.h"
|
||||
#include "alspan.h"
|
||||
#include "core/async_event.h"
|
||||
#include "core/except.h"
|
||||
#include "core/context.h"
|
||||
#include "core/effects/base.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/voice_change.h"
|
||||
#include "debug.h"
|
||||
#include "direct_defs.h"
|
||||
#include "error.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "opthelpers.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
|
||||
|
||||
static int EventThread(ALCcontext *context)
|
||||
namespace {
|
||||
|
||||
template<typename... Ts>
|
||||
struct overloaded : Ts... { using Ts::operator()...; };
|
||||
|
||||
template<typename... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
int EventThread(ALCcontext *context)
|
||||
{
|
||||
RingBuffer *ring{context->mAsyncEvents.get()};
|
||||
bool quitnow{false};
|
||||
|
|
@ -44,76 +58,98 @@ static int EventThread(ALCcontext *context)
|
|||
continue;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mEventCbLock};
|
||||
do {
|
||||
auto *evt_ptr = reinterpret_cast<AsyncEvent*>(evt_data.buf);
|
||||
evt_data.buf += sizeof(AsyncEvent);
|
||||
evt_data.len -= 1;
|
||||
|
||||
AsyncEvent evt{*evt_ptr};
|
||||
al::destroy_at(evt_ptr);
|
||||
ring->readAdvance(1);
|
||||
|
||||
quitnow = evt.EnumType == AsyncEvent::KillThread;
|
||||
std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
|
||||
auto evt_span = al::span{std::launder(reinterpret_cast<AsyncEvent*>(evt_data.buf)),
|
||||
evt_data.len};
|
||||
for(auto &event : evt_span)
|
||||
{
|
||||
quitnow = std::holds_alternative<AsyncKillThread>(event);
|
||||
if(quitnow) UNLIKELY break;
|
||||
|
||||
if(evt.EnumType == AsyncEvent::ReleaseEffectState)
|
||||
{
|
||||
al::intrusive_ptr<EffectState>{evt.u.mEffectState};
|
||||
continue;
|
||||
}
|
||||
|
||||
auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire);
|
||||
if(!context->mEventCb || !enabledevts.test(evt.EnumType))
|
||||
continue;
|
||||
|
||||
if(evt.EnumType == AsyncEvent::SourceStateChange)
|
||||
auto proc_killthread = [](AsyncKillThread&) { };
|
||||
auto proc_release = [](AsyncEffectReleaseEvent &evt)
|
||||
{
|
||||
al::intrusive_ptr<EffectState>{evt.mEffectState};
|
||||
};
|
||||
auto proc_srcstate = [context,enabledevts](AsyncSourceStateEvent &evt)
|
||||
{
|
||||
if(!context->mEventCb
|
||||
|| !enabledevts.test(al::to_underlying(AsyncEnableBits::SourceState)))
|
||||
return;
|
||||
|
||||
ALuint state{};
|
||||
std::string msg{"Source ID " + std::to_string(evt.u.srcstate.id)};
|
||||
std::string msg{"Source ID " + std::to_string(evt.mId)};
|
||||
msg += " state has changed to ";
|
||||
switch(evt.u.srcstate.state)
|
||||
switch(evt.mState)
|
||||
{
|
||||
case AsyncEvent::SrcState::Reset:
|
||||
case AsyncSrcState::Reset:
|
||||
msg += "AL_INITIAL";
|
||||
state = AL_INITIAL;
|
||||
break;
|
||||
case AsyncEvent::SrcState::Stop:
|
||||
case AsyncSrcState::Stop:
|
||||
msg += "AL_STOPPED";
|
||||
state = AL_STOPPED;
|
||||
break;
|
||||
case AsyncEvent::SrcState::Play:
|
||||
case AsyncSrcState::Play:
|
||||
msg += "AL_PLAYING";
|
||||
state = AL_PLAYING;
|
||||
break;
|
||||
case AsyncEvent::SrcState::Pause:
|
||||
case AsyncSrcState::Pause:
|
||||
msg += "AL_PAUSED";
|
||||
state = AL_PAUSED;
|
||||
break;
|
||||
}
|
||||
context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
|
||||
state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||
}
|
||||
else if(evt.EnumType == AsyncEvent::BufferCompleted)
|
||||
context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.mId, state,
|
||||
static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||
};
|
||||
auto proc_buffercomp = [context,enabledevts](AsyncBufferCompleteEvent &evt)
|
||||
{
|
||||
std::string msg{std::to_string(evt.u.bufcomp.count)};
|
||||
if(evt.u.bufcomp.count == 1) msg += " buffer completed";
|
||||
if(!context->mEventCb
|
||||
|| !enabledevts.test(al::to_underlying(AsyncEnableBits::BufferCompleted)))
|
||||
return;
|
||||
|
||||
std::string msg{std::to_string(evt.mCount)};
|
||||
if(evt.mCount == 1) msg += " buffer completed";
|
||||
else msg += " buffers completed";
|
||||
context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.u.bufcomp.id,
|
||||
evt.u.bufcomp.count, static_cast<ALsizei>(msg.length()), msg.c_str(),
|
||||
context->mEventParam);
|
||||
}
|
||||
else if(evt.EnumType == AsyncEvent::Disconnected)
|
||||
context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.mId, evt.mCount,
|
||||
static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||
};
|
||||
auto proc_disconnect = [context,enabledevts](AsyncDisconnectEvent &evt)
|
||||
{
|
||||
context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
|
||||
static_cast<ALsizei>(strlen(evt.u.disconnect.msg)), evt.u.disconnect.msg,
|
||||
context->mEventParam);
|
||||
}
|
||||
} while(evt_data.len != 0);
|
||||
context->debugMessage(DebugSource::System, DebugType::Error, 0,
|
||||
DebugSeverity::High, evt.msg);
|
||||
|
||||
if(context->mEventCb
|
||||
&& enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected)))
|
||||
context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
|
||||
static_cast<ALsizei>(evt.msg.length()), evt.msg.c_str(),
|
||||
context->mEventParam);
|
||||
};
|
||||
|
||||
std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect,
|
||||
proc_killthread}, event);
|
||||
}
|
||||
std::destroy(evt_span.begin(), evt_span.end());
|
||||
ring->readAdvance(evt_span.size());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr std::optional<AsyncEnableBits> GetEventType(ALenum etype) noexcept
|
||||
{
|
||||
switch(etype)
|
||||
{
|
||||
case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT: return AsyncEnableBits::BufferCompleted;
|
||||
case AL_EVENT_TYPE_DISCONNECTED_SOFT: return AsyncEnableBits::Disconnected;
|
||||
case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT: return AsyncEnableBits::SourceState;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void StartEventThrd(ALCcontext *ctx)
|
||||
{
|
||||
try {
|
||||
|
|
@ -138,7 +174,7 @@ void StopEventThrd(ALCcontext *ctx)
|
|||
evt_data = ring->getWriteVector().first;
|
||||
} while(evt_data.len == 0);
|
||||
}
|
||||
al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), AsyncEvent::KillThread);
|
||||
std::ignore = InitAsyncEvent<AsyncKillThread>(evt_data.buf);
|
||||
ring->writeAdvance(1);
|
||||
|
||||
ctx->mEventSem.post();
|
||||
|
|
@ -146,34 +182,25 @@ void StopEventThrd(ALCcontext *ctx)
|
|||
ctx->mEventThread.join();
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
AL_API DECL_FUNCEXT3(void, alEventControl,SOFT, ALsizei,count, const ALenum*,types, ALboolean,enable)
|
||||
FORCE_ALIGN void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count,
|
||||
const ALenum *types, ALboolean enable) noexcept
|
||||
try {
|
||||
if(count < 0)
|
||||
throw al::context_error{AL_INVALID_VALUE, "Controlling %d events", count};
|
||||
if(count <= 0) UNLIKELY return;
|
||||
|
||||
if(count < 0) context->setError(AL_INVALID_VALUE, "Controlling %d events", count);
|
||||
if(count <= 0) return;
|
||||
if(!types) return context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
if(!types)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
ContextBase::AsyncEventBitset flags{};
|
||||
const ALenum *types_end = types+count;
|
||||
auto bad_type = std::find_if_not(types, types_end,
|
||||
[&flags](ALenum type) noexcept -> bool
|
||||
{
|
||||
if(type == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT)
|
||||
flags.set(AsyncEvent::BufferCompleted);
|
||||
else if(type == AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT)
|
||||
flags.set(AsyncEvent::SourceStateChange);
|
||||
else if(type == AL_EVENT_TYPE_DISCONNECTED_SOFT)
|
||||
flags.set(AsyncEvent::Disconnected);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
if(bad_type != types_end)
|
||||
return context->setError(AL_INVALID_ENUM, "Invalid event type 0x%04x", *bad_type);
|
||||
for(ALenum evttype : al::span{types, static_cast<uint>(count)})
|
||||
{
|
||||
auto etype = GetEventType(evttype);
|
||||
if(!etype)
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid event type 0x%04x", evttype};
|
||||
flags.set(al::to_underlying(*etype));
|
||||
}
|
||||
|
||||
if(enable)
|
||||
{
|
||||
|
|
@ -196,20 +223,18 @@ START_API_FUNC
|
|||
/* Wait to ensure the event handler sees the changed flags before
|
||||
* returning.
|
||||
*/
|
||||
std::lock_guard<std::mutex> _{context->mEventCbLock};
|
||||
std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
|
||||
}
|
||||
}
|
||||
END_API_FUNC
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam)
|
||||
START_API_FUNC
|
||||
AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT,callback, void*,userParam)
|
||||
FORCE_ALIGN void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context,
|
||||
ALEVENTPROCSOFT callback, void *userParam) noexcept
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> __{context->mEventCbLock};
|
||||
std::lock_guard<std::mutex> eventlock{context->mEventCbLock};
|
||||
context->mEventCb = callback;
|
||||
context->mEventParam = userParam;
|
||||
}
|
||||
END_API_FUNC
|
||||
|
|
|
|||
|
|
@ -20,63 +20,59 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "alc/context.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "alstring.h"
|
||||
#include "core/except.h"
|
||||
#include "direct_defs.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extName)
|
||||
START_API_FUNC
|
||||
AL_API DECL_FUNC1(ALboolean, alIsExtensionPresent, const ALchar*,extName)
|
||||
FORCE_ALIGN ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extName) noexcept
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return AL_FALSE;
|
||||
|
||||
if(!extName) UNLIKELY
|
||||
{
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
return AL_FALSE;
|
||||
}
|
||||
|
||||
size_t len{strlen(extName)};
|
||||
const char *ptr{context->mExtensionList};
|
||||
while(ptr && *ptr)
|
||||
const std::string_view tofind{extName};
|
||||
for(std::string_view ext : context->mExtensions)
|
||||
{
|
||||
if(al::strncasecmp(ptr, extName, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len])))
|
||||
if(al::case_compare(ext, tofind) == 0)
|
||||
return AL_TRUE;
|
||||
|
||||
if((ptr=strchr(ptr, ' ')) != nullptr)
|
||||
{
|
||||
do {
|
||||
++ptr;
|
||||
} while(isspace(*ptr));
|
||||
}
|
||||
}
|
||||
|
||||
return AL_FALSE;
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName)
|
||||
START_API_FUNC
|
||||
AL_API ALvoid* AL_APIENTRY alGetProcAddress(const ALchar *funcName) noexcept
|
||||
{
|
||||
if(!funcName) return nullptr;
|
||||
return alcGetProcAddress(nullptr, funcName);
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName)
|
||||
START_API_FUNC
|
||||
FORCE_ALIGN ALvoid* AL_APIENTRY alGetProcAddressDirect(ALCcontext*, const ALchar *funcName) noexcept
|
||||
{
|
||||
if(!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);
|
||||
}
|
||||
END_API_FUNC
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,15 +1,40 @@
|
|||
#ifndef AL_FILTER_H
|
||||
#define AL_FILTER_H
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
|
||||
#define LOWPASSFREQREF 5000.0f
|
||||
#define HIGHPASSFREQREF 250.0f
|
||||
|
||||
inline constexpr float LowPassFreqRef{5000.0f};
|
||||
inline constexpr float HighPassFreqRef{250.0f};
|
||||
|
||||
template<typename T>
|
||||
struct FilterTable {
|
||||
static void setParami(struct ALfilter*, ALenum, int);
|
||||
static void setParamiv(struct ALfilter*, ALenum, const int*);
|
||||
static void setParamf(struct ALfilter*, ALenum, float);
|
||||
static void setParamfv(struct ALfilter*, ALenum, const float*);
|
||||
|
||||
static void getParami(const struct ALfilter*, ALenum, int*);
|
||||
static void getParamiv(const struct ALfilter*, ALenum, int*);
|
||||
static void getParamf(const struct ALfilter*, ALenum, float*);
|
||||
static void getParamfv(const struct ALfilter*, ALenum, float*);
|
||||
};
|
||||
|
||||
struct NullFilterTable : public FilterTable<NullFilterTable> { };
|
||||
struct LowpassFilterTable : public FilterTable<LowpassFilterTable> { };
|
||||
struct HighpassFilterTable : public FilterTable<HighpassFilterTable> { };
|
||||
struct BandpassFilterTable : public FilterTable<BandpassFilterTable> { };
|
||||
|
||||
|
||||
struct ALfilter {
|
||||
|
|
@ -17,36 +42,35 @@ struct ALfilter {
|
|||
|
||||
float Gain{1.0f};
|
||||
float GainHF{1.0f};
|
||||
float HFReference{LOWPASSFREQREF};
|
||||
float HFReference{LowPassFreqRef};
|
||||
float GainLF{1.0f};
|
||||
float LFReference{HIGHPASSFREQREF};
|
||||
float LFReference{HighPassFreqRef};
|
||||
|
||||
struct Vtable {
|
||||
void (*const setParami )(ALfilter *filter, ALenum param, int val);
|
||||
void (*const setParamiv)(ALfilter *filter, ALenum param, const int *vals);
|
||||
void (*const setParamf )(ALfilter *filter, ALenum param, float val);
|
||||
void (*const setParamfv)(ALfilter *filter, ALenum param, const float *vals);
|
||||
|
||||
void (*const getParami )(const ALfilter *filter, ALenum param, int *val);
|
||||
void (*const getParamiv)(const ALfilter *filter, ALenum param, int *vals);
|
||||
void (*const getParamf )(const ALfilter *filter, ALenum param, float *val);
|
||||
void (*const getParamfv)(const ALfilter *filter, ALenum param, float *vals);
|
||||
};
|
||||
const Vtable *vtab{nullptr};
|
||||
using TableTypes = std::variant<NullFilterTable,LowpassFilterTable,HighpassFilterTable,
|
||||
BandpassFilterTable>;
|
||||
TableTypes mTypeVariant;
|
||||
|
||||
/* Self ID */
|
||||
ALuint id{0};
|
||||
|
||||
void setParami(ALenum param, int value) { vtab->setParami(this, param, value); }
|
||||
void setParamiv(ALenum param, const int *values) { vtab->setParamiv(this, param, values); }
|
||||
void setParamf(ALenum param, float value) { vtab->setParamf(this, param, value); }
|
||||
void setParamfv(ALenum param, const float *values) { vtab->setParamfv(this, param, values); }
|
||||
void getParami(ALenum param, int *value) const { vtab->getParami(this, param, value); }
|
||||
void getParamiv(ALenum param, int *values) const { vtab->getParamiv(this, param, values); }
|
||||
void getParamf(ALenum param, float *value) const { vtab->getParamf(this, param, value); }
|
||||
void getParamfv(ALenum param, float *values) const { vtab->getParamfv(this, param, values); }
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
DISABLE_ALLOC()
|
||||
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
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "listener.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <mutex>
|
||||
|
||||
|
|
@ -30,9 +31,10 @@
|
|||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/context.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "core/except.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "alspan.h"
|
||||
#include "direct_defs.h"
|
||||
#include "error.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
|
|
@ -68,377 +70,333 @@ inline void CommitAndUpdateProps(ALCcontext *context)
|
|||
|
||||
} // namespace
|
||||
|
||||
AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC2(void, alListenerf, ALenum,param, ALfloat,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) noexcept
|
||||
try {
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
if(!(value >= 0.0f && std::isfinite(value)))
|
||||
return context->setError(AL_INVALID_VALUE, "Listener gain out of range");
|
||||
throw al::context_error{AL_INVALID_VALUE, "Listener gain out of range"};
|
||||
listener.Gain = value;
|
||||
UpdateProps(context.get());
|
||||
break;
|
||||
UpdateProps(context);
|
||||
return;
|
||||
|
||||
case AL_METERS_PER_UNIT:
|
||||
if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT))
|
||||
return context->setError(AL_INVALID_VALUE, "Listener meters per unit out of range");
|
||||
throw al::context_error{AL_INVALID_VALUE, "Listener meters per unit out of range"};
|
||||
listener.mMetersPerUnit = value;
|
||||
UpdateProps(context.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener float property");
|
||||
UpdateProps(context);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC4(void, alListener3f, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3)
|
||||
FORCE_ALIGN void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1,
|
||||
ALfloat value2, ALfloat value3) noexcept
|
||||
try {
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
|
||||
return context->setError(AL_INVALID_VALUE, "Listener position out of range");
|
||||
throw al::context_error{AL_INVALID_VALUE, "Listener position out of range"};
|
||||
listener.Position[0] = value1;
|
||||
listener.Position[1] = value2;
|
||||
listener.Position[2] = value3;
|
||||
CommitAndUpdateProps(context.get());
|
||||
break;
|
||||
CommitAndUpdateProps(context);
|
||||
return;
|
||||
|
||||
case AL_VELOCITY:
|
||||
if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3)))
|
||||
return context->setError(AL_INVALID_VALUE, "Listener velocity out of range");
|
||||
throw al::context_error{AL_INVALID_VALUE, "Listener velocity out of range"};
|
||||
listener.Velocity[0] = value1;
|
||||
listener.Velocity[1] = value2;
|
||||
listener.Velocity[2] = value3;
|
||||
CommitAndUpdateProps(context.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
|
||||
CommitAndUpdateProps(context);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
if(values)
|
||||
AL_API DECL_FUNC2(void, alListenerfv, ALenum,param, const ALfloat*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param,
|
||||
const ALfloat *values) noexcept
|
||||
try {
|
||||
if(!values)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
case AL_METERS_PER_UNIT:
|
||||
alListenerf(param, values[0]);
|
||||
return;
|
||||
case AL_GAIN:
|
||||
case AL_METERS_PER_UNIT:
|
||||
alListenerfDirect(context, param, *values);
|
||||
return;
|
||||
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alListener3f(param, values[0], values[1], values[2]);
|
||||
return;
|
||||
}
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
auto vals = al::span<const float,3>{values, 3_uz};
|
||||
alListener3fDirect(context, param, vals[0], vals[1], vals[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
if(!values) UNLIKELY
|
||||
return context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
if(!(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]) &&
|
||||
std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5])))
|
||||
auto vals = al::span<const float,6>{values, 6_uz};
|
||||
if(!std::all_of(vals.cbegin(), vals.cend(), [](float f) { return std::isfinite(f); }))
|
||||
return context->setError(AL_INVALID_VALUE, "Listener orientation out of range");
|
||||
/* AT then UP */
|
||||
listener.OrientAt[0] = values[0];
|
||||
listener.OrientAt[1] = values[1];
|
||||
listener.OrientAt[2] = values[2];
|
||||
listener.OrientUp[0] = values[3];
|
||||
listener.OrientUp[1] = values[4];
|
||||
listener.OrientUp[2] = values[5];
|
||||
CommitAndUpdateProps(context.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
|
||||
std::copy_n(vals.cbegin(), 3, listener.OrientAt.begin());
|
||||
std::copy_n(vals.cbegin()+3, 3, listener.OrientUp.begin());
|
||||
CommitAndUpdateProps(context);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param};
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alListeneri(ALenum param, ALint /*value*/)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
|
||||
START_API_FUNC
|
||||
{
|
||||
|
||||
AL_API DECL_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)
|
||||
{
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alListener3f(param, static_cast<ALfloat>(value1), static_cast<ALfloat>(value2),
|
||||
static_cast<ALfloat>(value3));
|
||||
alListener3fDirect(context, param, static_cast<ALfloat>(value1),
|
||||
static_cast<ALfloat>(value2), static_cast<ALfloat>(value3));
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
vals = {values, 3_uz};
|
||||
alListener3fDirect(context, param, static_cast<ALfloat>(vals[0]),
|
||||
static_cast<ALfloat>(vals[1]), static_cast<ALfloat>(vals[2]));
|
||||
return;
|
||||
|
||||
case AL_ORIENTATION:
|
||||
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
|
||||
|
||||
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");
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
AL_API DECL_FUNC2(void, alGetListenerf, ALenum,param, ALfloat*,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param,
|
||||
ALfloat *value) noexcept
|
||||
try {
|
||||
if(!value)
|
||||
context->setError(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;
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_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)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
*value1 = listener.Position[0];
|
||||
*value2 = listener.Position[1];
|
||||
*value3 = listener.Position[2];
|
||||
break;
|
||||
return;
|
||||
|
||||
case AL_VELOCITY:
|
||||
*value1 = listener.Velocity[0];
|
||||
*value2 = listener.Velocity[1];
|
||||
*value3 = listener.Velocity[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-float property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
|
||||
START_API_FUNC
|
||||
{
|
||||
AL_API DECL_FUNC2(void, alGetListenerfv, ALenum,param, ALfloat*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param,
|
||||
ALfloat *values) noexcept
|
||||
try {
|
||||
if(!values)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_GAIN:
|
||||
case AL_METERS_PER_UNIT:
|
||||
alGetListenerf(param, values);
|
||||
alGetListenerfDirect(context, param, values);
|
||||
return;
|
||||
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alGetListener3f(param, values+0, values+1, values+2);
|
||||
auto vals = al::span<ALfloat,3>{values, 3_uz};
|
||||
alGetListener3fDirect(context, param, &vals[0], &vals[1], &vals[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!values)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
al::span<ALfloat,6> vals{values, 6_uz};
|
||||
// AT then UP
|
||||
values[0] = listener.OrientAt[0];
|
||||
values[1] = listener.OrientAt[1];
|
||||
values[2] = listener.OrientAt[2];
|
||||
values[3] = listener.OrientUp[0];
|
||||
values[4] = listener.OrientUp[1];
|
||||
values[5] = listener.OrientUp[2];
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
|
||||
std::copy_n(listener.OrientAt.cbegin(), 3, vals.begin());
|
||||
std::copy_n(listener.OrientUp.cbegin(), 3, vals.begin()+3);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener float-vector property 0x%x", param};
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
|
||||
AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!value)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
{
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
|
||||
START_API_FUNC
|
||||
{
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
AL_API DECL_FUNC2(void, alGetListeneri, ALenum,param, ALint*,value)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) noexcept
|
||||
try {
|
||||
if(!value) throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
|
||||
AL_API DECL_FUNC4(void, alGetListener3i, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param,
|
||||
ALint *value1, ALint *value2, ALint *value3) noexcept
|
||||
try {
|
||||
if(!value1 || !value2 || !value3)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!value1 || !value2 || !value3)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
*value1 = static_cast<ALint>(listener.Position[0]);
|
||||
*value2 = static_cast<ALint>(listener.Position[1]);
|
||||
*value3 = static_cast<ALint>(listener.Position[2]);
|
||||
break;
|
||||
return;
|
||||
|
||||
case AL_VELOCITY:
|
||||
*value1 = static_cast<ALint>(listener.Velocity[0]);
|
||||
*value2 = static_cast<ALint>(listener.Velocity[1]);
|
||||
*value3 = static_cast<ALint>(listener.Velocity[2]);
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener 3-integer property 0x%x", param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
||||
AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
|
||||
START_API_FUNC
|
||||
{
|
||||
AL_API DECL_FUNC2(void, alGetListeneriv, ALenum,param, ALint*,values)
|
||||
FORCE_ALIGN void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param,
|
||||
ALint *values) noexcept
|
||||
try {
|
||||
if(!values)
|
||||
throw al::context_error{AL_INVALID_VALUE, "NULL pointer"};
|
||||
|
||||
switch(param)
|
||||
{
|
||||
case AL_POSITION:
|
||||
case AL_VELOCITY:
|
||||
alGetListener3i(param, values+0, values+1, values+2);
|
||||
auto vals = al::span<ALint,3>{values, 3_uz};
|
||||
alGetListener3iDirect(context, param, &vals[0], &vals[1], &vals[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
ContextRef context{GetContextRef()};
|
||||
if(!context) UNLIKELY return;
|
||||
|
||||
ALlistener &listener = context->mListener;
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
if(!values)
|
||||
context->setError(AL_INVALID_VALUE, "NULL pointer");
|
||||
else switch(param)
|
||||
std::lock_guard<std::mutex> proplock{context->mPropLock};
|
||||
|
||||
static constexpr auto f2i = [](const float val) noexcept { return static_cast<ALint>(val); };
|
||||
switch(param)
|
||||
{
|
||||
case AL_ORIENTATION:
|
||||
auto vals = al::span<ALint,6>{values, 6_uz};
|
||||
// AT then UP
|
||||
values[0] = static_cast<ALint>(listener.OrientAt[0]);
|
||||
values[1] = static_cast<ALint>(listener.OrientAt[1]);
|
||||
values[2] = static_cast<ALint>(listener.OrientAt[2]);
|
||||
values[3] = static_cast<ALint>(listener.OrientUp[0]);
|
||||
values[4] = static_cast<ALint>(listener.OrientUp[1]);
|
||||
values[5] = static_cast<ALint>(listener.OrientUp[2]);
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
|
||||
std::transform(listener.OrientAt.cbegin(), listener.OrientAt.cend(), vals.begin(), f2i);
|
||||
std::transform(listener.OrientUp.cbegin(), listener.OrientUp.cend(), vals.begin()+3, f2i);
|
||||
return;
|
||||
}
|
||||
throw al::context_error{AL_INVALID_ENUM, "Invalid listener integer-vector property 0x%x",
|
||||
param};
|
||||
}
|
||||
catch(al::context_error& e) {
|
||||
context->setError(e.errorCode(), "%s", e.what());
|
||||
}
|
||||
END_API_FUNC
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#include <array>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "almalloc.h"
|
||||
|
|
@ -18,7 +16,7 @@ struct ALlistener {
|
|||
float Gain{1.0f};
|
||||
float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT};
|
||||
|
||||
DISABLE_ALLOC()
|
||||
DISABLE_ALLOC
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,26 +2,26 @@
|
|||
#define AL_SOURCE_H
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "alc/alu.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "aldeque.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumbers.h"
|
||||
#include "alnumeric.h"
|
||||
#include "atomic.h"
|
||||
#include "alspan.h"
|
||||
#include "core/context.h"
|
||||
#include "core/voice.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
#include "eax/api.h"
|
||||
#include "eax/call.h"
|
||||
#include "eax/exception.h"
|
||||
#include "eax/fx_slot_index.h"
|
||||
|
|
@ -30,23 +30,23 @@
|
|||
|
||||
struct ALbuffer;
|
||||
struct ALeffectslot;
|
||||
|
||||
enum class Resampler : uint8_t;
|
||||
|
||||
enum class SourceStereo : bool {
|
||||
Normal = AL_NORMAL_SOFT,
|
||||
Enhanced = AL_SUPER_STEREO_SOFT
|
||||
};
|
||||
|
||||
#define DEFAULT_SENDS 2
|
||||
inline constexpr size_t DefaultSendCount{2};
|
||||
|
||||
#define INVALID_VOICE_IDX static_cast<ALuint>(-1)
|
||||
inline constexpr ALuint InvalidVoiceIndex{std::numeric_limits<ALuint>::max()};
|
||||
|
||||
extern bool sBufferSubDataCompat;
|
||||
inline bool sBufferSubDataCompat{false};
|
||||
|
||||
struct ALbufferQueueItem : public VoiceBufferItem {
|
||||
ALbuffer *mBuffer{nullptr};
|
||||
|
||||
DISABLE_ALLOC()
|
||||
DISABLE_ALLOC
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -88,6 +88,7 @@ struct ALsource {
|
|||
DirectMode DirectChannels{DirectMode::Off};
|
||||
SpatializeMode mSpatialize{SpatializeMode::Auto};
|
||||
SourceStereo mStereoMode{SourceStereo::Normal};
|
||||
bool mPanningEnabled{false};
|
||||
|
||||
bool DryGainHFAuto{true};
|
||||
bool WetGainAuto{true};
|
||||
|
|
@ -105,24 +106,27 @@ struct ALsource {
|
|||
|
||||
float Radius{0.0f};
|
||||
float EnhWidth{0.593f};
|
||||
float mPan{0.0f};
|
||||
|
||||
/** Direct filter and auxiliary send info. */
|
||||
struct {
|
||||
float Gain;
|
||||
float GainHF;
|
||||
float HFReference;
|
||||
float GainLF;
|
||||
float LFReference;
|
||||
} Direct;
|
||||
struct SendData {
|
||||
ALeffectslot *Slot;
|
||||
float Gain;
|
||||
float GainHF;
|
||||
float HFReference;
|
||||
float GainLF;
|
||||
float LFReference;
|
||||
struct DirectData {
|
||||
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
|
||||
|
|
@ -138,26 +142,28 @@ struct ALsource {
|
|||
ALenum state{AL_INITIAL};
|
||||
|
||||
/** Source Buffer Queue head. */
|
||||
al::deque<ALbufferQueueItem> mQueue;
|
||||
std::deque<ALbufferQueueItem> mQueue;
|
||||
|
||||
bool mPropsDirty{true};
|
||||
|
||||
/* Index into the context's Voices array. Lazily updated, only checked and
|
||||
* reset when looking up the voice.
|
||||
*/
|
||||
ALuint VoiceIdx{INVALID_VOICE_IDX};
|
||||
ALuint VoiceIdx{InvalidVoiceIndex};
|
||||
|
||||
/** Self ID */
|
||||
ALuint id{0};
|
||||
|
||||
|
||||
ALsource();
|
||||
ALsource() noexcept;
|
||||
~ALsource();
|
||||
|
||||
ALsource(const ALsource&) = delete;
|
||||
ALsource& operator=(const ALsource&) = delete;
|
||||
|
||||
DISABLE_ALLOC()
|
||||
static void SetName(ALCcontext *context, ALuint id, std::string_view name);
|
||||
|
||||
DISABLE_ALLOC
|
||||
|
||||
#ifdef ALSOFT_EAX
|
||||
public:
|
||||
|
|
@ -171,18 +177,18 @@ public:
|
|||
private:
|
||||
using Exception = EaxSourceException;
|
||||
|
||||
static constexpr auto eax_max_speakers = 9;
|
||||
static constexpr auto eax_max_speakers{9u};
|
||||
|
||||
using EaxFxSlotIds = const GUID* [EAX_MAX_FXSLOTS];
|
||||
using EaxFxSlotIds = std::array<const GUID*,EAX_MAX_FXSLOTS>;
|
||||
|
||||
static constexpr const EaxFxSlotIds eax4_fx_slot_ids = {
|
||||
static constexpr const EaxFxSlotIds eax4_fx_slot_ids{
|
||||
&EAXPROPERTYID_EAX40_FXSlot0,
|
||||
&EAXPROPERTYID_EAX40_FXSlot1,
|
||||
&EAXPROPERTYID_EAX40_FXSlot2,
|
||||
&EAXPROPERTYID_EAX40_FXSlot3,
|
||||
};
|
||||
|
||||
static constexpr const EaxFxSlotIds eax5_fx_slot_ids = {
|
||||
static constexpr const EaxFxSlotIds eax5_fx_slot_ids{
|
||||
&EAXPROPERTYID_EAX50_FXSlot0,
|
||||
&EAXPROPERTYID_EAX50_FXSlot1,
|
||||
&EAXPROPERTYID_EAX50_FXSlot2,
|
||||
|
|
@ -215,11 +221,6 @@ private:
|
|||
Eax3Props source;
|
||||
EaxSends sends;
|
||||
EAX40ACTIVEFXSLOTS active_fx_slots;
|
||||
|
||||
bool operator==(const Eax4Props& rhs) noexcept
|
||||
{
|
||||
return std::memcmp(this, &rhs, sizeof(Eax4Props)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax4State {
|
||||
|
|
@ -232,11 +233,6 @@ private:
|
|||
EaxSends sends;
|
||||
EAX50ACTIVEFXSLOTS active_fx_slots;
|
||||
EaxSpeakerLevels speaker_levels;
|
||||
|
||||
bool operator==(const Eax5Props& rhs) noexcept
|
||||
{
|
||||
return std::memcmp(this, &rhs, sizeof(Eax5Props)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Eax5State {
|
||||
|
|
@ -546,7 +542,24 @@ private:
|
|||
struct Eax5SourceAllValidator {
|
||||
void operator()(const EAX50SOURCEPROPERTIES& props) const
|
||||
{
|
||||
Eax3SourceAllValidator{}(static_cast<const Eax3Props&>(props));
|
||||
Eax2SourceDirectValidator{}(props.lDirect);
|
||||
Eax2SourceDirectHfValidator{}(props.lDirectHF);
|
||||
Eax2SourceRoomValidator{}(props.lRoom);
|
||||
Eax2SourceRoomHfValidator{}(props.lRoomHF);
|
||||
Eax2SourceObstructionValidator{}(props.lObstruction);
|
||||
Eax2SourceObstructionLfRatioValidator{}(props.flObstructionLFRatio);
|
||||
Eax2SourceOcclusionValidator{}(props.lOcclusion);
|
||||
Eax2SourceOcclusionLfRatioValidator{}(props.flOcclusionLFRatio);
|
||||
Eax2SourceOcclusionRoomRatioValidator{}(props.flOcclusionRoomRatio);
|
||||
Eax3SourceOcclusionDirectRatioValidator{}(props.flOcclusionDirectRatio);
|
||||
Eax3SourceExclusionValidator{}(props.lExclusion);
|
||||
Eax3SourceExclusionLfRatioValidator{}(props.flExclusionLFRatio);
|
||||
Eax2SourceOutsideVolumeHfValidator{}(props.lOutsideVolumeHF);
|
||||
Eax3SourceDopplerFactorValidator{}(props.flDopplerFactor);
|
||||
Eax3SourceRolloffFactorValidator{}(props.flRolloffFactor);
|
||||
Eax2SourceRoomRolloffFactorValidator{}(props.flRoomRolloffFactor);
|
||||
Eax2SourceAirAbsorptionFactorValidator{}(props.flAirAbsorptionFactor);
|
||||
Eax5SourceFlagsValidator{}(props.ulFlags);
|
||||
Eax5SourceMacroFXFactorValidator{}(props.flMacroFXFactor);
|
||||
}
|
||||
};
|
||||
|
|
@ -809,39 +822,38 @@ private:
|
|||
[[noreturn]] static void eax_fail_unknown_active_fx_slot_id();
|
||||
[[noreturn]] static void eax_fail_unknown_receiving_fx_slot_id();
|
||||
|
||||
void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept;
|
||||
void eax1_set_defaults(Eax1Props& props) noexcept;
|
||||
static void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept;
|
||||
static void eax1_set_defaults(Eax1Props& props) noexcept;
|
||||
void eax1_set_defaults() noexcept;
|
||||
void eax2_set_defaults(Eax2Props& props) noexcept;
|
||||
static void eax2_set_defaults(Eax2Props& props) noexcept;
|
||||
void eax2_set_defaults() noexcept;
|
||||
void eax3_set_defaults(Eax3Props& props) noexcept;
|
||||
static void eax3_set_defaults(Eax3Props& props) noexcept;
|
||||
void eax3_set_defaults() noexcept;
|
||||
void eax4_set_sends_defaults(EaxSends& sends) noexcept;
|
||||
void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept;
|
||||
static void eax4_set_sends_defaults(EaxSends& sends) noexcept;
|
||||
static void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept;
|
||||
void eax4_set_defaults() noexcept;
|
||||
void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept;
|
||||
void eax5_set_sends_defaults(EaxSends& sends) noexcept;
|
||||
void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept;
|
||||
void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept;
|
||||
void eax5_set_defaults(Eax5Props& props) noexcept;
|
||||
static void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept;
|
||||
static void eax5_set_sends_defaults(EaxSends& sends) noexcept;
|
||||
static void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept;
|
||||
static void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept;
|
||||
static void eax5_set_defaults(Eax5Props& props) noexcept;
|
||||
void eax5_set_defaults() noexcept;
|
||||
void eax_set_defaults() noexcept;
|
||||
|
||||
void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept;
|
||||
void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept;
|
||||
void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept;
|
||||
void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept;
|
||||
static void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept;
|
||||
static void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept;
|
||||
static void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept;
|
||||
static void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept;
|
||||
|
||||
static float eax_calculate_dst_occlusion_mb(
|
||||
long src_occlusion_mb,
|
||||
float path_ratio,
|
||||
float lf_ratio) noexcept;
|
||||
|
||||
EaxAlLowPassParam eax_create_direct_filter_param() const noexcept;
|
||||
[[nodiscard]] auto eax_create_direct_filter_param() const noexcept -> EaxAlLowPassParam;
|
||||
|
||||
EaxAlLowPassParam eax_create_room_filter_param(
|
||||
const ALeffectslot& fx_slot,
|
||||
const EAXSOURCEALLSENDPROPERTIES& send) const noexcept;
|
||||
[[nodiscard]] auto eax_create_room_filter_param(const ALeffectslot& fx_slot,
|
||||
const EAXSOURCEALLSENDPROPERTIES& send) const noexcept -> EaxAlLowPassParam;
|
||||
|
||||
void eax_update_direct_filter();
|
||||
void eax_update_room_filters();
|
||||
|
|
@ -894,16 +906,16 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void eax_get_active_fx_slot_id(const EaxCall& call, const GUID* ids, size_t max_count);
|
||||
void eax1_get(const EaxCall& call, const Eax1Props& props);
|
||||
void eax2_get(const EaxCall& call, const Eax2Props& props);
|
||||
void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props);
|
||||
void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props);
|
||||
void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props);
|
||||
void eax3_get(const EaxCall& call, const Eax3Props& props);
|
||||
static void eax_get_active_fx_slot_id(const EaxCall& call, const al::span<const GUID> src_ids);
|
||||
static void eax1_get(const EaxCall& call, const Eax1Props& props);
|
||||
static void eax2_get(const EaxCall& call, const Eax2Props& props);
|
||||
static void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props);
|
||||
static void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props);
|
||||
static void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props);
|
||||
static void eax3_get(const EaxCall& call, const Eax3Props& props);
|
||||
void eax4_get(const EaxCall& call, const Eax4Props& props);
|
||||
void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props);
|
||||
void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props);
|
||||
static void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props);
|
||||
static void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props);
|
||||
void eax5_get(const EaxCall& call, const Eax5Props& props);
|
||||
void eax_get(const EaxCall& call);
|
||||
|
||||
|
|
@ -976,21 +988,21 @@ private:
|
|||
}
|
||||
|
||||
template<typename TValidator, size_t TIdCount>
|
||||
void eax_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount])
|
||||
void eax_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids)
|
||||
{
|
||||
const auto src_ids = call.get_values<const GUID>(TIdCount);
|
||||
std::for_each(src_ids.cbegin(), src_ids.cend(), TValidator{});
|
||||
std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids);
|
||||
std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids.begin());
|
||||
}
|
||||
|
||||
template<size_t TIdCount>
|
||||
void eax4_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount])
|
||||
void eax4_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids)
|
||||
{
|
||||
eax_defer_active_fx_slot_id<Eax4ActiveFxSlotIdValidator>(call, dst_ids);
|
||||
}
|
||||
|
||||
template<size_t TIdCount>
|
||||
void eax5_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount])
|
||||
void eax5_defer_active_fx_slot_id(const EaxCall& call, const al::span<GUID,TIdCount> dst_ids)
|
||||
{
|
||||
eax_defer_active_fx_slot_id<Eax5ActiveFxSlotIdValidator>(call, dst_ids);
|
||||
}
|
||||
|
|
@ -1022,12 +1034,12 @@ private:
|
|||
void eax_set_efx_wet_gain_auto();
|
||||
void eax_set_efx_wet_gain_hf_auto();
|
||||
|
||||
void eax1_set(const EaxCall& call, Eax1Props& props);
|
||||
void eax2_set(const EaxCall& call, Eax2Props& props);
|
||||
static void eax1_set(const EaxCall& call, Eax1Props& props);
|
||||
static void eax2_set(const EaxCall& call, Eax2Props& props);
|
||||
void eax3_set(const EaxCall& call, Eax3Props& props);
|
||||
void eax4_set(const EaxCall& call, Eax4Props& props);
|
||||
void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props);
|
||||
void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props);
|
||||
static void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props);
|
||||
static void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props);
|
||||
void eax5_set(const EaxCall& call, Eax5Props& props);
|
||||
void eax_set(const EaxCall& call);
|
||||
|
||||
|
|
@ -1041,4 +1053,19 @@ private:
|
|||
|
||||
void UpdateAllSourceProps(ALCcontext *context);
|
||||
|
||||
struct SourceSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
gsl::owner<std::array<ALsource,64>*> Sources{nullptr};
|
||||
|
||||
SourceSubList() noexcept = default;
|
||||
SourceSubList(const SourceSubList&) = delete;
|
||||
SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
|
||||
~SourceSubList();
|
||||
|
||||
SourceSubList& operator=(const SourceSubList&) = delete;
|
||||
SourceSubList& operator=(SourceSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
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 <cstdlib>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
|
|
@ -34,25 +31,47 @@
|
|||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "alfstream.h"
|
||||
#include "almalloc.h"
|
||||
#include "alstring.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "strutils.h"
|
||||
#include "vector.h"
|
||||
|
||||
#if defined(ALSOFT_UWP)
|
||||
#include <winrt/Windows.Media.Core.h> // !!This is important!!
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
using namespace winrt;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
#if defined(_WIN32) && !defined(_GAMING_XBOX) && !defined(ALSOFT_UWP)
|
||||
struct CoTaskMemDeleter {
|
||||
void operator()(void *mem) const { CoTaskMemFree(mem); }
|
||||
};
|
||||
#endif
|
||||
|
||||
struct ConfigEntry {
|
||||
std::string key;
|
||||
std::string value;
|
||||
};
|
||||
al::vector<ConfigEntry> ConfOpts;
|
||||
std::vector<ConfigEntry> ConfOpts;
|
||||
|
||||
|
||||
std::string &lstrip(std::string &line)
|
||||
|
|
@ -72,57 +91,48 @@ bool readline(std::istream &f, std::string &output)
|
|||
return std::getline(f, output) && !output.empty();
|
||||
}
|
||||
|
||||
std::string expdup(const char *str)
|
||||
std::string expdup(std::string_view str)
|
||||
{
|
||||
std::string output;
|
||||
|
||||
std::string envval;
|
||||
while(*str != '\0')
|
||||
while(!str.empty())
|
||||
{
|
||||
const char *addstr;
|
||||
size_t addstrlen;
|
||||
|
||||
if(str[0] != '$')
|
||||
if(auto nextpos = str.find('$'))
|
||||
{
|
||||
const char *next = std::strchr(str, '$');
|
||||
addstr = str;
|
||||
addstrlen = next ? static_cast<size_t>(next-str) : std::strlen(str);
|
||||
output += str.substr(0, nextpos);
|
||||
if(nextpos == std::string_view::npos)
|
||||
break;
|
||||
|
||||
str += addstrlen;
|
||||
str.remove_prefix(nextpos);
|
||||
}
|
||||
else
|
||||
|
||||
str.remove_prefix(1);
|
||||
if(str.empty())
|
||||
{
|
||||
str++;
|
||||
if(*str == '$')
|
||||
{
|
||||
const char *next = std::strchr(str+1, '$');
|
||||
addstr = str;
|
||||
addstrlen = next ? static_cast<size_t>(next-str) : std::strlen(str);
|
||||
|
||||
str += addstrlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool hasbraces{(*str == '{')};
|
||||
|
||||
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();
|
||||
}
|
||||
output += '$';
|
||||
break;
|
||||
}
|
||||
if(addstrlen == 0)
|
||||
if(str.front() == '$')
|
||||
{
|
||||
output += '$';
|
||||
str.remove_prefix(1);
|
||||
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;
|
||||
|
|
@ -140,44 +150,43 @@ void LoadConfigFromFile(std::istream &f)
|
|||
|
||||
if(buffer[0] == '[')
|
||||
{
|
||||
auto line = const_cast<char*>(buffer.data());
|
||||
char *section = line+1;
|
||||
char *endsection;
|
||||
|
||||
endsection = std::strchr(section, ']');
|
||||
if(!endsection || section == endsection)
|
||||
auto endpos = buffer.find(']', 1);
|
||||
if(endpos == 1 || endpos == std::string::npos)
|
||||
{
|
||||
ERR(" config parse error: bad line \"%s\"\n", line);
|
||||
ERR(" config parse error: bad line \"%s\"\n", buffer.c_str());
|
||||
continue;
|
||||
}
|
||||
if(endsection[1] != 0)
|
||||
if(buffer[endpos+1] != '\0')
|
||||
{
|
||||
char *end = endsection+1;
|
||||
while(std::isspace(*end))
|
||||
++end;
|
||||
if(*end != 0 && *end != '#')
|
||||
size_t last{endpos+1};
|
||||
while(last < buffer.size() && std::isspace(buffer[last]))
|
||||
++last;
|
||||
|
||||
if(last < buffer.size() && buffer[last] != '#')
|
||||
{
|
||||
ERR(" config parse error: bad line \"%s\"\n", line);
|
||||
ERR(" config parse error: bad line \"%s\"\n", buffer.c_str());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*endsection = 0;
|
||||
|
||||
auto section = std::string_view{buffer}.substr(1, endpos-1);
|
||||
|
||||
curSection.clear();
|
||||
if(al::strcasecmp(section, "general") != 0)
|
||||
if(al::case_compare(section, "general"sv) != 0)
|
||||
{
|
||||
do {
|
||||
char *nextp = std::strchr(section, '%');
|
||||
if(!nextp)
|
||||
auto nextp = section.find('%');
|
||||
if(nextp == std::string_view::npos)
|
||||
{
|
||||
curSection += section;
|
||||
break;
|
||||
}
|
||||
|
||||
curSection.append(section, nextp);
|
||||
section = nextp;
|
||||
curSection += section.substr(0, nextp);
|
||||
section.remove_prefix(nextp);
|
||||
|
||||
if(((section[1] >= '0' && section[1] <= '9') ||
|
||||
if(section.size() > 2 &&
|
||||
((section[1] >= '0' && section[1] <= '9') ||
|
||||
(section[1] >= 'a' && section[1] <= 'f') ||
|
||||
(section[1] >= 'A' && section[1] <= 'F')) &&
|
||||
((section[2] >= '0' && section[2] <= '9') ||
|
||||
|
|
@ -198,19 +207,19 @@ void LoadConfigFromFile(std::istream &f)
|
|||
else if(section[2] >= 'A' && section[2] <= 'F')
|
||||
b |= (section[2]-'A'+0x0a);
|
||||
curSection += static_cast<char>(b);
|
||||
section += 3;
|
||||
section.remove_prefix(3);
|
||||
}
|
||||
else if(section[1] == '%')
|
||||
else if(section.size() > 1 && section[1] == '%')
|
||||
{
|
||||
curSection += '%';
|
||||
section += 2;
|
||||
section.remove_prefix(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
curSection += '%';
|
||||
section += 1;
|
||||
section.remove_prefix(1);
|
||||
}
|
||||
} while(*section != 0);
|
||||
} while(!section.empty());
|
||||
}
|
||||
|
||||
continue;
|
||||
|
|
@ -228,16 +237,17 @@ void LoadConfigFromFile(std::istream &f)
|
|||
ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str());
|
||||
continue;
|
||||
}
|
||||
auto keyend = sep++;
|
||||
while(keyend > 0 && std::isspace(buffer[keyend-1]))
|
||||
--keyend;
|
||||
if(!keyend)
|
||||
auto keypart = std::string_view{buffer}.substr(0, sep++);
|
||||
while(!keypart.empty() && std::isspace(keypart.back()))
|
||||
keypart.remove_suffix(1);
|
||||
if(keypart.empty())
|
||||
{
|
||||
ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str());
|
||||
continue;
|
||||
}
|
||||
while(sep < buffer.size() && std::isspace(buffer[sep]))
|
||||
sep++;
|
||||
auto valpart = std::string_view{buffer}.substr(sep);
|
||||
while(!valpart.empty() && std::isspace(valpart.front()))
|
||||
valpart.remove_prefix(1);
|
||||
|
||||
std::string fullKey;
|
||||
if(!curSection.empty())
|
||||
|
|
@ -245,20 +255,24 @@ void LoadConfigFromFile(std::istream &f)
|
|||
fullKey += curSection;
|
||||
fullKey += '/';
|
||||
}
|
||||
fullKey += buffer.substr(0u, keyend);
|
||||
fullKey += keypart;
|
||||
|
||||
std::string value{(sep < buffer.size()) ? buffer.substr(sep) : std::string{}};
|
||||
if(value.size() > 1)
|
||||
if(valpart.size() > size_t{std::numeric_limits<int>::max()})
|
||||
{
|
||||
if((value.front() == '"' && value.back() == '"')
|
||||
|| (value.front() == '\'' && value.back() == '\''))
|
||||
ERR(" config parse error: value too long in line \"%s\"\n", buffer.c_str());
|
||||
continue;
|
||||
}
|
||||
if(valpart.size() > 1)
|
||||
{
|
||||
if((valpart.front() == '"' && valpart.back() == '"')
|
||||
|| (valpart.front() == '\'' && valpart.back() == '\''))
|
||||
{
|
||||
value.pop_back();
|
||||
value.erase(value.begin());
|
||||
valpart.remove_prefix(1);
|
||||
valpart.remove_suffix(1);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE(" found '%s' = '%s'\n", fullKey.c_str(), value.c_str());
|
||||
TRACE(" setting '%s' = '%.*s'\n", fullKey.c_str(), al::sizei(valpart), valpart.data());
|
||||
|
||||
/* Check if we already have this option set */
|
||||
auto find_key = [&fullKey](const ConfigEntry &entry) -> bool
|
||||
|
|
@ -266,61 +280,49 @@ void LoadConfigFromFile(std::istream &f)
|
|||
auto ent = std::find_if(ConfOpts.begin(), ConfOpts.end(), find_key);
|
||||
if(ent != ConfOpts.end())
|
||||
{
|
||||
if(!value.empty())
|
||||
ent->value = expdup(value.c_str());
|
||||
if(!valpart.empty())
|
||||
ent->value = expdup(valpart);
|
||||
else
|
||||
ConfOpts.erase(ent);
|
||||
}
|
||||
else if(!value.empty())
|
||||
ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(value.c_str())});
|
||||
else if(!valpart.empty())
|
||||
ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(valpart)});
|
||||
}
|
||||
ConfOpts.shrink_to_fit();
|
||||
}
|
||||
|
||||
const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName)
|
||||
const char *GetConfigValue(const std::string_view devName, const std::string_view blockName,
|
||||
const std::string_view keyName)
|
||||
{
|
||||
if(!keyName)
|
||||
if(keyName.empty())
|
||||
return nullptr;
|
||||
|
||||
std::string key;
|
||||
if(blockName && al::strcasecmp(blockName, "general") != 0)
|
||||
if(!blockName.empty() && al::case_compare(blockName, "general"sv) != 0)
|
||||
{
|
||||
key = blockName;
|
||||
if(devName)
|
||||
{
|
||||
key += '/';
|
||||
key += devName;
|
||||
}
|
||||
key += '/';
|
||||
key += keyName;
|
||||
}
|
||||
else
|
||||
if(!devName.empty())
|
||||
{
|
||||
if(devName)
|
||||
{
|
||||
key = devName;
|
||||
key += '/';
|
||||
}
|
||||
key += keyName;
|
||||
key += devName;
|
||||
key += '/';
|
||||
}
|
||||
key += keyName;
|
||||
|
||||
auto iter = std::find_if(ConfOpts.cbegin(), ConfOpts.cend(),
|
||||
[&key](const ConfigEntry &entry) -> bool
|
||||
{ return entry.key == key; });
|
||||
[&key](const ConfigEntry &entry) -> bool { return entry.key == key; });
|
||||
if(iter != ConfOpts.cend())
|
||||
{
|
||||
TRACE("Found %s = \"%s\"\n", key.c_str(), iter->value.c_str());
|
||||
TRACE("Found option %s = \"%s\"\n", key.c_str(), iter->value.c_str());
|
||||
if(!iter->value.empty())
|
||||
return iter->value.c_str();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!devName)
|
||||
{
|
||||
TRACE("Key %s not found\n", key.c_str());
|
||||
if(devName.empty())
|
||||
return nullptr;
|
||||
}
|
||||
return GetConfigValue(nullptr, blockName, keyName);
|
||||
return GetConfigValue({}, blockName, keyName);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -329,33 +331,48 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha
|
|||
#ifdef _WIN32
|
||||
void ReadALConfig()
|
||||
{
|
||||
WCHAR buffer[MAX_PATH];
|
||||
if(SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE) != FALSE)
|
||||
{
|
||||
std::string filepath{wstr_to_utf8(buffer)};
|
||||
filepath += "\\alsoft.ini";
|
||||
namespace fs = std::filesystem;
|
||||
fs::path path;
|
||||
|
||||
TRACE("Loading config %s...\n", filepath.c_str());
|
||||
al::ifstream f{filepath};
|
||||
if(f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
#if !defined(_GAMING_XBOX)
|
||||
{
|
||||
#if !defined(ALSOFT_UWP)
|
||||
std::unique_ptr<WCHAR,CoTaskMemDeleter> bufstore;
|
||||
const HRESULT hr{SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DONT_UNEXPAND,
|
||||
nullptr, al::out_ptr(bufstore))};
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
const std::wstring_view buffer{bufstore.get()};
|
||||
#else
|
||||
winrt::Windows::Storage::ApplicationDataContainer localSettings = winrt::Windows::Storage::ApplicationData::Current().LocalSettings();
|
||||
auto bufstore = Windows::Storage::ApplicationData::Current().RoamingFolder().Path();
|
||||
std::wstring_view buffer{bufstore};
|
||||
{
|
||||
#endif
|
||||
path = fs::path{buffer};
|
||||
path /= L"alsoft.ini";
|
||||
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string ppath{GetProcBinary().path};
|
||||
if(!ppath.empty())
|
||||
path = fs::u8path(GetProcBinary().path);
|
||||
if(!path.empty())
|
||||
{
|
||||
ppath += "\\alsoft.ini";
|
||||
TRACE("Loading config %s...\n", ppath.c_str());
|
||||
al::ifstream f{ppath};
|
||||
if(f.is_open())
|
||||
path /= "alsoft.ini";
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
|
||||
if(auto confpath = al::getenv(L"ALSOFT_CONF"))
|
||||
{
|
||||
TRACE("Loading config %s...\n", wstr_to_utf8(confpath->c_str()).c_str());
|
||||
al::ifstream f{*confpath};
|
||||
if(f.is_open())
|
||||
path = *confpath;
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
}
|
||||
|
|
@ -364,13 +381,12 @@ void ReadALConfig()
|
|||
|
||||
void ReadALConfig()
|
||||
{
|
||||
const char *str{"/etc/openal/alsoft.conf"};
|
||||
namespace fs = std::filesystem;
|
||||
fs::path path{"/etc/openal/alsoft.conf"};
|
||||
|
||||
TRACE("Loading config %s...\n", str);
|
||||
al::ifstream f{str};
|
||||
if(f.is_open())
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
f.close();
|
||||
|
||||
std::string confpaths{al::getenv("XDG_CONFIG_DIRS").value_or("/etc/xdg")};
|
||||
/* Go through the list in reverse, since "the order of base directories
|
||||
|
|
@ -378,48 +394,43 @@ void ReadALConfig()
|
|||
* important". Ergo, we need to load the settings from the later dirs
|
||||
* first so that the settings in the earlier dirs override them.
|
||||
*/
|
||||
std::string fname;
|
||||
while(!confpaths.empty())
|
||||
{
|
||||
auto next = confpaths.find_last_of(':');
|
||||
auto next = confpaths.rfind(':');
|
||||
if(next < confpaths.length())
|
||||
{
|
||||
fname = confpaths.substr(next+1);
|
||||
path = fs::path{std::string_view{confpaths}.substr(next+1)}.lexically_normal();
|
||||
confpaths.erase(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
fname = confpaths;
|
||||
path = fs::path{confpaths}.lexically_normal();
|
||||
confpaths.clear();
|
||||
}
|
||||
|
||||
if(fname.empty() || fname.front() != '/')
|
||||
WARN("Ignoring XDG config dir: %s\n", fname.c_str());
|
||||
if(!path.is_absolute())
|
||||
WARN("Ignoring XDG config dir: %s\n", path.u8string().c_str());
|
||||
else
|
||||
{
|
||||
if(fname.back() != '/') fname += "/alsoft.conf";
|
||||
else fname += "alsoft.conf";
|
||||
path /= "alsoft.conf";
|
||||
|
||||
TRACE("Loading config %s...\n", fname.c_str());
|
||||
f = al::ifstream{fname};
|
||||
if(f.is_open())
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
fname.clear();
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
CFBundleRef mainBundle = CFBundleGetMainBundle();
|
||||
if(mainBundle)
|
||||
{
|
||||
unsigned char fileName[PATH_MAX];
|
||||
CFURLRef configURL;
|
||||
CFURLRef configURL{CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""),
|
||||
nullptr)};
|
||||
|
||||
if((configURL=CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), nullptr)) &&
|
||||
CFURLGetFileSystemRepresentation(configURL, true, fileName, sizeof(fileName)))
|
||||
std::array<unsigned char,PATH_MAX> fileName{};
|
||||
if(configURL && CFURLGetFileSystemRepresentation(configURL, true, fileName.data(), fileName.size()))
|
||||
{
|
||||
f = al::ifstream{reinterpret_cast<char*>(fileName)};
|
||||
if(f.is_open())
|
||||
if(std::ifstream f{reinterpret_cast<char*>(fileName.data())}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
}
|
||||
|
|
@ -427,102 +438,100 @@ void ReadALConfig()
|
|||
|
||||
if(auto homedir = al::getenv("HOME"))
|
||||
{
|
||||
fname = *homedir;
|
||||
if(fname.back() != '/') fname += "/.alsoftrc";
|
||||
else fname += ".alsoftrc";
|
||||
path = *homedir;
|
||||
path /= ".alsoftrc";
|
||||
|
||||
TRACE("Loading config %s...\n", fname.c_str());
|
||||
f = al::ifstream{fname};
|
||||
if(f.is_open())
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
|
||||
if(auto configdir = al::getenv("XDG_CONFIG_HOME"))
|
||||
{
|
||||
fname = *configdir;
|
||||
if(fname.back() != '/') fname += "/alsoft.conf";
|
||||
else fname += "alsoft.conf";
|
||||
path = *configdir;
|
||||
path /= "alsoft.conf";
|
||||
}
|
||||
else
|
||||
{
|
||||
fname.clear();
|
||||
path.clear();
|
||||
if(auto homedir = al::getenv("HOME"))
|
||||
{
|
||||
fname = *homedir;
|
||||
if(fname.back() != '/') fname += "/.config/alsoft.conf";
|
||||
else fname += ".config/alsoft.conf";
|
||||
path = *homedir;
|
||||
path /= ".config/alsoft.conf";
|
||||
}
|
||||
}
|
||||
if(!fname.empty())
|
||||
if(!path.empty())
|
||||
{
|
||||
TRACE("Loading config %s...\n", fname.c_str());
|
||||
f = al::ifstream{fname};
|
||||
if(f.is_open())
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
|
||||
std::string ppath{GetProcBinary().path};
|
||||
if(!ppath.empty())
|
||||
path = GetProcBinary().path;
|
||||
if(!path.empty())
|
||||
{
|
||||
if(ppath.back() != '/') ppath += "/alsoft.conf";
|
||||
else ppath += "alsoft.conf";
|
||||
path /= "alsoft.conf";
|
||||
|
||||
TRACE("Loading config %s...\n", ppath.c_str());
|
||||
f = al::ifstream{ppath};
|
||||
if(f.is_open())
|
||||
TRACE("Loading config %s...\n", path.u8string().c_str());
|
||||
if(std::ifstream f{path}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
|
||||
if(auto confname = al::getenv("ALSOFT_CONF"))
|
||||
{
|
||||
TRACE("Loading config %s...\n", confname->c_str());
|
||||
f = al::ifstream{*confname};
|
||||
if(f.is_open())
|
||||
if(std::ifstream f{*confname}; f.is_open())
|
||||
LoadConfigFromFile(f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName)
|
||||
std::optional<std::string> ConfigValueStr(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName)
|
||||
{
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return val;
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName)
|
||||
std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName,
|
||||
const std::string_view keyName)
|
||||
{
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return static_cast<int>(std::strtol(val, nullptr, 0));
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName)
|
||||
std::optional<unsigned int> ConfigValueUInt(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName)
|
||||
{
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return static_cast<unsigned int>(std::strtoul(val, nullptr, 0));
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName)
|
||||
std::optional<float> ConfigValueFloat(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName)
|
||||
{
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return std::strtof(val, nullptr);
|
||||
return al::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName)
|
||||
std::optional<bool> ConfigValueBool(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName)
|
||||
{
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
||||
|| al::strcasecmp(val, "true")==0 || atoi(val) != 0;
|
||||
return al::nullopt;
|
||||
|| al::strcasecmp(val, "true") == 0 || atoi(val) != 0;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def)
|
||||
bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName,
|
||||
const std::string_view keyName, bool def)
|
||||
{
|
||||
if(const char *val{GetConfigValue(devName, blockName, keyName)})
|
||||
return (al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
||||
|| al::strcasecmp(val, "true") == 0 || atoi(val) != 0);
|
||||
return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0
|
||||
|| al::strcasecmp(val, "true") == 0 || atoi(val) != 0;
|
||||
return def;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,25 @@
|
|||
#ifndef ALCONFIG_H
|
||||
#define ALCONFIG_H
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "aloptional.h"
|
||||
|
||||
void ReadALConfig();
|
||||
|
||||
bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def);
|
||||
bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName,
|
||||
const std::string_view keyName, bool def);
|
||||
|
||||
al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName);
|
||||
al::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName);
|
||||
al::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName);
|
||||
al::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName);
|
||||
al::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName);
|
||||
std::optional<std::string> ConfigValueStr(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName);
|
||||
std::optional<int> ConfigValueInt(const std::string_view devName, const std::string_view blockName,
|
||||
const std::string_view keyName);
|
||||
std::optional<unsigned int> ConfigValueUInt(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName);
|
||||
std::optional<float> ConfigValueFloat(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName);
|
||||
std::optional<bool> ConfigValueBool(const std::string_view devName,
|
||||
const std::string_view blockName, const std::string_view keyName);
|
||||
|
||||
#endif /* ALCONFIG_H */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,20 +2,20 @@
|
|||
#define ALU_H
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include "aloptional.h"
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
struct ALCcontext;
|
||||
struct ALCdevice;
|
||||
struct EffectSlot;
|
||||
|
||||
enum class StereoEncoding : unsigned char;
|
||||
enum class StereoEncoding : std::uint8_t;
|
||||
|
||||
|
||||
constexpr float GainMixMax{1000.0f}; /* +60dB */
|
||||
|
||||
|
||||
enum CompatFlags : uint8_t {
|
||||
enum CompatFlags : std::uint8_t {
|
||||
ReverseX,
|
||||
ReverseY,
|
||||
ReverseZ,
|
||||
|
|
@ -31,7 +31,7 @@ void aluInit(CompatFlagBitset flags, const float nfcscale);
|
|||
* Set up the appropriate panning method and mixing method given the device
|
||||
* properties.
|
||||
*/
|
||||
void aluInitRenderer(ALCdevice *device, int hrtf_id, al::optional<StereoEncoding> stereomode);
|
||||
void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncoding> stereomode);
|
||||
|
||||
void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,29 +31,33 @@
|
|||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "albyte.h"
|
||||
#include "albit.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "alstring.h"
|
||||
#include "althrd_setname.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "vector.h"
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char alsaDevice[] = "ALSA Default";
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "ALSA Default"sv; }
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
|
|
@ -248,59 +252,97 @@ struct DevMap {
|
|||
{ }
|
||||
};
|
||||
|
||||
al::vector<DevMap> PlaybackDevices;
|
||||
al::vector<DevMap> CaptureDevices;
|
||||
std::vector<DevMap> PlaybackDevices;
|
||||
std::vector<DevMap> CaptureDevices;
|
||||
|
||||
|
||||
const char *prefix_name(snd_pcm_stream_t stream)
|
||||
std::string_view prefix_name(snd_pcm_stream_t stream) noexcept
|
||||
{
|
||||
assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE);
|
||||
return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix";
|
||||
if(stream == SND_PCM_STREAM_PLAYBACK)
|
||||
return "device-prefix"sv;
|
||||
return "capture-prefix"sv;
|
||||
}
|
||||
|
||||
al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
||||
struct SndCtlCardInfo {
|
||||
snd_ctl_card_info_t *mInfo{};
|
||||
|
||||
SndCtlCardInfo() { snd_ctl_card_info_malloc(&mInfo); }
|
||||
~SndCtlCardInfo() { if(mInfo) snd_ctl_card_info_free(mInfo); }
|
||||
SndCtlCardInfo(const SndCtlCardInfo&) = delete;
|
||||
SndCtlCardInfo& operator=(const SndCtlCardInfo&) = delete;
|
||||
|
||||
[[nodiscard]]
|
||||
operator snd_ctl_card_info_t*() const noexcept { return mInfo; }
|
||||
};
|
||||
|
||||
struct SndPcmInfo {
|
||||
snd_pcm_info_t *mInfo{};
|
||||
|
||||
SndPcmInfo() { snd_pcm_info_malloc(&mInfo); }
|
||||
~SndPcmInfo() { if(mInfo) snd_pcm_info_free(mInfo); }
|
||||
SndPcmInfo(const SndPcmInfo&) = delete;
|
||||
SndPcmInfo& operator=(const SndPcmInfo&) = delete;
|
||||
|
||||
[[nodiscard]]
|
||||
operator snd_pcm_info_t*() const noexcept { return mInfo; }
|
||||
};
|
||||
|
||||
struct SndCtl {
|
||||
snd_ctl_t *mHandle{};
|
||||
|
||||
SndCtl() = default;
|
||||
~SndCtl() { if(mHandle) snd_ctl_close(mHandle); }
|
||||
SndCtl(const SndCtl&) = delete;
|
||||
SndCtl& operator=(const SndCtl&) = delete;
|
||||
|
||||
[[nodiscard]]
|
||||
auto open(const char *name, int mode) { return snd_ctl_open(&mHandle, name, mode); }
|
||||
|
||||
[[nodiscard]]
|
||||
operator snd_ctl_t*() const noexcept { return mHandle; }
|
||||
};
|
||||
|
||||
|
||||
std::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
||||
{
|
||||
al::vector<DevMap> devlist;
|
||||
std::vector<DevMap> devlist;
|
||||
|
||||
snd_ctl_card_info_t *info;
|
||||
snd_ctl_card_info_malloc(&info);
|
||||
snd_pcm_info_t *pcminfo;
|
||||
snd_pcm_info_malloc(&pcminfo);
|
||||
SndCtlCardInfo info;
|
||||
SndPcmInfo pcminfo;
|
||||
|
||||
auto defname = ConfigValueStr(nullptr, "alsa",
|
||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "device" : "capture");
|
||||
devlist.emplace_back(alsaDevice, defname ? defname->c_str() : "default");
|
||||
auto defname = ConfigValueStr({}, "alsa"sv,
|
||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "device"sv : "capture"sv);
|
||||
devlist.emplace_back(GetDefaultName(), defname ? std::string_view{*defname} : "default"sv);
|
||||
|
||||
if(auto customdevs = ConfigValueStr(nullptr, "alsa",
|
||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices" : "custom-captures"))
|
||||
if(auto customdevs = ConfigValueStr({}, "alsa"sv,
|
||||
(stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices"sv : "custom-captures"sv))
|
||||
{
|
||||
size_t nextpos{customdevs->find_first_not_of(';')};
|
||||
size_t curpos;
|
||||
while((curpos=nextpos) < customdevs->length())
|
||||
size_t curpos{customdevs->find_first_not_of(';')};
|
||||
while(curpos < customdevs->length())
|
||||
{
|
||||
nextpos = customdevs->find_first_of(';', curpos+1);
|
||||
|
||||
size_t seppos{customdevs->find_first_of('=', curpos)};
|
||||
size_t nextpos{customdevs->find(';', curpos+1)};
|
||||
const size_t seppos{customdevs->find('=', curpos)};
|
||||
if(seppos == curpos || seppos >= nextpos)
|
||||
{
|
||||
std::string spec{customdevs->substr(curpos, nextpos-curpos)};
|
||||
const std::string spec{customdevs->substr(curpos, nextpos-curpos)};
|
||||
ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
devlist.emplace_back(customdevs->substr(curpos, seppos-curpos),
|
||||
customdevs->substr(seppos+1, nextpos-seppos-1));
|
||||
const auto &entry = devlist.back();
|
||||
const std::string_view strview{*customdevs};
|
||||
const auto &entry = devlist.emplace_back(strview.substr(curpos, seppos-curpos),
|
||||
strview.substr(seppos+1, nextpos-seppos-1));
|
||||
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
||||
}
|
||||
|
||||
if(nextpos < customdevs->length())
|
||||
nextpos = customdevs->find_first_not_of(';', nextpos+1);
|
||||
curpos = nextpos;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string main_prefix{
|
||||
ConfigValueStr(nullptr, "alsa", prefix_name(stream)).value_or("plughw:")};
|
||||
const std::string main_prefix{ConfigValueStr({}, "alsa"sv, prefix_name(stream))
|
||||
.value_or("plughw:")};
|
||||
|
||||
int card{-1};
|
||||
int err{snd_card_next(&card)};
|
||||
|
|
@ -308,16 +350,17 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
|||
{
|
||||
std::string name{"hw:" + std::to_string(card)};
|
||||
|
||||
snd_ctl_t *handle;
|
||||
if((err=snd_ctl_open(&handle, name.c_str(), 0)) < 0)
|
||||
SndCtl handle;
|
||||
err = handle.open(name.c_str(), 0);
|
||||
if(err < 0)
|
||||
{
|
||||
ERR("control open (hw:%d): %s\n", card, snd_strerror(err));
|
||||
continue;
|
||||
}
|
||||
if((err=snd_ctl_card_info(handle, info)) < 0)
|
||||
err = snd_ctl_card_info(handle, info);
|
||||
if(err < 0)
|
||||
{
|
||||
ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err));
|
||||
snd_ctl_close(handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -326,8 +369,7 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
|||
name = prefix_name(stream);
|
||||
name += '-';
|
||||
name += cardid;
|
||||
const std::string card_prefix{
|
||||
ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(main_prefix)};
|
||||
const std::string card_prefix{ConfigValueStr({}, "alsa"sv, name).value_or(main_prefix)};
|
||||
|
||||
int dev{-1};
|
||||
while(true)
|
||||
|
|
@ -339,7 +381,8 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
|||
snd_pcm_info_set_device(pcminfo, static_cast<uint>(dev));
|
||||
snd_pcm_info_set_subdevice(pcminfo, 0);
|
||||
snd_pcm_info_set_stream(pcminfo, stream);
|
||||
if((err=snd_ctl_pcm_info(handle, pcminfo)) < 0)
|
||||
err = snd_ctl_pcm_info(handle, pcminfo);
|
||||
if(err < 0)
|
||||
{
|
||||
if(err != -ENOENT)
|
||||
ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err));
|
||||
|
|
@ -352,8 +395,8 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
|||
name += cardid;
|
||||
name += '-';
|
||||
name += std::to_string(dev);
|
||||
const std::string device_prefix{
|
||||
ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(card_prefix)};
|
||||
const std::string device_prefix{ConfigValueStr({}, "alsa"sv, name)
|
||||
.value_or(card_prefix)};
|
||||
|
||||
/* "CardName, PcmName (CARD=cardid,DEV=dev)" */
|
||||
name = cardname;
|
||||
|
|
@ -372,18 +415,13 @@ al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
|
|||
device += ",DEV=";
|
||||
device += std::to_string(dev);
|
||||
|
||||
devlist.emplace_back(std::move(name), std::move(device));
|
||||
const auto &entry = devlist.back();
|
||||
const auto &entry = devlist.emplace_back(std::move(name), std::move(device));
|
||||
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
||||
}
|
||||
snd_ctl_close(handle);
|
||||
}
|
||||
if(err < 0)
|
||||
ERR("snd_card_next failed: %s\n", snd_strerror(err));
|
||||
|
||||
snd_pcm_info_free(pcminfo);
|
||||
snd_ctl_card_info_free(info);
|
||||
|
||||
return devlist;
|
||||
}
|
||||
|
||||
|
|
@ -392,7 +430,6 @@ int verify_state(snd_pcm_t *handle)
|
|||
{
|
||||
snd_pcm_state_t state{snd_pcm_state(handle)};
|
||||
|
||||
int err;
|
||||
switch(state)
|
||||
{
|
||||
case SND_PCM_STATE_OPEN:
|
||||
|
|
@ -405,15 +442,27 @@ int verify_state(snd_pcm_t *handle)
|
|||
break;
|
||||
|
||||
case SND_PCM_STATE_XRUN:
|
||||
if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0)
|
||||
if(int err{snd_pcm_recover(handle, -EPIPE, 1)}; err < 0)
|
||||
return err;
|
||||
break;
|
||||
case SND_PCM_STATE_SUSPENDED:
|
||||
if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0)
|
||||
if(int err{snd_pcm_recover(handle, -ESTRPIPE, 1)}; err < 0)
|
||||
return err;
|
||||
break;
|
||||
|
||||
case SND_PCM_STATE_DISCONNECTED:
|
||||
return -ENODEV;
|
||||
|
||||
/* ALSA headers have made this enum public, leaving us in a bind: use
|
||||
* the enum despite being private and internal to the libasound, or
|
||||
* ignore when an enum value isn't handled. We can't rely on it being
|
||||
* declared either, since older headers don't have it and it could be
|
||||
* removed in the future. We can't even really rely on its value, since
|
||||
* being private/internal means it's subject to change, but this is the
|
||||
* best we can do.
|
||||
*/
|
||||
case 1024 /*SND_PCM_STATE_PRIVATE1*/:
|
||||
assert(state != 1024);
|
||||
}
|
||||
|
||||
return state;
|
||||
|
|
@ -427,7 +476,7 @@ struct AlsaPlayback final : public BackendBase {
|
|||
int mixerProc();
|
||||
int mixerNoMMapProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -439,12 +488,10 @@ struct AlsaPlayback final : public BackendBase {
|
|||
std::mutex mMutex;
|
||||
|
||||
uint mFrameStep{};
|
||||
al::vector<al::byte> mBuffer;
|
||||
std::vector<std::byte> mBuffer;
|
||||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(AlsaPlayback)
|
||||
};
|
||||
|
||||
AlsaPlayback::~AlsaPlayback()
|
||||
|
|
@ -458,7 +505,7 @@ AlsaPlayback::~AlsaPlayback()
|
|||
int AlsaPlayback::mixerProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
|
||||
const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
|
||||
|
|
@ -506,7 +553,7 @@ int AlsaPlayback::mixerProc()
|
|||
avail -= avail%update_size;
|
||||
|
||||
// it is possible that contiguous areas are smaller, thus we use a loop
|
||||
std::lock_guard<std::mutex> _{mMutex};
|
||||
std::lock_guard<std::mutex> dlock{mMutex};
|
||||
while(avail > 0)
|
||||
{
|
||||
snd_pcm_uframes_t frames{avail};
|
||||
|
|
@ -520,6 +567,7 @@ int AlsaPlayback::mixerProc()
|
|||
break;
|
||||
}
|
||||
|
||||
/* NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) */
|
||||
char *WritePtr{static_cast<char*>(areas->addr) + (offset * areas->step / 8)};
|
||||
mDevice->renderSamples(WritePtr, static_cast<uint>(frames), mFrameStep);
|
||||
|
||||
|
|
@ -541,7 +589,7 @@ int AlsaPlayback::mixerProc()
|
|||
int AlsaPlayback::mixerNoMMapProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
const snd_pcm_uframes_t update_size{mDevice->UpdateSize};
|
||||
const snd_pcm_uframes_t buffer_size{mDevice->BufferSize};
|
||||
|
|
@ -585,13 +633,13 @@ int AlsaPlayback::mixerNoMMapProc()
|
|||
continue;
|
||||
}
|
||||
|
||||
al::byte *WritePtr{mBuffer.data()};
|
||||
auto WritePtr = mBuffer.begin();
|
||||
avail = snd_pcm_bytes_to_frames(mPcmHandle, static_cast<ssize_t>(mBuffer.size()));
|
||||
std::lock_guard<std::mutex> _{mMutex};
|
||||
mDevice->renderSamples(WritePtr, static_cast<uint>(avail), mFrameStep);
|
||||
std::lock_guard<std::mutex> dlock{mMutex};
|
||||
mDevice->renderSamples(al::to_address(WritePtr), static_cast<uint>(avail), mFrameStep);
|
||||
while(avail > 0)
|
||||
{
|
||||
snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, WritePtr,
|
||||
snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, al::to_address(WritePtr),
|
||||
static_cast<snd_pcm_uframes_t>(avail))};
|
||||
switch(ret)
|
||||
{
|
||||
|
|
@ -626,10 +674,10 @@ int AlsaPlayback::mixerNoMMapProc()
|
|||
}
|
||||
|
||||
|
||||
void AlsaPlayback::open(const char *name)
|
||||
void AlsaPlayback::open(std::string_view name)
|
||||
{
|
||||
std::string driver{"default"};
|
||||
if(name)
|
||||
if(!name.empty())
|
||||
{
|
||||
if(PlaybackDevices.empty())
|
||||
PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
|
||||
|
|
@ -638,13 +686,13 @@ void AlsaPlayback::open(const char *name)
|
|||
[name](const DevMap &entry) -> bool { return entry.name == name; });
|
||||
if(iter == PlaybackDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
driver = iter->device_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = alsaDevice;
|
||||
if(auto driveropt = ConfigValueStr(nullptr, "alsa", "device"))
|
||||
name = GetDefaultName();
|
||||
if(auto driveropt = ConfigValueStr({}, "alsa"sv, "device"sv))
|
||||
driver = std::move(driveropt).value();
|
||||
}
|
||||
TRACE("Opening device \"%s\"\n", driver.c_str());
|
||||
|
|
@ -692,15 +740,14 @@ bool AlsaPlayback::reset()
|
|||
break;
|
||||
}
|
||||
|
||||
bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", true)};
|
||||
bool allowmmap{GetConfigValueBool(mDevice->DeviceName, "alsa"sv, "mmap"sv, true)};
|
||||
uint periodLen{static_cast<uint>(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)};
|
||||
uint bufferLen{static_cast<uint>(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)};
|
||||
uint rate{mDevice->Frequency};
|
||||
|
||||
int err{};
|
||||
HwParamsPtr hp{CreateHwParams()};
|
||||
#define CHECK(x) do { \
|
||||
if((err=(x)) < 0) \
|
||||
if(int err{x}; err < 0) \
|
||||
throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
|
||||
snd_strerror(err)}; \
|
||||
} while(0)
|
||||
|
|
@ -715,17 +762,18 @@ bool AlsaPlayback::reset()
|
|||
/* test and set format (implicitly sets sample bits) */
|
||||
if(snd_pcm_hw_params_test_format(mPcmHandle, hp.get(), format) < 0)
|
||||
{
|
||||
static const struct {
|
||||
struct FormatMap {
|
||||
snd_pcm_format_t format;
|
||||
DevFmtType fmttype;
|
||||
} formatlist[] = {
|
||||
{ SND_PCM_FORMAT_FLOAT, DevFmtFloat },
|
||||
{ SND_PCM_FORMAT_S32, DevFmtInt },
|
||||
{ SND_PCM_FORMAT_U32, DevFmtUInt },
|
||||
{ SND_PCM_FORMAT_S16, DevFmtShort },
|
||||
{ SND_PCM_FORMAT_U16, DevFmtUShort },
|
||||
{ SND_PCM_FORMAT_S8, DevFmtByte },
|
||||
{ SND_PCM_FORMAT_U8, DevFmtUByte },
|
||||
};
|
||||
static constexpr std::array formatlist{
|
||||
FormatMap{SND_PCM_FORMAT_FLOAT, DevFmtFloat },
|
||||
FormatMap{SND_PCM_FORMAT_S32, DevFmtInt },
|
||||
FormatMap{SND_PCM_FORMAT_U32, DevFmtUInt },
|
||||
FormatMap{SND_PCM_FORMAT_S16, DevFmtShort },
|
||||
FormatMap{SND_PCM_FORMAT_U16, DevFmtUShort},
|
||||
FormatMap{SND_PCM_FORMAT_S8, DevFmtByte },
|
||||
FormatMap{SND_PCM_FORMAT_U8, DevFmtUByte },
|
||||
};
|
||||
|
||||
for(const auto &fmt : formatlist)
|
||||
|
|
@ -750,7 +798,7 @@ bool AlsaPlayback::reset()
|
|||
else mDevice->FmtChans = DevFmtStereo;
|
||||
}
|
||||
/* set rate (implicitly constrains period/buffer parameters) */
|
||||
if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "allow-resampler", false)
|
||||
if(!GetConfigValueBool(mDevice->DeviceName, "alsa", "allow-resampler", false)
|
||||
|| !mDevice->Flags.test(FrequencyRequest))
|
||||
{
|
||||
if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 0) < 0)
|
||||
|
|
@ -760,10 +808,10 @@ bool AlsaPlayback::reset()
|
|||
WARN("Failed to enable ALSA resampler\n");
|
||||
CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp.get(), &rate, nullptr));
|
||||
/* set period time (implicitly constrains period/buffer parameters) */
|
||||
if((err=snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)) < 0)
|
||||
if(int err{snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)}; err < 0)
|
||||
ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err));
|
||||
/* set buffer time (implicitly sets buffer size/bytes/time and period size/bytes) */
|
||||
if((err=snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)) < 0)
|
||||
if(int err{snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)}; err < 0)
|
||||
ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err));
|
||||
/* install and prepare hardware configuration */
|
||||
CHECK(snd_pcm_hw_params(mPcmHandle, hp.get()));
|
||||
|
|
@ -798,11 +846,10 @@ bool AlsaPlayback::reset()
|
|||
|
||||
void AlsaPlayback::start()
|
||||
{
|
||||
int err{};
|
||||
snd_pcm_access_t access{};
|
||||
HwParamsPtr hp{CreateHwParams()};
|
||||
#define CHECK(x) do { \
|
||||
if((err=(x)) < 0) \
|
||||
if(int err{x}; err < 0) \
|
||||
throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
|
||||
snd_strerror(err)}; \
|
||||
} while(0)
|
||||
|
|
@ -849,10 +896,9 @@ void AlsaPlayback::stop()
|
|||
|
||||
ClockLatency AlsaPlayback::getClockLatency()
|
||||
{
|
||||
ClockLatency ret;
|
||||
|
||||
std::lock_guard<std::mutex> _{mMutex};
|
||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
||||
std::lock_guard<std::mutex> dlock{mMutex};
|
||||
ClockLatency ret{};
|
||||
ret.ClockTime = mDevice->getClockTime();
|
||||
snd_pcm_sframes_t delay{};
|
||||
int err{snd_pcm_delay(mPcmHandle, &delay)};
|
||||
if(err < 0)
|
||||
|
|
@ -871,23 +917,21 @@ struct AlsaCapture final : public BackendBase {
|
|||
AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~AlsaCapture() override;
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
void captureSamples(std::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
ClockLatency getClockLatency() override;
|
||||
|
||||
snd_pcm_t *mPcmHandle{nullptr};
|
||||
|
||||
al::vector<al::byte> mBuffer;
|
||||
std::vector<std::byte> mBuffer;
|
||||
|
||||
bool mDoCapture{false};
|
||||
RingBufferPtr mRing{nullptr};
|
||||
|
||||
snd_pcm_sframes_t mLastAvail{0};
|
||||
|
||||
DEF_NEWDEL(AlsaCapture)
|
||||
};
|
||||
|
||||
AlsaCapture::~AlsaCapture()
|
||||
|
|
@ -898,10 +942,10 @@ AlsaCapture::~AlsaCapture()
|
|||
}
|
||||
|
||||
|
||||
void AlsaCapture::open(const char *name)
|
||||
void AlsaCapture::open(std::string_view name)
|
||||
{
|
||||
std::string driver{"default"};
|
||||
if(name)
|
||||
if(!name.empty())
|
||||
{
|
||||
if(CaptureDevices.empty())
|
||||
CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
|
||||
|
|
@ -910,19 +954,18 @@ void AlsaCapture::open(const char *name)
|
|||
[name](const DevMap &entry) -> bool { return entry.name == name; });
|
||||
if(iter == CaptureDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
driver = iter->device_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
name = alsaDevice;
|
||||
if(auto driveropt = ConfigValueStr(nullptr, "alsa", "capture"))
|
||||
name = GetDefaultName();
|
||||
if(auto driveropt = ConfigValueStr({}, "alsa"sv, "capture"sv))
|
||||
driver = std::move(driveropt).value();
|
||||
}
|
||||
|
||||
TRACE("Opening device \"%s\"\n", driver.c_str());
|
||||
int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)};
|
||||
if(err < 0)
|
||||
if(int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)}; err < 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not open ALSA device \"%s\"", driver.c_str()};
|
||||
|
||||
|
|
@ -955,13 +998,15 @@ void AlsaCapture::open(const char *name)
|
|||
break;
|
||||
}
|
||||
|
||||
snd_pcm_uframes_t bufferSizeInFrames{maxu(mDevice->BufferSize, 100*mDevice->Frequency/1000)};
|
||||
snd_pcm_uframes_t periodSizeInFrames{minu(mDevice->BufferSize, 25*mDevice->Frequency/1000)};
|
||||
snd_pcm_uframes_t bufferSizeInFrames{std::max(mDevice->BufferSize,
|
||||
100u*mDevice->Frequency/1000u)};
|
||||
snd_pcm_uframes_t periodSizeInFrames{std::min(mDevice->BufferSize,
|
||||
25u*mDevice->Frequency/1000u)};
|
||||
|
||||
bool needring{false};
|
||||
HwParamsPtr hp{CreateHwParams()};
|
||||
#define CHECK(x) do { \
|
||||
if((err=(x)) < 0) \
|
||||
if(int err{x}; err < 0) \
|
||||
throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \
|
||||
snd_strerror(err)}; \
|
||||
} while(0)
|
||||
|
|
@ -999,13 +1044,11 @@ void AlsaCapture::open(const char *name)
|
|||
|
||||
void AlsaCapture::start()
|
||||
{
|
||||
int err{snd_pcm_prepare(mPcmHandle)};
|
||||
if(err < 0)
|
||||
if(int err{snd_pcm_prepare(mPcmHandle)}; err < 0)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_prepare failed: %s",
|
||||
snd_strerror(err)};
|
||||
|
||||
err = snd_pcm_start(mPcmHandle);
|
||||
if(err < 0)
|
||||
if(int err{snd_pcm_start(mPcmHandle)}; err < 0)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_start failed: %s",
|
||||
snd_strerror(err)};
|
||||
|
||||
|
|
@ -1024,25 +1067,27 @@ void AlsaCapture::stop()
|
|||
/* The ring buffer implicitly captures when checking availability.
|
||||
* Direct access needs to explicitly capture it into temp storage.
|
||||
*/
|
||||
auto temp = al::vector<al::byte>(
|
||||
auto temp = std::vector<std::byte>(
|
||||
static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, avail)));
|
||||
captureSamples(temp.data(), avail);
|
||||
mBuffer = std::move(temp);
|
||||
}
|
||||
int err{snd_pcm_drop(mPcmHandle)};
|
||||
if(err < 0)
|
||||
if(int err{snd_pcm_drop(mPcmHandle)}; err < 0)
|
||||
ERR("drop failed: %s\n", snd_strerror(err));
|
||||
mDoCapture = false;
|
||||
}
|
||||
|
||||
void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
|
||||
void AlsaCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
{
|
||||
if(mRing)
|
||||
{
|
||||
mRing->read(buffer, samples);
|
||||
std::ignore = mRing->read(buffer, samples);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto outspan = al::span{buffer,
|
||||
static_cast<size_t>(snd_pcm_frames_to_bytes(mPcmHandle, samples))};
|
||||
auto outiter = outspan.begin();
|
||||
mLastAvail -= samples;
|
||||
while(mDevice->Connected.load(std::memory_order_acquire) && samples > 0)
|
||||
{
|
||||
|
|
@ -1055,20 +1100,21 @@ void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
|
|||
if(static_cast<snd_pcm_uframes_t>(amt) > samples) amt = samples;
|
||||
|
||||
amt = snd_pcm_frames_to_bytes(mPcmHandle, amt);
|
||||
std::copy_n(mBuffer.begin(), amt, buffer);
|
||||
std::copy_n(mBuffer.begin(), amt, outiter);
|
||||
|
||||
mBuffer.erase(mBuffer.begin(), mBuffer.begin()+amt);
|
||||
amt = snd_pcm_bytes_to_frames(mPcmHandle, amt);
|
||||
}
|
||||
else if(mDoCapture)
|
||||
amt = snd_pcm_readi(mPcmHandle, buffer, samples);
|
||||
amt = snd_pcm_readi(mPcmHandle, al::to_address(outiter), samples);
|
||||
if(amt < 0)
|
||||
{
|
||||
ERR("read error: %s\n", snd_strerror(static_cast<int>(amt)));
|
||||
|
||||
if(amt == -EAGAIN)
|
||||
continue;
|
||||
if((amt=snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1)) >= 0)
|
||||
amt = snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1);
|
||||
if(amt >= 0)
|
||||
{
|
||||
amt = snd_pcm_start(mPcmHandle);
|
||||
if(amt >= 0)
|
||||
|
|
@ -1088,12 +1134,12 @@ void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
|
|||
continue;
|
||||
}
|
||||
|
||||
buffer = buffer + amt;
|
||||
outiter += amt;
|
||||
samples -= static_cast<uint>(amt);
|
||||
}
|
||||
if(samples > 0)
|
||||
std::fill_n(buffer, snd_pcm_frames_to_bytes(mPcmHandle, samples),
|
||||
al::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0));
|
||||
std::fill_n(outiter, snd_pcm_frames_to_bytes(mPcmHandle, samples),
|
||||
std::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0));
|
||||
}
|
||||
|
||||
uint AlsaCapture::availableSamples()
|
||||
|
|
@ -1105,7 +1151,8 @@ uint AlsaCapture::availableSamples()
|
|||
{
|
||||
ERR("avail update failed: %s\n", snd_strerror(static_cast<int>(avail)));
|
||||
|
||||
if((avail=snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1)) >= 0)
|
||||
avail = snd_pcm_recover(mPcmHandle, static_cast<int>(avail), 1);
|
||||
if(avail >= 0)
|
||||
{
|
||||
if(mDoCapture)
|
||||
avail = snd_pcm_start(mPcmHandle);
|
||||
|
|
@ -1141,7 +1188,8 @@ uint AlsaCapture::availableSamples()
|
|||
|
||||
if(amt == -EAGAIN)
|
||||
continue;
|
||||
if((amt=snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1)) >= 0)
|
||||
amt = snd_pcm_recover(mPcmHandle, static_cast<int>(amt), 1);
|
||||
if(amt >= 0)
|
||||
{
|
||||
if(mDoCapture)
|
||||
amt = snd_pcm_start(mPcmHandle);
|
||||
|
|
@ -1168,9 +1216,8 @@ uint AlsaCapture::availableSamples()
|
|||
|
||||
ClockLatency AlsaCapture::getClockLatency()
|
||||
{
|
||||
ClockLatency ret;
|
||||
|
||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
||||
ClockLatency ret{};
|
||||
ret.ClockTime = mDevice->getClockTime();
|
||||
snd_pcm_sframes_t delay{};
|
||||
int err{snd_pcm_delay(mPcmHandle, &delay)};
|
||||
if(err < 0)
|
||||
|
|
@ -1189,13 +1236,9 @@ ClockLatency AlsaCapture::getClockLatency()
|
|||
|
||||
bool AlsaBackendFactory::init()
|
||||
{
|
||||
bool error{false};
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(!alsa_handle)
|
||||
{
|
||||
std::string missing_funcs;
|
||||
|
||||
alsa_handle = LoadLib("libasound.so.2");
|
||||
if(!alsa_handle)
|
||||
{
|
||||
|
|
@ -1203,52 +1246,47 @@ bool AlsaBackendFactory::init()
|
|||
return false;
|
||||
}
|
||||
|
||||
error = false;
|
||||
std::string missing_funcs;
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \
|
||||
if(p##f == nullptr) { \
|
||||
error = true; \
|
||||
missing_funcs += "\n" #f; \
|
||||
} \
|
||||
if(p##f == nullptr) missing_funcs += "\n" #f; \
|
||||
} while(0)
|
||||
ALSA_FUNCS(LOAD_FUNC);
|
||||
#undef LOAD_FUNC
|
||||
|
||||
if(error)
|
||||
if(!missing_funcs.empty())
|
||||
{
|
||||
WARN("Missing expected functions:%s\n", missing_funcs.c_str());
|
||||
CloseLib(alsa_handle);
|
||||
alsa_handle = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return !error;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AlsaBackendFactory::querySupport(BackendType type)
|
||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||
|
||||
std::string AlsaBackendFactory::probe(BackendType type)
|
||||
auto AlsaBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
|
||||
std::vector<std::string> outnames;
|
||||
auto add_device = [&outnames](const DevMap &entry) -> void
|
||||
{
|
||||
/* +1 to also append the null char (to ensure a null-separated list and
|
||||
* double-null terminated list).
|
||||
*/
|
||||
outnames.append(entry.name.c_str(), entry.name.length()+1);
|
||||
};
|
||||
{ outnames.emplace_back(entry.name); };
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK);
|
||||
outnames.reserve(PlaybackDevices.size());
|
||||
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
||||
break;
|
||||
|
||||
case BackendType::Capture:
|
||||
CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE);
|
||||
outnames.reserve(CaptureDevices.size());
|
||||
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct AlsaBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_ALSA_H */
|
||||
|
|
|
|||
|
|
@ -7,17 +7,6 @@
|
|||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <mmreg.h>
|
||||
|
||||
#include "albit.h"
|
||||
#include "core/logging.h"
|
||||
#include "aloptional.h"
|
||||
#endif
|
||||
|
||||
#include "atomic.h"
|
||||
#include "core/devformat.h"
|
||||
|
||||
|
||||
|
|
@ -25,10 +14,12 @@ namespace al {
|
|||
|
||||
backend_exception::backend_exception(backend_error code, const char *msg, ...) : mErrorCode{code}
|
||||
{
|
||||
/* NOLINTBEGIN(*-array-to-pointer-decay) */
|
||||
std::va_list args;
|
||||
va_start(args, msg);
|
||||
setMessage(msg, args);
|
||||
va_end(args);
|
||||
/* NOLINTEND(*-array-to-pointer-decay) */
|
||||
}
|
||||
backend_exception::~backend_exception() = default;
|
||||
|
||||
|
|
@ -38,7 +29,7 @@ backend_exception::~backend_exception() = default;
|
|||
bool BackendBase::reset()
|
||||
{ throw al::backend_exception{al::backend_error::DeviceError, "Invalid BackendBase call"}; }
|
||||
|
||||
void BackendBase::captureSamples(al::byte*, uint)
|
||||
void BackendBase::captureSamples(std::byte*, uint)
|
||||
{ }
|
||||
|
||||
uint BackendBase::availableSamples()
|
||||
|
|
@ -46,27 +37,26 @@ uint BackendBase::availableSamples()
|
|||
|
||||
ClockLatency BackendBase::getClockLatency()
|
||||
{
|
||||
ClockLatency ret;
|
||||
ClockLatency ret{};
|
||||
|
||||
uint refcount;
|
||||
do {
|
||||
refcount = mDevice->waitForMix();
|
||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
||||
ret.ClockTime = mDevice->getClockTime();
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
} while(refcount != ReadRef(mDevice->MixCount));
|
||||
} while(refcount != mDevice->mMixCount.load(std::memory_order_relaxed));
|
||||
|
||||
/* NOTE: The device will generally have about all but one periods filled at
|
||||
* any given time during playback. Without a more accurate measurement from
|
||||
* the output, this is an okay approximation.
|
||||
*/
|
||||
ret.Latency = std::max(std::chrono::seconds{mDevice->BufferSize-mDevice->UpdateSize},
|
||||
std::chrono::seconds::zero());
|
||||
ret.Latency = std::chrono::seconds{mDevice->BufferSize - mDevice->UpdateSize};
|
||||
ret.Latency /= mDevice->Frequency;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BackendBase::setDefaultWFXChannelOrder()
|
||||
void BackendBase::setDefaultWFXChannelOrder() const
|
||||
{
|
||||
mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex);
|
||||
|
||||
|
|
@ -126,6 +116,24 @@ void BackendBase::setDefaultWFXChannelOrder()
|
|||
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
||||
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
||||
break;
|
||||
case DevFmtX7144:
|
||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||
mDevice->RealOut.ChannelIndex[FrontCenter] = 2;
|
||||
mDevice->RealOut.ChannelIndex[LFE] = 3;
|
||||
mDevice->RealOut.ChannelIndex[BackLeft] = 4;
|
||||
mDevice->RealOut.ChannelIndex[BackRight] = 5;
|
||||
mDevice->RealOut.ChannelIndex[SideLeft] = 6;
|
||||
mDevice->RealOut.ChannelIndex[SideRight] = 7;
|
||||
mDevice->RealOut.ChannelIndex[TopFrontLeft] = 8;
|
||||
mDevice->RealOut.ChannelIndex[TopFrontRight] = 9;
|
||||
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
||||
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
||||
mDevice->RealOut.ChannelIndex[BottomFrontLeft] = 12;
|
||||
mDevice->RealOut.ChannelIndex[BottomFrontRight] = 13;
|
||||
mDevice->RealOut.ChannelIndex[BottomBackLeft] = 14;
|
||||
mDevice->RealOut.ChannelIndex[BottomBackRight] = 15;
|
||||
break;
|
||||
case DevFmtX3D71:
|
||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||
|
|
@ -141,7 +149,7 @@ void BackendBase::setDefaultWFXChannelOrder()
|
|||
}
|
||||
}
|
||||
|
||||
void BackendBase::setDefaultChannelOrder()
|
||||
void BackendBase::setDefaultChannelOrder() const
|
||||
{
|
||||
mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex);
|
||||
|
||||
|
|
@ -179,6 +187,24 @@ void BackendBase::setDefaultChannelOrder()
|
|||
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
||||
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
||||
break;
|
||||
case DevFmtX7144:
|
||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||
mDevice->RealOut.ChannelIndex[BackLeft] = 2;
|
||||
mDevice->RealOut.ChannelIndex[BackRight] = 3;
|
||||
mDevice->RealOut.ChannelIndex[FrontCenter] = 4;
|
||||
mDevice->RealOut.ChannelIndex[LFE] = 5;
|
||||
mDevice->RealOut.ChannelIndex[SideLeft] = 6;
|
||||
mDevice->RealOut.ChannelIndex[SideRight] = 7;
|
||||
mDevice->RealOut.ChannelIndex[TopFrontLeft] = 8;
|
||||
mDevice->RealOut.ChannelIndex[TopFrontRight] = 9;
|
||||
mDevice->RealOut.ChannelIndex[TopBackLeft] = 10;
|
||||
mDevice->RealOut.ChannelIndex[TopBackRight] = 11;
|
||||
mDevice->RealOut.ChannelIndex[BottomFrontLeft] = 12;
|
||||
mDevice->RealOut.ChannelIndex[BottomFrontRight] = 13;
|
||||
mDevice->RealOut.ChannelIndex[BottomBackLeft] = 14;
|
||||
mDevice->RealOut.ChannelIndex[BottomBackRight] = 15;
|
||||
break;
|
||||
case DevFmtX3D71:
|
||||
mDevice->RealOut.ChannelIndex[FrontLeft] = 0;
|
||||
mDevice->RealOut.ChannelIndex[FrontRight] = 1;
|
||||
|
|
|
|||
|
|
@ -3,13 +3,16 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <ratio>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "albyte.h"
|
||||
#include "core/device.h"
|
||||
#include "core/except.h"
|
||||
#include "alc/events.h"
|
||||
|
||||
|
||||
using uint = unsigned int;
|
||||
|
|
@ -20,27 +23,33 @@ struct ClockLatency {
|
|||
};
|
||||
|
||||
struct BackendBase {
|
||||
virtual void open(const char *name) = 0;
|
||||
virtual void open(std::string_view name) = 0;
|
||||
|
||||
virtual bool reset();
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual void captureSamples(al::byte *buffer, uint samples);
|
||||
virtual void captureSamples(std::byte *buffer, uint samples);
|
||||
virtual uint availableSamples();
|
||||
|
||||
virtual ClockLatency getClockLatency();
|
||||
|
||||
DeviceBase *const mDevice;
|
||||
|
||||
BackendBase() = delete;
|
||||
BackendBase(const BackendBase&) = delete;
|
||||
BackendBase(BackendBase&&) = delete;
|
||||
BackendBase(DeviceBase *device) noexcept : mDevice{device} { }
|
||||
virtual ~BackendBase() = default;
|
||||
|
||||
void operator=(const BackendBase&) = delete;
|
||||
void operator=(BackendBase&&) = delete;
|
||||
|
||||
protected:
|
||||
/** Sets the default channel order used by most non-WaveFormatEx-based APIs. */
|
||||
void setDefaultChannelOrder();
|
||||
void setDefaultChannelOrder() const;
|
||||
/** Sets the default channel order used by WaveFormatEx. */
|
||||
void setDefaultWFXChannelOrder();
|
||||
void setDefaultWFXChannelOrder() const;
|
||||
};
|
||||
using BackendPtr = std::unique_ptr<BackendBase>;
|
||||
|
||||
|
|
@ -50,18 +59,6 @@ enum class BackendType {
|
|||
};
|
||||
|
||||
|
||||
/* Helper to get the current clock time from the device's ClockBase, and
|
||||
* SamplesDone converted from the sample rate.
|
||||
*/
|
||||
inline std::chrono::nanoseconds GetDeviceClockTime(DeviceBase *device)
|
||||
{
|
||||
using std::chrono::seconds;
|
||||
using std::chrono::nanoseconds;
|
||||
|
||||
auto ns = nanoseconds{seconds{device->SamplesDone}} / device->Frequency;
|
||||
return device->ClockBase + ns;
|
||||
}
|
||||
|
||||
/* Helper to get the device latency from the backend, including any fixed
|
||||
* latency from post-processing.
|
||||
*/
|
||||
|
|
@ -74,16 +71,24 @@ inline ClockLatency GetClockLatency(DeviceBase *device, BackendBase *backend)
|
|||
|
||||
|
||||
struct BackendFactory {
|
||||
virtual bool init() = 0;
|
||||
|
||||
virtual bool querySupport(BackendType type) = 0;
|
||||
|
||||
virtual std::string probe(BackendType type) = 0;
|
||||
|
||||
virtual BackendPtr createBackend(DeviceBase *device, BackendType type) = 0;
|
||||
|
||||
protected:
|
||||
BackendFactory() = default;
|
||||
BackendFactory(const BackendFactory&) = delete;
|
||||
BackendFactory(BackendFactory&&) = delete;
|
||||
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 {
|
||||
|
|
@ -98,15 +103,15 @@ class backend_exception final : public base_exception {
|
|||
backend_error mErrorCode;
|
||||
|
||||
public:
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
[[gnu::format(gnu_printf, 3, 4)]]
|
||||
#ifdef __MINGW32__
|
||||
[[gnu::format(__MINGW_PRINTF_FORMAT, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
backend_exception(backend_error code, const char *msg, ...);
|
||||
~backend_exception() override;
|
||||
|
||||
backend_error errorCode() const noexcept { return mErrorCode; }
|
||||
[[nodiscard]] auto errorCode() const noexcept -> backend_error { return mErrorCode; }
|
||||
};
|
||||
|
||||
} // namespace al
|
||||
|
|
|
|||
|
|
@ -22,18 +22,20 @@
|
|||
|
||||
#include "coreaudio.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "alnumeric.h"
|
||||
#include "alstring.h"
|
||||
#include "core/converter.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
|
|
@ -42,18 +44,40 @@
|
|||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
#define CAN_ENUMERATE 0
|
||||
#else
|
||||
#include <IOKit/audio/IOAudioTypes.h>
|
||||
#define CAN_ENUMERATE 1
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto OutputElement = 0;
|
||||
constexpr auto InputElement = 1;
|
||||
|
||||
struct FourCCPrinter {
|
||||
char mString[sizeof(UInt32) + 1]{};
|
||||
|
||||
constexpr FourCCPrinter(UInt32 code) noexcept
|
||||
{
|
||||
for(size_t i{0};i < sizeof(UInt32);++i)
|
||||
{
|
||||
const auto ch = static_cast<char>(code & 0xff);
|
||||
/* If this breaks early it'll leave the first byte null, to get
|
||||
* read as a 0-length string.
|
||||
*/
|
||||
if(ch <= 0x1f || ch >= 0x7f)
|
||||
break;
|
||||
mString[sizeof(UInt32)-1-i] = ch;
|
||||
code >>= 8;
|
||||
}
|
||||
}
|
||||
constexpr FourCCPrinter(int code) noexcept : FourCCPrinter{static_cast<UInt32>(code)} { }
|
||||
|
||||
constexpr const char *c_str() const noexcept { return mString; }
|
||||
};
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
struct DeviceEntry {
|
||||
AudioDeviceID mId;
|
||||
|
|
@ -147,7 +171,8 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
|
|||
&propSize);
|
||||
if(err)
|
||||
{
|
||||
ERR("kAudioDevicePropertyStreamConfiguration size query failed: %u\n", err);
|
||||
ERR("kAudioDevicePropertyStreamConfiguration size query failed: '%s' (%u)\n",
|
||||
FourCCPrinter{err}.c_str(), err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +183,8 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture)
|
|||
buflist);
|
||||
if(err)
|
||||
{
|
||||
ERR("kAudioDevicePropertyStreamConfiguration query failed: %u\n", err);
|
||||
ERR("kAudioDevicePropertyStreamConfiguration query failed: '%s' (%u)\n",
|
||||
FourCCPrinter{err}.c_str(), err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +208,7 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
|
|||
auto devIds = std::vector<AudioDeviceID>(propSize/sizeof(AudioDeviceID), kAudioDeviceUnknown);
|
||||
if(auto err = GetHwProperty(kAudioHardwarePropertyDevices, propSize, devIds.data()))
|
||||
{
|
||||
ERR("Failed to get device list: %u\n", err);
|
||||
ERR("Failed to get device list: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -247,6 +273,48 @@ void EnumerateDevices(std::vector<DeviceEntry> &list, bool isCapture)
|
|||
newdevs.swap(list);
|
||||
}
|
||||
|
||||
struct DeviceHelper {
|
||||
DeviceHelper()
|
||||
{
|
||||
AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
|
||||
OSStatus status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
|
||||
if (status != noErr)
|
||||
ERR("AudioObjectAddPropertyListener fail: %d", status);
|
||||
}
|
||||
~DeviceHelper()
|
||||
{
|
||||
AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice,
|
||||
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};
|
||||
OSStatus status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil);
|
||||
if (status != noErr)
|
||||
ERR("AudioObjectRemovePropertyListener fail: %d", status);
|
||||
}
|
||||
|
||||
static OSStatus DeviceListenerProc(AudioObjectID /*inObjectID*/, UInt32 inNumberAddresses,
|
||||
const AudioObjectPropertyAddress *inAddresses, void* /*inClientData*/)
|
||||
{
|
||||
for(UInt32 i = 0; i < inNumberAddresses; ++i)
|
||||
{
|
||||
switch(inAddresses[i].mSelector)
|
||||
{
|
||||
case kAudioHardwarePropertyDefaultOutputDevice:
|
||||
case kAudioHardwarePropertyDefaultSystemOutputDevice:
|
||||
alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback,
|
||||
"Default playback device changed: "+std::to_string(inAddresses[i].mSelector));
|
||||
break;
|
||||
case kAudioHardwarePropertyDefaultInputDevice:
|
||||
alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture,
|
||||
"Default capture device changed: "+std::to_string(inAddresses[i].mSelector));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return noErr;
|
||||
}
|
||||
};
|
||||
|
||||
static std::optional<DeviceHelper> sDeviceHelper;
|
||||
|
||||
#else
|
||||
|
||||
static constexpr char ca_device[] = "CoreAudio Default";
|
||||
|
|
@ -260,15 +328,8 @@ struct CoreAudioPlayback final : public BackendBase {
|
|||
OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData) noexcept;
|
||||
static OSStatus MixerProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData) noexcept
|
||||
{
|
||||
return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp,
|
||||
inBusNumber, inNumberFrames, ioData);
|
||||
}
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -277,8 +338,6 @@ struct CoreAudioPlayback final : public BackendBase {
|
|||
|
||||
uint mFrameSize{0u};
|
||||
AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD
|
||||
|
||||
DEF_NEWDEL(CoreAudioPlayback)
|
||||
};
|
||||
|
||||
CoreAudioPlayback::~CoreAudioPlayback()
|
||||
|
|
@ -301,11 +360,11 @@ OSStatus CoreAudioPlayback::MixerProc(AudioUnitRenderActionFlags*, const AudioTi
|
|||
}
|
||||
|
||||
|
||||
void CoreAudioPlayback::open(const char *name)
|
||||
void CoreAudioPlayback::open(std::string_view name)
|
||||
{
|
||||
#if CAN_ENUMERATE
|
||||
AudioDeviceID audioDevice{kAudioDeviceUnknown};
|
||||
if(!name)
|
||||
if(name.empty())
|
||||
GetHwProperty(kAudioHardwarePropertyDefaultOutputDevice, sizeof(audioDevice),
|
||||
&audioDevice);
|
||||
else
|
||||
|
|
@ -318,16 +377,16 @@ void CoreAudioPlayback::open(const char *name)
|
|||
auto devmatch = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), find_name);
|
||||
if(devmatch == PlaybackList.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
|
||||
audioDevice = devmatch->mId;
|
||||
}
|
||||
#else
|
||||
if(!name)
|
||||
if(name.empty())
|
||||
name = ca_device;
|
||||
else if(strcmp(name, ca_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
else if(name != ca_device)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
#endif
|
||||
|
||||
/* open the default output unit */
|
||||
|
|
@ -351,7 +410,7 @@ void CoreAudioPlayback::open(const char *name)
|
|||
OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not create component instance: %u", err};
|
||||
"Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(audioDevice != kAudioDeviceUnknown)
|
||||
|
|
@ -362,7 +421,7 @@ void CoreAudioPlayback::open(const char *name)
|
|||
err = AudioUnitInitialize(audioUnit);
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not initialize audio unit: %u", err};
|
||||
"Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
/* WARNING: I don't know if "valid" audio unit values are guaranteed to be
|
||||
* non-0. If not, this logic is broken.
|
||||
|
|
@ -375,7 +434,7 @@ void CoreAudioPlayback::open(const char *name)
|
|||
mAudioUnit = audioUnit;
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(name)
|
||||
if(!name.empty())
|
||||
mDevice->DeviceName = name;
|
||||
else
|
||||
{
|
||||
|
|
@ -388,6 +447,21 @@ void CoreAudioPlayback::open(const char *name)
|
|||
if(!devname.empty()) mDevice->DeviceName = std::move(devname);
|
||||
else mDevice->DeviceName = "Unknown Device Name";
|
||||
}
|
||||
|
||||
if(audioDevice != kAudioDeviceUnknown)
|
||||
{
|
||||
UInt32 type{};
|
||||
err = GetDevProperty(audioDevice, kAudioDevicePropertyDataSource, false,
|
||||
kAudioObjectPropertyElementMaster, sizeof(type), &type);
|
||||
if(err != noErr)
|
||||
ERR("Failed to get audio device type: %u\n", err);
|
||||
else
|
||||
{
|
||||
TRACE("Got device type '%s'\n", FourCCPrinter{type}.c_str());
|
||||
mDevice->Flags.set(DirectEar, (type == kIOAudioOutputPortSubTypeHeadphones));
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
mDevice->DeviceName = name;
|
||||
#endif
|
||||
|
|
@ -397,7 +471,7 @@ bool CoreAudioPlayback::reset()
|
|||
{
|
||||
OSStatus err{AudioUnitUninitialize(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
ERR("-- AudioUnitUninitialize failed.\n");
|
||||
ERR("AudioUnitUninitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
|
||||
/* retrieve default output unit's properties (output side) */
|
||||
AudioStreamBasicDescription streamFormat{};
|
||||
|
|
@ -406,7 +480,8 @@ bool CoreAudioPlayback::reset()
|
|||
OutputElement, &streamFormat, &size);
|
||||
if(err != noErr || size != sizeof(streamFormat))
|
||||
{
|
||||
ERR("AudioUnitGetProperty failed\n");
|
||||
ERR("AudioUnitGetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(),
|
||||
err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -473,7 +548,8 @@ bool CoreAudioPlayback::reset()
|
|||
OutputElement, &streamFormat, sizeof(streamFormat));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
ERR("AudioUnitSetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(),
|
||||
err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -482,14 +558,16 @@ bool CoreAudioPlayback::reset()
|
|||
/* setup callback */
|
||||
mFrameSize = mDevice->frameSizeFromFmt();
|
||||
AURenderCallbackStruct input{};
|
||||
input.inputProc = CoreAudioPlayback::MixerProcC;
|
||||
input.inputProc = [](void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept
|
||||
{ return static_cast<CoreAudioPlayback*>(inRefCon)->MixerProc(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); };
|
||||
input.inputProcRefCon = this;
|
||||
|
||||
err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input, OutputElement, &input, sizeof(AURenderCallbackStruct));
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitSetProperty failed\n");
|
||||
ERR("AudioUnitSetProperty(SetRenderCallback) failed: '%s' (%u)\n",
|
||||
FourCCPrinter{err}.c_str(), err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -497,7 +575,7 @@ bool CoreAudioPlayback::reset()
|
|||
err = AudioUnitInitialize(mAudioUnit);
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitInitialize failed\n");
|
||||
ERR("AudioUnitInitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -509,14 +587,14 @@ void CoreAudioPlayback::start()
|
|||
const OSStatus err{AudioOutputUnitStart(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"AudioOutputUnitStart failed: %d", err};
|
||||
"AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
}
|
||||
|
||||
void CoreAudioPlayback::stop()
|
||||
{
|
||||
OSStatus err{AudioOutputUnitStop(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed\n");
|
||||
ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -527,18 +605,11 @@ struct CoreAudioCapture final : public BackendBase {
|
|||
OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
|
||||
UInt32 inNumberFrames, AudioBufferList *ioData) noexcept;
|
||||
static OSStatus RecordProcC(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData) noexcept
|
||||
{
|
||||
return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp,
|
||||
inBusNumber, inNumberFrames, ioData);
|
||||
}
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
void captureSamples(std::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
|
||||
AudioUnit mAudioUnit{0};
|
||||
|
|
@ -548,11 +619,9 @@ struct CoreAudioCapture final : public BackendBase {
|
|||
|
||||
SampleConverterPtr mConverter;
|
||||
|
||||
al::vector<char> mCaptureData;
|
||||
std::vector<char> mCaptureData;
|
||||
|
||||
RingBufferPtr mRing{nullptr};
|
||||
|
||||
DEF_NEWDEL(CoreAudioCapture)
|
||||
};
|
||||
|
||||
CoreAudioCapture::~CoreAudioCapture()
|
||||
|
|
@ -568,7 +637,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
|||
AudioBufferList*) noexcept
|
||||
{
|
||||
union {
|
||||
al::byte _[maxz(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))];
|
||||
std::byte buf[std::max(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))];
|
||||
AudioBufferList list;
|
||||
} audiobuf{};
|
||||
|
||||
|
|
@ -581,20 +650,20 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
|||
inNumberFrames, &audiobuf.list)};
|
||||
if(err != noErr)
|
||||
{
|
||||
ERR("AudioUnitRender capture error: %d\n", err);
|
||||
ERR("AudioUnitRender capture error: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
return err;
|
||||
}
|
||||
|
||||
mRing->write(mCaptureData.data(), inNumberFrames);
|
||||
std::ignore = mRing->write(mCaptureData.data(), inNumberFrames);
|
||||
return noErr;
|
||||
}
|
||||
|
||||
|
||||
void CoreAudioCapture::open(const char *name)
|
||||
void CoreAudioCapture::open(std::string_view name)
|
||||
{
|
||||
#if CAN_ENUMERATE
|
||||
AudioDeviceID audioDevice{kAudioDeviceUnknown};
|
||||
if(!name)
|
||||
if(name.empty())
|
||||
GetHwProperty(kAudioHardwarePropertyDefaultInputDevice, sizeof(audioDevice),
|
||||
&audioDevice);
|
||||
else
|
||||
|
|
@ -607,16 +676,16 @@ void CoreAudioCapture::open(const char *name)
|
|||
auto devmatch = std::find_if(CaptureList.cbegin(), CaptureList.cend(), find_name);
|
||||
if(devmatch == CaptureList.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
|
||||
audioDevice = devmatch->mId;
|
||||
}
|
||||
#else
|
||||
if(!name)
|
||||
if(name.empty())
|
||||
name = ca_device;
|
||||
else if(strcmp(name, ca_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
else if(name != ca_device)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
#endif
|
||||
|
||||
AudioComponentDescription desc{};
|
||||
|
|
@ -640,7 +709,7 @@ void CoreAudioCapture::open(const char *name)
|
|||
OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not create component instance: %u", err};
|
||||
"Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
// Turn off AudioUnit output
|
||||
UInt32 enableIO{0};
|
||||
|
|
@ -648,7 +717,8 @@ void CoreAudioCapture::open(const char *name)
|
|||
kAudioUnitScope_Output, OutputElement, &enableIO, sizeof(enableIO));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not disable audio unit output property: %u", err};
|
||||
"Could not disable audio unit output property: '%s' (%u)", FourCCPrinter{err}.c_str(),
|
||||
err};
|
||||
|
||||
// Turn on AudioUnit input
|
||||
enableIO = 1;
|
||||
|
|
@ -656,7 +726,8 @@ void CoreAudioCapture::open(const char *name)
|
|||
kAudioUnitScope_Input, InputElement, &enableIO, sizeof(enableIO));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not enable audio unit input property: %u", err};
|
||||
"Could not enable audio unit input property: '%s' (%u)", FourCCPrinter{err}.c_str(),
|
||||
err};
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(audioDevice != kAudioDeviceUnknown)
|
||||
|
|
@ -666,14 +737,15 @@ void CoreAudioCapture::open(const char *name)
|
|||
|
||||
// set capture callback
|
||||
AURenderCallbackStruct input{};
|
||||
input.inputProc = CoreAudioCapture::RecordProcC;
|
||||
input.inputProc = [](void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) noexcept
|
||||
{ return static_cast<CoreAudioCapture*>(inRefCon)->RecordProc(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); };
|
||||
input.inputProcRefCon = this;
|
||||
|
||||
err = AudioUnitSetProperty(mAudioUnit, kAudioOutputUnitProperty_SetInputCallback,
|
||||
kAudioUnitScope_Global, InputElement, &input, sizeof(AURenderCallbackStruct));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not set capture callback: %u", err};
|
||||
"Could not set capture callback: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
// Disable buffer allocation for capture
|
||||
UInt32 flag{0};
|
||||
|
|
@ -681,13 +753,14 @@ void CoreAudioCapture::open(const char *name)
|
|||
kAudioUnitScope_Output, InputElement, &flag, sizeof(flag));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not disable buffer allocation property: %u", err};
|
||||
"Could not disable buffer allocation property: '%s' (%u)", FourCCPrinter{err}.c_str(),
|
||||
err};
|
||||
|
||||
// Initialize the device
|
||||
err = AudioUnitInitialize(mAudioUnit);
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not initialize audio unit: %u", err};
|
||||
"Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
// Get the hardware format
|
||||
AudioStreamBasicDescription hardwareFormat{};
|
||||
|
|
@ -696,7 +769,7 @@ void CoreAudioCapture::open(const char *name)
|
|||
InputElement, &hardwareFormat, &propertySize);
|
||||
if(err != noErr || propertySize != sizeof(hardwareFormat))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not get input format: %u", err};
|
||||
"Could not get input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
// Set up the requested format description
|
||||
AudioStreamBasicDescription requestedFormat{};
|
||||
|
|
@ -749,6 +822,7 @@ void CoreAudioCapture::open(const char *name)
|
|||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
case DevFmtX714:
|
||||
case DevFmtX7144:
|
||||
case DevFmtX3D71:
|
||||
case DevFmtAmbi3D:
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "%s not supported",
|
||||
|
|
@ -777,14 +851,14 @@ void CoreAudioCapture::open(const char *name)
|
|||
InputElement, &outputFormat, sizeof(outputFormat));
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not set input format: %u", err};
|
||||
"Could not set input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
/* Calculate the minimum AudioUnit output format frame count for the pre-
|
||||
* conversion ring buffer. Ensure at least 100ms for the total buffer.
|
||||
*/
|
||||
double srateScale{outputFormat.mSampleRate / mDevice->Frequency};
|
||||
auto FrameCount64 = maxu64(static_cast<uint64_t>(std::ceil(mDevice->BufferSize*srateScale)),
|
||||
static_cast<UInt32>(outputFormat.mSampleRate)/10);
|
||||
auto FrameCount64 = std::max(static_cast<uint64_t>(std::ceil(mDevice->BufferSize*srateScale)),
|
||||
static_cast<UInt32>(outputFormat.mSampleRate)/10_u64);
|
||||
FrameCount64 += MaxResamplerPadding;
|
||||
if(FrameCount64 > std::numeric_limits<int32_t>::max())
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
|
|
@ -796,11 +870,11 @@ void CoreAudioCapture::open(const char *name)
|
|||
kAudioUnitScope_Global, OutputElement, &outputFrameCount, &propertySize);
|
||||
if(err != noErr || propertySize != sizeof(outputFrameCount))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Could not get input frame count: %u", err};
|
||||
"Could not get input frame count: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
|
||||
mCaptureData.resize(outputFrameCount * mFrameSize);
|
||||
|
||||
outputFrameCount = static_cast<UInt32>(maxu64(outputFrameCount, FrameCount64));
|
||||
outputFrameCount = static_cast<UInt32>(std::max(uint64_t{outputFrameCount}, FrameCount64));
|
||||
mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false);
|
||||
|
||||
/* Set up sample converter if needed */
|
||||
|
|
@ -810,7 +884,7 @@ void CoreAudioCapture::open(const char *name)
|
|||
mDevice->Frequency, Resampler::FastBSinc24);
|
||||
|
||||
#if CAN_ENUMERATE
|
||||
if(name)
|
||||
if(!name.empty())
|
||||
mDevice->DeviceName = name;
|
||||
else
|
||||
{
|
||||
|
|
@ -834,21 +908,21 @@ void CoreAudioCapture::start()
|
|||
OSStatus err{AudioOutputUnitStart(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"AudioOutputUnitStart failed: %d", err};
|
||||
"AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err};
|
||||
}
|
||||
|
||||
void CoreAudioCapture::stop()
|
||||
{
|
||||
OSStatus err{AudioOutputUnitStop(mAudioUnit)};
|
||||
if(err != noErr)
|
||||
ERR("AudioOutputUnitStop failed\n");
|
||||
ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err);
|
||||
}
|
||||
|
||||
void CoreAudioCapture::captureSamples(al::byte *buffer, uint samples)
|
||||
void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
{
|
||||
if(!mConverter)
|
||||
{
|
||||
mRing->read(buffer, samples);
|
||||
std::ignore = mRing->read(buffer, samples);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -882,28 +956,34 @@ BackendFactory &CoreAudioBackendFactory::getFactory()
|
|||
return factory;
|
||||
}
|
||||
|
||||
bool CoreAudioBackendFactory::init() { return true; }
|
||||
bool CoreAudioBackendFactory::init()
|
||||
{
|
||||
#if CAN_ENUMERATE
|
||||
sDeviceHelper.emplace();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreAudioBackendFactory::querySupport(BackendType type)
|
||||
{ return type == BackendType::Playback || type == BackendType::Capture; }
|
||||
|
||||
std::string CoreAudioBackendFactory::probe(BackendType type)
|
||||
auto CoreAudioBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
std::vector<std::string> outnames;
|
||||
#if CAN_ENUMERATE
|
||||
auto append_name = [&outnames](const DeviceEntry &entry) -> void
|
||||
{
|
||||
/* Includes null char. */
|
||||
outnames.append(entry.mName.c_str(), entry.mName.length()+1);
|
||||
};
|
||||
{ outnames.emplace_back(entry.mName); };
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
EnumerateDevices(PlaybackList, false);
|
||||
outnames.reserve(PlaybackList.size());
|
||||
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
||||
break;
|
||||
case BackendType::Capture:
|
||||
EnumerateDevices(CaptureList, true);
|
||||
outnames.reserve(CaptureList.size());
|
||||
std::for_each(CaptureList.cbegin(), CaptureList.cend(), append_name);
|
||||
break;
|
||||
}
|
||||
|
|
@ -914,8 +994,7 @@ std::string CoreAudioBackendFactory::probe(BackendType type)
|
|||
{
|
||||
case BackendType::Playback:
|
||||
case BackendType::Capture:
|
||||
/* Includes null char. */
|
||||
outnames.append(ca_device, sizeof(ca_device));
|
||||
outnames.emplace_back(ca_device);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -930,3 +1009,18 @@ BackendPtr CoreAudioBackendFactory::createBackend(DeviceBase *device, BackendTyp
|
|||
return BackendPtr{new CoreAudioCapture{device}};
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
alc::EventSupport CoreAudioBackendFactory::queryEventSupport(alc::EventType eventType, BackendType)
|
||||
{
|
||||
switch(eventType)
|
||||
{
|
||||
case alc::EventType::DefaultDeviceChanged:
|
||||
return alc::EventSupport::FullSupport;
|
||||
|
||||
case alc::EventType::DeviceAdded:
|
||||
case alc::EventType::DeviceRemoved:
|
||||
case alc::EventType::Count:
|
||||
break;
|
||||
}
|
||||
return alc::EventSupport::NoSupport;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,17 @@
|
|||
|
||||
struct CoreAudioBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_COREAUDIO_H */
|
||||
|
|
|
|||
|
|
@ -25,10 +25,6 @@
|
|||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
|
||||
#include <cguid.h>
|
||||
#include <mmreg.h>
|
||||
#ifndef _WAVEFORMATEXTENSIBLE_
|
||||
|
|
@ -36,15 +32,21 @@
|
|||
#include <ksmedia.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#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 "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
|
|
@ -52,7 +54,6 @@
|
|||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "strutils.h"
|
||||
#include "threads.h"
|
||||
|
||||
/* MinGW-w64 needs this for some unknown reason now. */
|
||||
using LPCWAVEFORMATEX = const WAVEFORMATEX*;
|
||||
|
|
@ -129,10 +130,10 @@ struct DevMap {
|
|||
{ }
|
||||
};
|
||||
|
||||
al::vector<DevMap> PlaybackDevices;
|
||||
al::vector<DevMap> CaptureDevices;
|
||||
std::vector<DevMap> PlaybackDevices;
|
||||
std::vector<DevMap> CaptureDevices;
|
||||
|
||||
bool checkName(const al::vector<DevMap> &list, const std::string &name)
|
||||
bool checkName(const al::span<DevMap> list, const std::string &name)
|
||||
{
|
||||
auto match_name = [&name](const DevMap &entry) -> bool
|
||||
{ return entry.name == name; };
|
||||
|
|
@ -144,7 +145,7 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi
|
|||
if(!guid)
|
||||
return TRUE;
|
||||
|
||||
auto& devices = *static_cast<al::vector<DevMap>*>(data);
|
||||
auto& devices = *static_cast<std::vector<DevMap>*>(data);
|
||||
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)};
|
||||
|
||||
int count{1};
|
||||
|
|
@ -176,7 +177,7 @@ struct DSoundPlayback final : public BackendBase {
|
|||
|
||||
int mixerProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -189,8 +190,6 @@ struct DSoundPlayback final : public BackendBase {
|
|||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(DSoundPlayback)
|
||||
};
|
||||
|
||||
DSoundPlayback::~DSoundPlayback()
|
||||
|
|
@ -209,7 +208,7 @@ DSoundPlayback::~DSoundPlayback()
|
|||
FORCE_ALIGN int DSoundPlayback::mixerProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
DSBCAPS DSBCaps{};
|
||||
DSBCaps.dwSize = sizeof(DSBCaps);
|
||||
|
|
@ -299,24 +298,22 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
|
|||
return 0;
|
||||
}
|
||||
|
||||
void DSoundPlayback::open(const char *name)
|
||||
void DSoundPlayback::open(std::string_view name)
|
||||
{
|
||||
HRESULT hr;
|
||||
if(PlaybackDevices.empty())
|
||||
{
|
||||
/* Initialize COM to prevent name truncation */
|
||||
HRESULT hrcom{CoInitialize(nullptr)};
|
||||
ComWrapper com{};
|
||||
hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
|
||||
if(FAILED(hr))
|
||||
ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
|
||||
if(SUCCEEDED(hrcom))
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
const GUID *guid{nullptr};
|
||||
if(!name && !PlaybackDevices.empty())
|
||||
if(name.empty() && !PlaybackDevices.empty())
|
||||
{
|
||||
name = PlaybackDevices[0].name.c_str();
|
||||
name = PlaybackDevices[0].name;
|
||||
guid = &PlaybackDevices[0].guid;
|
||||
}
|
||||
else
|
||||
|
|
@ -332,7 +329,7 @@ void DSoundPlayback::open(const char *name)
|
|||
[&id](const DevMap &entry) -> bool { return entry.guid == id; });
|
||||
if(iter == PlaybackDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
}
|
||||
guid = &iter->guid;
|
||||
}
|
||||
|
|
@ -347,7 +344,7 @@ void DSoundPlayback::open(const char *name)
|
|||
//DirectSound Init code
|
||||
ComPtr<IDirectSound> ds;
|
||||
if(SUCCEEDED(hr))
|
||||
hr = DirectSoundCreate(guid, ds.getPtr(), nullptr);
|
||||
hr = DirectSoundCreate(guid, al::out_ptr(ds), nullptr);
|
||||
if(SUCCEEDED(hr))
|
||||
hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
|
||||
if(FAILED(hr))
|
||||
|
|
@ -425,49 +422,53 @@ bool DSoundPlayback::reset()
|
|||
case DevFmtX51: OutputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1; break;
|
||||
case DevFmtX61: OutputType.dwChannelMask = X6DOT1; break;
|
||||
case DevFmtX71: OutputType.dwChannelMask = X7DOT1; break;
|
||||
case DevFmtX7144: mDevice->FmtChans = DevFmtX714;
|
||||
/* fall-through */
|
||||
case DevFmtX714: OutputType.dwChannelMask = X7DOT1DOT4; break;
|
||||
case DevFmtX3D71: OutputType.dwChannelMask = X7DOT1; break;
|
||||
}
|
||||
|
||||
retry_open:
|
||||
hr = S_OK;
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
OutputType.Format.nChannels = static_cast<WORD>(mDevice->channelsFromFmt());
|
||||
OutputType.Format.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
|
||||
OutputType.Format.nBlockAlign = static_cast<WORD>(OutputType.Format.nChannels *
|
||||
OutputType.Format.wBitsPerSample / 8);
|
||||
OutputType.Format.nSamplesPerSec = mDevice->Frequency;
|
||||
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
|
||||
OutputType.Format.nBlockAlign;
|
||||
OutputType.Format.cbSize = 0;
|
||||
do {
|
||||
hr = S_OK;
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
OutputType.Format.nChannels = static_cast<WORD>(mDevice->channelsFromFmt());
|
||||
OutputType.Format.wBitsPerSample = static_cast<WORD>(mDevice->bytesFromFmt() * 8);
|
||||
OutputType.Format.nBlockAlign = static_cast<WORD>(OutputType.Format.nChannels *
|
||||
OutputType.Format.wBitsPerSample / 8);
|
||||
OutputType.Format.nSamplesPerSec = mDevice->Frequency;
|
||||
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
|
||||
OutputType.Format.nBlockAlign;
|
||||
OutputType.Format.cbSize = 0;
|
||||
|
||||
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)
|
||||
if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat)
|
||||
{
|
||||
DSBUFFERDESC DSBDescription{};
|
||||
DSBDescription.dwSize = sizeof(DSBDescription);
|
||||
DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, mPrimaryBuffer.getPtr(), nullptr);
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
|
||||
}
|
||||
OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
/* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */
|
||||
OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
|
||||
OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
if(mDevice->FmtType == DevFmtFloat)
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
else
|
||||
OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
|
||||
mPrimaryBuffer = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(SUCCEEDED(hr) && !mPrimaryBuffer)
|
||||
{
|
||||
DSBUFFERDESC DSBDescription{};
|
||||
DSBDescription.dwSize = sizeof(DSBDescription);
|
||||
DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mPrimaryBuffer), nullptr);
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
|
||||
}
|
||||
|
||||
if(FAILED(hr))
|
||||
break;
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
||||
if(num_updates > MAX_UPDATES)
|
||||
num_updates = MAX_UPDATES;
|
||||
|
|
@ -480,26 +481,21 @@ retry_open:
|
|||
DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
|
||||
DSBDescription.lpwfxFormat = &OutputType.Format;
|
||||
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, mBuffer.getPtr(), nullptr);
|
||||
if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
|
||||
{
|
||||
mDevice->FmtType = DevFmtShort;
|
||||
goto retry_open;
|
||||
}
|
||||
}
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mBuffer), nullptr);
|
||||
if(SUCCEEDED(hr) || mDevice->FmtType != DevFmtFloat)
|
||||
break;
|
||||
mDevice->FmtType = DevFmtShort;
|
||||
} while(FAILED(hr));
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
void *ptr;
|
||||
hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
|
||||
hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, al::out_ptr(mNotifies));
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
mNotifies = ComPtr<IDirectSoundNotify>{static_cast<IDirectSoundNotify*>(ptr)};
|
||||
|
||||
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
||||
assert(num_updates <= MAX_UPDATES);
|
||||
|
||||
std::array<DSBPOSITIONNOTIFY,MAX_UPDATES> nots;
|
||||
std::array<DSBPOSITIONNOTIFY,MAX_UPDATES> nots{};
|
||||
for(uint i{0};i < num_updates;++i)
|
||||
{
|
||||
nots[i].dwOffset = i * mDevice->UpdateSize * OutputType.Format.nBlockAlign;
|
||||
|
|
@ -550,10 +546,10 @@ struct DSoundCapture final : public BackendBase {
|
|||
DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~DSoundCapture() override;
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
void captureSamples(std::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
|
||||
ComPtr<IDirectSoundCapture> mDSC;
|
||||
|
|
@ -562,8 +558,6 @@ struct DSoundCapture final : public BackendBase {
|
|||
DWORD mCursor{0u};
|
||||
|
||||
RingBufferPtr mRing;
|
||||
|
||||
DEF_NEWDEL(DSoundCapture)
|
||||
};
|
||||
|
||||
DSoundCapture::~DSoundCapture()
|
||||
|
|
@ -577,24 +571,22 @@ DSoundCapture::~DSoundCapture()
|
|||
}
|
||||
|
||||
|
||||
void DSoundCapture::open(const char *name)
|
||||
void DSoundCapture::open(std::string_view name)
|
||||
{
|
||||
HRESULT hr;
|
||||
if(CaptureDevices.empty())
|
||||
{
|
||||
/* Initialize COM to prevent name truncation */
|
||||
HRESULT hrcom{CoInitialize(nullptr)};
|
||||
ComWrapper com{};
|
||||
hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
|
||||
if(FAILED(hr))
|
||||
ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr);
|
||||
if(SUCCEEDED(hrcom))
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
const GUID *guid{nullptr};
|
||||
if(!name && !CaptureDevices.empty())
|
||||
if(name.empty() && !CaptureDevices.empty())
|
||||
{
|
||||
name = CaptureDevices[0].name.c_str();
|
||||
name = CaptureDevices[0].name;
|
||||
guid = &CaptureDevices[0].guid;
|
||||
}
|
||||
else
|
||||
|
|
@ -610,7 +602,7 @@ void DSoundCapture::open(const char *name)
|
|||
[&id](const DevMap &entry) -> bool { return entry.guid == id; });
|
||||
if(iter == CaptureDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
}
|
||||
guid = &iter->guid;
|
||||
}
|
||||
|
|
@ -641,6 +633,7 @@ void DSoundCapture::open(const char *name)
|
|||
case DevFmtX61: InputType.dwChannelMask = X6DOT1; break;
|
||||
case DevFmtX71: InputType.dwChannelMask = X7DOT1; break;
|
||||
case DevFmtX714: InputType.dwChannelMask = X7DOT1DOT4; break;
|
||||
case DevFmtX7144:
|
||||
case DevFmtX3D71:
|
||||
case DevFmtAmbi3D:
|
||||
WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans));
|
||||
|
|
@ -657,6 +650,7 @@ void DSoundCapture::open(const char *name)
|
|||
InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec *
|
||||
InputType.Format.nBlockAlign;
|
||||
InputType.Format.cbSize = 0;
|
||||
/* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */
|
||||
InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample;
|
||||
if(mDevice->FmtType == DevFmtFloat)
|
||||
InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
|
|
@ -669,8 +663,7 @@ void DSoundCapture::open(const char *name)
|
|||
InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
}
|
||||
|
||||
uint samples{mDevice->BufferSize};
|
||||
samples = maxu(samples, 100 * mDevice->Frequency / 1000);
|
||||
const uint samples{std::max(mDevice->BufferSize, mDevice->Frequency/10u)};
|
||||
|
||||
DSCBUFFERDESC DSCBDescription{};
|
||||
DSCBDescription.dwSize = sizeof(DSCBDescription);
|
||||
|
|
@ -679,9 +672,9 @@ void DSoundCapture::open(const char *name)
|
|||
DSCBDescription.lpwfxFormat = &InputType.Format;
|
||||
|
||||
//DirectSoundCapture Init code
|
||||
hr = DirectSoundCaptureCreate(guid, mDSC.getPtr(), nullptr);
|
||||
hr = DirectSoundCaptureCreate(guid, al::out_ptr(mDSC), nullptr);
|
||||
if(SUCCEEDED(hr))
|
||||
mDSC->CreateCaptureBuffer(&DSCBDescription, mDSCbuffer.getPtr(), nullptr);
|
||||
mDSC->CreateCaptureBuffer(&DSCBDescription, al::out_ptr(mDSCbuffer), nullptr);
|
||||
if(SUCCEEDED(hr))
|
||||
mRing = RingBuffer::Create(mDevice->BufferSize, InputType.Format.nBlockAlign, false);
|
||||
|
||||
|
|
@ -719,8 +712,8 @@ void DSoundCapture::stop()
|
|||
}
|
||||
}
|
||||
|
||||
void DSoundCapture::captureSamples(al::byte *buffer, uint samples)
|
||||
{ mRing->read(buffer, samples); }
|
||||
void DSoundCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
{ std::ignore = mRing->read(buffer, samples); }
|
||||
|
||||
uint DSoundCapture::availableSamples()
|
||||
{
|
||||
|
|
@ -743,9 +736,9 @@ uint DSoundCapture::availableSamples()
|
|||
}
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
mRing->write(ReadPtr1, ReadCnt1/FrameSize);
|
||||
std::ignore = mRing->write(ReadPtr1, ReadCnt1/FrameSize);
|
||||
if(ReadPtr2 != nullptr && ReadCnt2 > 0)
|
||||
mRing->write(ReadPtr2, ReadCnt2/FrameSize);
|
||||
std::ignore = mRing->write(ReadPtr2, ReadCnt2/FrameSize);
|
||||
hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2);
|
||||
mCursor = ReadCursor;
|
||||
}
|
||||
|
|
@ -802,40 +795,32 @@ bool DSoundBackendFactory::init()
|
|||
bool DSoundBackendFactory::querySupport(BackendType type)
|
||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||
|
||||
std::string DSoundBackendFactory::probe(BackendType type)
|
||||
auto DSoundBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
std::vector<std::string> outnames;
|
||||
auto add_device = [&outnames](const DevMap &entry) -> void
|
||||
{
|
||||
/* +1 to also append the null char (to ensure a null-separated list and
|
||||
* double-null terminated list).
|
||||
*/
|
||||
outnames.append(entry.name.c_str(), entry.name.length()+1);
|
||||
};
|
||||
{ outnames.emplace_back(entry.name); };
|
||||
|
||||
/* Initialize COM to prevent name truncation */
|
||||
HRESULT hr;
|
||||
HRESULT hrcom{CoInitialize(nullptr)};
|
||||
ComWrapper com{};
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
PlaybackDevices.clear();
|
||||
hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices);
|
||||
if(FAILED(hr))
|
||||
if(HRESULT hr{DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices)}; FAILED(hr))
|
||||
ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr);
|
||||
outnames.reserve(PlaybackDevices.size());
|
||||
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
||||
break;
|
||||
|
||||
case BackendType::Capture:
|
||||
CaptureDevices.clear();
|
||||
hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices);
|
||||
if(FAILED(hr))
|
||||
if(HRESULT hr{DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices)};FAILED(hr))
|
||||
ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr);
|
||||
outnames.reserve(CaptureDevices.size());
|
||||
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
||||
break;
|
||||
}
|
||||
if(SUCCEEDED(hrcom))
|
||||
CoUninitialize();
|
||||
|
||||
return outnames;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct DSoundBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_DSOUND_H */
|
||||
|
|
|
|||
|
|
@ -22,23 +22,26 @@
|
|||
|
||||
#include "jack.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory.h>
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "alc/alconfig.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alsem.h"
|
||||
#include "alstring.h"
|
||||
#include "althrd_setname.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <jack/ringbuffer.h>
|
||||
|
|
@ -46,6 +49,8 @@
|
|||
|
||||
namespace {
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
#define JACK_FUNCS(MAGIC) \
|
||||
MAGIC(jack_client_open); \
|
||||
|
|
@ -99,19 +104,13 @@ decltype(jack_error_callback) * pjack_error_callback;
|
|||
#endif
|
||||
|
||||
|
||||
constexpr char JackDefaultAudioType[] = JACK_DEFAULT_AUDIO_TYPE;
|
||||
|
||||
jack_options_t ClientOptions = JackNullOption;
|
||||
|
||||
bool jack_load()
|
||||
{
|
||||
bool error{false};
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(!jack_handle)
|
||||
{
|
||||
std::string missing_funcs;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define JACKLIB "libjack.dll"
|
||||
#else
|
||||
|
|
@ -124,13 +123,10 @@ bool jack_load()
|
|||
return false;
|
||||
}
|
||||
|
||||
error = false;
|
||||
std::string missing_funcs;
|
||||
#define LOAD_FUNC(f) do { \
|
||||
p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \
|
||||
if(p##f == nullptr) { \
|
||||
error = true; \
|
||||
missing_funcs += "\n" #f; \
|
||||
} \
|
||||
if(p##f == nullptr) missing_funcs += "\n" #f; \
|
||||
} while(0)
|
||||
JACK_FUNCS(LOAD_FUNC);
|
||||
#undef LOAD_FUNC
|
||||
|
|
@ -139,61 +135,66 @@ bool jack_load()
|
|||
LOAD_SYM(jack_error_callback);
|
||||
#undef LOAD_SYM
|
||||
|
||||
if(error)
|
||||
if(!missing_funcs.empty())
|
||||
{
|
||||
WARN("Missing expected functions:%s\n", missing_funcs.c_str());
|
||||
CloseLib(jack_handle);
|
||||
jack_handle = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return !error;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
struct JackDeleter {
|
||||
void operator()(void *ptr) { jack_free(ptr); }
|
||||
};
|
||||
using JackPortsPtr = std::unique_ptr<const char*[],JackDeleter>;
|
||||
using JackPortsPtr = std::unique_ptr<const char*[],JackDeleter>; /* NOLINT(*-avoid-c-arrays) */
|
||||
|
||||
struct DeviceEntry {
|
||||
std::string mName;
|
||||
std::string mPattern;
|
||||
|
||||
DeviceEntry() = default;
|
||||
DeviceEntry(const DeviceEntry&) = default;
|
||||
DeviceEntry(DeviceEntry&&) = default;
|
||||
template<typename T, typename U>
|
||||
DeviceEntry(T&& name, U&& pattern)
|
||||
: mName{std::forward<T>(name)}, mPattern{std::forward<U>(pattern)}
|
||||
{ }
|
||||
~DeviceEntry();
|
||||
|
||||
DeviceEntry& operator=(const DeviceEntry&) = default;
|
||||
DeviceEntry& operator=(DeviceEntry&&) = default;
|
||||
};
|
||||
DeviceEntry::~DeviceEntry() = default;
|
||||
|
||||
al::vector<DeviceEntry> PlaybackList;
|
||||
std::vector<DeviceEntry> PlaybackList;
|
||||
|
||||
|
||||
void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &list)
|
||||
void EnumerateDevices(jack_client_t *client, std::vector<DeviceEntry> &list)
|
||||
{
|
||||
std::remove_reference_t<decltype(list)>{}.swap(list);
|
||||
|
||||
if(JackPortsPtr ports{jack_get_ports(client, nullptr, JackDefaultAudioType, JackPortIsInput)})
|
||||
if(JackPortsPtr ports{jack_get_ports(client, nullptr, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput)})
|
||||
{
|
||||
for(size_t i{0};ports[i];++i)
|
||||
{
|
||||
const char *sep{std::strchr(ports[i], ':')};
|
||||
if(!sep || ports[i] == sep) continue;
|
||||
const std::string_view portname{ports[i]};
|
||||
const size_t seppos{portname.find(':')};
|
||||
if(seppos == 0 || seppos >= portname.size())
|
||||
continue;
|
||||
|
||||
const al::span<const char> portdev{ports[i], sep};
|
||||
const auto portdev = portname.substr(0, seppos);
|
||||
auto check_name = [portdev](const DeviceEntry &entry) -> bool
|
||||
{
|
||||
const size_t len{portdev.size()};
|
||||
return entry.mName.length() == len
|
||||
&& entry.mName.compare(0, len, portdev.data(), len) == 0;
|
||||
};
|
||||
{ return entry.mName == portdev; };
|
||||
if(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend())
|
||||
continue;
|
||||
|
||||
std::string name{portdev.data(), portdev.size()};
|
||||
list.emplace_back(name, name+":");
|
||||
const auto &entry = list.back();
|
||||
const auto &entry = list.emplace_back(portdev, std::string{portdev}+":");
|
||||
TRACE("Got device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
||||
}
|
||||
/* There are ports but couldn't get device names from them. Add a
|
||||
|
|
@ -202,11 +203,11 @@ void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &list)
|
|||
if(ports[0] && list.empty())
|
||||
{
|
||||
WARN("No device names found in available ports, adding a generic name.\n");
|
||||
list.emplace_back("JACK", "");
|
||||
list.emplace_back("JACK"sv, ""sv);
|
||||
}
|
||||
}
|
||||
|
||||
if(auto listopt = ConfigValueStr(nullptr, "jack", "custom-devices"))
|
||||
if(auto listopt = ConfigValueStr({}, "jack", "custom-devices"))
|
||||
{
|
||||
for(size_t strpos{0};strpos < listopt->size();)
|
||||
{
|
||||
|
|
@ -214,38 +215,32 @@ void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &list)
|
|||
size_t seppos{listopt->find('=', strpos)};
|
||||
if(seppos >= nextpos || seppos == strpos)
|
||||
{
|
||||
const std::string entry{listopt->substr(strpos, nextpos-strpos)};
|
||||
ERR("Invalid device entry: \"%s\"\n", entry.c_str());
|
||||
const auto entry = std::string_view{*listopt}.substr(strpos, nextpos-strpos);
|
||||
ERR("Invalid device entry: \"%.*s\"\n", al::sizei(entry), entry.data());
|
||||
if(nextpos != std::string::npos) ++nextpos;
|
||||
strpos = nextpos;
|
||||
continue;
|
||||
}
|
||||
|
||||
const al::span<const char> name{listopt->data()+strpos, seppos-strpos};
|
||||
const al::span<const char> pattern{listopt->data()+(seppos+1),
|
||||
std::min(nextpos, listopt->size())-(seppos+1)};
|
||||
const auto name = std::string_view{*listopt}.substr(strpos, seppos-strpos);
|
||||
const auto pattern = std::string_view{*listopt}.substr(seppos+1,
|
||||
std::min(nextpos, listopt->size())-(seppos+1));
|
||||
|
||||
/* Check if this custom pattern already exists in the list. */
|
||||
auto check_pattern = [pattern](const DeviceEntry &entry) -> bool
|
||||
{
|
||||
const size_t len{pattern.size()};
|
||||
return entry.mPattern.length() == len
|
||||
&& entry.mPattern.compare(0, len, pattern.data(), len) == 0;
|
||||
};
|
||||
{ return entry.mPattern == pattern; };
|
||||
auto itemmatch = std::find_if(list.begin(), list.end(), check_pattern);
|
||||
if(itemmatch != list.end())
|
||||
{
|
||||
/* If so, replace the name with this custom one. */
|
||||
itemmatch->mName.assign(name.data(), name.size());
|
||||
itemmatch->mName = name;
|
||||
TRACE("Customized device name: %s = %s\n", itemmatch->mName.c_str(),
|
||||
itemmatch->mPattern.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, add a new device entry. */
|
||||
list.emplace_back(std::string{name.data(), name.size()},
|
||||
std::string{pattern.data(), pattern.size()});
|
||||
const auto &entry = list.back();
|
||||
const auto &entry = list.emplace_back(name, pattern);
|
||||
TRACE("Got custom device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
||||
}
|
||||
|
||||
|
|
@ -295,7 +290,7 @@ struct JackPlayback final : public BackendBase {
|
|||
|
||||
int mixerProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -304,7 +299,7 @@ struct JackPlayback final : public BackendBase {
|
|||
std::string mPortPattern;
|
||||
|
||||
jack_client_t *mClient{nullptr};
|
||||
std::array<jack_port_t*,MAX_OUTPUT_CHANNELS> mPort{};
|
||||
std::array<jack_port_t*,MaxOutputChannels> mPort{};
|
||||
|
||||
std::mutex mMutex;
|
||||
|
||||
|
|
@ -315,8 +310,6 @@ struct JackPlayback final : public BackendBase {
|
|||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(JackPlayback)
|
||||
};
|
||||
|
||||
JackPlayback::~JackPlayback()
|
||||
|
|
@ -336,22 +329,22 @@ JackPlayback::~JackPlayback()
|
|||
|
||||
int JackPlayback::processRt(jack_nframes_t numframes) noexcept
|
||||
{
|
||||
std::array<jack_default_audio_sample_t*,MAX_OUTPUT_CHANNELS> out;
|
||||
size_t numchans{0};
|
||||
auto outptrs = std::array<jack_default_audio_sample_t*,MaxOutputChannels>{};
|
||||
auto numchans = size_t{0};
|
||||
for(auto port : mPort)
|
||||
{
|
||||
if(!port || numchans == mDevice->RealOut.Buffer.size())
|
||||
break;
|
||||
out[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
|
||||
outptrs[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
|
||||
}
|
||||
|
||||
const auto dst = al::span{outptrs}.first(numchans);
|
||||
if(mPlaying.load(std::memory_order_acquire)) LIKELY
|
||||
mDevice->renderSamples({out.data(), numchans}, static_cast<uint>(numframes));
|
||||
mDevice->renderSamples(dst, static_cast<uint>(numframes));
|
||||
else
|
||||
{
|
||||
auto clear_buf = [numframes](float *outbuf) -> void
|
||||
{ std::fill_n(outbuf, numframes, 0.0f); };
|
||||
std::for_each(out.begin(), out.begin()+numchans, clear_buf);
|
||||
std::for_each(dst.begin(), dst.end(), [numframes](float *outbuf) -> void
|
||||
{ std::fill_n(outbuf, numframes, 0.0f); });
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -360,53 +353,46 @@ int JackPlayback::processRt(jack_nframes_t numframes) noexcept
|
|||
|
||||
int JackPlayback::process(jack_nframes_t numframes) noexcept
|
||||
{
|
||||
std::array<jack_default_audio_sample_t*,MAX_OUTPUT_CHANNELS> out;
|
||||
std::array<al::span<float>,MaxOutputChannels> out;
|
||||
size_t numchans{0};
|
||||
for(auto port : mPort)
|
||||
{
|
||||
if(!port) break;
|
||||
out[numchans++] = static_cast<float*>(jack_port_get_buffer(port, numframes));
|
||||
out[numchans++] = {static_cast<float*>(jack_port_get_buffer(port, numframes)), numframes};
|
||||
}
|
||||
|
||||
jack_nframes_t total{0};
|
||||
size_t total{0};
|
||||
if(mPlaying.load(std::memory_order_acquire)) LIKELY
|
||||
{
|
||||
auto data = mRing->getReadVector();
|
||||
jack_nframes_t todo{minu(numframes, static_cast<uint>(data.first.len))};
|
||||
auto write_first = [&data,numchans,todo](float *outbuf) -> float*
|
||||
{
|
||||
const 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;
|
||||
const auto update_size = size_t{mDevice->UpdateSize};
|
||||
|
||||
todo = minu(numframes-total, static_cast<uint>(data.second.len));
|
||||
if(todo > 0)
|
||||
const auto outlen = size_t{numframes / update_size};
|
||||
const auto len1 = size_t{std::min(data.first.len/update_size, outlen)};
|
||||
const auto len2 = size_t{std::min(data.second.len/update_size, outlen-len1)};
|
||||
|
||||
auto src = al::span{reinterpret_cast<float*>(data.first.buf), update_size*len1*numchans};
|
||||
for(size_t i{0};i < len1;++i)
|
||||
{
|
||||
auto write_second = [&data,numchans,todo](float *outbuf) -> float*
|
||||
for(size_t c{0};c < numchans;++c)
|
||||
{
|
||||
const float *RESTRICT in = reinterpret_cast<float*>(data.second.buf);
|
||||
auto deinterlace_input = [&in,numchans]() noexcept -> float
|
||||
{
|
||||
float ret{*in};
|
||||
in += numchans;
|
||||
return ret;
|
||||
};
|
||||
std::generate_n(outbuf, todo, deinterlace_input);
|
||||
data.second.buf += sizeof(float);
|
||||
return outbuf + todo;
|
||||
};
|
||||
std::transform(out.begin(), out.begin()+numchans, out.begin(), write_second);
|
||||
total += todo;
|
||||
const auto iter = std::copy_n(src.begin(), update_size, out[c].begin());
|
||||
out[c] = {iter, out[c].end()};
|
||||
src = src.subspan(update_size);
|
||||
}
|
||||
total += update_size;
|
||||
}
|
||||
|
||||
src = al::span{reinterpret_cast<float*>(data.second.buf), update_size*len2*numchans};
|
||||
for(size_t i{0};i < len2;++i)
|
||||
{
|
||||
for(size_t c{0};c < numchans;++c)
|
||||
{
|
||||
const auto iter = std::copy_n(src.begin(), update_size, out[c].begin());
|
||||
out[c] = {iter, out[c].end()};
|
||||
src = src.subspan(update_size);
|
||||
}
|
||||
total += update_size;
|
||||
}
|
||||
|
||||
mRing->readAdvance(total);
|
||||
|
|
@ -415,8 +401,8 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept
|
|||
|
||||
if(numframes > total)
|
||||
{
|
||||
const jack_nframes_t todo{numframes - total};
|
||||
auto clear_buf = [todo](float *outbuf) -> void { std::fill_n(outbuf, todo, 0.0f); };
|
||||
auto clear_buf = [](const al::span<float> outbuf) -> void
|
||||
{ std::fill(outbuf.begin(), outbuf.end(), 0.0f); };
|
||||
std::for_each(out.begin(), out.begin()+numchans, clear_buf);
|
||||
}
|
||||
|
||||
|
|
@ -426,45 +412,70 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept
|
|||
int JackPlayback::mixerProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
const size_t frame_step{mDevice->channelsFromFmt()};
|
||||
const auto update_size = uint{mDevice->UpdateSize};
|
||||
const auto num_channels = size_t{mDevice->channelsFromFmt()};
|
||||
auto outptrs = std::vector<float*>(num_channels);
|
||||
|
||||
while(!mKillNow.load(std::memory_order_acquire)
|
||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||
{
|
||||
if(mRing->writeSpace() < mDevice->UpdateSize)
|
||||
if(mRing->writeSpace() < update_size)
|
||||
{
|
||||
mSem.wait();
|
||||
continue;
|
||||
}
|
||||
|
||||
auto data = mRing->getWriteVector();
|
||||
size_t todo{data.first.len + data.second.len};
|
||||
todo -= todo%mDevice->UpdateSize;
|
||||
const auto len1 = size_t{data.first.len / update_size};
|
||||
const auto len2 = size_t{data.second.len / update_size};
|
||||
|
||||
const auto len1 = static_cast<uint>(minz(data.first.len, todo));
|
||||
const auto len2 = static_cast<uint>(minz(data.second.len, todo-len1));
|
||||
|
||||
std::lock_guard<std::mutex> _{mMutex};
|
||||
mDevice->renderSamples(data.first.buf, len1, frame_step);
|
||||
std::lock_guard<std::mutex> dlock{mMutex};
|
||||
auto buffer = al::span{reinterpret_cast<float*>(data.first.buf),
|
||||
data.first.len*num_channels};
|
||||
auto bufiter = buffer.begin();
|
||||
for(size_t i{0};i < len1;++i)
|
||||
{
|
||||
std::generate_n(outptrs.begin(), outptrs.size(), [&bufiter,update_size]
|
||||
{
|
||||
auto ret = al::to_address(bufiter);
|
||||
bufiter += ptrdiff_t(update_size);
|
||||
return ret;
|
||||
});
|
||||
mDevice->renderSamples(outptrs, update_size);
|
||||
}
|
||||
if(len2 > 0)
|
||||
mDevice->renderSamples(data.second.buf, len2, frame_step);
|
||||
mRing->writeAdvance(todo);
|
||||
{
|
||||
buffer = al::span{reinterpret_cast<float*>(data.second.buf),
|
||||
data.second.len*num_channels};
|
||||
bufiter = buffer.begin();
|
||||
for(size_t i{0};i < len2;++i)
|
||||
{
|
||||
std::generate_n(outptrs.begin(), outptrs.size(), [&bufiter,update_size]
|
||||
{
|
||||
auto ret = al::to_address(bufiter);
|
||||
bufiter += ptrdiff_t(update_size);
|
||||
return ret;
|
||||
});
|
||||
mDevice->renderSamples(outptrs, update_size);
|
||||
}
|
||||
}
|
||||
mRing->writeAdvance((len1+len2) * update_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void JackPlayback::open(const char *name)
|
||||
void JackPlayback::open(std::string_view name)
|
||||
{
|
||||
if(!mClient)
|
||||
{
|
||||
const PathNamePair &binname = GetProcBinary();
|
||||
const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
|
||||
|
||||
jack_status_t status;
|
||||
jack_status_t status{};
|
||||
mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
|
||||
if(mClient == nullptr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
|
|
@ -481,9 +492,9 @@ void JackPlayback::open(const char *name)
|
|||
if(PlaybackList.empty())
|
||||
EnumerateDevices(mClient, PlaybackList);
|
||||
|
||||
if(!name && !PlaybackList.empty())
|
||||
if(name.empty() && !PlaybackList.empty())
|
||||
{
|
||||
name = PlaybackList[0].mName.c_str();
|
||||
name = PlaybackList[0].mName;
|
||||
mPortPattern = PlaybackList[0].mPattern;
|
||||
}
|
||||
else
|
||||
|
|
@ -493,14 +504,10 @@ void JackPlayback::open(const char *name)
|
|||
auto iter = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), check_name);
|
||||
if(iter == PlaybackList.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name?name:""};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
mPortPattern = iter->mPattern;
|
||||
}
|
||||
|
||||
mRTMixing = GetConfigValueBool(name, "jack", "rt-mix", true);
|
||||
jack_set_process_callback(mClient,
|
||||
mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this);
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
|
|
@ -511,6 +518,10 @@ bool JackPlayback::reset()
|
|||
std::for_each(mPort.begin(), mPort.end(), unregister_port);
|
||||
mPort.fill(nullptr);
|
||||
|
||||
mRTMixing = GetConfigValueBool(mDevice->DeviceName, "jack", "rt-mix", true);
|
||||
jack_set_process_callback(mClient,
|
||||
mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this);
|
||||
|
||||
/* Ignore the requested buffer metrics and just keep one JACK-sized buffer
|
||||
* ready for when requested.
|
||||
*/
|
||||
|
|
@ -525,9 +536,9 @@ bool JackPlayback::reset()
|
|||
}
|
||||
else
|
||||
{
|
||||
const char *devname{mDevice->DeviceName.c_str()};
|
||||
const std::string_view devname{mDevice->DeviceName};
|
||||
uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
|
||||
bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
||||
bufsize = std::max(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
||||
mDevice->BufferSize = bufsize + mDevice->UpdateSize;
|
||||
}
|
||||
|
||||
|
|
@ -535,27 +546,27 @@ bool JackPlayback::reset()
|
|||
mDevice->FmtType = DevFmtFloat;
|
||||
|
||||
int port_num{0};
|
||||
auto ports_end = mPort.begin() + mDevice->channelsFromFmt();
|
||||
auto bad_port = mPort.begin();
|
||||
while(bad_port != ports_end)
|
||||
auto ports = al::span{mPort}.first(mDevice->channelsFromFmt());
|
||||
auto bad_port = ports.begin();
|
||||
while(bad_port != ports.end())
|
||||
{
|
||||
std::string name{"channel_" + std::to_string(++port_num)};
|
||||
*bad_port = jack_port_register(mClient, name.c_str(), JackDefaultAudioType,
|
||||
*bad_port = jack_port_register(mClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE,
|
||||
JackPortIsOutput | JackPortIsTerminal, 0);
|
||||
if(!*bad_port) break;
|
||||
++bad_port;
|
||||
}
|
||||
if(bad_port != ports_end)
|
||||
if(bad_port != ports.end())
|
||||
{
|
||||
ERR("Failed to register enough JACK ports for %s output\n",
|
||||
DevFmtChannelsString(mDevice->FmtChans));
|
||||
if(bad_port == mPort.begin()) return false;
|
||||
if(bad_port == ports.begin()) return false;
|
||||
|
||||
if(bad_port == mPort.begin()+1)
|
||||
if(bad_port == ports.begin()+1)
|
||||
mDevice->FmtChans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
ports_end = mPort.begin()+2;
|
||||
const auto ports_end = ports.begin()+2;
|
||||
while(bad_port != ports_end)
|
||||
{
|
||||
jack_port_unregister(mClient, *(--bad_port));
|
||||
|
|
@ -575,10 +586,10 @@ void JackPlayback::start()
|
|||
if(jack_activate(mClient))
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to activate client"};
|
||||
|
||||
const char *devname{mDevice->DeviceName.c_str()};
|
||||
const std::string_view devname{mDevice->DeviceName};
|
||||
if(ConfigValueBool(devname, "jack", "connect-ports").value_or(true))
|
||||
{
|
||||
JackPortsPtr pnames{jack_get_ports(mClient, mPortPattern.c_str(), JackDefaultAudioType,
|
||||
JackPortsPtr pnames{jack_get_ports(mClient, mPortPattern.c_str(), JACK_DEFAULT_AUDIO_TYPE,
|
||||
JackPortIsInput)};
|
||||
if(!pnames)
|
||||
{
|
||||
|
|
@ -586,7 +597,7 @@ void JackPlayback::start()
|
|||
throw al::backend_exception{al::backend_error::DeviceError, "No playback ports found"};
|
||||
}
|
||||
|
||||
for(size_t i{0};i < al::size(mPort) && mPort[i];++i)
|
||||
for(size_t i{0};i < std::size(mPort) && mPort[i];++i)
|
||||
{
|
||||
if(!pnames[i])
|
||||
{
|
||||
|
|
@ -613,7 +624,7 @@ void JackPlayback::start()
|
|||
else
|
||||
{
|
||||
uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)};
|
||||
bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
||||
bufsize = std::max(NextPowerOf2(bufsize), mDevice->UpdateSize);
|
||||
mDevice->BufferSize = bufsize + mDevice->UpdateSize;
|
||||
|
||||
mRing = RingBuffer::Create(bufsize, mDevice->frameSizeFromFmt(), true);
|
||||
|
|
@ -651,10 +662,9 @@ void JackPlayback::stop()
|
|||
|
||||
ClockLatency JackPlayback::getClockLatency()
|
||||
{
|
||||
ClockLatency ret;
|
||||
|
||||
std::lock_guard<std::mutex> _{mMutex};
|
||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
||||
std::lock_guard<std::mutex> dlock{mMutex};
|
||||
ClockLatency ret{};
|
||||
ret.ClockTime = mDevice->getClockTime();
|
||||
ret.Latency = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->UpdateSize};
|
||||
ret.Latency /= mDevice->Frequency;
|
||||
|
||||
|
|
@ -674,7 +684,7 @@ bool JackBackendFactory::init()
|
|||
if(!jack_load())
|
||||
return false;
|
||||
|
||||
if(!GetConfigValueBool(nullptr, "jack", "spawn-server", false))
|
||||
if(!GetConfigValueBool({}, "jack", "spawn-server", false))
|
||||
ClientOptions = static_cast<jack_options_t>(ClientOptions | JackNoStartServer);
|
||||
|
||||
const PathNamePair &binname = GetProcBinary();
|
||||
|
|
@ -682,7 +692,7 @@ bool JackBackendFactory::init()
|
|||
|
||||
void (*old_error_cb)(const char*){&jack_error_callback ? jack_error_callback : nullptr};
|
||||
jack_set_error_function(jack_msg_handler);
|
||||
jack_status_t status;
|
||||
jack_status_t status{};
|
||||
jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)};
|
||||
jack_set_error_function(old_error_cb);
|
||||
if(!client)
|
||||
|
|
@ -700,18 +710,15 @@ bool JackBackendFactory::init()
|
|||
bool JackBackendFactory::querySupport(BackendType type)
|
||||
{ return (type == BackendType::Playback); }
|
||||
|
||||
std::string JackBackendFactory::probe(BackendType type)
|
||||
auto JackBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
std::vector<std::string> outnames;
|
||||
auto append_name = [&outnames](const DeviceEntry &entry) -> void
|
||||
{
|
||||
/* Includes null char. */
|
||||
outnames.append(entry.mName.c_str(), entry.mName.length()+1);
|
||||
};
|
||||
{ outnames.emplace_back(entry.mName); };
|
||||
|
||||
const PathNamePair &binname = GetProcBinary();
|
||||
const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
|
||||
jack_status_t status;
|
||||
jack_status_t status{};
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
|
|
@ -722,6 +729,7 @@ std::string JackBackendFactory::probe(BackendType type)
|
|||
}
|
||||
else
|
||||
WARN("jack_client_open() failed, 0x%02x\n", status);
|
||||
outnames.reserve(PlaybackList.size());
|
||||
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
||||
break;
|
||||
case BackendType::Capture:
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct JackBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_JACK_H */
|
||||
|
|
|
|||
|
|
@ -30,16 +30,14 @@ namespace {
|
|||
struct LoopbackBackend final : public BackendBase {
|
||||
LoopbackBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
DEF_NEWDEL(LoopbackBackend)
|
||||
};
|
||||
|
||||
|
||||
void LoopbackBackend::open(const char *name)
|
||||
void LoopbackBackend::open(std::string_view name)
|
||||
{
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
|
@ -65,8 +63,8 @@ bool LoopbackBackendFactory::init()
|
|||
bool LoopbackBackendFactory::querySupport(BackendType)
|
||||
{ return true; }
|
||||
|
||||
std::string LoopbackBackendFactory::probe(BackendType)
|
||||
{ return std::string{}; }
|
||||
auto LoopbackBackendFactory::enumerate(BackendType) -> std::vector<std::string>
|
||||
{ return {}; }
|
||||
|
||||
BackendPtr LoopbackBackendFactory::createBackend(DeviceBase *device, BackendType)
|
||||
{ return BackendPtr{new LoopbackBackend{device}}; }
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct LoopbackBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_LOOPBACK_H */
|
||||
|
|
|
|||
|
|
@ -30,10 +30,11 @@
|
|||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
#include "core/device.h"
|
||||
#include "almalloc.h"
|
||||
#include "alstring.h"
|
||||
#include "althrd_setname.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "threads.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
@ -41,8 +42,9 @@ namespace {
|
|||
using std::chrono::seconds;
|
||||
using std::chrono::milliseconds;
|
||||
using std::chrono::nanoseconds;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
constexpr char nullDevice[] = "No Output";
|
||||
[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "No Output"sv; }
|
||||
|
||||
|
||||
struct NullBackend final : public BackendBase {
|
||||
|
|
@ -50,15 +52,13 @@ struct NullBackend final : public BackendBase {
|
|||
|
||||
int mixerProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(NullBackend)
|
||||
};
|
||||
|
||||
int NullBackend::mixerProc()
|
||||
|
|
@ -66,7 +66,7 @@ int NullBackend::mixerProc()
|
|||
const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
int64_t done{0};
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
|
@ -105,13 +105,13 @@ int NullBackend::mixerProc()
|
|||
}
|
||||
|
||||
|
||||
void NullBackend::open(const char *name)
|
||||
void NullBackend::open(std::string_view name)
|
||||
{
|
||||
if(!name)
|
||||
name = nullDevice;
|
||||
else if(strcmp(name, nullDevice) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
if(name.empty())
|
||||
name = GetDeviceName();
|
||||
else if(name != GetDeviceName())
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
|
@ -150,19 +150,17 @@ bool NullBackendFactory::init()
|
|||
bool NullBackendFactory::querySupport(BackendType type)
|
||||
{ return (type == BackendType::Playback); }
|
||||
|
||||
std::string NullBackendFactory::probe(BackendType type)
|
||||
auto NullBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
/* Includes null char. */
|
||||
outnames.append(nullDevice, sizeof(nullDevice));
|
||||
break;
|
||||
/* Include null char. */
|
||||
return std::vector{std::string{GetDeviceName()}};
|
||||
case BackendType::Capture:
|
||||
break;
|
||||
}
|
||||
return outnames;
|
||||
return {};
|
||||
}
|
||||
|
||||
BackendPtr NullBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct NullBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_NULL_H */
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@
|
|||
#include "oboe.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "alnumeric.h"
|
||||
#include "alstring.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
#include "ringbuffer.h"
|
||||
|
|
@ -17,7 +18,9 @@
|
|||
|
||||
namespace {
|
||||
|
||||
constexpr char device_name[] = "Oboe Default";
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "Oboe Default"sv; }
|
||||
|
||||
|
||||
struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback {
|
||||
|
|
@ -28,7 +31,9 @@ struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback
|
|||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
|
||||
int32_t numFrames) override;
|
||||
|
||||
void open(const char *name) override;
|
||||
void onErrorAfterClose(oboe::AudioStream* /* audioStream */, oboe::Result /* error */) override;
|
||||
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -46,14 +51,20 @@ oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStrea
|
|||
return oboe::DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
|
||||
void OboePlayback::open(const char *name)
|
||||
void OboePlayback::onErrorAfterClose(oboe::AudioStream*, oboe::Result error)
|
||||
{
|
||||
if(!name)
|
||||
name = device_name;
|
||||
else if(std::strcmp(name, device_name) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
if(error == oboe::Result::ErrorDisconnected)
|
||||
mDevice->handleDisconnect("Oboe AudioStream was disconnected: %s", oboe::convertToText(error));
|
||||
TRACE("Error was %s", oboe::convertToText(error));
|
||||
}
|
||||
|
||||
void OboePlayback::open(std::string_view name)
|
||||
{
|
||||
if(name.empty())
|
||||
name = GetDeviceName();
|
||||
else if(name != GetDeviceName())
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
|
||||
/* Open a basic output stream, just to ensure it can work. */
|
||||
oboe::ManagedStream stream;
|
||||
|
|
@ -72,6 +83,7 @@ bool OboePlayback::reset()
|
|||
oboe::AudioStreamBuilder builder;
|
||||
builder.setDirection(oboe::Direction::Output);
|
||||
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
|
||||
builder.setUsage(oboe::Usage::Game);
|
||||
/* Don't let Oboe convert. We should be able to handle anything it gives
|
||||
* back.
|
||||
*/
|
||||
|
|
@ -135,7 +147,7 @@ bool OboePlayback::reset()
|
|||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
mStream->setBufferSizeInFrames(mini(static_cast<int32_t>(mDevice->BufferSize),
|
||||
mStream->setBufferSizeInFrames(std::min(static_cast<int32_t>(mDevice->BufferSize),
|
||||
mStream->getBufferCapacityInFrames()));
|
||||
TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
|
||||
|
||||
|
|
@ -164,6 +176,9 @@ bool OboePlayback::reset()
|
|||
mDevice->FmtType = DevFmtInt;
|
||||
break;
|
||||
case oboe::AudioFormat::I24:
|
||||
#endif
|
||||
#if OBOE_VERSION_MAJOR > 1 || (OBOE_VERSION_MAJOR == 1 && OBOE_VERSION_MINOR >= 8)
|
||||
case oboe::AudioFormat::IEC61937:
|
||||
#endif
|
||||
case oboe::AudioFormat::Unspecified:
|
||||
case oboe::AudioFormat::Invalid:
|
||||
|
|
@ -177,9 +192,9 @@ bool OboePlayback::reset()
|
|||
* FramesPerBurst may not necessarily be correct, but hopefully it can act as a minimum
|
||||
* update size.
|
||||
*/
|
||||
mDevice->UpdateSize = maxu(mDevice->Frequency / 100,
|
||||
mDevice->UpdateSize = std::max(mDevice->Frequency/100u,
|
||||
static_cast<uint32_t>(mStream->getFramesPerBurst()));
|
||||
mDevice->BufferSize = maxu(mDevice->UpdateSize * 2,
|
||||
mDevice->BufferSize = std::max(mDevice->UpdateSize*2u,
|
||||
static_cast<uint32_t>(mStream->getBufferSizeInFrames()));
|
||||
|
||||
return true;
|
||||
|
|
@ -197,8 +212,7 @@ void OboePlayback::stop()
|
|||
{
|
||||
oboe::Result result{mStream->stop()};
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
ERR("Failed to stop stream: %s\n", oboe::convertToText(result));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -212,28 +226,28 @@ struct OboeCapture final : public BackendBase, public oboe::AudioStreamCallback
|
|||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
|
||||
int32_t numFrames) override;
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
void captureSamples(std::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
};
|
||||
|
||||
oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *audioData,
|
||||
int32_t numFrames)
|
||||
{
|
||||
mRing->write(audioData, static_cast<uint32_t>(numFrames));
|
||||
std::ignore = mRing->write(audioData, static_cast<uint32_t>(numFrames));
|
||||
return oboe::DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
|
||||
void OboeCapture::open(const char *name)
|
||||
void OboeCapture::open(std::string_view name)
|
||||
{
|
||||
if(!name)
|
||||
name = device_name;
|
||||
else if(std::strcmp(name, device_name) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
if(name.empty())
|
||||
name = GetDeviceName();
|
||||
else if(name != GetDeviceName())
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
|
||||
oboe::AudioStreamBuilder builder;
|
||||
builder.setDirection(oboe::Direction::Input)
|
||||
|
|
@ -259,6 +273,7 @@ void OboeCapture::open(const char *name)
|
|||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
case DevFmtX714:
|
||||
case DevFmtX7144:
|
||||
case DevFmtX3D71:
|
||||
case DevFmtAmbi3D:
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
|
||||
|
|
@ -297,7 +312,7 @@ void OboeCapture::open(const char *name)
|
|||
TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
|
||||
|
||||
/* Ensure a minimum ringbuffer size of 100ms. */
|
||||
mRing = RingBuffer::Create(maxu(mDevice->BufferSize, mDevice->Frequency/10),
|
||||
mRing = RingBuffer::Create(std::max(mDevice->BufferSize, mDevice->Frequency/10u),
|
||||
static_cast<uint32_t>(mStream->getBytesPerFrame()), false);
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
|
|
@ -315,15 +330,14 @@ void OboeCapture::stop()
|
|||
{
|
||||
const oboe::Result result{mStream->stop()};
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to stop stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
ERR("Failed to stop stream: %s\n", oboe::convertToText(result));
|
||||
}
|
||||
|
||||
uint OboeCapture::availableSamples()
|
||||
{ return static_cast<uint>(mRing->readSpace()); }
|
||||
|
||||
void OboeCapture::captureSamples(al::byte *buffer, uint samples)
|
||||
{ mRing->read(buffer, samples); }
|
||||
void OboeCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
{ std::ignore = mRing->read(buffer, samples); }
|
||||
|
||||
} // namespace
|
||||
|
||||
|
|
@ -332,16 +346,15 @@ bool OboeBackendFactory::init() { return true; }
|
|||
bool OboeBackendFactory::querySupport(BackendType type)
|
||||
{ return type == BackendType::Playback || type == BackendType::Capture; }
|
||||
|
||||
std::string OboeBackendFactory::probe(BackendType type)
|
||||
auto OboeBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
case BackendType::Capture:
|
||||
/* Includes null char. */
|
||||
return std::string{device_name, sizeof(device_name)};
|
||||
return std::vector{std::string{GetDeviceName()}};
|
||||
}
|
||||
return std::string{};
|
||||
return {};
|
||||
}
|
||||
|
||||
BackendPtr OboeBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct OboeBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_OBOE_H */
|
||||
|
|
|
|||
|
|
@ -23,23 +23,26 @@
|
|||
|
||||
#include "opensl.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <jni.h>
|
||||
|
||||
#include <new>
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
#include "albit.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alsem.h"
|
||||
#include "alstring.h"
|
||||
#include "althrd_setname.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "opthelpers.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
|
@ -48,15 +51,17 @@
|
|||
|
||||
namespace {
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
/* Helper macros */
|
||||
#define EXTRACT_VCALL_ARGS(...) __VA_ARGS__))
|
||||
#define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
|
||||
#define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
|
||||
|
||||
|
||||
constexpr char opensl_device[] = "OpenSL";
|
||||
|
||||
[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "OpenSL"sv; }
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept
|
||||
{
|
||||
switch(chans)
|
||||
|
|
@ -80,6 +85,7 @@ constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept
|
|||
SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT |
|
||||
SL_SPEAKER_TOP_FRONT_LEFT | SL_SPEAKER_TOP_FRONT_RIGHT | SL_SPEAKER_TOP_BACK_LEFT |
|
||||
SL_SPEAKER_TOP_BACK_RIGHT;
|
||||
case DevFmtX7144:
|
||||
case DevFmtAmbi3D:
|
||||
break;
|
||||
}
|
||||
|
|
@ -159,12 +165,10 @@ struct OpenSLPlayback final : public BackendBase {
|
|||
~OpenSLPlayback() override;
|
||||
|
||||
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
||||
static void processC(SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
|
||||
{ static_cast<OpenSLPlayback*>(context)->process(bq); }
|
||||
|
||||
int mixerProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -189,8 +193,6 @@ struct OpenSLPlayback final : public BackendBase {
|
|||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(OpenSLPlayback)
|
||||
};
|
||||
|
||||
OpenSLPlayback::~OpenSLPlayback()
|
||||
|
|
@ -229,7 +231,7 @@ void OpenSLPlayback::process(SLAndroidSimpleBufferQueueItf) noexcept
|
|||
int OpenSLPlayback::mixerProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
SLPlayItf player;
|
||||
SLAndroidSimpleBufferQueueItf bufferQueue;
|
||||
|
|
@ -312,13 +314,13 @@ int OpenSLPlayback::mixerProc()
|
|||
}
|
||||
|
||||
|
||||
void OpenSLPlayback::open(const char *name)
|
||||
void OpenSLPlayback::open(std::string_view name)
|
||||
{
|
||||
if(!name)
|
||||
name = opensl_device;
|
||||
else if(strcmp(name, opensl_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
if(name.empty())
|
||||
name = GetDeviceName();
|
||||
else if(name != GetDeviceName())
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
|
||||
/* There's only one device, so if it's already open, there's nothing to do. */
|
||||
if(mEngineObj) return;
|
||||
|
|
@ -375,74 +377,6 @@ bool OpenSLPlayback::reset()
|
|||
|
||||
mRing = nullptr;
|
||||
|
||||
#if 0
|
||||
if(!mDevice->Flags.get<FrequencyRequest>())
|
||||
{
|
||||
/* FIXME: Disabled until I figure out how to get the Context needed for
|
||||
* the getSystemService call.
|
||||
*/
|
||||
JNIEnv *env = Android_GetJNIEnv();
|
||||
jobject jctx = Android_GetContext();
|
||||
|
||||
/* Get necessary stuff for using java.lang.Integer,
|
||||
* android.content.Context, and android.media.AudioManager.
|
||||
*/
|
||||
jclass int_cls = JCALL(env,FindClass)("java/lang/Integer");
|
||||
jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls,
|
||||
"parseInt", "(Ljava/lang/String;)I"
|
||||
);
|
||||
TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint);
|
||||
|
||||
jclass ctx_cls = JCALL(env,FindClass)("android/content/Context");
|
||||
jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls,
|
||||
"AUDIO_SERVICE", "Ljava/lang/String;"
|
||||
);
|
||||
jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls,
|
||||
"getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"
|
||||
);
|
||||
TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n",
|
||||
ctx_cls, ctx_audsvc, ctx_getSysSvc);
|
||||
|
||||
jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager");
|
||||
jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls,
|
||||
"PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;"
|
||||
);
|
||||
jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls,
|
||||
"getProperty", "(Ljava/lang/String;)Ljava/lang/String;"
|
||||
);
|
||||
TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n",
|
||||
audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty);
|
||||
|
||||
const char *strchars;
|
||||
jstring strobj;
|
||||
|
||||
/* Now make the calls. */
|
||||
//AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
|
||||
strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc);
|
||||
jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj);
|
||||
strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
|
||||
TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
|
||||
JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
|
||||
|
||||
//String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
|
||||
strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate);
|
||||
jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj);
|
||||
strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
|
||||
TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr);
|
||||
JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
|
||||
|
||||
//int sampleRate = Integer.parseInt(srateStr);
|
||||
sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr);
|
||||
|
||||
strchars = JCALL(env,GetStringUTFChars)(srateStr, nullptr);
|
||||
TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars);
|
||||
JCALL(env,ReleaseStringUTFChars)(srateStr, strchars);
|
||||
|
||||
if(!sampleRate) sampleRate = device->Frequency;
|
||||
else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
|
||||
}
|
||||
#endif
|
||||
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
mDevice->FmtType = DevFmtShort;
|
||||
|
||||
|
|
@ -564,7 +498,9 @@ void OpenSLPlayback::start()
|
|||
PrintErr(result, "bufferQueue->GetInterface");
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(bufferQueue,RegisterCallback)(&OpenSLPlayback::processC, this);
|
||||
result = VCALL(bufferQueue,RegisterCallback)(
|
||||
[](SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
|
||||
{ static_cast<OpenSLPlayback*>(context)->process(bq); }, this);
|
||||
PrintErr(result, "bufferQueue->RegisterCallback");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS != result)
|
||||
|
|
@ -628,8 +564,8 @@ ClockLatency OpenSLPlayback::getClockLatency()
|
|||
{
|
||||
ClockLatency ret;
|
||||
|
||||
std::lock_guard<std::mutex> _{mMutex};
|
||||
ret.ClockTime = GetDeviceClockTime(mDevice);
|
||||
std::lock_guard<std::mutex> dlock{mMutex};
|
||||
ret.ClockTime = mDevice->getClockTime();
|
||||
ret.Latency = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize};
|
||||
ret.Latency /= mDevice->Frequency;
|
||||
|
||||
|
|
@ -642,13 +578,11 @@ struct OpenSLCapture final : public BackendBase {
|
|||
~OpenSLCapture() override;
|
||||
|
||||
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
||||
static void processC(SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
|
||||
{ static_cast<OpenSLCapture*>(context)->process(bq); }
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
void captureSamples(std::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
|
||||
/* engine interfaces */
|
||||
|
|
@ -662,8 +596,6 @@ struct OpenSLCapture final : public BackendBase {
|
|||
uint mSplOffset{0u};
|
||||
|
||||
uint mFrameSize{0};
|
||||
|
||||
DEF_NEWDEL(OpenSLCapture)
|
||||
};
|
||||
|
||||
OpenSLCapture::~OpenSLCapture()
|
||||
|
|
@ -686,13 +618,13 @@ void OpenSLCapture::process(SLAndroidSimpleBufferQueueItf) noexcept
|
|||
}
|
||||
|
||||
|
||||
void OpenSLCapture::open(const char* name)
|
||||
void OpenSLCapture::open(std::string_view name)
|
||||
{
|
||||
if(!name)
|
||||
name = opensl_device;
|
||||
else if(strcmp(name, opensl_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
if(name.empty())
|
||||
name = GetDeviceName();
|
||||
else if(name != GetDeviceName())
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
|
||||
SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
|
||||
PrintErr(result, "slCreateEngine");
|
||||
|
|
@ -710,10 +642,10 @@ void OpenSLCapture::open(const char* name)
|
|||
{
|
||||
mFrameSize = mDevice->frameSizeFromFmt();
|
||||
/* Ensure the total length is at least 100ms */
|
||||
uint length{maxu(mDevice->BufferSize, mDevice->Frequency/10)};
|
||||
uint length{std::max(mDevice->BufferSize, mDevice->Frequency/10u)};
|
||||
/* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
|
||||
uint update_len{clampu(mDevice->BufferSize/3, mDevice->Frequency/100,
|
||||
mDevice->Frequency/100*5)};
|
||||
uint update_len{std::clamp(mDevice->BufferSize/3u, mDevice->Frequency/100u,
|
||||
mDevice->Frequency/100u*5u)};
|
||||
uint num_updates{(length+update_len-1) / update_len};
|
||||
|
||||
mRing = RingBuffer::Create(num_updates, update_len*mFrameSize, false);
|
||||
|
|
@ -813,13 +745,15 @@ void OpenSLCapture::open(const char* name)
|
|||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
result = VCALL(bufferQueue,RegisterCallback)(&OpenSLCapture::processC, this);
|
||||
result = VCALL(bufferQueue,RegisterCallback)(
|
||||
[](SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
|
||||
{ static_cast<OpenSLCapture*>(context)->process(bq); }, this);
|
||||
PrintErr(result, "bufferQueue->RegisterCallback");
|
||||
}
|
||||
if(SL_RESULT_SUCCESS == result)
|
||||
{
|
||||
const uint chunk_size{mDevice->UpdateSize * mFrameSize};
|
||||
const auto silence = (mDevice->FmtType == DevFmtUByte) ? al::byte{0x80} : al::byte{0};
|
||||
const auto silence = (mDevice->FmtType == DevFmtUByte) ? std::byte{0x80} : std::byte{0};
|
||||
|
||||
auto data = mRing->getWriteVector();
|
||||
std::fill_n(data.first.buf, data.first.len*chunk_size, silence);
|
||||
|
|
@ -883,7 +817,7 @@ void OpenSLCapture::stop()
|
|||
}
|
||||
}
|
||||
|
||||
void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
|
||||
void OpenSLCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
{
|
||||
const uint update_size{mDevice->UpdateSize};
|
||||
const uint chunk_size{update_size * mFrameSize};
|
||||
|
|
@ -895,7 +829,7 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
|
|||
auto rdata = mRing->getReadVector();
|
||||
for(uint i{0};i < samples;)
|
||||
{
|
||||
const uint rem{minu(samples - i, update_size - mSplOffset)};
|
||||
const uint rem{std::min(samples - i, update_size - mSplOffset)};
|
||||
std::copy_n(rdata.first.buf + mSplOffset*size_t{mFrameSize}, rem*size_t{mFrameSize},
|
||||
buffer + i*size_t{mFrameSize});
|
||||
|
||||
|
|
@ -932,7 +866,7 @@ void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
|
|||
return;
|
||||
|
||||
/* For each buffer chunk that was fully read, queue another writable buffer
|
||||
* chunk to keep the OpenSL queue full. This is rather convulated, as a
|
||||
* chunk to keep the OpenSL queue full. This is rather convoluted, as a
|
||||
* result of the ring buffer holding more elements than are writable at a
|
||||
* given time. The end of the write vector increments when the read pointer
|
||||
* advances, which will "expose" a previously unwritable element. So for
|
||||
|
|
@ -975,18 +909,15 @@ bool OSLBackendFactory::init() { return true; }
|
|||
bool OSLBackendFactory::querySupport(BackendType type)
|
||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||
|
||||
std::string OSLBackendFactory::probe(BackendType type)
|
||||
auto OSLBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
case BackendType::Capture:
|
||||
/* Includes null char. */
|
||||
outnames.append(opensl_device, sizeof(opensl_device));
|
||||
break;
|
||||
return std::vector{std::string{GetDeviceName()}};
|
||||
}
|
||||
return outnames;
|
||||
return {};
|
||||
}
|
||||
|
||||
BackendPtr OSLBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct OSLBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_OSL_H */
|
||||
|
|
|
|||
|
|
@ -31,27 +31,26 @@
|
|||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "alstring.h"
|
||||
#include "althrd_setname.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "vector.h"
|
||||
|
||||
#include <sys/soundcard.h>
|
||||
|
||||
|
|
@ -83,117 +82,148 @@
|
|||
|
||||
namespace {
|
||||
|
||||
constexpr char DefaultName[] = "OSS Default";
|
||||
std::string DefaultPlayback{"/dev/dsp"};
|
||||
std::string DefaultCapture{"/dev/dsp"};
|
||||
using namespace std::string_literals;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "OSS Default"sv; }
|
||||
|
||||
std::string DefaultPlayback{"/dev/dsp"s};
|
||||
std::string DefaultCapture{"/dev/dsp"s};
|
||||
|
||||
struct DevMap {
|
||||
std::string name;
|
||||
std::string device_name;
|
||||
|
||||
template<typename T, typename U>
|
||||
DevMap(T&& name_, U&& devname_)
|
||||
: name{std::forward<T>(name_)}, device_name{std::forward<U>(devname_)}
|
||||
{ }
|
||||
};
|
||||
|
||||
al::vector<DevMap> PlaybackDevices;
|
||||
al::vector<DevMap> CaptureDevices;
|
||||
std::vector<DevMap> PlaybackDevices;
|
||||
std::vector<DevMap> CaptureDevices;
|
||||
|
||||
|
||||
#ifdef ALC_OSS_COMPAT
|
||||
|
||||
#define DSP_CAP_OUTPUT 0x00020000
|
||||
#define DSP_CAP_INPUT 0x00010000
|
||||
void ALCossListPopulate(al::vector<DevMap> &devlist, int type)
|
||||
void ALCossListPopulate(std::vector<DevMap> &devlist, int type)
|
||||
{
|
||||
devlist.emplace_back(DevMap{DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback});
|
||||
devlist.emplace_back(GetDefaultName(), (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void ALCossListAppend(al::vector<DevMap> &list, al::span<const char> handle, al::span<const char> path)
|
||||
class FileHandle {
|
||||
int mFd{-1};
|
||||
|
||||
public:
|
||||
FileHandle() = default;
|
||||
FileHandle(const FileHandle&) = delete;
|
||||
FileHandle& operator=(const FileHandle&) = delete;
|
||||
~FileHandle() { if(mFd != -1) ::close(mFd); }
|
||||
|
||||
template<typename ...Args>
|
||||
[[nodiscard]] auto open(const char *fname, Args&& ...args) -> bool
|
||||
{
|
||||
close();
|
||||
mFd = ::open(fname, std::forward<Args>(args)...);
|
||||
return mFd != -1;
|
||||
}
|
||||
void close()
|
||||
{
|
||||
if(mFd != -1)
|
||||
::close(mFd);
|
||||
mFd = -1;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto get() const noexcept -> int { return mFd; }
|
||||
};
|
||||
|
||||
void ALCossListAppend(std::vector<DevMap> &list, std::string_view handle, std::string_view path)
|
||||
{
|
||||
#ifdef ALC_OSS_DEVNODE_TRUC
|
||||
for(size_t i{0};i < path.size();++i)
|
||||
{
|
||||
if(path[i] == '.' && handle.size() + i >= path.size())
|
||||
if(path[i] == '.' && handle.size() >= path.size() - i)
|
||||
{
|
||||
const size_t hoffset{handle.size() + i - path.size()};
|
||||
if(strncmp(path.data() + i, handle.data() + hoffset, path.size() - i) == 0)
|
||||
handle = handle.first(hoffset);
|
||||
path = path.first(i);
|
||||
handle = handle.substr(0, hoffset);
|
||||
path = path.substr(0, i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(handle.empty())
|
||||
handle = path;
|
||||
|
||||
std::string basename{handle.data(), handle.size()};
|
||||
std::string devname{path.data(), path.size()};
|
||||
|
||||
auto match_devname = [&devname](const DevMap &entry) -> bool
|
||||
{ return entry.device_name == devname; };
|
||||
auto match_devname = [path](const DevMap &entry) -> bool
|
||||
{ return entry.device_name == path; };
|
||||
if(std::find_if(list.cbegin(), list.cend(), match_devname) != list.cend())
|
||||
return;
|
||||
|
||||
auto checkName = [&list](const std::string &name) -> bool
|
||||
auto checkName = [&list](const std::string_view name) -> bool
|
||||
{
|
||||
auto match_name = [&name](const DevMap &entry) -> bool { return entry.name == name; };
|
||||
auto match_name = [name](const DevMap &entry) -> bool { return entry.name == name; };
|
||||
return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
|
||||
};
|
||||
int count{1};
|
||||
std::string newname{basename};
|
||||
std::string newname{handle};
|
||||
while(checkName(newname))
|
||||
{
|
||||
newname = basename;
|
||||
newname = handle;
|
||||
newname += " #";
|
||||
newname += std::to_string(++count);
|
||||
}
|
||||
|
||||
list.emplace_back(DevMap{std::move(newname), std::move(devname)});
|
||||
const DevMap &entry = list.back();
|
||||
const DevMap &entry = list.emplace_back(std::move(newname), path);
|
||||
|
||||
TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str());
|
||||
}
|
||||
|
||||
void ALCossListPopulate(al::vector<DevMap> &devlist, int type_flag)
|
||||
void ALCossListPopulate(std::vector<DevMap> &devlist, int type_flag)
|
||||
{
|
||||
int fd{open("/dev/mixer", O_RDONLY)};
|
||||
if(fd < 0)
|
||||
oss_sysinfo si{};
|
||||
FileHandle file;
|
||||
if(!file.open("/dev/mixer", O_RDONLY))
|
||||
{
|
||||
TRACE("Could not open /dev/mixer: %s\n", strerror(errno));
|
||||
TRACE("Could not open /dev/mixer: %s\n", std::generic_category().message(errno).c_str());
|
||||
goto done;
|
||||
}
|
||||
|
||||
oss_sysinfo si;
|
||||
if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1)
|
||||
if(ioctl(file.get(), SNDCTL_SYSINFO, &si) == -1)
|
||||
{
|
||||
TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno));
|
||||
TRACE("SNDCTL_SYSINFO failed: %s\n", std::generic_category().message(errno).c_str());
|
||||
goto done;
|
||||
}
|
||||
|
||||
for(int i{0};i < si.numaudios;i++)
|
||||
{
|
||||
oss_audioinfo ai;
|
||||
oss_audioinfo ai{};
|
||||
ai.dev = i;
|
||||
if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1)
|
||||
if(ioctl(file.get(), SNDCTL_AUDIOINFO, &ai) == -1)
|
||||
{
|
||||
ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno));
|
||||
ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i,
|
||||
std::generic_category().message(errno).c_str());
|
||||
continue;
|
||||
}
|
||||
if(!(ai.caps&type_flag) || ai.devnode[0] == '\0')
|
||||
continue;
|
||||
|
||||
al::span<const char> handle;
|
||||
std::string_view handle;
|
||||
if(ai.handle[0] != '\0')
|
||||
handle = {ai.handle, strnlen(ai.handle, sizeof(ai.handle))};
|
||||
else
|
||||
handle = {ai.name, strnlen(ai.name, sizeof(ai.name))};
|
||||
al::span<const char> devnode{ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode))};
|
||||
const std::string_view devnode{ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode))};
|
||||
|
||||
ALCossListAppend(devlist, handle, devnode);
|
||||
}
|
||||
|
||||
done:
|
||||
if(fd >= 0)
|
||||
close(fd);
|
||||
fd = -1;
|
||||
file.close();
|
||||
|
||||
const char *defdev{((type_flag==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback).c_str()};
|
||||
auto iter = std::find_if(devlist.cbegin(), devlist.cend(),
|
||||
|
|
@ -201,7 +231,7 @@ done:
|
|||
{ return entry.device_name == defdev; }
|
||||
);
|
||||
if(iter == devlist.cend())
|
||||
devlist.insert(devlist.begin(), DevMap{DefaultName, defdev});
|
||||
devlist.insert(devlist.begin(), DevMap{GetDefaultName(), defdev});
|
||||
else
|
||||
{
|
||||
DevMap entry{std::move(*iter)};
|
||||
|
|
@ -231,19 +261,17 @@ struct OSSPlayback final : public BackendBase {
|
|||
|
||||
int mixerProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
int mFd{-1};
|
||||
|
||||
al::vector<al::byte> mMixData;
|
||||
std::vector<std::byte> mMixData;
|
||||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(OSSPlayback)
|
||||
};
|
||||
|
||||
OSSPlayback::~OSSPlayback()
|
||||
|
|
@ -257,7 +285,7 @@ OSSPlayback::~OSSPlayback()
|
|||
int OSSPlayback::mixerProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
const size_t frame_step{mDevice->channelsFromFmt()};
|
||||
const size_t frame_size{mDevice->frameSizeFromFmt()};
|
||||
|
|
@ -269,38 +297,38 @@ int OSSPlayback::mixerProc()
|
|||
pollitem.fd = mFd;
|
||||
pollitem.events = POLLOUT;
|
||||
|
||||
int pret{poll(&pollitem, 1, 1000)};
|
||||
if(pret < 0)
|
||||
if(int pret{poll(&pollitem, 1, 1000)}; pret < 0)
|
||||
{
|
||||
if(errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
ERR("poll failed: %s\n", strerror(errno));
|
||||
mDevice->handleDisconnect("Failed waiting for playback buffer: %s", strerror(errno));
|
||||
const auto errstr = std::generic_category().message(errno);
|
||||
ERR("poll failed: %s\n", errstr.c_str());
|
||||
mDevice->handleDisconnect("Failed waiting for playback buffer: %s", errstr.c_str());
|
||||
break;
|
||||
}
|
||||
else if(pret == 0)
|
||||
else if(pret == 0) /* NOLINT(*-else-after-return) 'pret' is local to the if/else blocks */
|
||||
{
|
||||
WARN("poll timeout\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
al::byte *write_ptr{mMixData.data()};
|
||||
size_t to_write{mMixData.size()};
|
||||
mDevice->renderSamples(write_ptr, static_cast<uint>(to_write/frame_size), frame_step);
|
||||
while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
|
||||
al::span write_buf{mMixData};
|
||||
mDevice->renderSamples(write_buf.data(), static_cast<uint>(write_buf.size()/frame_size),
|
||||
frame_step);
|
||||
while(!write_buf.empty() && !mKillNow.load(std::memory_order_acquire))
|
||||
{
|
||||
ssize_t wrote{write(mFd, write_ptr, to_write)};
|
||||
ssize_t wrote{write(mFd, write_buf.data(), write_buf.size())};
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||
continue;
|
||||
ERR("write failed: %s\n", strerror(errno));
|
||||
mDevice->handleDisconnect("Failed writing playback samples: %s", strerror(errno));
|
||||
const auto errstr = std::generic_category().message(errno);
|
||||
ERR("write failed: %s\n", errstr.c_str());
|
||||
mDevice->handleDisconnect("Failed writing playback samples: %s", errstr.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
to_write -= static_cast<size_t>(wrote);
|
||||
write_ptr += wrote;
|
||||
write_buf = write_buf.subspan(static_cast<size_t>(wrote));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,11 +336,11 @@ int OSSPlayback::mixerProc()
|
|||
}
|
||||
|
||||
|
||||
void OSSPlayback::open(const char *name)
|
||||
void OSSPlayback::open(std::string_view name)
|
||||
{
|
||||
const char *devname{DefaultPlayback.c_str()};
|
||||
if(!name)
|
||||
name = DefaultName;
|
||||
if(name.empty())
|
||||
name = GetDefaultName();
|
||||
else
|
||||
{
|
||||
if(PlaybackDevices.empty())
|
||||
|
|
@ -324,14 +352,14 @@ void OSSPlayback::open(const char *name)
|
|||
);
|
||||
if(iter == PlaybackDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
devname = iter->device_name.c_str();
|
||||
}
|
||||
|
||||
int fd{::open(devname, O_WRONLY)};
|
||||
if(fd == -1)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname,
|
||||
strerror(errno)};
|
||||
std::generic_category().message(errno).c_str()};
|
||||
|
||||
if(mFd != -1)
|
||||
::close(mFd);
|
||||
|
|
@ -367,15 +395,14 @@ bool OSSPlayback::reset()
|
|||
uint ossSpeed{mDevice->Frequency};
|
||||
uint frameSize{numChannels * mDevice->bytesFromFmt()};
|
||||
/* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */
|
||||
uint log2FragmentSize{maxu(log2i(mDevice->UpdateSize*frameSize), 4)};
|
||||
uint log2FragmentSize{std::max(log2i(mDevice->UpdateSize*frameSize), 4u)};
|
||||
uint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
|
||||
|
||||
audio_buf_info info{};
|
||||
const char *err;
|
||||
#define CHECKERR(func) if((func) < 0) { \
|
||||
err = #func; \
|
||||
goto err; \
|
||||
}
|
||||
#define CHECKERR(func) if((func) < 0) \
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "%s failed: %s\n", #func, \
|
||||
std::generic_category().message(errno).c_str()};
|
||||
|
||||
/* Don't fail if SETFRAGMENT fails. We can handle just about anything
|
||||
* that's reported back via GETOSPACE */
|
||||
ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
|
||||
|
|
@ -383,12 +410,6 @@ bool OSSPlayback::reset()
|
|||
CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels));
|
||||
CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed));
|
||||
CHECKERR(ioctl(mFd, SNDCTL_DSP_GETOSPACE, &info));
|
||||
if(0)
|
||||
{
|
||||
err:
|
||||
ERR("%s failed: %s\n", err, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
#undef CHECKERR
|
||||
|
||||
if(mDevice->channelsFromFmt() != numChannels)
|
||||
|
|
@ -413,7 +434,7 @@ bool OSSPlayback::reset()
|
|||
|
||||
setDefaultChannelOrder();
|
||||
|
||||
mMixData.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt());
|
||||
mMixData.resize(size_t{mDevice->UpdateSize} * mDevice->frameSizeFromFmt());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -437,7 +458,7 @@ void OSSPlayback::stop()
|
|||
mThread.join();
|
||||
|
||||
if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
|
||||
ERR("Error resetting device: %s\n", strerror(errno));
|
||||
ERR("Error resetting device: %s\n", std::generic_category().message(errno).c_str());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -447,10 +468,10 @@ struct OSScapture final : public BackendBase {
|
|||
|
||||
int recordProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
void captureSamples(std::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
|
||||
int mFd{-1};
|
||||
|
|
@ -459,8 +480,6 @@ struct OSScapture final : public BackendBase {
|
|||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(OSScapture)
|
||||
};
|
||||
|
||||
OSScapture::~OSScapture()
|
||||
|
|
@ -474,7 +493,7 @@ OSScapture::~OSScapture()
|
|||
int OSScapture::recordProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(RECORD_THREAD_NAME);
|
||||
althrd_setname(GetRecordThreadName());
|
||||
|
||||
const size_t frame_size{mDevice->frameSizeFromFmt()};
|
||||
while(!mKillNow.load(std::memory_order_acquire))
|
||||
|
|
@ -483,16 +502,16 @@ int OSScapture::recordProc()
|
|||
pollitem.fd = mFd;
|
||||
pollitem.events = POLLIN;
|
||||
|
||||
int sret{poll(&pollitem, 1, 1000)};
|
||||
if(sret < 0)
|
||||
if(int pret{poll(&pollitem, 1, 1000)}; pret < 0)
|
||||
{
|
||||
if(errno == EINTR || errno == EAGAIN)
|
||||
continue;
|
||||
ERR("poll failed: %s\n", strerror(errno));
|
||||
mDevice->handleDisconnect("Failed to check capture samples: %s", strerror(errno));
|
||||
const auto errstr = std::generic_category().message(errno);
|
||||
ERR("poll failed: %s\n", errstr.c_str());
|
||||
mDevice->handleDisconnect("Failed to check capture samples: %s", errstr.c_str());
|
||||
break;
|
||||
}
|
||||
else if(sret == 0)
|
||||
else if(pret == 0) /* NOLINT(*-else-after-return) 'pret' is local to the if/else blocks */
|
||||
{
|
||||
WARN("poll timeout\n");
|
||||
continue;
|
||||
|
|
@ -504,8 +523,9 @@ int OSScapture::recordProc()
|
|||
ssize_t amt{read(mFd, vec.first.buf, vec.first.len*frame_size)};
|
||||
if(amt < 0)
|
||||
{
|
||||
ERR("read failed: %s\n", strerror(errno));
|
||||
mDevice->handleDisconnect("Failed reading capture samples: %s", strerror(errno));
|
||||
const auto errstr = std::generic_category().message(errno);
|
||||
ERR("read failed: %s\n", errstr.c_str());
|
||||
mDevice->handleDisconnect("Failed reading capture samples: %s", errstr.c_str());
|
||||
break;
|
||||
}
|
||||
mRing->writeAdvance(static_cast<size_t>(amt)/frame_size);
|
||||
|
|
@ -516,11 +536,11 @@ int OSScapture::recordProc()
|
|||
}
|
||||
|
||||
|
||||
void OSScapture::open(const char *name)
|
||||
void OSScapture::open(std::string_view name)
|
||||
{
|
||||
const char *devname{DefaultCapture.c_str()};
|
||||
if(!name)
|
||||
name = DefaultName;
|
||||
if(name.empty())
|
||||
name = GetDefaultName();
|
||||
else
|
||||
{
|
||||
if(CaptureDevices.empty())
|
||||
|
|
@ -532,14 +552,14 @@ void OSScapture::open(const char *name)
|
|||
);
|
||||
if(iter == CaptureDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%s\" not found", name};
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
devname = iter->device_name.c_str();
|
||||
}
|
||||
|
||||
mFd = ::open(devname, O_RDONLY);
|
||||
if(mFd == -1)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname,
|
||||
strerror(errno)};
|
||||
std::generic_category().message(errno).c_str()};
|
||||
|
||||
int ossFormat{};
|
||||
switch(mDevice->FmtType)
|
||||
|
|
@ -566,13 +586,13 @@ void OSScapture::open(const char *name)
|
|||
uint frameSize{numChannels * mDevice->bytesFromFmt()};
|
||||
uint ossSpeed{mDevice->Frequency};
|
||||
/* according to the OSS spec, 16 bytes are the minimum */
|
||||
uint log2FragmentSize{maxu(log2i(mDevice->BufferSize * frameSize / periods), 4)};
|
||||
uint log2FragmentSize{std::max(log2i(mDevice->BufferSize * frameSize / periods), 4u)};
|
||||
uint numFragmentsLogSize{(periods << 16) | log2FragmentSize};
|
||||
|
||||
audio_buf_info info{};
|
||||
#define CHECKERR(func) if((func) < 0) { \
|
||||
throw al::backend_exception{al::backend_error::DeviceError, #func " failed: %s", \
|
||||
strerror(errno)}; \
|
||||
std::generic_category().message(errno).c_str()}; \
|
||||
}
|
||||
CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
|
||||
CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat));
|
||||
|
|
@ -617,11 +637,11 @@ void OSScapture::stop()
|
|||
mThread.join();
|
||||
|
||||
if(ioctl(mFd, SNDCTL_DSP_RESET) != 0)
|
||||
ERR("Error resetting device: %s\n", strerror(errno));
|
||||
ERR("Error resetting device: %s\n", std::generic_category().message(errno).c_str());
|
||||
}
|
||||
|
||||
void OSScapture::captureSamples(al::byte *buffer, uint samples)
|
||||
{ mRing->read(buffer, samples); }
|
||||
void OSScapture::captureSamples(std::byte *buffer, uint samples)
|
||||
{ std::ignore = mRing->read(buffer, samples); }
|
||||
|
||||
uint OSScapture::availableSamples()
|
||||
{ return static_cast<uint>(mRing->readSpace()); }
|
||||
|
|
@ -637,9 +657,9 @@ BackendFactory &OSSBackendFactory::getFactory()
|
|||
|
||||
bool OSSBackendFactory::init()
|
||||
{
|
||||
if(auto devopt = ConfigValueStr(nullptr, "oss", "device"))
|
||||
if(auto devopt = ConfigValueStr({}, "oss", "device"))
|
||||
DefaultPlayback = std::move(*devopt);
|
||||
if(auto capopt = ConfigValueStr(nullptr, "oss", "capture"))
|
||||
if(auto capopt = ConfigValueStr({}, "oss", "capture"))
|
||||
DefaultCapture = std::move(*capopt);
|
||||
|
||||
return true;
|
||||
|
|
@ -648,18 +668,13 @@ bool OSSBackendFactory::init()
|
|||
bool OSSBackendFactory::querySupport(BackendType type)
|
||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||
|
||||
std::string OSSBackendFactory::probe(BackendType type)
|
||||
auto OSSBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
|
||||
std::vector<std::string> outnames;
|
||||
auto add_device = [&outnames](const DevMap &entry) -> void
|
||||
{
|
||||
struct stat buf;
|
||||
if(stat(entry.device_name.c_str(), &buf) == 0)
|
||||
{
|
||||
/* Includes null char. */
|
||||
outnames.append(entry.name.c_str(), entry.name.length()+1);
|
||||
}
|
||||
if(struct stat buf{}; stat(entry.device_name.c_str(), &buf) == 0)
|
||||
outnames.emplace_back(entry.name);
|
||||
};
|
||||
|
||||
switch(type)
|
||||
|
|
@ -667,12 +682,14 @@ std::string OSSBackendFactory::probe(BackendType type)
|
|||
case BackendType::Playback:
|
||||
PlaybackDevices.clear();
|
||||
ALCossListPopulate(PlaybackDevices, DSP_CAP_OUTPUT);
|
||||
outnames.reserve(PlaybackDevices.size());
|
||||
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
||||
break;
|
||||
|
||||
case BackendType::Capture:
|
||||
CaptureDevices.clear();
|
||||
ALCossListPopulate(CaptureDevices, DSP_CAP_INPUT);
|
||||
outnames.reserve(CaptureDevices.size());
|
||||
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct OSSBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_OSS_H */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,22 +2,26 @@
|
|||
#define BACKENDS_PIPEWIRE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "alc/events.h"
|
||||
#include "base.h"
|
||||
|
||||
struct DeviceBase;
|
||||
|
||||
struct PipeWireBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_PIPEWIRE_H */
|
||||
|
|
|
|||
|
|
@ -26,21 +26,20 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "albit.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alstring.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include <portaudio.h>
|
||||
#include <portaudio.h> /* NOLINT(*-duplicate-include) Not the same header. */
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char pa_device[] = "PortAudio Default";
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
void *pa_handle;
|
||||
#define MAKE_FUNC(x) decltype(x) * p##x
|
||||
|
|
@ -51,6 +50,8 @@ MAKE_FUNC(Pa_StartStream);
|
|||
MAKE_FUNC(Pa_StopStream);
|
||||
MAKE_FUNC(Pa_OpenStream);
|
||||
MAKE_FUNC(Pa_CloseStream);
|
||||
MAKE_FUNC(Pa_GetDeviceCount);
|
||||
MAKE_FUNC(Pa_GetDeviceInfo);
|
||||
MAKE_FUNC(Pa_GetDefaultOutputDevice);
|
||||
MAKE_FUNC(Pa_GetDefaultInputDevice);
|
||||
MAKE_FUNC(Pa_GetStreamInfo);
|
||||
|
|
@ -64,12 +65,49 @@ MAKE_FUNC(Pa_GetStreamInfo);
|
|||
#define Pa_StopStream pPa_StopStream
|
||||
#define Pa_OpenStream pPa_OpenStream
|
||||
#define Pa_CloseStream pPa_CloseStream
|
||||
#define Pa_GetDeviceCount pPa_GetDeviceCount
|
||||
#define Pa_GetDeviceInfo pPa_GetDeviceInfo
|
||||
#define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice
|
||||
#define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice
|
||||
#define Pa_GetStreamInfo pPa_GetStreamInfo
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct DeviceEntry {
|
||||
std::string mName;
|
||||
bool mIsPlayback{};
|
||||
bool mIsCapture{};
|
||||
};
|
||||
std::vector<DeviceEntry> DeviceNames;
|
||||
|
||||
void EnumerateDevices()
|
||||
{
|
||||
const auto devcount = Pa_GetDeviceCount();
|
||||
if(devcount < 0)
|
||||
{
|
||||
ERR("Error getting device count: %s\n", Pa_GetErrorText(devcount));
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<DeviceEntry>(static_cast<uint>(devcount)).swap(DeviceNames);
|
||||
PaDeviceIndex idx{0};
|
||||
for(auto &entry : DeviceNames)
|
||||
{
|
||||
if(auto info = Pa_GetDeviceInfo(idx); info && info->name)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
entry.mName = "OpenAL Soft on "+std::string{info->name};
|
||||
#else
|
||||
entry.mName = info->name;
|
||||
#endif
|
||||
entry.mIsPlayback = (info->maxOutputChannels > 0);
|
||||
entry.mIsCapture = (info->maxInputChannels > 0);
|
||||
TRACE("Device %d \"%s\": %d playback, %d capture channels\n", idx, entry.mName.c_str(),
|
||||
info->maxOutputChannels, info->maxInputChannels);
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
|
||||
struct PortPlayback final : public BackendBase {
|
||||
PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
|
|
@ -77,24 +115,17 @@ struct PortPlayback final : public BackendBase {
|
|||
|
||||
int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
||||
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept;
|
||||
static int writeCallbackC(const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||
const PaStreamCallbackFlags statusFlags, void *userData) noexcept
|
||||
{
|
||||
return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
|
||||
framesPerBuffer, timeInfo, statusFlags);
|
||||
}
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
PaStream *mStream{nullptr};
|
||||
PaStreamParameters mParams{};
|
||||
DevFmtChannels mChannelConfig{};
|
||||
uint mAmbiOrder{};
|
||||
uint mUpdateSize{0u};
|
||||
|
||||
DEF_NEWDEL(PortPlayback)
|
||||
};
|
||||
|
||||
PortPlayback::~PortPlayback()
|
||||
|
|
@ -115,22 +146,38 @@ int PortPlayback::writeCallback(const void*, void *outputBuffer, unsigned long f
|
|||
}
|
||||
|
||||
|
||||
void PortPlayback::open(const char *name)
|
||||
void PortPlayback::open(std::string_view name)
|
||||
{
|
||||
if(!name)
|
||||
name = pa_device;
|
||||
else if(strcmp(name, pa_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
if(DeviceNames.empty())
|
||||
EnumerateDevices();
|
||||
|
||||
int deviceid{-1};
|
||||
if(name.empty())
|
||||
{
|
||||
if(auto devidopt = ConfigValueInt({}, "port", "device"))
|
||||
deviceid = *devidopt;
|
||||
if(deviceid < 0 || static_cast<uint>(deviceid) >= DeviceNames.size())
|
||||
deviceid = Pa_GetDefaultOutputDevice();
|
||||
name = DeviceNames.at(static_cast<uint>(deviceid)).mName;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(),
|
||||
[name](const DeviceEntry &entry) { return entry.mIsPlayback && name == entry.mName; });
|
||||
if(iter == DeviceNames.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
deviceid = static_cast<int>(std::distance(DeviceNames.cbegin(), iter));
|
||||
}
|
||||
|
||||
PaStreamParameters params{};
|
||||
auto devidopt = ConfigValueInt(nullptr, "port", "device");
|
||||
if(devidopt && *devidopt >= 0) params.device = *devidopt;
|
||||
else params.device = Pa_GetDefaultOutputDevice();
|
||||
params.device = deviceid;
|
||||
params.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
|
||||
params.hostApiSpecificStreamInfo = nullptr;
|
||||
|
||||
params.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
mChannelConfig = mDevice->FmtChans;
|
||||
mAmbiOrder = mDevice->mAmbiOrder;
|
||||
params.channelCount = static_cast<int>(mDevice->channelsFromFmt());
|
||||
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
|
|
@ -155,19 +202,21 @@ void PortPlayback::open(const char *name)
|
|||
break;
|
||||
}
|
||||
|
||||
retry_open:
|
||||
PaStream *stream{};
|
||||
PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency, mDevice->UpdateSize,
|
||||
paNoFlag, &PortPlayback::writeCallbackC, this)};
|
||||
if(err != paNoError)
|
||||
static constexpr auto writeCallback = [](const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||
const PaStreamCallbackFlags statusFlags, void *userData) noexcept
|
||||
{
|
||||
if(params.sampleFormat == paFloat32)
|
||||
{
|
||||
params.sampleFormat = paInt16;
|
||||
goto retry_open;
|
||||
}
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
|
||||
Pa_GetErrorText(err)};
|
||||
return static_cast<PortPlayback*>(userData)->writeCallback(inputBuffer, outputBuffer,
|
||||
framesPerBuffer, timeInfo, statusFlags);
|
||||
};
|
||||
PaStream *stream{};
|
||||
while(PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency,
|
||||
mDevice->UpdateSize, paNoFlag, writeCallback, this)})
|
||||
{
|
||||
if(params.sampleFormat != paFloat32)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
|
||||
Pa_GetErrorText(err)};
|
||||
params.sampleFormat = paInt16;
|
||||
}
|
||||
|
||||
Pa_CloseStream(mStream);
|
||||
|
|
@ -182,7 +231,18 @@ bool PortPlayback::reset()
|
|||
{
|
||||
const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)};
|
||||
mDevice->Frequency = static_cast<uint>(streamInfo->sampleRate);
|
||||
mDevice->FmtChans = mChannelConfig;
|
||||
mDevice->mAmbiOrder = mAmbiOrder;
|
||||
mDevice->UpdateSize = mUpdateSize;
|
||||
mDevice->BufferSize = mUpdateSize * 2;
|
||||
if(streamInfo->outputLatency > 0.0f)
|
||||
{
|
||||
const double sampleLatency{streamInfo->outputLatency * streamInfo->sampleRate};
|
||||
TRACE("Reported stream latency: %f sec (%f samples)\n", streamInfo->outputLatency,
|
||||
sampleLatency);
|
||||
mDevice->BufferSize = static_cast<uint>(std::clamp(sampleLatency,
|
||||
double(mDevice->BufferSize), double{std::numeric_limits<int>::max()}));
|
||||
}
|
||||
|
||||
if(mParams.sampleFormat == paInt8)
|
||||
mDevice->FmtType = DevFmtByte;
|
||||
|
|
@ -200,15 +260,6 @@ bool PortPlayback::reset()
|
|||
return false;
|
||||
}
|
||||
|
||||
if(mParams.channelCount >= 2)
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
else if(mParams.channelCount == 1)
|
||||
mDevice->FmtChans = DevFmtMono;
|
||||
else
|
||||
{
|
||||
ERR("Unexpected channel count: %u\n", mParams.channelCount);
|
||||
return false;
|
||||
}
|
||||
setDefaultChannelOrder();
|
||||
|
||||
return true;
|
||||
|
|
@ -216,16 +267,14 @@ bool PortPlayback::reset()
|
|||
|
||||
void PortPlayback::start()
|
||||
{
|
||||
const PaError err{Pa_StartStream(mStream)};
|
||||
if(err == paNoError)
|
||||
if(const PaError err{Pa_StartStream(mStream)}; err != paNoError)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to start playback: %s",
|
||||
Pa_GetErrorText(err)};
|
||||
}
|
||||
|
||||
void PortPlayback::stop()
|
||||
{
|
||||
PaError err{Pa_StopStream(mStream)};
|
||||
if(err != paNoError)
|
||||
if(PaError err{Pa_StopStream(mStream)}; err != paNoError)
|
||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
|
|
@ -235,27 +284,18 @@ struct PortCapture final : public BackendBase {
|
|||
~PortCapture() override;
|
||||
|
||||
int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
||||
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept;
|
||||
static int readCallbackC(const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||
const PaStreamCallbackFlags statusFlags, void *userData) noexcept
|
||||
{
|
||||
return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
|
||||
framesPerBuffer, timeInfo, statusFlags);
|
||||
}
|
||||
const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) const noexcept;
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
void captureSamples(std::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
|
||||
PaStream *mStream{nullptr};
|
||||
PaStreamParameters mParams;
|
||||
PaStreamParameters mParams{};
|
||||
|
||||
RingBufferPtr mRing{nullptr};
|
||||
|
||||
DEF_NEWDEL(PortCapture)
|
||||
};
|
||||
|
||||
PortCapture::~PortCapture()
|
||||
|
|
@ -268,30 +308,43 @@ PortCapture::~PortCapture()
|
|||
|
||||
|
||||
int PortCapture::readCallback(const void *inputBuffer, void*, unsigned long framesPerBuffer,
|
||||
const PaStreamCallbackTimeInfo*, const PaStreamCallbackFlags) noexcept
|
||||
const PaStreamCallbackTimeInfo*, const PaStreamCallbackFlags) const noexcept
|
||||
{
|
||||
mRing->write(inputBuffer, framesPerBuffer);
|
||||
std::ignore = mRing->write(inputBuffer, framesPerBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void PortCapture::open(const char *name)
|
||||
void PortCapture::open(std::string_view name)
|
||||
{
|
||||
if(!name)
|
||||
name = pa_device;
|
||||
else if(strcmp(name, pa_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
if(DeviceNames.empty())
|
||||
EnumerateDevices();
|
||||
|
||||
uint samples{mDevice->BufferSize};
|
||||
samples = maxu(samples, 100 * mDevice->Frequency / 1000);
|
||||
uint frame_size{mDevice->frameSizeFromFmt()};
|
||||
int deviceid{};
|
||||
if(name.empty())
|
||||
{
|
||||
if(auto devidopt = ConfigValueInt({}, "port", "capture"))
|
||||
deviceid = *devidopt;
|
||||
if(deviceid < 0 || static_cast<uint>(deviceid) >= DeviceNames.size())
|
||||
deviceid = Pa_GetDefaultInputDevice();
|
||||
name = DeviceNames.at(static_cast<uint>(deviceid)).mName;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(),
|
||||
[name](const DeviceEntry &entry) { return entry.mIsCapture && name == entry.mName; });
|
||||
if(iter == DeviceNames.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Device name \"%.*s\" not found", al::sizei(name), name.data()};
|
||||
deviceid = static_cast<int>(std::distance(DeviceNames.cbegin(), iter));
|
||||
}
|
||||
|
||||
const uint samples{std::max(mDevice->BufferSize, mDevice->Frequency/10u)};
|
||||
const uint frame_size{mDevice->frameSizeFromFmt()};
|
||||
|
||||
mRing = RingBuffer::Create(samples, frame_size, false);
|
||||
|
||||
auto devidopt = ConfigValueInt(nullptr, "port", "capture");
|
||||
if(devidopt && *devidopt >= 0) mParams.device = *devidopt;
|
||||
else mParams.device = Pa_GetDefaultOutputDevice();
|
||||
mParams.device = deviceid;
|
||||
mParams.suggestedLatency = 0.0f;
|
||||
mParams.hostApiSpecificStreamInfo = nullptr;
|
||||
|
||||
|
|
@ -319,8 +372,15 @@ void PortCapture::open(const char *name)
|
|||
}
|
||||
mParams.channelCount = static_cast<int>(mDevice->channelsFromFmt());
|
||||
|
||||
static constexpr auto readCallback = [](const void *inputBuffer, void *outputBuffer,
|
||||
unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo,
|
||||
const PaStreamCallbackFlags statusFlags, void *userData) noexcept
|
||||
{
|
||||
return static_cast<PortCapture*>(userData)->readCallback(inputBuffer, outputBuffer,
|
||||
framesPerBuffer, timeInfo, statusFlags);
|
||||
};
|
||||
PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->Frequency,
|
||||
paFramesPerBufferUnspecified, paNoFlag, &PortCapture::readCallbackC, this)};
|
||||
paFramesPerBufferUnspecified, paNoFlag, readCallback, this)};
|
||||
if(err != paNoError)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
|
||||
Pa_GetErrorText(err)};
|
||||
|
|
@ -331,16 +391,14 @@ void PortCapture::open(const char *name)
|
|||
|
||||
void PortCapture::start()
|
||||
{
|
||||
const PaError err{Pa_StartStream(mStream)};
|
||||
if(err != paNoError)
|
||||
if(const PaError err{Pa_StartStream(mStream)}; err != paNoError)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to start recording: %s", Pa_GetErrorText(err)};
|
||||
}
|
||||
|
||||
void PortCapture::stop()
|
||||
{
|
||||
PaError err{Pa_StopStream(mStream)};
|
||||
if(err != paNoError)
|
||||
if(PaError err{Pa_StopStream(mStream)}; err != paNoError)
|
||||
ERR("Error stopping stream: %s\n", Pa_GetErrorText(err));
|
||||
}
|
||||
|
||||
|
|
@ -348,16 +406,14 @@ void PortCapture::stop()
|
|||
uint PortCapture::availableSamples()
|
||||
{ return static_cast<uint>(mRing->readSpace()); }
|
||||
|
||||
void PortCapture::captureSamples(al::byte *buffer, uint samples)
|
||||
{ mRing->read(buffer, samples); }
|
||||
void PortCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
{ std::ignore = mRing->read(buffer, samples); }
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
bool PortBackendFactory::init()
|
||||
{
|
||||
PaError err;
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
if(!pa_handle)
|
||||
{
|
||||
|
|
@ -391,12 +447,15 @@ bool PortBackendFactory::init()
|
|||
LOAD_FUNC(Pa_StopStream);
|
||||
LOAD_FUNC(Pa_OpenStream);
|
||||
LOAD_FUNC(Pa_CloseStream);
|
||||
LOAD_FUNC(Pa_GetDeviceCount);
|
||||
LOAD_FUNC(Pa_GetDeviceInfo);
|
||||
LOAD_FUNC(Pa_GetDefaultOutputDevice);
|
||||
LOAD_FUNC(Pa_GetDefaultInputDevice);
|
||||
LOAD_FUNC(Pa_GetStreamInfo);
|
||||
#undef LOAD_FUNC
|
||||
|
||||
if((err=Pa_Initialize()) != paNoError)
|
||||
const PaError err{Pa_Initialize()};
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
CloseLib(pa_handle);
|
||||
|
|
@ -405,7 +464,8 @@ bool PortBackendFactory::init()
|
|||
}
|
||||
}
|
||||
#else
|
||||
if((err=Pa_Initialize()) != paNoError)
|
||||
const PaError err{Pa_Initialize()};
|
||||
if(err != paNoError)
|
||||
{
|
||||
ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err));
|
||||
return false;
|
||||
|
|
@ -417,18 +477,52 @@ bool PortBackendFactory::init()
|
|||
bool PortBackendFactory::querySupport(BackendType type)
|
||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||
|
||||
std::string PortBackendFactory::probe(BackendType type)
|
||||
auto PortBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
std::vector<std::string> devices;
|
||||
|
||||
EnumerateDevices();
|
||||
int defaultid{-1};
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
defaultid = Pa_GetDefaultOutputDevice();
|
||||
if(auto devidopt = ConfigValueInt({}, "port", "device"); devidopt && *devidopt >= 0
|
||||
&& static_cast<uint>(*devidopt) < DeviceNames.size())
|
||||
defaultid = *devidopt;
|
||||
|
||||
for(size_t i{0};i < DeviceNames.size();++i)
|
||||
{
|
||||
if(DeviceNames[i].mIsPlayback)
|
||||
{
|
||||
if(defaultid >= 0 && static_cast<uint>(defaultid) == i)
|
||||
devices.emplace(devices.cbegin(), DeviceNames[i].mName);
|
||||
else
|
||||
devices.emplace_back(DeviceNames[i].mName);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BackendType::Capture:
|
||||
/* Includes null char. */
|
||||
outnames.append(pa_device, sizeof(pa_device));
|
||||
defaultid = Pa_GetDefaultInputDevice();
|
||||
if(auto devidopt = ConfigValueInt({}, "port", "capture"); devidopt && *devidopt >= 0
|
||||
&& static_cast<uint>(*devidopt) < DeviceNames.size())
|
||||
defaultid = *devidopt;
|
||||
|
||||
for(size_t i{0};i < DeviceNames.size();++i)
|
||||
{
|
||||
if(DeviceNames[i].mIsCapture)
|
||||
{
|
||||
if(defaultid >= 0 && static_cast<uint>(defaultid) == i)
|
||||
devices.emplace(devices.cbegin(), DeviceNames[i].mName);
|
||||
else
|
||||
devices.emplace_back(DeviceNames[i].mName);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return outnames;
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
BackendPtr PortBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct PortBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_PORTAUDIO_H */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,19 +1,27 @@
|
|||
#ifndef BACKENDS_PULSEAUDIO_H
|
||||
#define BACKENDS_PULSEAUDIO_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "alc/events.h"
|
||||
#include "base.h"
|
||||
|
||||
struct DeviceBase;
|
||||
|
||||
class PulseBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
|
|
@ -46,17 +47,17 @@ namespace {
|
|||
#define DEVNAME_PREFIX ""
|
||||
#endif
|
||||
|
||||
constexpr char defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
|
||||
constexpr auto getDevicePrefix() noexcept -> std::string_view { return DEVNAME_PREFIX; }
|
||||
constexpr auto getDefaultDeviceName() noexcept -> std::string_view
|
||||
{ return DEVNAME_PREFIX "Default Device"; }
|
||||
|
||||
struct Sdl2Backend final : public BackendBase {
|
||||
Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~Sdl2Backend() override;
|
||||
|
||||
void audioCallback(Uint8 *stream, int len) noexcept;
|
||||
static void audioCallbackC(void *ptr, Uint8 *stream, int len) noexcept
|
||||
{ static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); }
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -68,8 +69,6 @@ struct Sdl2Backend final : public BackendBase {
|
|||
DevFmtChannels mFmtChans{};
|
||||
DevFmtType mFmtType{};
|
||||
uint mUpdateSize{0u};
|
||||
|
||||
DEF_NEWDEL(Sdl2Backend)
|
||||
};
|
||||
|
||||
Sdl2Backend::~Sdl2Backend()
|
||||
|
|
@ -86,7 +85,7 @@ void Sdl2Backend::audioCallback(Uint8 *stream, int len) noexcept
|
|||
mDevice->renderSamples(stream, ulen / mFrameSize, mDevice->channelsFromFmt());
|
||||
}
|
||||
|
||||
void Sdl2Backend::open(const char *name)
|
||||
void Sdl2Backend::open(std::string_view name)
|
||||
{
|
||||
SDL_AudioSpec want{}, have{};
|
||||
|
||||
|
|
@ -102,24 +101,39 @@ void Sdl2Backend::open(const char *name)
|
|||
case DevFmtFloat: want.format = AUDIO_F32; break;
|
||||
}
|
||||
want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2;
|
||||
want.samples = static_cast<Uint16>(minu(mDevice->UpdateSize, 8192));
|
||||
want.callback = &Sdl2Backend::audioCallbackC;
|
||||
want.samples = static_cast<Uint16>(std::min(mDevice->UpdateSize, 8192u));
|
||||
want.callback = [](void *ptr, Uint8 *stream, int len) noexcept
|
||||
{ return static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); };
|
||||
want.userdata = this;
|
||||
|
||||
/* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't
|
||||
* necessarily the first in the list.
|
||||
*/
|
||||
const auto defaultDeviceName = getDefaultDeviceName();
|
||||
SDL_AudioDeviceID devid;
|
||||
if(!name || strcmp(name, defaultDeviceName) == 0)
|
||||
if(name.empty() || name == defaultDeviceName)
|
||||
{
|
||||
name = defaultDeviceName;
|
||||
devid = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t prefix_len = strlen(DEVNAME_PREFIX);
|
||||
if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
|
||||
devid = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
|
||||
const auto namePrefix = getDevicePrefix();
|
||||
if(name.size() >= namePrefix.size() && name.substr(0, namePrefix.size()) == namePrefix)
|
||||
{
|
||||
/* Copy the string_view to a string to ensure it's null terminated
|
||||
* for this call.
|
||||
*/
|
||||
const std::string devname{name.substr(namePrefix.size())};
|
||||
devid = SDL_OpenAudioDevice(devname.c_str(), SDL_FALSE, &want, &have,
|
||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
}
|
||||
else
|
||||
devid = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
{
|
||||
const std::string devname{name};
|
||||
devid = SDL_OpenAudioDevice(devname.c_str(), SDL_FALSE, &want, &have,
|
||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
}
|
||||
}
|
||||
if(!devid)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()};
|
||||
|
|
@ -161,7 +175,7 @@ void Sdl2Backend::open(const char *name)
|
|||
mFmtType = devtype;
|
||||
mUpdateSize = have.samples;
|
||||
|
||||
mDevice->DeviceName = name ? name : defaultDeviceName;
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
bool Sdl2Backend::reset()
|
||||
|
|
@ -195,23 +209,27 @@ bool SDL2BackendFactory::init()
|
|||
bool SDL2BackendFactory::querySupport(BackendType type)
|
||||
{ return type == BackendType::Playback; }
|
||||
|
||||
std::string SDL2BackendFactory::probe(BackendType type)
|
||||
auto SDL2BackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
std::vector<std::string> outnames;
|
||||
|
||||
if(type != BackendType::Playback)
|
||||
return outnames;
|
||||
|
||||
int num_devices{SDL_GetNumAudioDevices(SDL_FALSE)};
|
||||
if(num_devices <= 0)
|
||||
return outnames;
|
||||
|
||||
/* Includes null char. */
|
||||
outnames.append(defaultDeviceName, sizeof(defaultDeviceName));
|
||||
outnames.reserve(static_cast<unsigned int>(num_devices));
|
||||
outnames.emplace_back(getDefaultDeviceName());
|
||||
for(int i{0};i < num_devices;++i)
|
||||
{
|
||||
std::string name{DEVNAME_PREFIX};
|
||||
name += SDL_GetAudioDeviceName(i, SDL_FALSE);
|
||||
if(!name.empty())
|
||||
outnames.append(name.c_str(), name.length()+1);
|
||||
std::string outname{getDevicePrefix()};
|
||||
if(const char *name = SDL_GetAudioDeviceName(i, SDL_FALSE))
|
||||
outname += name;
|
||||
else
|
||||
outname += "Unknown Device Name #"+std::to_string(i);
|
||||
outnames.emplace_back(std::move(outname));
|
||||
}
|
||||
return outnames;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct SDL2BackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_SDL2_H */
|
||||
|
|
|
|||
|
|
@ -22,31 +22,35 @@
|
|||
|
||||
#include "sndio.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <inttypes.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "alnumeric.h"
|
||||
#include "alstring.h"
|
||||
#include "althrd_setname.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "vector.h"
|
||||
|
||||
#include <sndio.h>
|
||||
#include <sndio.h> /* NOLINT(*-duplicate-include) Not the same header. */
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
static const char sndio_device[] = "SndIO Default";
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "SndIO Default"sv; }
|
||||
|
||||
struct SioPar : public sio_par {
|
||||
SioPar() { sio_initpar(this); }
|
||||
SioPar() : sio_par{} { sio_initpar(this); }
|
||||
|
||||
void clear() { sio_initpar(this); }
|
||||
};
|
||||
|
|
@ -57,7 +61,7 @@ struct SndioPlayback final : public BackendBase {
|
|||
|
||||
int mixerProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -65,12 +69,10 @@ struct SndioPlayback final : public BackendBase {
|
|||
sio_hdl *mSndHandle{nullptr};
|
||||
uint mFrameStep{};
|
||||
|
||||
al::vector<al::byte> mBuffer;
|
||||
std::vector<std::byte> mBuffer;
|
||||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(SndioPlayback)
|
||||
};
|
||||
|
||||
SndioPlayback::~SndioPlayback()
|
||||
|
|
@ -86,12 +88,12 @@ int SndioPlayback::mixerProc()
|
|||
const size_t frameSize{frameStep * mDevice->bytesFromFmt()};
|
||||
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
while(!mKillNow.load(std::memory_order_acquire)
|
||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||
{
|
||||
al::span<al::byte> buffer{mBuffer};
|
||||
al::span<std::byte> buffer{mBuffer};
|
||||
|
||||
mDevice->renderSamples(buffer.data(), static_cast<uint>(buffer.size() / frameSize),
|
||||
frameStep);
|
||||
|
|
@ -112,13 +114,13 @@ int SndioPlayback::mixerProc()
|
|||
}
|
||||
|
||||
|
||||
void SndioPlayback::open(const char *name)
|
||||
void SndioPlayback::open(std::string_view name)
|
||||
{
|
||||
if(!name)
|
||||
name = sndio_device;
|
||||
else if(strcmp(name, sndio_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
if(name.empty())
|
||||
name = GetDefaultName();
|
||||
else if(name != GetDefaultName())
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
|
||||
sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)};
|
||||
if(!sndHandle)
|
||||
|
|
@ -136,72 +138,75 @@ bool SndioPlayback::reset()
|
|||
SioPar par;
|
||||
|
||||
auto tryfmt = mDevice->FmtType;
|
||||
retry_params:
|
||||
switch(tryfmt)
|
||||
while(true)
|
||||
{
|
||||
case DevFmtByte:
|
||||
par.bits = 8;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
par.bits = 8;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
par.bits = 16;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
par.bits = 16;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
case DevFmtInt:
|
||||
par.bits = 32;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
par.bits = 32;
|
||||
par.sig = 0;
|
||||
break;
|
||||
}
|
||||
par.bps = SIO_BPS(par.bits);
|
||||
par.le = SIO_LE_NATIVE;
|
||||
par.msb = 1;
|
||||
switch(tryfmt)
|
||||
{
|
||||
case DevFmtByte:
|
||||
par.bits = 8;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
par.bits = 8;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtShort:
|
||||
par.bits = 16;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
par.bits = 16;
|
||||
par.sig = 0;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
case DevFmtInt:
|
||||
par.bits = 32;
|
||||
par.sig = 1;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
par.bits = 32;
|
||||
par.sig = 0;
|
||||
break;
|
||||
}
|
||||
par.bps = SIO_BPS(par.bits);
|
||||
par.le = SIO_LE_NATIVE;
|
||||
par.msb = 1;
|
||||
|
||||
par.rate = mDevice->Frequency;
|
||||
par.pchan = mDevice->channelsFromFmt();
|
||||
par.rate = mDevice->Frequency;
|
||||
par.pchan = mDevice->channelsFromFmt();
|
||||
|
||||
par.round = mDevice->UpdateSize;
|
||||
par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize;
|
||||
if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
|
||||
par.round = mDevice->UpdateSize;
|
||||
par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize;
|
||||
if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize;
|
||||
|
||||
try {
|
||||
if(!sio_setpar(mSndHandle, &par))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to set device parameters"};
|
||||
try {
|
||||
if(!sio_setpar(mSndHandle, &par))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to set device parameters"};
|
||||
|
||||
par.clear();
|
||||
if(!sio_getpar(mSndHandle, &par))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to get device parameters"};
|
||||
par.clear();
|
||||
if(!sio_getpar(mSndHandle, &par))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to get device parameters"};
|
||||
|
||||
if(par.bps > 1 && par.le != SIO_LE_NATIVE)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"%s-endian samples not supported", par.le ? "Little" : "Big"};
|
||||
if(par.bits < par.bps*8 && !par.msb)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"MSB-padded samples not supported (%u of %u bits)", par.bits, par.bps*8};
|
||||
if(par.pchan < 1)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"No playback channels on device"};
|
||||
}
|
||||
catch(al::backend_exception &e) {
|
||||
if(tryfmt == DevFmtShort)
|
||||
throw;
|
||||
par.clear();
|
||||
tryfmt = DevFmtShort;
|
||||
goto retry_params;
|
||||
if(par.bps > 1 && par.le != SIO_LE_NATIVE)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"%s-endian samples not supported", par.le ? "Little" : "Big"};
|
||||
if(par.bits < par.bps*8 && !par.msb)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"MSB-padded samples not supported (%u of %u bits)", par.bits, par.bps*8};
|
||||
if(par.pchan < 1)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"No playback channels on device"};
|
||||
|
||||
break;
|
||||
}
|
||||
catch(al::backend_exception &e) {
|
||||
if(tryfmt == DevFmtShort)
|
||||
throw;
|
||||
par.clear();
|
||||
tryfmt = DevFmtShort;
|
||||
}
|
||||
}
|
||||
|
||||
if(par.bps == 1)
|
||||
|
|
@ -229,11 +234,11 @@ retry_params:
|
|||
mDevice->UpdateSize = par.round;
|
||||
mDevice->BufferSize = par.bufsz + par.round;
|
||||
|
||||
mBuffer.resize(mDevice->UpdateSize * par.pchan*par.bps);
|
||||
mBuffer.resize(size_t{mDevice->UpdateSize} * par.pchan*par.bps);
|
||||
if(par.sig == 1)
|
||||
std::fill(mBuffer.begin(), mBuffer.end(), al::byte{});
|
||||
std::fill(mBuffer.begin(), mBuffer.end(), std::byte{});
|
||||
else if(par.bits == 8)
|
||||
std::fill_n(mBuffer.data(), mBuffer.size(), al::byte(0x80));
|
||||
std::fill_n(mBuffer.data(), mBuffer.size(), std::byte(0x80));
|
||||
else if(par.bits == 16)
|
||||
std::fill_n(reinterpret_cast<uint16_t*>(mBuffer.data()), mBuffer.size()/2, 0x8000);
|
||||
else if(par.bits == 32)
|
||||
|
|
@ -280,10 +285,10 @@ struct SndioCapture final : public BackendBase {
|
|||
|
||||
int recordProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
void captureSamples(std::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
|
||||
sio_hdl *mSndHandle{nullptr};
|
||||
|
|
@ -292,8 +297,6 @@ struct SndioCapture final : public BackendBase {
|
|||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(SndioCapture)
|
||||
};
|
||||
|
||||
SndioCapture::~SndioCapture()
|
||||
|
|
@ -306,7 +309,7 @@ SndioCapture::~SndioCapture()
|
|||
int SndioCapture::recordProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(RECORD_THREAD_NAME);
|
||||
althrd_setname(GetRecordThreadName());
|
||||
|
||||
const uint frameSize{mDevice->frameSizeFromFmt()};
|
||||
|
||||
|
|
@ -317,29 +320,30 @@ int SndioCapture::recordProc()
|
|||
return 1;
|
||||
}
|
||||
|
||||
auto fds = std::make_unique<pollfd[]>(static_cast<uint>(nfds_pre));
|
||||
auto fds = std::vector<pollfd>(static_cast<uint>(nfds_pre));
|
||||
|
||||
while(!mKillNow.load(std::memory_order_acquire)
|
||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||
{
|
||||
/* Wait until there's some samples to read. */
|
||||
const int nfds{sio_pollfd(mSndHandle, fds.get(), POLLIN)};
|
||||
const int nfds{sio_pollfd(mSndHandle, fds.data(), POLLIN)};
|
||||
if(nfds <= 0)
|
||||
{
|
||||
mDevice->handleDisconnect("Failed to get polling fds: %d", nfds);
|
||||
break;
|
||||
}
|
||||
int pollres{::poll(fds.get(), static_cast<uint>(nfds), 2000)};
|
||||
int pollres{::poll(fds.data(), fds.size(), 2000)};
|
||||
if(pollres < 0)
|
||||
{
|
||||
if(errno == EINTR) continue;
|
||||
mDevice->handleDisconnect("Poll error: %s", strerror(errno));
|
||||
mDevice->handleDisconnect("Poll error: %s",
|
||||
std::generic_category().message(errno).c_str());
|
||||
break;
|
||||
}
|
||||
if(pollres == 0)
|
||||
continue;
|
||||
|
||||
const int revents{sio_revents(mSndHandle, fds.get())};
|
||||
const int revents{sio_revents(mSndHandle, fds.data())};
|
||||
if((revents&POLLHUP))
|
||||
{
|
||||
mDevice->handleDisconnect("Got POLLHUP from poll events");
|
||||
|
|
@ -349,7 +353,7 @@ int SndioCapture::recordProc()
|
|||
continue;
|
||||
|
||||
auto data = mRing->getWriteVector();
|
||||
al::span<al::byte> buffer{data.first.buf, data.first.len*frameSize};
|
||||
al::span<std::byte> buffer{data.first.buf, data.first.len*frameSize};
|
||||
while(!buffer.empty())
|
||||
{
|
||||
size_t got{sio_read(mSndHandle, buffer.data(), buffer.size())};
|
||||
|
|
@ -373,8 +377,8 @@ int SndioCapture::recordProc()
|
|||
if(buffer.empty())
|
||||
{
|
||||
/* Got samples to read, but no place to store it. Drop it. */
|
||||
static char junk[4096];
|
||||
sio_read(mSndHandle, junk, sizeof(junk) - (sizeof(junk)%frameSize));
|
||||
static std::array<char,4096> junk;
|
||||
sio_read(mSndHandle, junk.data(), junk.size() - (junk.size()%frameSize));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -382,13 +386,13 @@ int SndioCapture::recordProc()
|
|||
}
|
||||
|
||||
|
||||
void SndioCapture::open(const char *name)
|
||||
void SndioCapture::open(std::string_view name)
|
||||
{
|
||||
if(!name)
|
||||
name = sndio_device;
|
||||
else if(strcmp(name, sndio_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
if(name.empty())
|
||||
name = GetDefaultName();
|
||||
else if(name != GetDefaultName())
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
|
||||
mSndHandle = sio_open(nullptr, SIO_REC, true);
|
||||
if(mSndHandle == nullptr)
|
||||
|
|
@ -431,12 +435,12 @@ void SndioCapture::open(const char *name)
|
|||
par.rchan = mDevice->channelsFromFmt();
|
||||
par.rate = mDevice->Frequency;
|
||||
|
||||
par.appbufsz = maxu(mDevice->BufferSize, mDevice->Frequency/10);
|
||||
par.round = minu(par.appbufsz/2, mDevice->Frequency/40);
|
||||
par.appbufsz = std::max(mDevice->BufferSize, mDevice->Frequency/10u);
|
||||
par.round = std::min(par.appbufsz/2u, mDevice->Frequency/40u);
|
||||
|
||||
if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par))
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to set device praameters"};
|
||||
"Failed to set device parameters"};
|
||||
|
||||
if(par.bps > 1 && par.le != SIO_LE_NATIVE)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
|
|
@ -461,7 +465,7 @@ void SndioCapture::open(const char *name)
|
|||
DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans),
|
||||
mDevice->Frequency, par.sig?'s':'u', par.bps*8, par.rchan, par.rate};
|
||||
|
||||
mRing = RingBuffer::Create(mDevice->BufferSize, par.bps*par.rchan, false);
|
||||
mRing = RingBuffer::Create(mDevice->BufferSize, size_t{par.bps}*par.rchan, false);
|
||||
mDevice->BufferSize = static_cast<uint>(mRing->writeSpace());
|
||||
mDevice->UpdateSize = par.round;
|
||||
|
||||
|
|
@ -496,8 +500,8 @@ void SndioCapture::stop()
|
|||
ERR("Error stopping device\n");
|
||||
}
|
||||
|
||||
void SndioCapture::captureSamples(al::byte *buffer, uint samples)
|
||||
{ mRing->read(buffer, samples); }
|
||||
void SndioCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
{ std::ignore = mRing->read(buffer, samples); }
|
||||
|
||||
uint SndioCapture::availableSamples()
|
||||
{ return static_cast<uint>(mRing->readSpace()); }
|
||||
|
|
@ -516,18 +520,15 @@ bool SndIOBackendFactory::init()
|
|||
bool SndIOBackendFactory::querySupport(BackendType type)
|
||||
{ return (type == BackendType::Playback || type == BackendType::Capture); }
|
||||
|
||||
std::string SndIOBackendFactory::probe(BackendType type)
|
||||
auto SndIOBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
case BackendType::Capture:
|
||||
/* Includes null char. */
|
||||
outnames.append(sndio_device, sizeof(sndio_device));
|
||||
break;
|
||||
return std::vector{std::string{GetDefaultName()}};
|
||||
}
|
||||
return outnames;
|
||||
return {};
|
||||
}
|
||||
|
||||
BackendPtr SndIOBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct SndIOBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_SNDIO_H */
|
||||
|
|
|
|||
|
|
@ -35,24 +35,26 @@
|
|||
#include <poll.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "alstring.h"
|
||||
#include "althrd_setname.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "threads.h"
|
||||
#include "vector.h"
|
||||
|
||||
#include <sys/audioio.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char solaris_device[] = "Solaris Default";
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "Solaris Default"sv; }
|
||||
|
||||
std::string solaris_driver{"/dev/audio"};
|
||||
|
||||
|
|
@ -63,7 +65,7 @@ struct SolarisBackend final : public BackendBase {
|
|||
|
||||
int mixerProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -71,12 +73,10 @@ struct SolarisBackend final : public BackendBase {
|
|||
int mFd{-1};
|
||||
|
||||
uint mFrameStep{};
|
||||
al::vector<al::byte> mBuffer;
|
||||
std::vector<std::byte> mBuffer;
|
||||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(SolarisBackend)
|
||||
};
|
||||
|
||||
SolarisBackend::~SolarisBackend()
|
||||
|
|
@ -89,10 +89,10 @@ SolarisBackend::~SolarisBackend()
|
|||
int SolarisBackend::mixerProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
const size_t frame_step{mDevice->channelsFromFmt()};
|
||||
const uint frame_size{mDevice->frameSizeFromFmt()};
|
||||
const size_t frame_size{mDevice->frameSizeFromFmt()};
|
||||
|
||||
while(!mKillNow.load(std::memory_order_acquire)
|
||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||
|
|
@ -116,12 +116,12 @@ int SolarisBackend::mixerProc()
|
|||
continue;
|
||||
}
|
||||
|
||||
al::byte *write_ptr{mBuffer.data()};
|
||||
size_t to_write{mBuffer.size()};
|
||||
mDevice->renderSamples(write_ptr, static_cast<uint>(to_write/frame_size), frame_step);
|
||||
while(to_write > 0 && !mKillNow.load(std::memory_order_acquire))
|
||||
al::span<std::byte> buffer{mBuffer};
|
||||
mDevice->renderSamples(buffer.data(), static_cast<uint>(buffer.size()/frame_size),
|
||||
frame_step);
|
||||
while(!buffer.empty() && !mKillNow.load(std::memory_order_acquire))
|
||||
{
|
||||
ssize_t wrote{write(mFd, write_ptr, to_write)};
|
||||
ssize_t wrote{write(mFd, buffer.data(), buffer.size())};
|
||||
if(wrote < 0)
|
||||
{
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
|
||||
|
|
@ -131,8 +131,7 @@ int SolarisBackend::mixerProc()
|
|||
break;
|
||||
}
|
||||
|
||||
to_write -= static_cast<size_t>(wrote);
|
||||
write_ptr += wrote;
|
||||
buffer = buffer.subspan(static_cast<size_t>(wrote));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -140,13 +139,13 @@ int SolarisBackend::mixerProc()
|
|||
}
|
||||
|
||||
|
||||
void SolarisBackend::open(const char *name)
|
||||
void SolarisBackend::open(std::string_view name)
|
||||
{
|
||||
if(!name)
|
||||
name = solaris_device;
|
||||
else if(strcmp(name, solaris_device) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
if(name.empty())
|
||||
name = GetDefaultName();
|
||||
else if(name != GetDefaultName())
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
|
||||
int fd{::open(solaris_driver.c_str(), O_WRONLY)};
|
||||
if(fd == -1)
|
||||
|
|
@ -231,7 +230,7 @@ bool SolarisBackend::reset()
|
|||
setDefaultChannelOrder();
|
||||
|
||||
mBuffer.resize(mDevice->UpdateSize * size_t{frame_size});
|
||||
std::fill(mBuffer.begin(), mBuffer.end(), al::byte{});
|
||||
std::fill(mBuffer.begin(), mBuffer.end(), std::byte{});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -268,7 +267,7 @@ BackendFactory &SolarisBackendFactory::getFactory()
|
|||
|
||||
bool SolarisBackendFactory::init()
|
||||
{
|
||||
if(auto devopt = ConfigValueStr(nullptr, "solaris", "device"))
|
||||
if(auto devopt = ConfigValueStr({}, "solaris", "device"))
|
||||
solaris_driver = std::move(*devopt);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -276,23 +275,19 @@ bool SolarisBackendFactory::init()
|
|||
bool SolarisBackendFactory::querySupport(BackendType type)
|
||||
{ return type == BackendType::Playback; }
|
||||
|
||||
std::string SolarisBackendFactory::probe(BackendType type)
|
||||
auto SolarisBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
{
|
||||
struct stat buf;
|
||||
if(stat(solaris_driver.c_str(), &buf) == 0)
|
||||
outnames.append(solaris_device, sizeof(solaris_device));
|
||||
}
|
||||
break;
|
||||
if(struct stat buf{}; stat(solaris_driver.c_str(), &buf) == 0)
|
||||
return std::vector{std::string{GetDefaultName()}};
|
||||
break;
|
||||
|
||||
case BackendType::Capture:
|
||||
break;
|
||||
}
|
||||
return outnames;
|
||||
return {};
|
||||
}
|
||||
|
||||
BackendPtr SolarisBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct SolarisBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_SOLARIS_H */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -5,15 +5,17 @@
|
|||
|
||||
struct WasapiBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_WASAPI_H */
|
||||
|
|
|
|||
|
|
@ -31,24 +31,26 @@
|
|||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "albit.h"
|
||||
#include "albyte.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alstring.h"
|
||||
#include "althrd_setname.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "opthelpers.h"
|
||||
#include "strutils.h"
|
||||
#include "threads.h"
|
||||
#include "vector.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
using std::chrono::seconds;
|
||||
using std::chrono::milliseconds;
|
||||
using std::chrono::nanoseconds;
|
||||
|
|
@ -56,38 +58,43 @@ using std::chrono::nanoseconds;
|
|||
using ubyte = unsigned char;
|
||||
using ushort = unsigned short;
|
||||
|
||||
constexpr char waveDevice[] = "Wave File Writer";
|
||||
struct FileDeleter {
|
||||
void operator()(gsl::owner<FILE*> f) { fclose(f); }
|
||||
};
|
||||
using FilePtr = std::unique_ptr<FILE,FileDeleter>;
|
||||
|
||||
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,
|
||||
0x00, 0x38, 0x9b, 0x71
|
||||
};
|
||||
constexpr ubyte SUBTYPE_FLOAT[]{
|
||||
}};
|
||||
constexpr std::array<ubyte,16> SUBTYPE_FLOAT{{
|
||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa,
|
||||
0x00, 0x38, 0x9b, 0x71
|
||||
};
|
||||
}};
|
||||
|
||||
constexpr ubyte SUBTYPE_BFORMAT_PCM[]{
|
||||
constexpr std::array<ubyte,16> SUBTYPE_BFORMAT_PCM{{
|
||||
0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
|
||||
0xca, 0x00, 0x00, 0x00
|
||||
};
|
||||
}};
|
||||
|
||||
constexpr ubyte SUBTYPE_BFORMAT_FLOAT[]{
|
||||
constexpr std::array<ubyte,16> SUBTYPE_BFORMAT_FLOAT{{
|
||||
0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1,
|
||||
0xca, 0x00, 0x00, 0x00
|
||||
};
|
||||
}};
|
||||
|
||||
void fwrite16le(ushort val, FILE *f)
|
||||
{
|
||||
ubyte data[2]{ static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff) };
|
||||
fwrite(data, 1, 2, f);
|
||||
std::array data{static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff)};
|
||||
fwrite(data.data(), 1, data.size(), f);
|
||||
}
|
||||
|
||||
void fwrite32le(uint val, FILE *f)
|
||||
{
|
||||
ubyte data[4]{ static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff),
|
||||
static_cast<ubyte>((val>>16)&0xff), static_cast<ubyte>((val>>24)&0xff) };
|
||||
fwrite(data, 1, 4, f);
|
||||
std::array data{static_cast<ubyte>(val&0xff), static_cast<ubyte>((val>>8)&0xff),
|
||||
static_cast<ubyte>((val>>16)&0xff), static_cast<ubyte>((val>>24)&0xff)};
|
||||
fwrite(data.data(), 1, data.size(), f);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -97,34 +104,27 @@ struct WaveBackend final : public BackendBase {
|
|||
|
||||
int mixerProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
FILE *mFile{nullptr};
|
||||
FilePtr mFile{nullptr};
|
||||
long mDataStart{-1};
|
||||
|
||||
al::vector<al::byte> mBuffer;
|
||||
std::vector<std::byte> mBuffer;
|
||||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(WaveBackend)
|
||||
};
|
||||
|
||||
WaveBackend::~WaveBackend()
|
||||
{
|
||||
if(mFile)
|
||||
fclose(mFile);
|
||||
mFile = nullptr;
|
||||
}
|
||||
WaveBackend::~WaveBackend() = default;
|
||||
|
||||
int WaveBackend::mixerProc()
|
||||
{
|
||||
const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2};
|
||||
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
const size_t frameStep{mDevice->channelsFromFmt()};
|
||||
const size_t frameSize{mDevice->frameSizeFromFmt()};
|
||||
|
|
@ -155,13 +155,13 @@ int WaveBackend::mixerProc()
|
|||
|
||||
if(bytesize == 2)
|
||||
{
|
||||
const size_t len{mBuffer.size() & ~size_t{1}};
|
||||
const size_t len{mBuffer.size() & ~1_uz};
|
||||
for(size_t i{0};i < len;i+=2)
|
||||
std::swap(mBuffer[i], mBuffer[i+1]);
|
||||
}
|
||||
else if(bytesize == 4)
|
||||
{
|
||||
const size_t len{mBuffer.size() & ~size_t{3}};
|
||||
const size_t len{mBuffer.size() & ~3_uz};
|
||||
for(size_t i{0};i < len;i+=4)
|
||||
{
|
||||
std::swap(mBuffer[i ], mBuffer[i+3]);
|
||||
|
|
@ -170,8 +170,8 @@ int WaveBackend::mixerProc()
|
|||
}
|
||||
}
|
||||
|
||||
const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile)};
|
||||
if(fs < mDevice->UpdateSize || ferror(mFile))
|
||||
const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile.get())};
|
||||
if(fs < mDevice->UpdateSize || ferror(mFile.get()))
|
||||
{
|
||||
ERR("Error writing to file\n");
|
||||
mDevice->handleDisconnect("Failed to write playback samples");
|
||||
|
|
@ -195,32 +195,32 @@ int WaveBackend::mixerProc()
|
|||
return 0;
|
||||
}
|
||||
|
||||
void WaveBackend::open(const char *name)
|
||||
void WaveBackend::open(std::string_view name)
|
||||
{
|
||||
auto fname = ConfigValueStr(nullptr, "wave", "file");
|
||||
auto fname = ConfigValueStr({}, "wave", "file");
|
||||
if(!fname) throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"No wave output filename"};
|
||||
|
||||
if(!name)
|
||||
name = waveDevice;
|
||||
else if(strcmp(name, waveDevice) != 0)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
if(name.empty())
|
||||
name = GetDeviceName();
|
||||
else if(name != GetDeviceName())
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
|
||||
/* There's only one "device", so if it's already open, we're done. */
|
||||
if(mFile) return;
|
||||
|
||||
#ifdef _WIN32
|
||||
{
|
||||
std::wstring wname{utf8_to_wstr(fname->c_str())};
|
||||
mFile = _wfopen(wname.c_str(), L"wb");
|
||||
std::wstring wname{utf8_to_wstr(fname.value())};
|
||||
mFile = FilePtr{_wfopen(wname.c_str(), L"wb")};
|
||||
}
|
||||
#else
|
||||
mFile = fopen(fname->c_str(), "wb");
|
||||
mFile = FilePtr{fopen(fname->c_str(), "wb")};
|
||||
#endif
|
||||
if(!mFile)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Could not open file '%s': %s",
|
||||
fname->c_str(), strerror(errno)};
|
||||
fname->c_str(), std::generic_category().message(errno).c_str()};
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
|
@ -229,12 +229,11 @@ bool WaveBackend::reset()
|
|||
{
|
||||
uint channels{0}, bytes{0}, chanmask{0};
|
||||
bool isbformat{false};
|
||||
size_t val;
|
||||
|
||||
fseek(mFile, 0, SEEK_SET);
|
||||
clearerr(mFile);
|
||||
fseek(mFile.get(), 0, SEEK_SET);
|
||||
clearerr(mFile.get());
|
||||
|
||||
if(GetConfigValueBool(nullptr, "wave", "bformat", false))
|
||||
if(GetConfigValueBool({}, "wave", "bformat", false))
|
||||
{
|
||||
mDevice->FmtChans = DevFmtAmbi3D;
|
||||
mDevice->mAmbiOrder = 1;
|
||||
|
|
@ -265,6 +264,9 @@ bool WaveBackend::reset()
|
|||
case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break;
|
||||
case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break;
|
||||
case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
|
||||
case DevFmtX7144:
|
||||
mDevice->FmtChans = DevFmtX714;
|
||||
[[fallthrough]];
|
||||
case DevFmtX714:
|
||||
chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400 | 0x1000 | 0x4000
|
||||
| 0x8000 | 0x20000;
|
||||
|
|
@ -273,7 +275,7 @@ bool WaveBackend::reset()
|
|||
case DevFmtX3D71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break;
|
||||
case DevFmtAmbi3D:
|
||||
/* .amb output requires FuMa */
|
||||
mDevice->mAmbiOrder = minu(mDevice->mAmbiOrder, 3);
|
||||
mDevice->mAmbiOrder = std::min(mDevice->mAmbiOrder, 3u);
|
||||
mDevice->mAmbiLayout = DevAmbiLayout::FuMa;
|
||||
mDevice->mAmbiScale = DevAmbiScaling::FuMa;
|
||||
isbformat = true;
|
||||
|
|
@ -283,49 +285,48 @@ bool WaveBackend::reset()
|
|||
bytes = mDevice->bytesFromFmt();
|
||||
channels = mDevice->channelsFromFmt();
|
||||
|
||||
rewind(mFile);
|
||||
rewind(mFile.get());
|
||||
|
||||
fputs("RIFF", mFile);
|
||||
fwrite32le(0xFFFFFFFF, mFile); // 'RIFF' header len; filled in at close
|
||||
fputs("RIFF", mFile.get());
|
||||
fwrite32le(0xFFFFFFFF, mFile.get()); // 'RIFF' header len; filled in at close
|
||||
|
||||
fputs("WAVE", mFile);
|
||||
fputs("WAVE", mFile.get());
|
||||
|
||||
fputs("fmt ", mFile);
|
||||
fwrite32le(40, mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE
|
||||
fputs("fmt ", mFile.get());
|
||||
fwrite32le(40, mFile.get()); // 'fmt ' header len; 40 bytes for EXTENSIBLE
|
||||
|
||||
// 16-bit val, format type id (extensible: 0xFFFE)
|
||||
fwrite16le(0xFFFE, mFile);
|
||||
fwrite16le(0xFFFE, mFile.get());
|
||||
// 16-bit val, channel count
|
||||
fwrite16le(static_cast<ushort>(channels), mFile);
|
||||
fwrite16le(static_cast<ushort>(channels), mFile.get());
|
||||
// 32-bit val, frequency
|
||||
fwrite32le(mDevice->Frequency, mFile);
|
||||
fwrite32le(mDevice->Frequency, mFile.get());
|
||||
// 32-bit val, bytes per second
|
||||
fwrite32le(mDevice->Frequency * channels * bytes, mFile);
|
||||
fwrite32le(mDevice->Frequency * channels * bytes, mFile.get());
|
||||
// 16-bit val, frame size
|
||||
fwrite16le(static_cast<ushort>(channels * bytes), mFile);
|
||||
fwrite16le(static_cast<ushort>(channels * bytes), mFile.get());
|
||||
// 16-bit val, bits per sample
|
||||
fwrite16le(static_cast<ushort>(bytes * 8), mFile);
|
||||
fwrite16le(static_cast<ushort>(bytes * 8), mFile.get());
|
||||
// 16-bit val, extra byte count
|
||||
fwrite16le(22, mFile);
|
||||
fwrite16le(22, mFile.get());
|
||||
// 16-bit val, valid bits per sample
|
||||
fwrite16le(static_cast<ushort>(bytes * 8), mFile);
|
||||
fwrite16le(static_cast<ushort>(bytes * 8), mFile.get());
|
||||
// 32-bit val, channel mask
|
||||
fwrite32le(chanmask, mFile);
|
||||
fwrite32le(chanmask, mFile.get());
|
||||
// 16 byte GUID, sub-type format
|
||||
val = fwrite((mDevice->FmtType == DevFmtFloat) ?
|
||||
(isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) :
|
||||
(isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, mFile);
|
||||
(void)val;
|
||||
std::ignore = fwrite((mDevice->FmtType == DevFmtFloat) ?
|
||||
(isbformat ? SUBTYPE_BFORMAT_FLOAT.data() : SUBTYPE_FLOAT.data()) :
|
||||
(isbformat ? SUBTYPE_BFORMAT_PCM.data() : SUBTYPE_PCM.data()), 1, 16, mFile.get());
|
||||
|
||||
fputs("data", mFile);
|
||||
fwrite32le(0xFFFFFFFF, mFile); // 'data' header len; filled in at close
|
||||
fputs("data", mFile.get());
|
||||
fwrite32le(0xFFFFFFFF, mFile.get()); // 'data' header len; filled in at close
|
||||
|
||||
if(ferror(mFile))
|
||||
if(ferror(mFile.get()))
|
||||
{
|
||||
ERR("Error writing header: %s\n", strerror(errno));
|
||||
ERR("Error writing header: %s\n", std::generic_category().message(errno).c_str());
|
||||
return false;
|
||||
}
|
||||
mDataStart = ftell(mFile);
|
||||
mDataStart = ftell(mFile.get());
|
||||
|
||||
setDefaultWFXChannelOrder();
|
||||
|
||||
|
|
@ -337,7 +338,7 @@ bool WaveBackend::reset()
|
|||
|
||||
void WaveBackend::start()
|
||||
{
|
||||
if(mDataStart > 0 && fseek(mFile, 0, SEEK_END) != 0)
|
||||
if(mDataStart > 0 && fseek(mFile.get(), 0, SEEK_END) != 0)
|
||||
WARN("Failed to seek on output file\n");
|
||||
try {
|
||||
mKillNow.store(false, std::memory_order_release);
|
||||
|
|
@ -357,14 +358,14 @@ void WaveBackend::stop()
|
|||
|
||||
if(mDataStart > 0)
|
||||
{
|
||||
long size{ftell(mFile)};
|
||||
long size{ftell(mFile.get())};
|
||||
if(size > 0)
|
||||
{
|
||||
long dataLen{size - mDataStart};
|
||||
if(fseek(mFile, 4, SEEK_SET) == 0)
|
||||
fwrite32le(static_cast<uint>(size-8), mFile); // 'WAVE' header len
|
||||
if(fseek(mFile, mDataStart-4, SEEK_SET) == 0)
|
||||
fwrite32le(static_cast<uint>(dataLen), mFile); // 'data' header len
|
||||
if(fseek(mFile.get(), 4, SEEK_SET) == 0)
|
||||
fwrite32le(static_cast<uint>(size-8), mFile.get()); // 'WAVE' header len
|
||||
if(fseek(mFile.get(), mDataStart-4, SEEK_SET) == 0)
|
||||
fwrite32le(static_cast<uint>(dataLen), mFile.get()); // 'data' header len
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -378,19 +379,16 @@ bool WaveBackendFactory::init()
|
|||
bool WaveBackendFactory::querySupport(BackendType type)
|
||||
{ return type == BackendType::Playback; }
|
||||
|
||||
std::string WaveBackendFactory::probe(BackendType type)
|
||||
auto WaveBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
/* Includes null char. */
|
||||
outnames.append(waveDevice, sizeof(waveDevice));
|
||||
break;
|
||||
return std::vector{std::string{GetDeviceName()}};
|
||||
case BackendType::Capture:
|
||||
break;
|
||||
}
|
||||
return outnames;
|
||||
return {};
|
||||
}
|
||||
|
||||
BackendPtr WaveBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct WaveBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_WAVE_H */
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@
|
|||
|
||||
#include "winmm.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <memory.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
|
@ -38,13 +38,15 @@
|
|||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include "alnumeric.h"
|
||||
#include "alsem.h"
|
||||
#include "alstring.h"
|
||||
#include "althrd_setname.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "strutils.h"
|
||||
#include "threads.h"
|
||||
#include "vector.h"
|
||||
|
||||
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
||||
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
||||
|
|
@ -55,13 +57,13 @@ namespace {
|
|||
#define DEVNAME_HEAD "OpenAL Soft on "
|
||||
|
||||
|
||||
al::vector<std::string> PlaybackDevices;
|
||||
al::vector<std::string> CaptureDevices;
|
||||
std::vector<std::string> PlaybackDevices;
|
||||
std::vector<std::string> CaptureDevices;
|
||||
|
||||
bool checkName(const al::vector<std::string> &list, const std::string &name)
|
||||
bool checkName(const std::vector<std::string> &list, const std::string &name)
|
||||
{ return std::find(list.cbegin(), list.cend(), name) != list.cend(); }
|
||||
|
||||
void ProbePlaybackDevices(void)
|
||||
void ProbePlaybackDevices()
|
||||
{
|
||||
PlaybackDevices.clear();
|
||||
|
||||
|
|
@ -74,7 +76,7 @@ void ProbePlaybackDevices(void)
|
|||
WAVEOUTCAPSW WaveCaps{};
|
||||
if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
||||
{
|
||||
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
|
||||
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(std::data(WaveCaps.szPname))};
|
||||
|
||||
int count{1};
|
||||
std::string newname{basename};
|
||||
|
|
@ -92,7 +94,7 @@ void ProbePlaybackDevices(void)
|
|||
}
|
||||
}
|
||||
|
||||
void ProbeCaptureDevices(void)
|
||||
void ProbeCaptureDevices()
|
||||
{
|
||||
CaptureDevices.clear();
|
||||
|
||||
|
|
@ -105,7 +107,7 @@ void ProbeCaptureDevices(void)
|
|||
WAVEINCAPSW WaveCaps{};
|
||||
if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
|
||||
{
|
||||
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)};
|
||||
const std::string basename{DEVNAME_HEAD + wstr_to_utf8(std::data(WaveCaps.szPname))};
|
||||
|
||||
int count{1};
|
||||
std::string newname{basename};
|
||||
|
|
@ -134,7 +136,7 @@ struct WinMMPlayback final : public BackendBase {
|
|||
|
||||
int mixerProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
|
@ -143,6 +145,7 @@ struct WinMMPlayback final : public BackendBase {
|
|||
al::semaphore mSem;
|
||||
uint mIdx{0u};
|
||||
std::array<WAVEHDR,4> mWaveBuffer{};
|
||||
al::vector<char,16> mBuffer;
|
||||
|
||||
HWAVEOUT mOutHdl{nullptr};
|
||||
|
||||
|
|
@ -150,8 +153,6 @@ struct WinMMPlayback final : public BackendBase {
|
|||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(WinMMPlayback)
|
||||
};
|
||||
|
||||
WinMMPlayback::~WinMMPlayback()
|
||||
|
|
@ -159,14 +160,11 @@ WinMMPlayback::~WinMMPlayback()
|
|||
if(mOutHdl)
|
||||
waveOutClose(mOutHdl);
|
||||
mOutHdl = nullptr;
|
||||
|
||||
al_free(mWaveBuffer[0].lpData);
|
||||
std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
|
||||
}
|
||||
|
||||
/* WinMMPlayback::waveOutProc
|
||||
*
|
||||
* Posts a message to 'WinMMPlayback::mixerProc' everytime a WaveOut Buffer is
|
||||
* Posts a message to 'WinMMPlayback::mixerProc' every time a WaveOut Buffer is
|
||||
* completed and returns to the application (for more data)
|
||||
*/
|
||||
void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT, UINT msg, DWORD_PTR, DWORD_PTR) noexcept
|
||||
|
|
@ -179,7 +177,7 @@ void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT, UINT msg, DWORD_PTR, DWORD_PT
|
|||
FORCE_ALIGN int WinMMPlayback::mixerProc()
|
||||
{
|
||||
SetRTPriority();
|
||||
althrd_setname(MIXER_THREAD_NAME);
|
||||
althrd_setname(GetMixerThreadName());
|
||||
|
||||
while(!mKillNow.load(std::memory_order_acquire)
|
||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||
|
|
@ -207,59 +205,55 @@ FORCE_ALIGN int WinMMPlayback::mixerProc()
|
|||
}
|
||||
|
||||
|
||||
void WinMMPlayback::open(const char *name)
|
||||
void WinMMPlayback::open(std::string_view name)
|
||||
{
|
||||
if(PlaybackDevices.empty())
|
||||
ProbePlaybackDevices();
|
||||
|
||||
// Find the Device ID matching the deviceName if valid
|
||||
auto iter = name ?
|
||||
auto iter = !name.empty() ?
|
||||
std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), name) :
|
||||
PlaybackDevices.cbegin();
|
||||
if(iter == PlaybackDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
|
||||
|
||||
DevFmtType fmttype{mDevice->FmtType};
|
||||
retry_open:
|
||||
WAVEFORMATEX format{};
|
||||
if(fmttype == DevFmtFloat)
|
||||
{
|
||||
format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
format.wBitsPerSample = 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
if(fmttype == DevFmtUByte || fmttype == DevFmtByte)
|
||||
format.wBitsPerSample = 8;
|
||||
else
|
||||
format.wBitsPerSample = 16;
|
||||
}
|
||||
format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
format.nBlockAlign = static_cast<WORD>(format.wBitsPerSample * format.nChannels / 8);
|
||||
format.nSamplesPerSec = mDevice->Frequency;
|
||||
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
||||
format.cbSize = 0;
|
||||
|
||||
HWAVEOUT outHandle{};
|
||||
MMRESULT res{waveOutOpen(&outHandle, DeviceID, &format,
|
||||
reinterpret_cast<DWORD_PTR>(&WinMMPlayback::waveOutProcC),
|
||||
reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
|
||||
if(res != MMSYSERR_NOERROR)
|
||||
{
|
||||
do {
|
||||
format = WAVEFORMATEX{};
|
||||
if(fmttype == DevFmtFloat)
|
||||
{
|
||||
fmttype = DevFmtShort;
|
||||
goto retry_open;
|
||||
format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
format.wBitsPerSample = 32;
|
||||
}
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: %u", res};
|
||||
}
|
||||
else
|
||||
{
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
if(fmttype == DevFmtUByte || fmttype == DevFmtByte)
|
||||
format.wBitsPerSample = 8;
|
||||
else
|
||||
format.wBitsPerSample = 16;
|
||||
}
|
||||
format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
format.nBlockAlign = static_cast<WORD>(format.wBitsPerSample * format.nChannels / 8);
|
||||
format.nSamplesPerSec = mDevice->Frequency;
|
||||
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
||||
format.cbSize = 0;
|
||||
|
||||
MMRESULT res{waveOutOpen(&mOutHdl, DeviceID, &format,
|
||||
reinterpret_cast<DWORD_PTR>(&WinMMPlayback::waveOutProcC),
|
||||
reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
|
||||
if(res == MMSYSERR_NOERROR) break;
|
||||
|
||||
if(fmttype != DevFmtFloat)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: %u",
|
||||
res};
|
||||
|
||||
fmttype = DevFmtShort;
|
||||
} while(true);
|
||||
|
||||
if(mOutHdl)
|
||||
waveOutClose(mOutHdl);
|
||||
mOutHdl = outHandle;
|
||||
mFormat = format;
|
||||
|
||||
mDevice->DeviceName = PlaybackDevices[DeviceID];
|
||||
|
|
@ -312,11 +306,11 @@ bool WinMMPlayback::reset()
|
|||
}
|
||||
setDefaultWFXChannelOrder();
|
||||
|
||||
uint BufferSize{mDevice->UpdateSize * mFormat.nChannels * mDevice->bytesFromFmt()};
|
||||
const uint BufferSize{mDevice->UpdateSize * mFormat.nChannels * mDevice->bytesFromFmt()};
|
||||
|
||||
al_free(mWaveBuffer[0].lpData);
|
||||
decltype(mBuffer)(BufferSize*mWaveBuffer.size()).swap(mBuffer);
|
||||
mWaveBuffer[0] = WAVEHDR{};
|
||||
mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize * mWaveBuffer.size()));
|
||||
mWaveBuffer[0].lpData = mBuffer.data();
|
||||
mWaveBuffer[0].dwBufferLength = BufferSize;
|
||||
for(size_t i{1};i < mWaveBuffer.size();i++)
|
||||
{
|
||||
|
|
@ -369,16 +363,17 @@ struct WinMMCapture final : public BackendBase {
|
|||
|
||||
int captureProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
void open(std::string_view name) override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
void captureSamples(std::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
|
||||
std::atomic<uint> mReadable{0u};
|
||||
al::semaphore mSem;
|
||||
uint mIdx{0};
|
||||
std::array<WAVEHDR,4> mWaveBuffer{};
|
||||
al::vector<char,16> mBuffer;
|
||||
|
||||
HWAVEIN mInHdl{nullptr};
|
||||
|
||||
|
|
@ -388,8 +383,6 @@ struct WinMMCapture final : public BackendBase {
|
|||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
std::thread mThread;
|
||||
|
||||
DEF_NEWDEL(WinMMCapture)
|
||||
};
|
||||
|
||||
WinMMCapture::~WinMMCapture()
|
||||
|
|
@ -398,14 +391,11 @@ WinMMCapture::~WinMMCapture()
|
|||
if(mInHdl)
|
||||
waveInClose(mInHdl);
|
||||
mInHdl = nullptr;
|
||||
|
||||
al_free(mWaveBuffer[0].lpData);
|
||||
std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{});
|
||||
}
|
||||
|
||||
/* WinMMCapture::waveInProc
|
||||
*
|
||||
* Posts a message to 'WinMMCapture::captureProc' everytime a WaveIn Buffer is
|
||||
* Posts a message to 'WinMMCapture::captureProc' every time a WaveIn Buffer is
|
||||
* completed and returns to the application (with more data).
|
||||
*/
|
||||
void CALLBACK WinMMCapture::waveInProc(HWAVEIN, UINT msg, DWORD_PTR, DWORD_PTR) noexcept
|
||||
|
|
@ -417,7 +407,7 @@ void CALLBACK WinMMCapture::waveInProc(HWAVEIN, UINT msg, DWORD_PTR, DWORD_PTR)
|
|||
|
||||
int WinMMCapture::captureProc()
|
||||
{
|
||||
althrd_setname(RECORD_THREAD_NAME);
|
||||
althrd_setname(GetRecordThreadName());
|
||||
|
||||
while(!mKillNow.load(std::memory_order_acquire) &&
|
||||
mDevice->Connected.load(std::memory_order_acquire))
|
||||
|
|
@ -434,7 +424,8 @@ int WinMMCapture::captureProc()
|
|||
WAVEHDR &waveHdr = mWaveBuffer[widx];
|
||||
widx = (widx+1) % mWaveBuffer.size();
|
||||
|
||||
mRing->write(waveHdr.lpData, waveHdr.dwBytesRecorded / mFormat.nBlockAlign);
|
||||
std::ignore = mRing->write(waveHdr.lpData,
|
||||
waveHdr.dwBytesRecorded / mFormat.nBlockAlign);
|
||||
mReadable.fetch_sub(1, std::memory_order_acq_rel);
|
||||
waveInAddBuffer(mInHdl, &waveHdr, sizeof(WAVEHDR));
|
||||
} while(--todo);
|
||||
|
|
@ -445,18 +436,18 @@ int WinMMCapture::captureProc()
|
|||
}
|
||||
|
||||
|
||||
void WinMMCapture::open(const char *name)
|
||||
void WinMMCapture::open(std::string_view name)
|
||||
{
|
||||
if(CaptureDevices.empty())
|
||||
ProbeCaptureDevices();
|
||||
|
||||
// Find the Device ID matching the deviceName if valid
|
||||
auto iter = name ?
|
||||
auto iter = !name.empty() ?
|
||||
std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), name) :
|
||||
CaptureDevices.cbegin();
|
||||
if(iter == CaptureDevices.cend())
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||
name};
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
|
||||
al::sizei(name), name.data()};
|
||||
auto DeviceID = static_cast<UINT>(std::distance(CaptureDevices.cbegin(), iter));
|
||||
|
||||
switch(mDevice->FmtChans)
|
||||
|
|
@ -470,6 +461,7 @@ void WinMMCapture::open(const char *name)
|
|||
case DevFmtX61:
|
||||
case DevFmtX71:
|
||||
case DevFmtX714:
|
||||
case DevFmtX7144:
|
||||
case DevFmtX3D71:
|
||||
case DevFmtAmbi3D:
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported",
|
||||
|
|
@ -513,14 +505,14 @@ void WinMMCapture::open(const char *name)
|
|||
|
||||
// Allocate circular memory buffer for the captured audio
|
||||
// Make sure circular buffer is at least 100ms in size
|
||||
uint CapturedDataSize{mDevice->BufferSize};
|
||||
CapturedDataSize = static_cast<uint>(maxz(CapturedDataSize, BufferSize*mWaveBuffer.size()));
|
||||
const auto CapturedDataSize = std::max<size_t>(mDevice->BufferSize,
|
||||
BufferSize*mWaveBuffer.size());
|
||||
|
||||
mRing = RingBuffer::Create(CapturedDataSize, mFormat.nBlockAlign, false);
|
||||
|
||||
al_free(mWaveBuffer[0].lpData);
|
||||
decltype(mBuffer)(BufferSize*mWaveBuffer.size()).swap(mBuffer);
|
||||
mWaveBuffer[0] = WAVEHDR{};
|
||||
mWaveBuffer[0].lpData = static_cast<char*>(al_calloc(16, BufferSize * mWaveBuffer.size()));
|
||||
mWaveBuffer[0].lpData = mBuffer.data();
|
||||
mWaveBuffer[0].dwBufferLength = BufferSize;
|
||||
for(size_t i{1};i < mWaveBuffer.size();++i)
|
||||
{
|
||||
|
|
@ -571,8 +563,8 @@ void WinMMCapture::stop()
|
|||
mIdx = 0;
|
||||
}
|
||||
|
||||
void WinMMCapture::captureSamples(al::byte *buffer, uint samples)
|
||||
{ mRing->read(buffer, samples); }
|
||||
void WinMMCapture::captureSamples(std::byte *buffer, uint samples)
|
||||
{ std::ignore = mRing->read(buffer, samples); }
|
||||
|
||||
uint WinMMCapture::availableSamples()
|
||||
{ return static_cast<uint>(mRing->readSpace()); }
|
||||
|
|
@ -586,26 +578,23 @@ bool WinMMBackendFactory::init()
|
|||
bool WinMMBackendFactory::querySupport(BackendType type)
|
||||
{ return type == BackendType::Playback || type == BackendType::Capture; }
|
||||
|
||||
std::string WinMMBackendFactory::probe(BackendType type)
|
||||
auto WinMMBackendFactory::enumerate(BackendType type) -> std::vector<std::string>
|
||||
{
|
||||
std::string outnames;
|
||||
std::vector<std::string> outnames;
|
||||
auto add_device = [&outnames](const std::string &dname) -> void
|
||||
{
|
||||
/* +1 to also append the null char (to ensure a null-separated list and
|
||||
* double-null terminated list).
|
||||
*/
|
||||
if(!dname.empty())
|
||||
outnames.append(dname.c_str(), dname.length()+1);
|
||||
};
|
||||
{ if(!dname.empty()) outnames.emplace_back(dname); };
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case BackendType::Playback:
|
||||
ProbePlaybackDevices();
|
||||
outnames.reserve(PlaybackDevices.size());
|
||||
std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device);
|
||||
break;
|
||||
|
||||
case BackendType::Capture:
|
||||
ProbeCaptureDevices();
|
||||
outnames.reserve(CaptureDevices.size());
|
||||
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
|
||||
struct WinMMBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
bool init() override;
|
||||
auto init() -> bool final;
|
||||
|
||||
bool querySupport(BackendType type) override;
|
||||
auto querySupport(BackendType type) -> bool final;
|
||||
|
||||
std::string probe(BackendType type) override;
|
||||
auto enumerate(BackendType type) -> std::vector<std::string> final;
|
||||
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
static auto getFactory() -> BackendFactory&;
|
||||
};
|
||||
|
||||
#endif /* BACKENDS_WINMM_H */
|
||||
|
|
|
|||
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