aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-11-28 12:51:46 +0100
committerSven Gothel <[email protected]>2023-11-28 12:51:46 +0100
commit1aaf4f070011490bcece50394b9b32dfa593fd9e (patch)
tree17d68284e401a35eea3d3a574d986d446a60763a
parent6e7cee4fa9a8af03f28ca26cd89f8357390dfc90 (diff)
parent571b546f35eead77ce109f8d4dd6c3de3199d573 (diff)
Merge remote-tracking branch 'upstream/master'
-rw-r--r--.github/workflows/ci.yml46
-rw-r--r--.github/workflows/makemhr.yml2
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt248
-rw-r--r--LICENSE-pffft38
-rw-r--r--OpenALConfig.cmake.in2
-rw-r--r--README.md27
-rw-r--r--al/auxeffectslot.cpp229
-rw-r--r--al/auxeffectslot.h7
-rw-r--r--al/buffer.cpp726
-rw-r--r--al/buffer.h7
-rw-r--r--al/debug.cpp570
-rw-r--r--al/debug.h69
-rw-r--r--al/direct_defs.h127
-rw-r--r--al/eax/api.h32
-rw-r--r--al/eax/call.h4
-rw-r--r--al/eax/effect.h113
-rw-r--r--al/eax/exception.cpp47
-rw-r--r--al/eax/exception.h6
-rw-r--r--al/eax/fx_slot_index.h7
-rw-r--r--al/eax/globals.cpp21
-rw-r--r--al/eax/globals.h24
-rw-r--r--al/eax/utils.cpp6
-rw-r--r--al/eax/utils.h12
-rw-r--r--al/eax/x_ram.h11
-rw-r--r--al/effect.cpp143
-rw-r--r--al/effect.h4
-rw-r--r--al/effects/autowah.cpp56
-rw-r--r--al/effects/chorus.cpp32
-rw-r--r--al/effects/compressor.cpp22
-rw-r--r--al/effects/convolution.cpp34
-rw-r--r--al/effects/distortion.cpp64
-rw-r--r--al/effects/echo.cpp64
-rw-r--r--al/effects/equalizer.cpp109
-rw-r--r--al/effects/fshifter.cpp53
-rw-r--r--al/effects/modulator.cpp53
-rw-r--r--al/effects/null.cpp5
-rw-r--r--al/effects/pshifter.cpp32
-rw-r--r--al/effects/reverb.cpp142
-rw-r--r--al/effects/vmorpher.cpp84
-rw-r--r--al/error.cpp54
-rw-r--r--al/event.cpp160
-rw-r--r--al/extension.cpp41
-rw-r--r--al/filter.cpp608
-rw-r--r--al/filter.h45
-rw-r--r--al/listener.cpp196
-rw-r--r--al/source.cpp2631
-rw-r--r--al/source.h14
-rw-r--r--al/state.cpp866
-rw-r--r--alc/alc.cpp1253
-rw-r--r--alc/alconfig.cpp49
-rw-r--r--alc/alconfig.h12
-rw-r--r--alc/alu.cpp483
-rw-r--r--alc/alu.h8
-rw-r--r--alc/backends/alsa.cpp48
-rw-r--r--alc/backends/base.cpp3
-rw-r--r--alc/backends/base.h11
-rw-r--r--alc/backends/coreaudio.cpp239
-rw-r--r--alc/backends/coreaudio.h2
-rw-r--r--alc/backends/dsound.cpp81
-rw-r--r--alc/backends/jack.cpp37
-rw-r--r--alc/backends/loopback.cpp4
-rw-r--r--alc/backends/null.cpp16
-rw-r--r--alc/backends/oboe.cpp43
-rw-r--r--alc/backends/opensl.cpp50
-rw-r--r--alc/backends/oss.cpp42
-rw-r--r--alc/backends/pipewire.cpp654
-rw-r--r--alc/backends/pipewire.h2
-rw-r--r--alc/backends/portaudio.cpp31
-rw-r--r--alc/backends/pulseaudio.cpp252
-rw-r--r--alc/backends/pulseaudio.h2
-rw-r--r--alc/backends/sdl2.cpp32
-rw-r--r--alc/backends/sndio.cpp46
-rw-r--r--alc/backends/solaris.cpp23
-rw-r--r--alc/backends/wasapi.cpp1813
-rw-r--r--alc/backends/wasapi.h2
-rw-r--r--alc/backends/wave.cpp25
-rw-r--r--alc/backends/winmm.cpp37
-rw-r--r--alc/context.cpp259
-rw-r--r--alc/context.h81
-rw-r--r--alc/device.cpp6
-rw-r--r--alc/device.h31
-rw-r--r--alc/effects/chorus.cpp16
-rw-r--r--alc/effects/convolution.cpp388
-rw-r--r--alc/effects/dedicated.cpp8
-rw-r--r--alc/effects/distortion.cpp4
-rw-r--r--alc/effects/echo.cpp10
-rw-r--r--alc/effects/fshifter.cpp12
-rw-r--r--alc/effects/modulator.cpp150
-rw-r--r--alc/effects/pshifter.cpp53
-rw-r--r--alc/effects/reverb.cpp163
-rw-r--r--alc/events.cpp95
-rw-r--r--alc/events.h50
-rw-r--r--alc/export_list.h915
-rw-r--r--alc/inprogext.h417
-rw-r--r--alc/panning.cpp92
-rw-r--r--alsoftrc.sample28
-rw-r--r--appveyor.yml4
-rw-r--r--cmake/FindOpenSL.cmake12
-rw-r--r--common/albit.h12
-rw-r--r--common/albyte.h17
-rw-r--r--common/alcomplex.cpp142
-rw-r--r--common/alcomplex.h18
-rw-r--r--common/aldeque.h16
-rw-r--r--common/almalloc.h75
-rw-r--r--common/alnumbers.h16
-rw-r--r--common/alnumeric.h64
-rw-r--r--common/aloptional.h353
-rw-r--r--common/alsem.cpp (renamed from common/threads.cpp)81
-rw-r--r--common/alsem.h (renamed from common/threads.h)27
-rw-r--r--common/alspan.h115
-rw-r--r--common/althrd_setname.cpp76
-rw-r--r--common/althrd_setname.h6
-rw-r--r--common/comptr.h129
-rw-r--r--common/dynload.cpp3
-rw-r--r--common/opthelpers.h3
-rw-r--r--common/pffft.cpp2259
-rw-r--r--common/pffft.h192
-rw-r--r--common/phase_shifter.h39
-rw-r--r--common/polyphase_resampler.cpp76
-rw-r--r--common/ringbuffer.cpp85
-rw-r--r--common/ringbuffer.h39
-rw-r--r--common/strutils.cpp29
-rw-r--r--common/strutils.h12
-rw-r--r--common/win_main_utf8.h13
-rw-r--r--config.h.in6
-rw-r--r--core/ambdec.cpp10
-rw-r--r--core/ambdec.h4
-rw-r--r--core/ambidefs.cpp236
-rw-r--r--core/ambidefs.h239
-rw-r--r--core/async_event.h92
-rw-r--r--core/bformatdec.cpp64
-rw-r--r--core/bformatdec.h24
-rw-r--r--core/bsinc_tables.cpp92
-rw-r--r--core/buffer_storage.cpp2
-rw-r--r--core/buffer_storage.h5
-rw-r--r--core/context.cpp11
-rw-r--r--core/context.h18
-rw-r--r--core/converter.cpp102
-rw-r--r--core/converter.h1
-rw-r--r--core/cpu_caps.cpp5
-rw-r--r--core/cpu_caps.h5
-rw-r--r--core/cubic_tables.cpp16
-rw-r--r--core/dbus_wrap.cpp8
-rw-r--r--core/dbus_wrap.h4
-rw-r--r--core/device.h34
-rw-r--r--core/effects/base.h7
-rw-r--r--core/filters/nfc.h2
-rw-r--r--core/fmt_traits.h28
-rw-r--r--core/fpu_ctrl.cpp71
-rw-r--r--core/helpers.cpp86
-rw-r--r--core/helpers.h14
-rw-r--r--core/hrtf.cpp70
-rw-r--r--core/hrtf.h6
-rw-r--r--core/logging.cpp106
-rw-r--r--core/logging.h33
-rw-r--r--core/mastering.cpp4
-rw-r--r--core/mixer.cpp7
-rw-r--r--core/mixer.h8
-rw-r--r--core/mixer/mixer_neon.cpp4
-rw-r--r--core/mixer/mixer_sse.cpp4
-rw-r--r--core/uhjfilter.cpp260
-rw-r--r--core/uhjfilter.h48
-rw-r--r--core/uiddefs.cpp6
-rw-r--r--core/voice.cpp73
-rw-r--r--core/voice.h9
-rw-r--r--docs/ambisonics.txt4
-rw-r--r--docs/env-vars.txt9
-rw-r--r--examples/alconvolve.c36
-rw-r--r--examples/alffplay.cpp12
-rw-r--r--examples/alhrtf.c2
-rw-r--r--examples/allatency.c2
-rw-r--r--examples/alloopback.c2
-rw-r--r--examples/almultireverb.c6
-rw-r--r--examples/alplay.c2
-rw-r--r--examples/alreverb.c4
-rw-r--r--examples/alstreamcb.cpp4
-rw-r--r--examples/altonegen.c75
-rw-r--r--include/AL/al.h311
-rw-r--r--include/AL/alc.h99
-rw-r--r--include/AL/alext.h270
-rw-r--r--include/AL/efx.h136
-rw-r--r--router/al.cpp14
-rw-r--r--router/alc.cpp44
-rw-r--r--router/router.cpp14
-rw-r--r--router/router.h18
-rw-r--r--tests/CMakeLists.txt24
-rw-r--r--tests/example.t.cpp16
-rw-r--r--utils/makemhr/loaddef.cpp7
-rw-r--r--utils/makemhr/loadsofa.cpp6
-rw-r--r--utils/makemhr/makemhr.cpp5
-rw-r--r--utils/makemhr/makemhr.h4
-rw-r--r--utils/openal-info.c64
-rw-r--r--utils/uhjdecoder.cpp53
-rw-r--r--utils/uhjencoder.cpp10
195 files changed, 13985 insertions, 8977 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1a94c760..b1c3b413 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,6 +1,6 @@
name: CI
-on: [push]
+on: [push, pull_request]
jobs:
build:
@@ -14,6 +14,7 @@ jobs:
name: "Win32-Release",
os: windows-latest,
cmake_opts: "-A Win32 \
+ -DALSOFT_TESTS=ON \
-DALSOFT_BUILD_ROUTER=ON \
-DALSOFT_REQUIRE_WINMM=ON \
-DALSOFT_REQUIRE_DSOUND=ON \
@@ -24,6 +25,7 @@ jobs:
name: "Win32-Debug",
os: windows-latest,
cmake_opts: "-A Win32 \
+ -DALSOFT_TESTS=ON \
-DALSOFT_BUILD_ROUTER=ON \
-DALSOFT_REQUIRE_WINMM=ON \
-DALSOFT_REQUIRE_DSOUND=ON \
@@ -34,6 +36,7 @@ jobs:
name: "Win64-Release",
os: windows-latest,
cmake_opts: "-A x64 \
+ -DALSOFT_TESTS=ON \
-DALSOFT_BUILD_ROUTER=ON \
-DALSOFT_REQUIRE_WINMM=ON \
-DALSOFT_REQUIRE_DSOUND=ON \
@@ -44,6 +47,7 @@ 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 \
@@ -51,9 +55,34 @@ jobs:
build_type: "Debug"
}
- {
+ name: "Win64-UWP",
+ os: windows-latest,
+ cmake_opts: "-A x64 \
+ -DALSOFT_TESTS=OFF \
+ -DCMAKE_SYSTEM_NAME=WindowsStore \
+ \"-DCMAKE_SYSTEM_VERSION=10.0\" \
+ -DALSOFT_BUILD_ROUTER=ON \
+ -DALSOFT_REQUIRE_WASAPI=ON",
+ build_type: "Release"
+ }
+ - {
name: "macOS-Release",
os: macos-latest,
- cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON",
+ cmake_opts: "-DALSOFT_REQUIRE_COREAUDIO=ON \
+ -DALSOFT_TESTS=ON",
+ build_type: "Release"
+ }
+ - {
+ name: "iOS-Release",
+ os: macos-latest,
+ cmake_opts: "-GXcode \
+ -DCMAKE_SYSTEM_NAME=iOS \
+ -DALSOFT_REQUIRE_COREAUDIO=ON \
+ -DALSOFT_UTILS=OFF \
+ -DALSOFT_EXAMPLES=OFF \
+ -DALSOFT_TESTS=OFF \
+ -DALSOFT_INSTALL=OFF \
+ \"-DCMAKE_OSX_ARCHITECTURES=arm64\"",
build_type: "Release"
}
- {
@@ -65,7 +94,8 @@ jobs:
-DALSOFT_REQUIRE_PORTAUDIO=ON \
-DALSOFT_REQUIRE_PULSEAUDIO=ON \
-DALSOFT_REQUIRE_JACK=ON \
- -DALSOFT_REQUIRE_PIPEWIRE=ON",
+ -DALSOFT_REQUIRE_PIPEWIRE=ON \
+ -DALSOFT_TESTS=ON",
deps_cmdline: "sudo apt update && sudo apt-get install -qq \
libpulse-dev \
portaudio19-dev \
@@ -78,7 +108,7 @@ jobs:
}
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v3
- name: Install Dependencies
shell: bash
@@ -97,6 +127,12 @@ jobs:
run: |
cmake --build build --config ${{matrix.config.build_type}}
+ - name: Test
+ shell: bash
+ run: |
+ cd build
+ ctest
+
- name: Create Archive
if: ${{ matrix.config.os == 'windows-latest' }}
shell: bash
@@ -109,7 +145,7 @@ jobs:
- name: Upload Archive
# Upload package as an artifact of this workflow.
- uses: actions/[email protected]
+ uses: actions/[email protected]
if: ${{ matrix.config.os == 'windows-latest' }}
with:
name: soft_oal-${{matrix.config.name}}
diff --git a/.github/workflows/makemhr.yml b/.github/workflows/makemhr.yml
index 7bde284c..65486150 100644
--- a/.github/workflows/makemhr.yml
+++ b/.github/workflows/makemhr.yml
@@ -55,7 +55,7 @@ jobs:
copy "libmysofa/windows/third-party/zlib-1.2.11/bin/zlib.dll" "Artifacts/zlib.dll"
- name: Upload makemhr artifact
- uses: actions/[email protected]
+ uses: actions/[email protected]
with:
name: makemhr
path: "Artifacts/"
diff --git a/.gitignore b/.gitignore
index 4a8212b5..688980a5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
build*/
winbuild
win64build
+.vs/
## kdevelop
*.kdev4
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 85571b45..2d572908 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,7 @@
# CMake build file list for OpenAL
-CMAKE_MINIMUM_REQUIRED(VERSION 3.2.0)
+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,6 +189,20 @@ set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0)
set(EXPORT_DECL "")
+# 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()
+
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)
@@ -341,32 +358,32 @@ else()
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()
@@ -452,6 +469,7 @@ if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON)
endif()
+set(ALSOFT_FORCE_ALIGN )
set(SSE_FLAGS )
set(FPMATH_SET "0")
if(CMAKE_SIZEOF_VOID_P MATCHES "4" AND HAVE_SSE2)
@@ -476,6 +494,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()
@@ -517,7 +536,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)
@@ -599,20 +618,21 @@ check_symbol_exists(getopt unistd.h HAVE_GETOPT)
# router, and certain tools and examples.
set(COMMON_OBJS
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/altraits.h
common/atomic.h
common/comptr.h
@@ -620,6 +640,8 @@ set(COMMON_OBJS
common/dynload.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
@@ -628,8 +650,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)
@@ -752,6 +772,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
@@ -808,6 +831,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)
@@ -825,7 +851,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
@@ -1002,37 +1027,39 @@ 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()
@@ -1152,7 +1179,8 @@ 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)
@@ -1322,11 +1350,11 @@ 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)
+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)
@@ -1361,7 +1389,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>
@@ -1392,13 +1420,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)
@@ -1475,8 +1521,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,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
@@ -1507,7 +1552,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 "")
@@ -1593,7 +1641,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})
@@ -1602,7 +1650,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()
@@ -1615,7 +1663,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
@@ -1657,22 +1705,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)
@@ -1683,43 +1731,43 @@ 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
- ${UNICODE_FLAG})
+ 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)
@@ -1733,7 +1781,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)
@@ -1770,7 +1818,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)
@@ -1782,6 +1830,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}
diff --git a/LICENSE-pffft b/LICENSE-pffft
new file mode 100644
index 00000000..5f1a8493
--- /dev/null
+++ b/LICENSE-pffft
@@ -0,0 +1,38 @@
+A modified PFFFT is included, with the following license.
+
+Copyright (c) 2023 Christopher Robinson
+
+Copyright (c) 2013 Julien Pommier ( [email protected] )
+
+Copyright (c) 2004 the University Corporation for Atmospheric
+Research ("UCAR"). All rights reserved. Developed by NCAR's
+Computational and Information Systems Laboratory, UCAR,
+www.cisl.ucar.edu.
+
+Redistribution and use of the Software in source and binary forms,
+with or without modification, is permitted provided that the
+following conditions are met:
+
+- Neither the names of NCAR's Computational and Information Systems
+Laboratory, the University Corporation for Atmospheric Research,
+nor the names of its sponsors or contributors may be used to
+endorse or promote products derived from this Software without
+specific prior written permission.
+
+- Redistributions of source code must retain the above copyright
+notices, this list of conditions, and the disclaimer below.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions, and the disclaimer below in the
+documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
diff --git a/OpenALConfig.cmake.in b/OpenALConfig.cmake.in
index 128c1a4e..9704d3c4 100644
--- a/OpenALConfig.cmake.in
+++ b/OpenALConfig.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.1...3.18)
include("${CMAKE_CURRENT_LIST_DIR}/OpenALTargets.cmake")
diff --git a/README.md b/README.md
index 50b7bfb4..dac53e71 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,33 @@ as application-agnostic behavior of the library. See alsoftrc.sample for
available settings.
+Language Bindings
+-----------------
+
+As a C API, OpenAL Soft can be used directly by any language that can use
+functions with C linkage. For languages that can't directly use C-style
+headers, bindings may be developed to allow code written in that language to
+call into the library. Some bindings for some languages are listed here.
+
+C# Bindings:
+* [OpenTK](https://opentk.net/) includes low-level C# bindings for the OpenAL
+API, including some extensions. It also includes utility libraries for math and
+linear algebra, which can be useful for 3D calculations.
+
+Java Bindings:
+* [JOAL](https://jogamp.org/joal/www/), part of the JogAmp project, includes
+Java bindings for the OpenAL API, usable with OpenAL Soft. It also includes a
+higher level Sound3D Toolkit API and utility functions to make easier use of
+OpenAL features and capabilities.
+
+Python Bindings:
+* [PyOpenAL](https://pypi.org/project/PyOpenAL/). Also includes methods to play
+wave files and, with PyOgg, also Vorbis, Opus, and FLAC.
+
+Other bindings for these and other languages also exist. This list will grow as
+more bindings are found.
+
+
Acknowledgements
----------------
diff --git a/al/auxeffectslot.cpp b/al/auxeffectslot.cpp
index 285da1d4..fb646389 100644
--- a/al/auxeffectslot.cpp
+++ b/al/auxeffectslot.cpp
@@ -47,6 +47,7 @@
#include "core/except.h"
#include "core/fpu_ctrl.h"
#include "core/logging.h"
+#include "direct_defs.h"
#include "effect.h"
#include "opthelpers.h"
@@ -165,7 +166,7 @@ void AddActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext *co
curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel);
context->mDevice->waitForMix();
- al::destroy_n(curarray->end(), curarray->size());
+ std::destroy_n(curarray->end(), curarray->size());
delete curarray;
}
@@ -204,7 +205,7 @@ void RemoveActiveEffectSlots(const al::span<ALeffectslot*> auxslots, ALCcontext
curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel);
context->mDevice->waitForMix();
- al::destroy_n(curarray->end(), curarray->size());
+ std::destroy_n(curarray->end(), curarray->size());
delete curarray;
}
@@ -229,7 +230,7 @@ EffectSlotType EffectSlotTypeFromEnum(ALenum type)
case AL_EFFECT_EAXREVERB: return EffectSlotType::EAXReverb;
case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return EffectSlotType::DedicatedLFE;
case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::DedicatedDialog;
- case AL_EFFECT_CONVOLUTION_REVERB_SOFT: return EffectSlotType::Convolution;
+ case AL_EFFECT_CONVOLUTION_SOFT: return EffectSlotType::Convolution;
}
ERR("Unhandled effect enum: 0x%04x\n", type);
return EffectSlotType::None;
@@ -238,7 +239,7 @@ EffectSlotType EffectSlotTypeFromEnum(ALenum type)
bool EnsureEffectSlots(ALCcontext *context, size_t needed)
{
size_t count{std::accumulate(context->mEffectSlotList.cbegin(),
- context->mEffectSlotList.cend(), size_t{0},
+ context->mEffectSlotList.cend(), 0_uz,
[](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
{ return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
@@ -285,11 +286,13 @@ ALeffectslot *AllocEffectSlot(ALCcontext *context)
void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot)
{
+ context->mEffectSlotNames.erase(slot->id);
+
const ALuint id{slot->id - 1};
const size_t lidx{id >> 6};
const ALuint slidx{id & 0x3f};
- al::destroy_at(slot);
+ std::destroy_at(slot);
context->mEffectSlotList[lidx].FreeMask |= 1_u64 << slidx;
context->mNumEffectSlots--;
@@ -309,12 +312,10 @@ inline void UpdateProps(ALeffectslot *slot, ALCcontext *context)
} // namespace
-AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGenAuxiliaryEffectSlots, ALsizei, ALuint*)
+FORCE_ALIGN void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n,
+ ALuint *effectslots) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Generating %d effect slots", n);
if(n <= 0) UNLIKELY return;
@@ -327,7 +328,7 @@ START_API_FUNC
device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n);
return;
}
- if(!EnsureEffectSlots(context.get(), static_cast<ALuint>(n)))
+ if(!EnsureEffectSlots(context, static_cast<ALuint>(n)))
{
context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n,
(n==1) ? "" : "s");
@@ -336,29 +337,26 @@ START_API_FUNC
if(n == 1)
{
- ALeffectslot *slot{AllocEffectSlot(context.get())};
+ ALeffectslot *slot{AllocEffectSlot(context)};
effectslots[0] = slot->id;
}
else
{
- al::vector<ALuint> ids;
+ std::vector<ALuint> ids;
ALsizei count{n};
ids.reserve(static_cast<ALuint>(count));
do {
- ALeffectslot *slot{AllocEffectSlot(context.get())};
+ ALeffectslot *slot{AllocEffectSlot(context)};
ids.emplace_back(slot->id);
} while(--count);
std::copy(ids.cbegin(), ids.cend(), effectslots);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots, ALsizei, const ALuint*)
+FORCE_ALIGN void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n,
+ const ALuint *effectslots) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Deleting %d effect slots", n);
if(n <= 0) UNLIKELY return;
@@ -366,7 +364,7 @@ START_API_FUNC
std::lock_guard<std::mutex> _{context->mEffectSlotLock};
if(n == 1)
{
- ALeffectslot *slot{LookupEffectSlot(context.get(), effectslots[0])};
+ ALeffectslot *slot{LookupEffectSlot(context, effectslots[0])};
if(!slot) UNLIKELY
{
context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[0]);
@@ -378,15 +376,15 @@ START_API_FUNC
effectslots[0]);
return;
}
- RemoveActiveEffectSlots({&slot, 1u}, context.get());
- FreeEffectSlot(context.get(), slot);
+ RemoveActiveEffectSlots({&slot, 1u}, context);
+ FreeEffectSlot(context, slot);
}
else
{
- auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
+ auto slots = std::vector<ALeffectslot*>(static_cast<ALuint>(n));
for(size_t i{0};i < slots.size();++i)
{
- ALeffectslot *slot{LookupEffectSlot(context.get(), effectslots[i])};
+ ALeffectslot *slot{LookupEffectSlot(context, effectslots[i])};
if(!slot) UNLIKELY
{
context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[i]);
@@ -410,30 +408,24 @@ START_API_FUNC
slots.erase(slots_end, slots.end());
/* All effectslots are valid, remove and delete them */
- RemoveActiveEffectSlots(slots, context.get());
+ RemoveActiveEffectSlots(slots, context);
for(ALeffectslot *slot : slots)
- FreeEffectSlot(context.get(), slot);
+ FreeEffectSlot(context, slot);
}
}
-END_API_FUNC
-AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsAuxiliaryEffectSlot, ALuint)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context,
+ ALuint effectslot) noexcept
{
- ContextRef context{GetContextRef()};
- if(context) LIKELY
- {
- std::lock_guard<std::mutex> _{context->mEffectSlotLock};
- if(LookupEffectSlot(context.get(), effectslot) != nullptr)
- return AL_TRUE;
- }
+ std::lock_guard<std::mutex> _{context->mEffectSlotLock};
+ if(LookupEffectSlot(context, effectslot) != nullptr)
+ return AL_TRUE;
return AL_FALSE;
}
-END_API_FUNC
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid)
-START_API_FUNC
+AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) noexcept
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
@@ -454,10 +446,8 @@ START_API_FUNC
AddActiveEffectSlots({&slot, 1}, context.get());
slot->mState = SlotState::Playing;
}
-END_API_FUNC
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids)
-START_API_FUNC
+AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) noexcept
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
@@ -466,7 +456,7 @@ START_API_FUNC
context->setError(AL_INVALID_VALUE, "Playing %d effect slots", n);
if(n <= 0) UNLIKELY return;
- auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
+ auto slots = std::vector<ALeffectslot*>(static_cast<ALuint>(n));
std::lock_guard<std::mutex> _{context->mEffectSlotLock};
for(size_t i{0};i < slots.size();++i)
{
@@ -489,10 +479,8 @@ START_API_FUNC
for(auto slot : slots)
slot->mState = SlotState::Playing;
}
-END_API_FUNC
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid)
-START_API_FUNC
+AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) noexcept
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
@@ -508,10 +496,8 @@ START_API_FUNC
RemoveActiveEffectSlots({&slot, 1}, context.get());
slot->mState = SlotState::Stopped;
}
-END_API_FUNC
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids)
-START_API_FUNC
+AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) noexcept
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
@@ -520,7 +506,7 @@ START_API_FUNC
context->setError(AL_INVALID_VALUE, "Stopping %d effect slots", n);
if(n <= 0) UNLIKELY return;
- auto slots = al::vector<ALeffectslot*>(static_cast<ALuint>(n));
+ auto slots = std::vector<ALeffectslot*>(static_cast<ALuint>(n));
std::lock_guard<std::mutex> _{context->mEffectSlotLock};
for(size_t i{0};i < slots.size();++i)
{
@@ -538,18 +524,15 @@ START_API_FUNC
for(auto slot : slots)
slot->mState = SlotState::Stopped;
}
-END_API_FUNC
-AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alAuxiliaryEffectSloti, ALuint, ALenum, ALint)
+FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot,
+ ALenum param, ALint value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mEffectSlotLock};
- ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
+ ALeffectslot *slot = LookupEffectSlot(context, effectslot);
if(!slot) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
@@ -565,12 +548,12 @@ START_API_FUNC
std::lock_guard<std::mutex> ___{device->EffectLock};
ALeffect *effect{value ? LookupEffect(device, static_cast<ALuint>(value)) : nullptr};
if(effect)
- err = slot->initEffect(effect->type, effect->Props, context.get());
+ err = slot->initEffect(effect->id, effect->type, effect->Props, context);
else
{
if(value != 0)
return context->setError(AL_INVALID_VALUE, "Invalid effect ID %u", value);
- err = slot->initEffect(AL_EFFECT_NULL, EffectProps{}, context.get());
+ err = slot->initEffect(0, AL_EFFECT_NULL, EffectProps{}, context);
}
}
if(err != AL_NO_ERROR) UNLIKELY
@@ -581,9 +564,9 @@ START_API_FUNC
if(slot->mState == SlotState::Initial) UNLIKELY
{
slot->mPropsDirty = false;
- slot->updateProps(context.get());
+ slot->updateProps(context);
- AddActiveEffectSlots({&slot, 1}, context.get());
+ AddActiveEffectSlots({&slot, 1}, context);
slot->mState = SlotState::Playing;
return;
}
@@ -599,7 +582,7 @@ START_API_FUNC
break;
case AL_EFFECTSLOT_TARGET_SOFT:
- target = LookupEffectSlot(context.get(), static_cast<ALuint>(value));
+ target = LookupEffectSlot(context, static_cast<ALuint>(value));
if(value && !target)
return context->setError(AL_INVALID_VALUE, "Invalid effect slot target ID");
if(slot->Target == target) UNLIKELY
@@ -623,7 +606,7 @@ START_API_FUNC
if(target) IncrementRef(target->ref);
DecrementRef(oldtarget->ref);
slot->Target = target;
- slot->updateProps(context.get());
+ slot->updateProps(context);
return;
}
@@ -677,12 +660,12 @@ START_API_FUNC
return context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x",
param);
}
- UpdateProps(slot, context.get());
+ UpdateProps(slot, context);
}
-END_API_FUNC
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotiv, ALuint, ALenum, const ALint*)
+FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot,
+ ALenum param, const ALint *values) noexcept
{
switch(param)
{
@@ -691,15 +674,12 @@ START_API_FUNC
case AL_EFFECTSLOT_TARGET_SOFT:
case AL_EFFECTSLOT_STATE_SOFT:
case AL_BUFFER:
- alAuxiliaryEffectSloti(effectslot, param, values[0]);
+ alAuxiliaryEffectSlotiDirect(context, effectslot, param, values[0]);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mEffectSlotLock};
- ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
+ ALeffectslot *slot = LookupEffectSlot(context, effectslot);
if(!slot) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
@@ -710,17 +690,14 @@ START_API_FUNC
"Invalid effect slot integer-vector property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotf, ALuint, ALenum, ALfloat)
+FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot,
+ ALenum param, ALfloat value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mEffectSlotLock};
- ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
+ ALeffectslot *slot = LookupEffectSlot(context, effectslot);
if(!slot) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
@@ -738,25 +715,22 @@ START_API_FUNC
return context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x",
param);
}
- UpdateProps(slot, context.get());
+ UpdateProps(slot, context);
}
-END_API_FUNC
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotfv, ALuint, ALenum, const ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot,
+ ALenum param, const ALfloat *values) noexcept
{
switch(param)
{
case AL_EFFECTSLOT_GAIN:
- alAuxiliaryEffectSlotf(effectslot, param, values[0]);
+ alAuxiliaryEffectSlotfDirect(context, effectslot, param, values[0]);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mEffectSlotLock};
- ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
+ ALeffectslot *slot = LookupEffectSlot(context, effectslot);
if(!slot) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
@@ -767,22 +741,23 @@ START_API_FUNC
"Invalid effect slot float-vector property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSloti, ALuint, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context,
+ ALuint effectslot, ALenum param, ALint *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mEffectSlotLock};
- ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
+ ALeffectslot *slot = LookupEffectSlot(context, effectslot);
if(!slot) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
switch(param)
{
+ case AL_EFFECTSLOT_EFFECT:
+ *value = static_cast<ALint>(slot->EffectId);
+ break;
+
case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
*value = slot->AuxSendAuto ? AL_TRUE : AL_FALSE;
break;
@@ -809,10 +784,10 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv, ALuint, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context,
+ ALuint effectslot, ALenum param, ALint *values) noexcept
{
switch(param)
{
@@ -821,15 +796,12 @@ START_API_FUNC
case AL_EFFECTSLOT_TARGET_SOFT:
case AL_EFFECTSLOT_STATE_SOFT:
case AL_BUFFER:
- alGetAuxiliaryEffectSloti(effectslot, param, values);
+ alGetAuxiliaryEffectSlotiDirect(context, effectslot, param, values);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mEffectSlotLock};
- ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
+ ALeffectslot *slot = LookupEffectSlot(context, effectslot);
if(!slot) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
@@ -840,16 +812,13 @@ START_API_FUNC
param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotf, ALuint, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context,
+ ALuint effectslot, ALenum param, ALfloat *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mEffectSlotLock};
- ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
+ ALeffectslot *slot = LookupEffectSlot(context, effectslot);
if(!slot) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
@@ -863,23 +832,20 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv, ALuint, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context,
+ ALuint effectslot, ALenum param, ALfloat *values) noexcept
{
switch(param)
{
case AL_EFFECTSLOT_GAIN:
- alGetAuxiliaryEffectSlotf(effectslot, param, values);
+ alGetAuxiliaryEffectSlotfDirect(context, effectslot, param, values);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mEffectSlotLock};
- ALeffectslot *slot = LookupEffectSlot(context.get(), effectslot);
+ ALeffectslot *slot = LookupEffectSlot(context, effectslot);
if(!slot) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot);
@@ -890,7 +856,6 @@ START_API_FUNC
param);
}
}
-END_API_FUNC
ALeffectslot::ALeffectslot(ALCcontext *context)
@@ -926,7 +891,7 @@ ALeffectslot::~ALeffectslot()
mSlot->InUse = false;
}
-ALenum ALeffectslot::initEffect(ALenum effectType, const EffectProps &effectProps,
+ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps,
ALCcontext *context)
{
EffectSlotType newtype{EffectSlotTypeFromEnum(effectType)};
@@ -955,6 +920,7 @@ ALenum ALeffectslot::initEffect(ALenum effectType, const EffectProps &effectProp
}
else if(newtype != EffectSlotType::None)
Effect.Props = effectProps;
+ EffectId = effectId;
/* Remove state references from old effect slot property updates. */
EffectSlotProps *props{context->mFreeEffectslotProps.load()};
@@ -1003,6 +969,17 @@ void ALeffectslot::updateProps(ALCcontext *context)
}
}
+void ALeffectslot::SetName(ALCcontext* context, ALuint id, std::string_view name)
+{
+ std::lock_guard<std::mutex> _{context->mEffectSlotLock};
+
+ auto slot = LookupEffectSlot(context, id);
+ if(!slot) UNLIKELY
+ return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", id);
+
+ context->mEffectSlotNames.insert_or_assign(id, name);
+}
+
void UpdateAllEffectSlotProps(ALCcontext *context)
{
std::lock_guard<std::mutex> _{context->mEffectSlotLock};
@@ -1023,11 +1000,14 @@ void UpdateAllEffectSlotProps(ALCcontext *context)
EffectSlotSubList::~EffectSlotSubList()
{
+ if(!EffectSlots)
+ return;
+
uint64_t usemask{~FreeMask};
while(usemask)
{
const int idx{al::countr_zero(usemask)};
- al::destroy_at(EffectSlots+idx);
+ std::destroy_at(EffectSlots+idx);
usemask &= ~(1_u64 << idx);
}
FreeMask = ~usemask;
@@ -1272,7 +1252,7 @@ void ALeffectslot::eax_fx_slot_load_effect(int version, ALenum altype)
void ALeffectslot::eax_fx_slot_set_volume()
{
- const auto volume = clamp(eax_.lVolume, EAXFXSLOT_MINVOLUME, EAXFXSLOT_MAXVOLUME);
+ const auto volume = std::clamp(eax_.lVolume, EAXFXSLOT_MINVOLUME, EAXFXSLOT_MAXVOLUME);
const auto gain = level_mb_to_gain(static_cast<float>(volume));
eax_set_efx_slot_gain(gain);
}
@@ -1470,7 +1450,8 @@ void ALeffectslot::eax_set_efx_slot_effect(EaxEffect &effect)
{
#define EAX_PREFIX "[EAX_SET_EFFECT_SLOT_EFFECT] "
- const auto error = initEffect(effect.al_effect_type_, effect.al_effect_props_, eax_al_context_);
+ const auto error = initEffect(0, effect.al_effect_type_, effect.al_effect_props_,
+ eax_al_context_);
if(error != AL_NO_ERROR) {
ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect.");
diff --git a/al/auxeffectslot.h b/al/auxeffectslot.h
index 3e9a2a4e..1ad0ffc4 100644
--- a/al/auxeffectslot.h
+++ b/al/auxeffectslot.h
@@ -3,6 +3,7 @@
#include <atomic>
#include <cstddef>
+#include <string_view>
#include "AL/al.h"
#include "AL/alc.h"
@@ -45,6 +46,7 @@ enum class SlotState : ALenum {
};
struct ALeffectslot {
+ ALuint EffectId{};
float Gain{1.0f};
bool AuxSendAuto{true};
ALeffectslot *Target{nullptr};
@@ -73,9 +75,12 @@ struct ALeffectslot {
ALeffectslot& operator=(const ALeffectslot&) = delete;
~ALeffectslot();
- ALenum initEffect(ALenum effectType, const EffectProps &effectProps, ALCcontext *context);
+ ALenum initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps,
+ ALCcontext *context);
void updateProps(ALCcontext *context);
+ static void SetName(ALCcontext *context, ALuint id, std::string_view name);
+
/* This can be new'd for the context's default effect slot. */
DEF_NEWDEL(ALeffectslot)
diff --git a/al/buffer.cpp b/al/buffer.cpp
index ee506596..7d043036 100644
--- a/al/buffer.cpp
+++ b/al/buffer.cpp
@@ -26,6 +26,7 @@
#include <array>
#include <atomic>
#include <cassert>
+#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
@@ -35,28 +36,31 @@
#include <mutex>
#include <new>
#include <numeric>
+#include <optional>
#include <stdexcept>
#include <utility>
+#include <vector>
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alext.h"
#include "albit.h"
-#include "albyte.h"
#include "alc/context.h"
#include "alc/device.h"
#include "alc/inprogext.h"
#include "almalloc.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "atomic.h"
#include "core/except.h"
#include "core/logging.h"
#include "core/voice.h"
+#include "direct_defs.h"
#include "opthelpers.h"
#ifdef ALSOFT_EAX
+#include <unordered_set>
+
#include "eax/globals.h"
#include "eax/x_ram.h"
#endif // ALSOFT_EAX
@@ -64,14 +68,14 @@
namespace {
-al::optional<AmbiLayout> AmbiLayoutFromEnum(ALenum layout)
+std::optional<AmbiLayout> AmbiLayoutFromEnum(ALenum layout)
{
switch(layout)
{
case AL_FUMA_SOFT: return AmbiLayout::FuMa;
case AL_ACN_SOFT: return AmbiLayout::ACN;
}
- return al::nullopt;
+ return std::nullopt;
}
ALenum EnumFromAmbiLayout(AmbiLayout layout)
{
@@ -83,7 +87,7 @@ ALenum EnumFromAmbiLayout(AmbiLayout layout)
throw std::runtime_error{"Invalid AmbiLayout: "+std::to_string(int(layout))};
}
-al::optional<AmbiScaling> AmbiScalingFromEnum(ALenum scale)
+std::optional<AmbiScaling> AmbiScalingFromEnum(ALenum scale)
{
switch(scale)
{
@@ -91,7 +95,7 @@ al::optional<AmbiScaling> AmbiScalingFromEnum(ALenum scale)
case AL_SN3D_SOFT: return AmbiScaling::SN3D;
case AL_N3D_SOFT: return AmbiScaling::N3D;
}
- return al::nullopt;
+ return std::nullopt;
}
ALenum EnumFromAmbiScaling(AmbiScaling scale)
{
@@ -106,7 +110,7 @@ ALenum EnumFromAmbiScaling(AmbiScaling scale)
}
#ifdef ALSOFT_EAX
-al::optional<EaxStorage> EaxStorageFromEnum(ALenum scale)
+std::optional<EaxStorage> EaxStorageFromEnum(ALenum scale)
{
switch(scale)
{
@@ -114,7 +118,7 @@ al::optional<EaxStorage> EaxStorageFromEnum(ALenum scale)
case AL_STORAGE_ACCESSIBLE: return EaxStorage::Accessible;
case AL_STORAGE_HARDWARE: return EaxStorage::Hardware;
}
- return al::nullopt;
+ return std::nullopt;
}
ALenum EnumFromEaxStorage(EaxStorage storage)
{
@@ -170,7 +174,7 @@ constexpr ALbitfieldSOFT INVALID_MAP_FLAGS{~unsigned(AL_MAP_READ_BIT_SOFT | AL_M
bool EnsureBuffers(ALCdevice *device, size_t needed)
{
- size_t count{std::accumulate(device->BufferList.cbegin(), device->BufferList.cend(), size_t{0},
+ size_t count{std::accumulate(device->BufferList.cbegin(), device->BufferList.cend(), 0_uz,
[](size_t cur, const BufferSubList &sublist) noexcept -> size_t
{ return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
@@ -218,11 +222,13 @@ void FreeBuffer(ALCdevice *device, ALbuffer *buffer)
eax_x_ram_clear(*device, *buffer);
#endif // ALSOFT_EAX
+ device->mBufferNames.erase(buffer->id);
+
const ALuint id{buffer->id - 1};
const size_t lidx{id >> 6};
const ALuint slidx{id & 0x3f};
- al::destroy_at(buffer);
+ std::destroy_at(buffer);
device->BufferList[lidx].FreeMask |= 1_u64 << slidx;
}
@@ -277,7 +283,7 @@ ALuint SanitizeAlignment(FmtType type, ALuint align)
/** Loads the specified data into the buffer, using the specified format. */
void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
- const FmtChannels DstChannels, const FmtType DstType, const al::byte *SrcData,
+ const FmtChannels DstChannels, const FmtType DstType, const std::byte *SrcData,
ALbitfieldSOFT access)
{
if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY
@@ -343,7 +349,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
*/
if(newsize != ALBuf->mDataStorage.size())
{
- auto newdata = al::vector<al::byte,16>(newsize, al::byte{});
+ auto newdata = decltype(ALBuf->mDataStorage)(newsize, std::byte{});
if((access&AL_PRESERVE_DATA_BIT_SOFT))
{
const size_t tocopy{minz(newdata.size(), ALBuf->mDataStorage.size())};
@@ -437,7 +443,7 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
/** Prepares the buffer to use caller-specified storage. */
void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
- const FmtChannels DstChannels, const FmtType DstType, al::byte *sdata, const ALuint sdatalen)
+ const FmtChannels DstChannels, const FmtType DstType, std::byte *sdata, const ALuint sdatalen)
{
if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY
return context->setError(AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u",
@@ -458,6 +464,7 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
{
case FmtUByte: return alignof(ALubyte);
case FmtShort: return alignof(ALshort);
+ case FmtInt: return alignof(ALint);
case FmtFloat: return alignof(ALfloat);
case FmtDouble: return alignof(ALdouble);
case FmtMulaw: return alignof(ALubyte);
@@ -506,7 +513,7 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
#endif
decltype(ALBuf->mDataStorage){}.swap(ALBuf->mDataStorage);
- ALBuf->mData = {static_cast<al::byte*>(sdata), sdatalen};
+ ALBuf->mData = {static_cast<std::byte*>(sdata), sdatalen};
#ifdef ALSOFT_EAX
eax_x_ram_clear(*context->mALDevice, *ALBuf);
@@ -536,108 +543,120 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
struct DecompResult { FmtChannels channels; FmtType type; };
-al::optional<DecompResult> DecomposeUserFormat(ALenum format)
+std::optional<DecompResult> DecomposeUserFormat(ALenum format)
{
struct FormatMap {
ALenum format;
FmtChannels channels;
FmtType type;
};
- static const std::array<FormatMap,63> UserFmtList{{
- { AL_FORMAT_MONO8, FmtMono, FmtUByte },
- { AL_FORMAT_MONO16, FmtMono, FmtShort },
- { AL_FORMAT_MONO_FLOAT32, FmtMono, FmtFloat },
- { AL_FORMAT_MONO_DOUBLE_EXT, FmtMono, FmtDouble },
- { AL_FORMAT_MONO_IMA4, FmtMono, FmtIMA4 },
- { AL_FORMAT_MONO_MSADPCM_SOFT, FmtMono, FmtMSADPCM },
- { AL_FORMAT_MONO_MULAW, FmtMono, FmtMulaw },
- { AL_FORMAT_MONO_ALAW_EXT, FmtMono, FmtAlaw },
-
- { AL_FORMAT_STEREO8, FmtStereo, FmtUByte },
- { AL_FORMAT_STEREO16, FmtStereo, FmtShort },
- { AL_FORMAT_STEREO_FLOAT32, FmtStereo, FmtFloat },
- { AL_FORMAT_STEREO_DOUBLE_EXT, FmtStereo, FmtDouble },
- { AL_FORMAT_STEREO_IMA4, FmtStereo, FmtIMA4 },
- { AL_FORMAT_STEREO_MSADPCM_SOFT, FmtStereo, FmtMSADPCM },
- { AL_FORMAT_STEREO_MULAW, FmtStereo, FmtMulaw },
- { AL_FORMAT_STEREO_ALAW_EXT, FmtStereo, FmtAlaw },
-
- { AL_FORMAT_REAR8, FmtRear, FmtUByte },
- { AL_FORMAT_REAR16, FmtRear, FmtShort },
- { AL_FORMAT_REAR32, FmtRear, FmtFloat },
- { AL_FORMAT_REAR_MULAW, FmtRear, FmtMulaw },
-
- { AL_FORMAT_QUAD8_LOKI, FmtQuad, FmtUByte },
- { AL_FORMAT_QUAD16_LOKI, FmtQuad, FmtShort },
-
- { AL_FORMAT_QUAD8, FmtQuad, FmtUByte },
- { AL_FORMAT_QUAD16, FmtQuad, FmtShort },
- { AL_FORMAT_QUAD32, FmtQuad, FmtFloat },
- { AL_FORMAT_QUAD_MULAW, FmtQuad, FmtMulaw },
-
- { AL_FORMAT_51CHN8, FmtX51, FmtUByte },
- { AL_FORMAT_51CHN16, FmtX51, FmtShort },
- { AL_FORMAT_51CHN32, FmtX51, FmtFloat },
- { AL_FORMAT_51CHN_MULAW, FmtX51, FmtMulaw },
-
- { AL_FORMAT_61CHN8, FmtX61, FmtUByte },
- { AL_FORMAT_61CHN16, FmtX61, FmtShort },
- { AL_FORMAT_61CHN32, FmtX61, FmtFloat },
- { AL_FORMAT_61CHN_MULAW, FmtX61, FmtMulaw },
-
- { AL_FORMAT_71CHN8, FmtX71, FmtUByte },
- { AL_FORMAT_71CHN16, FmtX71, FmtShort },
- { AL_FORMAT_71CHN32, FmtX71, FmtFloat },
- { AL_FORMAT_71CHN_MULAW, FmtX71, FmtMulaw },
-
- { AL_FORMAT_BFORMAT2D_8, FmtBFormat2D, FmtUByte },
- { AL_FORMAT_BFORMAT2D_16, FmtBFormat2D, FmtShort },
- { AL_FORMAT_BFORMAT2D_FLOAT32, FmtBFormat2D, FmtFloat },
- { AL_FORMAT_BFORMAT2D_MULAW, FmtBFormat2D, FmtMulaw },
-
- { AL_FORMAT_BFORMAT3D_8, FmtBFormat3D, FmtUByte },
- { AL_FORMAT_BFORMAT3D_16, FmtBFormat3D, FmtShort },
- { AL_FORMAT_BFORMAT3D_FLOAT32, FmtBFormat3D, FmtFloat },
- { AL_FORMAT_BFORMAT3D_MULAW, FmtBFormat3D, FmtMulaw },
-
- { AL_FORMAT_UHJ2CHN8_SOFT, FmtUHJ2, FmtUByte },
- { AL_FORMAT_UHJ2CHN16_SOFT, FmtUHJ2, FmtShort },
- { AL_FORMAT_UHJ2CHN_FLOAT32_SOFT, FmtUHJ2, FmtFloat },
- { AL_FORMAT_UHJ2CHN_MULAW_SOFT, FmtUHJ2, FmtMulaw },
- { AL_FORMAT_UHJ2CHN_ALAW_SOFT, FmtUHJ2, FmtAlaw },
- { AL_FORMAT_UHJ2CHN_IMA4_SOFT, FmtUHJ2, FmtIMA4 },
- { AL_FORMAT_UHJ2CHN_MSADPCM_SOFT, FmtUHJ2, FmtMSADPCM },
-
- { AL_FORMAT_UHJ3CHN8_SOFT, FmtUHJ3, FmtUByte },
- { AL_FORMAT_UHJ3CHN16_SOFT, FmtUHJ3, FmtShort },
- { AL_FORMAT_UHJ3CHN_FLOAT32_SOFT, FmtUHJ3, FmtFloat },
- { AL_FORMAT_UHJ3CHN_MULAW_SOFT, FmtUHJ3, FmtMulaw },
- { AL_FORMAT_UHJ3CHN_ALAW_SOFT, FmtUHJ3, FmtAlaw },
-
- { AL_FORMAT_UHJ4CHN8_SOFT, FmtUHJ4, FmtUByte },
- { AL_FORMAT_UHJ4CHN16_SOFT, FmtUHJ4, FmtShort },
- { AL_FORMAT_UHJ4CHN_FLOAT32_SOFT, FmtUHJ4, FmtFloat },
- { AL_FORMAT_UHJ4CHN_MULAW_SOFT, FmtUHJ4, FmtMulaw },
- { AL_FORMAT_UHJ4CHN_ALAW_SOFT, FmtUHJ4, FmtAlaw },
- }};
+ static constexpr std::array UserFmtList{
+ FormatMap{AL_FORMAT_MONO8, FmtMono, FmtUByte },
+ FormatMap{AL_FORMAT_MONO16, FmtMono, FmtShort },
+ FormatMap{AL_FORMAT_MONO_I32, FmtMono, FmtInt },
+ FormatMap{AL_FORMAT_MONO_FLOAT32, FmtMono, FmtFloat },
+ FormatMap{AL_FORMAT_MONO_DOUBLE_EXT, FmtMono, FmtDouble },
+ FormatMap{AL_FORMAT_MONO_IMA4, FmtMono, FmtIMA4 },
+ FormatMap{AL_FORMAT_MONO_MSADPCM_SOFT, FmtMono, FmtMSADPCM},
+ FormatMap{AL_FORMAT_MONO_MULAW, FmtMono, FmtMulaw },
+ FormatMap{AL_FORMAT_MONO_ALAW_EXT, FmtMono, FmtAlaw },
+
+ FormatMap{AL_FORMAT_STEREO8, FmtStereo, FmtUByte },
+ FormatMap{AL_FORMAT_STEREO16, FmtStereo, FmtShort },
+ FormatMap{AL_FORMAT_STEREO_I32, FmtStereo, FmtInt },
+ FormatMap{AL_FORMAT_STEREO_FLOAT32, FmtStereo, FmtFloat },
+ FormatMap{AL_FORMAT_STEREO_DOUBLE_EXT, FmtStereo, FmtDouble },
+ FormatMap{AL_FORMAT_STEREO_IMA4, FmtStereo, FmtIMA4 },
+ FormatMap{AL_FORMAT_STEREO_MSADPCM_SOFT, FmtStereo, FmtMSADPCM},
+ FormatMap{AL_FORMAT_STEREO_MULAW, FmtStereo, FmtMulaw },
+ FormatMap{AL_FORMAT_STEREO_ALAW_EXT, FmtStereo, FmtAlaw },
+
+ FormatMap{AL_FORMAT_REAR8, FmtRear, FmtUByte},
+ FormatMap{AL_FORMAT_REAR16, FmtRear, FmtShort},
+ FormatMap{AL_FORMAT_REAR32, FmtRear, FmtFloat},
+ FormatMap{AL_FORMAT_REAR_I32, FmtRear, FmtInt },
+ FormatMap{AL_FORMAT_REAR_FLOAT32, FmtRear, FmtFloat},
+ FormatMap{AL_FORMAT_REAR_MULAW, FmtRear, FmtMulaw},
+
+ FormatMap{AL_FORMAT_QUAD8_LOKI, FmtQuad, FmtUByte},
+ FormatMap{AL_FORMAT_QUAD16_LOKI, FmtQuad, FmtShort},
+
+ FormatMap{AL_FORMAT_QUAD8, FmtQuad, FmtUByte},
+ FormatMap{AL_FORMAT_QUAD16, FmtQuad, FmtShort},
+ FormatMap{AL_FORMAT_QUAD32, FmtQuad, FmtFloat},
+ FormatMap{AL_FORMAT_QUAD_I32, FmtQuad, FmtInt },
+ FormatMap{AL_FORMAT_QUAD_FLOAT32, FmtQuad, FmtFloat},
+ FormatMap{AL_FORMAT_QUAD_MULAW, FmtQuad, FmtMulaw},
+
+ FormatMap{AL_FORMAT_51CHN8, FmtX51, FmtUByte},
+ FormatMap{AL_FORMAT_51CHN16, FmtX51, FmtShort},
+ FormatMap{AL_FORMAT_51CHN32, FmtX51, FmtFloat},
+ FormatMap{AL_FORMAT_51CHN_I32, FmtX51, FmtInt },
+ FormatMap{AL_FORMAT_51CHN_FLOAT32, FmtX51, FmtFloat},
+ FormatMap{AL_FORMAT_51CHN_MULAW, FmtX51, FmtMulaw},
+
+ FormatMap{AL_FORMAT_61CHN8, FmtX61, FmtUByte},
+ FormatMap{AL_FORMAT_61CHN16, FmtX61, FmtShort},
+ FormatMap{AL_FORMAT_61CHN32, FmtX61, FmtFloat},
+ FormatMap{AL_FORMAT_61CHN_I32, FmtX61, FmtInt },
+ FormatMap{AL_FORMAT_61CHN_FLOAT32, FmtX61, FmtFloat},
+ FormatMap{AL_FORMAT_61CHN_MULAW, FmtX61, FmtMulaw},
+
+ FormatMap{AL_FORMAT_71CHN8, FmtX71, FmtUByte},
+ FormatMap{AL_FORMAT_71CHN16, FmtX71, FmtShort},
+ FormatMap{AL_FORMAT_71CHN32, FmtX71, FmtFloat},
+ FormatMap{AL_FORMAT_71CHN_I32, FmtX71, FmtInt },
+ FormatMap{AL_FORMAT_71CHN_FLOAT32, FmtX71, FmtFloat},
+ FormatMap{AL_FORMAT_71CHN_MULAW, FmtX71, FmtMulaw},
+
+ FormatMap{AL_FORMAT_BFORMAT2D_8, FmtBFormat2D, FmtUByte},
+ FormatMap{AL_FORMAT_BFORMAT2D_16, FmtBFormat2D, FmtShort},
+ FormatMap{AL_FORMAT_BFORMAT2D_FLOAT32, FmtBFormat2D, FmtFloat},
+ FormatMap{AL_FORMAT_BFORMAT2D_MULAW, FmtBFormat2D, FmtMulaw},
+
+ FormatMap{AL_FORMAT_BFORMAT3D_8, FmtBFormat3D, FmtUByte},
+ FormatMap{AL_FORMAT_BFORMAT3D_16, FmtBFormat3D, FmtShort},
+ FormatMap{AL_FORMAT_BFORMAT3D_FLOAT32, FmtBFormat3D, FmtFloat},
+ FormatMap{AL_FORMAT_BFORMAT3D_MULAW, FmtBFormat3D, FmtMulaw},
+
+ FormatMap{AL_FORMAT_UHJ2CHN8_SOFT, FmtUHJ2, FmtUByte },
+ FormatMap{AL_FORMAT_UHJ2CHN16_SOFT, FmtUHJ2, FmtShort },
+ FormatMap{AL_FORMAT_UHJ2CHN_I32, FmtUHJ2, FmtInt },
+ FormatMap{AL_FORMAT_UHJ2CHN_FLOAT32_SOFT, FmtUHJ2, FmtFloat },
+ FormatMap{AL_FORMAT_UHJ2CHN_MULAW_SOFT, FmtUHJ2, FmtMulaw },
+ FormatMap{AL_FORMAT_UHJ2CHN_ALAW_SOFT, FmtUHJ2, FmtAlaw },
+ FormatMap{AL_FORMAT_UHJ2CHN_IMA4_SOFT, FmtUHJ2, FmtIMA4 },
+ FormatMap{AL_FORMAT_UHJ2CHN_MSADPCM_SOFT, FmtUHJ2, FmtMSADPCM},
+
+ FormatMap{AL_FORMAT_UHJ3CHN8_SOFT, FmtUHJ3, FmtUByte},
+ FormatMap{AL_FORMAT_UHJ3CHN16_SOFT, FmtUHJ3, FmtShort},
+ FormatMap{AL_FORMAT_UHJ3CHN_I32, FmtUHJ3, FmtInt },
+ FormatMap{AL_FORMAT_UHJ3CHN_FLOAT32_SOFT, FmtUHJ3, FmtFloat},
+ FormatMap{AL_FORMAT_UHJ3CHN_MULAW_SOFT, FmtUHJ3, FmtMulaw},
+ FormatMap{AL_FORMAT_UHJ3CHN_ALAW_SOFT, FmtUHJ3, FmtAlaw },
+
+ FormatMap{AL_FORMAT_UHJ4CHN8_SOFT, FmtUHJ4, FmtUByte},
+ FormatMap{AL_FORMAT_UHJ4CHN16_SOFT, FmtUHJ4, FmtShort},
+ FormatMap{AL_FORMAT_UHJ4CHN_I32, FmtUHJ4, FmtInt },
+ FormatMap{AL_FORMAT_UHJ4CHN_FLOAT32_SOFT, FmtUHJ4, FmtFloat},
+ FormatMap{AL_FORMAT_UHJ4CHN_MULAW_SOFT, FmtUHJ4, FmtMulaw},
+ FormatMap{AL_FORMAT_UHJ4CHN_ALAW_SOFT, FmtUHJ4, FmtAlaw },
+ };
for(const auto &fmt : UserFmtList)
{
if(fmt.format == format)
- return al::make_optional<DecompResult>({fmt.channels, fmt.type});
+ return DecompResult{fmt.channels, fmt.type};
}
- return al::nullopt;
+ return std::nullopt;
}
} // namespace
-AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGenBuffers, ALsizei, ALuint*)
+FORCE_ALIGN void AL_APIENTRY alGenBuffersDirect(ALCcontext *context, ALsizei n, ALuint *buffers) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Generating %d buffers", n);
if(n <= 0) UNLIKELY return;
@@ -661,7 +680,7 @@ START_API_FUNC
/* Store the allocated buffer IDs in a separate local list, to avoid
* modifying the user storage in case of failure.
*/
- al::vector<ALuint> ids;
+ std::vector<ALuint> ids;
ids.reserve(static_cast<ALuint>(n));
do {
ALbuffer *buffer{AllocBuffer(device)};
@@ -670,14 +689,11 @@ START_API_FUNC
std::copy(ids.begin(), ids.end(), buffers);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alDeleteBuffers, ALsizei, const ALuint*)
+FORCE_ALIGN void AL_APIENTRY alDeleteBuffersDirect(ALCcontext *context, ALsizei n,
+ const ALuint *buffers) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Deleting %d buffers", n);
if(n <= 0) UNLIKELY return;
@@ -714,35 +730,32 @@ START_API_FUNC
};
std::for_each(buffers, buffers_end, delete_buffer);
}
-END_API_FUNC
-AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsBuffer, ALuint)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsBufferDirect(ALCcontext *context, ALuint buffer) noexcept
{
- ContextRef context{GetContextRef()};
- if(context) LIKELY
- {
- ALCdevice *device{context->mALDevice.get()};
- std::lock_guard<std::mutex> _{device->BufferLock};
- if(!buffer || LookupBuffer(device, buffer))
- return AL_TRUE;
- }
+ ALCdevice *device{context->mALDevice.get()};
+ std::lock_guard<std::mutex> _{device->BufferLock};
+ if(!buffer || LookupBuffer(device, buffer))
+ return AL_TRUE;
return AL_FALSE;
}
-END_API_FUNC
-
-AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
-START_API_FUNC
-{ alBufferStorageSOFT(buffer, format, data, size, freq, 0); }
-END_API_FUNC
-AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags)
-START_API_FUNC
+AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq) noexcept
{
- ContextRef context{GetContextRef()};
+ auto context = GetContextRef();
if(!context) UNLIKELY return;
+ alBufferStorageDirectSOFT(context.get(), buffer, format, data, size, freq, 0);
+}
+FORCE_ALIGN void AL_APIENTRY alBufferDataDirect(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq) noexcept
+{ alBufferStorageDirectSOFT(context, buffer, format, data, size, freq, 0); }
+
+AL_API DECL_FUNCEXT6(void, alBufferStorage,SOFT, ALuint, ALenum, const ALvoid*, ALsizei, ALsizei, ALbitfieldSOFT)
+FORCE_ALIGN void AL_APIENTRY alBufferStorageDirectSOFT(ALCcontext *context, ALuint buffer,
+ ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) noexcept
+{
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -766,20 +779,16 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
else
{
- LoadData(context.get(), albuf, freq, static_cast<ALuint>(size), usrfmt->channels,
- usrfmt->type, static_cast<const al::byte*>(data), flags);
+ LoadData(context, albuf, freq, static_cast<ALuint>(size), usrfmt->channels,
+ usrfmt->type, static_cast<const std::byte*>(data), flags);
}
}
}
-END_API_FUNC
-void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size,
- ALsizei freq)
-START_API_FUNC
+DECL_FUNC5(void, alBufferDataStatic, ALuint, ALenum, ALvoid*, ALsizei, ALsizei)
+FORCE_ALIGN void AL_APIENTRY alBufferDataStaticDirect(ALCcontext *context, const ALuint buffer,
+ ALenum format, ALvoid *data, ALsizei size, ALsizei freq) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -795,17 +804,14 @@ START_API_FUNC
if(!usrfmt) UNLIKELY
return context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
- PrepareUserPtr(context.get(), albuf, freq, usrfmt->channels, usrfmt->type,
- static_cast<al::byte*>(data), static_cast<ALuint>(size));
+ PrepareUserPtr(context, albuf, freq, usrfmt->channels, usrfmt->type,
+ static_cast<std::byte*>(data), static_cast<ALuint>(size));
}
-END_API_FUNC
-AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access)
-START_API_FUNC
+AL_API DECL_FUNCEXT4(void*, alMapBuffer,SOFT, ALuint, ALsizei, ALsizei, ALbitfieldSOFT)
+FORCE_ALIGN void* AL_APIENTRY alMapBufferDirectSOFT(ALCcontext *context, ALuint buffer,
+ ALsizei offset, ALsizei length, ALbitfieldSOFT access) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return nullptr;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -852,14 +858,10 @@ START_API_FUNC
return nullptr;
}
-END_API_FUNC
-AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer)
-START_API_FUNC
+AL_API DECL_FUNCEXT1(void, alUnmapBuffer,SOFT, ALuint)
+FORCE_ALIGN void AL_APIENTRY alUnmapBufferDirectSOFT(ALCcontext *context, ALuint buffer) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -875,14 +877,11 @@ START_API_FUNC
albuf->MappedSize = 0;
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length)
-START_API_FUNC
+AL_API DECL_FUNCEXT3(void, alFlushMappedBuffer,SOFT, ALuint, ALsizei, ALsizei)
+FORCE_ALIGN void AL_APIENTRY alFlushMappedBufferDirectSOFT(ALCcontext *context, ALuint buffer,
+ ALsizei offset, ALsizei length) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -900,21 +899,18 @@ START_API_FUNC
else
{
/* FIXME: Need to use some method of double-buffering for the mixer and
- * app to hold separate memory, which can be safely transfered
+ * app to hold separate memory, which can be safely transferred
* asynchronously. Currently we just say the app shouldn't write where
* OpenAL's reading, and hope for the best...
*/
std::atomic_thread_fence(std::memory_order_seq_cst);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length)
-START_API_FUNC
+AL_API DECL_FUNCEXT5(void, alBufferSubData,SOFT, ALuint, ALenum, const ALvoid*, ALsizei, ALsizei)
+FORCE_ALIGN void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALuint buffer,
+ ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -966,61 +962,12 @@ START_API_FUNC
assert(al::to_underlying(usrfmt->type) == al::to_underlying(albuf->mType));
memcpy(albuf->mData.data()+offset, data, static_cast<ALuint>(length));
}
-END_API_FUNC
-
-
-AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint /*buffer*/, ALuint /*samplerate*/,
- ALenum /*internalformat*/, ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/,
- const ALvoid* /*data*/)
-START_API_FUNC
-{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
- context->setError(AL_INVALID_OPERATION, "alBufferSamplesSOFT not supported");
-}
-END_API_FUNC
-
-AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/,
- ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, const ALvoid* /*data*/)
-START_API_FUNC
-{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
- context->setError(AL_INVALID_OPERATION, "alBufferSubSamplesSOFT not supported");
-}
-END_API_FUNC
-
-AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/,
- ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, ALvoid* /*data*/)
-START_API_FUNC
-{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
- context->setError(AL_INVALID_OPERATION, "alGetBufferSamplesSOFT not supported");
-}
-END_API_FUNC
-AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum /*format*/)
-START_API_FUNC
-{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return AL_FALSE;
-
- context->setError(AL_INVALID_OPERATION, "alIsBufferFormatSupportedSOFT not supported");
- return AL_FALSE;
-}
-END_API_FUNC
-
-AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat /*value*/)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alBufferf, ALuint, ALenum, ALfloat)
+FORCE_ALIGN void AL_APIENTRY alBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param,
+ ALfloat /*value*/) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -1032,15 +979,11 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param,
- ALfloat /*value1*/, ALfloat /*value2*/, ALfloat /*value3*/)
-START_API_FUNC
+AL_API DECL_FUNC5(void, alBuffer3f, ALuint, ALenum, ALfloat, ALfloat, ALfloat)
+FORCE_ALIGN void AL_APIENTRY alBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param,
+ ALfloat /*value1*/, ALfloat /*value2*/, ALfloat /*value3*/) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -1052,14 +995,11 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alBufferfv, ALuint, ALenum, const ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param,
+ const ALfloat *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -1073,15 +1013,12 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alBufferi, ALuint, ALenum, ALint)
+FORCE_ALIGN void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param,
+ ALint value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -1135,15 +1072,11 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param,
- ALint /*value1*/, ALint /*value2*/, ALint /*value3*/)
-START_API_FUNC
+AL_API DECL_FUNC5(void, alBuffer3i, ALuint, ALenum, ALint, ALint, ALint)
+FORCE_ALIGN void AL_APIENTRY alBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param,
+ ALint /*value1*/, ALint /*value2*/, ALint /*value3*/) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -1155,36 +1088,31 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alBufferiv, ALuint, ALenum, const ALint*)
+FORCE_ALIGN void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param,
+ const ALint *values) noexcept
{
- if(values)
+ if(!values) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+
+ switch(param)
{
- switch(param)
- {
- case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
- case AL_PACK_BLOCK_ALIGNMENT_SOFT:
- case AL_AMBISONIC_LAYOUT_SOFT:
- case AL_AMBISONIC_SCALING_SOFT:
- case AL_UNPACK_AMBISONIC_ORDER_SOFT:
- alBufferi(buffer, param, values[0]);
- return;
- }
+ case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
+ case AL_PACK_BLOCK_ALIGNMENT_SOFT:
+ case AL_AMBISONIC_LAYOUT_SOFT:
+ case AL_AMBISONIC_SCALING_SOFT:
+ case AL_UNPACK_AMBISONIC_ORDER_SOFT:
+ alBufferiDirect(context, buffer, param, values[0]);
+ return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
ALbuffer *albuf = LookupBuffer(device, buffer);
if(!albuf) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
- else if(!values) UNLIKELY
- context->setError(AL_INVALID_VALUE, "NULL pointer");
else switch(param)
{
case AL_LOOP_POINTS_SOFT:
@@ -1206,15 +1134,12 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetBufferf, ALuint, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param,
+ ALfloat *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -1234,14 +1159,11 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
-START_API_FUNC
+AL_API DECL_FUNC5(void, alGetBuffer3f, ALuint, ALenum, ALfloat*, ALfloat*, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param,
+ ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -1255,21 +1177,18 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetBufferfv, ALuint, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param,
+ ALfloat *values) noexcept
{
switch(param)
{
case AL_SEC_LENGTH_SOFT:
- alGetBufferf(buffer, param, values);
+ alGetBufferfDirect(context, buffer, param, values);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -1283,15 +1202,12 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetBufferi, ALuint, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param,
+ ALint *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
ALbuffer *albuf = LookupBuffer(device, buffer);
@@ -1351,14 +1267,11 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3)
-START_API_FUNC
+AL_API DECL_FUNC5(void, alGetBuffer3i, ALuint, ALenum, ALint*, ALint*, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param,
+ ALint *value1, ALint *value2, ALint *value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
@@ -1371,10 +1284,10 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetBufferiv, ALuint, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param,
+ ALint *values) noexcept
{
switch(param)
{
@@ -1390,13 +1303,10 @@ START_API_FUNC
case AL_AMBISONIC_LAYOUT_SOFT:
case AL_AMBISONIC_SCALING_SOFT:
case AL_UNPACK_AMBISONIC_ORDER_SOFT:
- alGetBufferi(buffer, param, values);
+ alGetBufferiDirect(context, buffer, param, values);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
ALbuffer *albuf = LookupBuffer(device, buffer);
@@ -1415,16 +1325,12 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq,
- ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr)
-START_API_FUNC
+AL_API DECL_FUNCEXT5(void, alBufferCallback,SOFT, ALuint, ALenum, ALsizei, ALBUFFERCALLBACKTYPESOFT, ALvoid*)
+FORCE_ALIGN void AL_APIENTRY alBufferCallbackDirectSOFT(ALCcontext *context, ALuint buffer,
+ ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
@@ -1441,18 +1347,15 @@ START_API_FUNC
if(!usrfmt) UNLIKELY
context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
else
- PrepareCallback(context.get(), albuf, freq, usrfmt->channels, usrfmt->type, callback,
+ PrepareCallback(context, albuf, freq, usrfmt->channels, usrfmt->type, callback,
userptr);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **value)
-START_API_FUNC
+AL_API DECL_FUNCEXT3(void, alGetBufferPtr,SOFT, ALuint, ALenum, ALvoid**)
+FORCE_ALIGN void AL_APIENTRY alGetBufferPtrDirectSOFT(ALCcontext *context, ALuint buffer,
+ ALenum param, ALvoid **value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
ALbuffer *albuf = LookupBuffer(device, buffer);
@@ -1463,7 +1366,7 @@ START_API_FUNC
else switch(param)
{
case AL_BUFFER_CALLBACK_FUNCTION_SOFT:
- *value = reinterpret_cast<void*>(albuf->mCallback);
+ *value = al::bit_cast<void*>(albuf->mCallback);
break;
case AL_BUFFER_CALLBACK_USER_PARAM_SOFT:
*value = albuf->mUserData;
@@ -1473,14 +1376,11 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer pointer property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3)
-START_API_FUNC
+AL_API DECL_FUNCEXT5(void, alGetBuffer3Ptr,SOFT, ALuint, ALenum, ALvoid**, ALvoid**, ALvoid**)
+FORCE_ALIGN void AL_APIENTRY alGetBuffer3PtrDirectSOFT(ALCcontext *context, ALuint buffer,
+ ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
@@ -1493,22 +1393,19 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer 3-pointer property 0x%04x", param);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **values)
-START_API_FUNC
+AL_API DECL_FUNCEXT3(void, alGetBufferPtrv,SOFT, ALuint, ALenum, ALvoid**)
+FORCE_ALIGN void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALuint buffer,
+ ALenum param, ALvoid **values) noexcept
{
switch(param)
{
case AL_BUFFER_CALLBACK_FUNCTION_SOFT:
case AL_BUFFER_CALLBACK_USER_PARAM_SOFT:
- alGetBufferPtrSOFT(buffer, param, values);
+ alGetBufferPtrDirectSOFT(context, buffer, param, values);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->BufferLock};
if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
@@ -1521,16 +1418,69 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid buffer pointer-vector property 0x%04x", param);
}
}
-END_API_FUNC
+
+
+AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint /*buffer*/, ALuint /*samplerate*/,
+ ALenum /*internalformat*/, ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/,
+ const ALvoid* /*data*/) noexcept
+{
+ ContextRef context{GetContextRef()};
+ if(!context) UNLIKELY return;
+
+ context->setError(AL_INVALID_OPERATION, "alBufferSamplesSOFT not supported");
+}
+
+AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/,
+ ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, const ALvoid* /*data*/) noexcept
+{
+ ContextRef context{GetContextRef()};
+ if(!context) UNLIKELY return;
+
+ context->setError(AL_INVALID_OPERATION, "alBufferSubSamplesSOFT not supported");
+}
+
+AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/,
+ ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, ALvoid* /*data*/) noexcept
+{
+ ContextRef context{GetContextRef()};
+ if(!context) UNLIKELY return;
+
+ context->setError(AL_INVALID_OPERATION, "alGetBufferSamplesSOFT not supported");
+}
+
+AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum /*format*/) noexcept
+{
+ ContextRef context{GetContextRef()};
+ if(!context) UNLIKELY return AL_FALSE;
+
+ context->setError(AL_INVALID_OPERATION, "alIsBufferFormatSupportedSOFT not supported");
+ return AL_FALSE;
+}
+
+
+void ALbuffer::SetName(ALCcontext *context, ALuint id, std::string_view name)
+{
+ ALCdevice *device{context->mALDevice.get()};
+ std::lock_guard<std::mutex> _{device->BufferLock};
+
+ auto buffer = LookupBuffer(device, id);
+ if(!buffer) UNLIKELY
+ return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", id);
+
+ device->mBufferNames.insert_or_assign(id, name);
+}
BufferSubList::~BufferSubList()
{
+ if(!Buffers)
+ return;
+
uint64_t usemask{~FreeMask};
while(usemask)
{
const int idx{al::countr_zero(usemask)};
- al::destroy_at(Buffers+idx);
+ std::destroy_at(Buffers+idx);
usemask &= ~(1_u64 << idx);
}
FreeMask = ~usemask;
@@ -1540,93 +1490,82 @@ BufferSubList::~BufferSubList()
#ifdef ALSOFT_EAX
-FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint* buffers, ALint value)
-START_API_FUNC
+FORCE_ALIGN DECL_FUNC3(ALboolean, EAXSetBufferMode, ALsizei, const ALuint*, ALint)
+FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, ALsizei n,
+ const ALuint *buffers, ALint value) noexcept
{
#define EAX_PREFIX "[EAXSetBufferMode] "
- const auto context = ContextRef{GetContextRef()};
- if(!context)
- {
- ERR(EAX_PREFIX "%s\n", "No current context.");
- return ALC_FALSE;
- }
-
if(!eax_g_is_enabled)
{
context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled.");
- return ALC_FALSE;
+ return AL_FALSE;
}
const auto storage = EaxStorageFromEnum(value);
if(!storage)
{
context->setError(AL_INVALID_ENUM, EAX_PREFIX "Unsupported X-RAM mode 0x%x", value);
- return ALC_FALSE;
+ return AL_FALSE;
}
if(n == 0)
- return ALC_TRUE;
+ return AL_TRUE;
if(n < 0)
{
context->setError(AL_INVALID_VALUE, EAX_PREFIX "Buffer count %d out of range", n);
- return ALC_FALSE;
+ return AL_FALSE;
}
if(!buffers)
{
context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Null AL buffers");
- return ALC_FALSE;
+ return AL_FALSE;
}
auto device = context->mALDevice.get();
std::lock_guard<std::mutex> device_lock{device->BufferLock};
- size_t total_needed{0};
- // Validate the buffers.
- //
- for(auto i = 0;i < n;++i)
+ /* Special-case setting a single buffer, to avoid extraneous allocations. */
+ if(n == 1)
{
- const auto bufid = buffers[i];
+ const auto bufid = buffers[0];
if(bufid == AL_NONE)
- continue;
+ return AL_TRUE;
const auto buffer = LookupBuffer(device, bufid);
if(!buffer) UNLIKELY
{
ERR(EAX_PREFIX "Invalid buffer ID %u.\n", bufid);
- return ALC_FALSE;
+ return AL_FALSE;
}
/* TODO: Is the store location allowed to change for in-use buffers, or
* only when not set/queued on a source?
*/
- if(*storage == EaxStorage::Hardware && !buffer->eax_x_ram_is_hardware)
+ if(*storage == EaxStorage::Hardware)
{
- /* FIXME: This doesn't account for duplicate buffers. When the same
- * buffer ID is specified multiple times in the provided list, it
- * counts each instance as more memory that needs to fit in X-RAM.
- */
- if(std::numeric_limits<size_t>::max()-buffer->OriginalSize < total_needed) UNLIKELY
+ if(!buffer->eax_x_ram_is_hardware
+ && buffer->OriginalSize > device->eax_x_ram_free_size) UNLIKELY
{
- context->setError(AL_OUT_OF_MEMORY, EAX_PREFIX "Size overflow (%u + %zu)\n",
- buffer->OriginalSize, total_needed);
- return ALC_FALSE;
+ context->setError(AL_OUT_OF_MEMORY,
+ EAX_PREFIX "Out of X-RAM memory (need: %u, avail: %u)", buffer->OriginalSize,
+ device->eax_x_ram_free_size);
+ return AL_FALSE;
}
- total_needed += buffer->OriginalSize;
+
+ eax_x_ram_apply(*device, *buffer);
}
- }
- if(total_needed > device->eax_x_ram_free_size)
- {
- context->setError(AL_OUT_OF_MEMORY,EAX_PREFIX "Out of X-RAM memory (need: %zu, avail: %u)",
- total_needed, device->eax_x_ram_free_size);
- return ALC_FALSE;
+ else
+ eax_x_ram_clear(*device, *buffer);
+ buffer->eax_x_ram_mode = *storage;
+ return AL_TRUE;
}
- // Update the mode.
- //
+ /* Validate the buffers. */
+ std::unordered_set<ALbuffer*> buflist;
for(auto i = 0;i < n;++i)
{
const auto bufid = buffers[i];
@@ -1634,8 +1573,47 @@ START_API_FUNC
continue;
const auto buffer = LookupBuffer(device, bufid);
- assert(buffer);
+ if(!buffer) UNLIKELY
+ {
+ ERR(EAX_PREFIX "Invalid buffer ID %u.\n", bufid);
+ return AL_FALSE;
+ }
+
+ /* TODO: Is the store location allowed to change for in-use buffers, or
+ * only when not set/queued on a source?
+ */
+
+ buflist.emplace(buffer);
+ }
+
+ if(*storage == EaxStorage::Hardware)
+ {
+ size_t total_needed{0};
+ for(ALbuffer *buffer : buflist)
+ {
+ if(!buffer->eax_x_ram_is_hardware)
+ {
+ if(std::numeric_limits<size_t>::max()-buffer->OriginalSize < total_needed) UNLIKELY
+ {
+ context->setError(AL_OUT_OF_MEMORY, EAX_PREFIX "Size overflow (%u + %zu)\n",
+ buffer->OriginalSize, total_needed);
+ return AL_FALSE;
+ }
+ total_needed += buffer->OriginalSize;
+ }
+ }
+ if(total_needed > device->eax_x_ram_free_size)
+ {
+ context->setError(AL_OUT_OF_MEMORY,
+ EAX_PREFIX "Out of X-RAM memory (need: %zu, avail: %u)", total_needed,
+ device->eax_x_ram_free_size);
+ return AL_FALSE;
+ }
+ }
+ /* Update the mode. */
+ for(ALbuffer *buffer : buflist)
+ {
if(*storage == EaxStorage::Hardware)
eax_x_ram_apply(*device, *buffer);
else
@@ -1647,20 +1625,13 @@ START_API_FUNC
#undef EAX_PREFIX
}
-END_API_FUNC
-FORCE_ALIGN ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint* pReserved)
-START_API_FUNC
+FORCE_ALIGN DECL_FUNC2(ALenum, EAXGetBufferMode, ALuint, ALint*)
+FORCE_ALIGN ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer,
+ ALint *pReserved) noexcept
{
#define EAX_PREFIX "[EAXGetBufferMode] "
- const auto context = ContextRef{GetContextRef()};
- if(!context)
- {
- ERR(EAX_PREFIX "%s\n", "No current context.");
- return AL_NONE;
- }
-
if(!eax_g_is_enabled)
{
context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled.");
@@ -1687,6 +1658,5 @@ START_API_FUNC
#undef EAX_PREFIX
}
-END_API_FUNC
#endif // ALSOFT_EAX
diff --git a/al/buffer.h b/al/buffer.h
index 64ebe1f3..f936cf98 100644
--- a/al/buffer.h
+++ b/al/buffer.h
@@ -2,10 +2,11 @@
#define AL_BUFFER_H
#include <atomic>
+#include <cstddef>
+#include <string_view>
#include "AL/al.h"
-#include "albyte.h"
#include "alc/inprogext.h"
#include "almalloc.h"
#include "atomic.h"
@@ -26,7 +27,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};
@@ -47,6 +48,8 @@ struct ALbuffer : public BufferStorage {
/* Self ID */
ALuint id{0};
+ static void SetName(ALCcontext *context, ALuint id, std::string_view name);
+
DISABLE_ALLOC()
#ifdef ALSOFT_EAX
diff --git a/al/debug.cpp b/al/debug.cpp
new file mode 100644
index 00000000..b76ec9af
--- /dev/null
+++ b/al/debug.cpp
@@ -0,0 +1,570 @@
+#include "config.h"
+
+#include "debug.h"
+
+#include <algorithm>
+#include <array>
+#include <cstring>
+#include <mutex>
+#include <optional>
+#include <stddef.h>
+#include <stdexcept>
+#include <string>
+#include <utility>
+
+#include "AL/al.h"
+
+#include "alc/context.h"
+#include "alc/inprogext.h"
+#include "alspan.h"
+#include "auxeffectslot.h"
+#include "buffer.h"
+#include "core/logging.h"
+#include "direct_defs.h"
+#include "effect.h"
+#include "filter.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 std::optional<DebugSource> GetDebugSource(ALenum source) noexcept
+{
+ 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 std::optional<DebugType> GetDebugType(ALenum type) noexcept
+{
+ 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 std::optional<DebugSeverity> GetDebugSeverity(ALenum severity) noexcept
+{
+ 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;
+}
+
+
+ALenum GetDebugSourceEnum(DebugSource source)
+{
+ 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))};
+}
+
+ALenum GetDebugTypeEnum(DebugType type)
+{
+ 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))};
+}
+
+ALenum GetDebugSeverityEnum(DebugSeverity severity)
+{
+ 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))};
+}
+
+
+const char *GetDebugSourceName(DebugSource source)
+{
+ switch(source)
+ {
+ case DebugSource::API: return "API";
+ case DebugSource::System: return "Audio System";
+ case DebugSource::ThirdParty: return "Third Party";
+ case DebugSource::Application: return "Application";
+ case DebugSource::Other: return "Other";
+ }
+ return "<invalid source>";
+}
+
+const char *GetDebugTypeName(DebugType type)
+{
+ switch(type)
+ {
+ case DebugType::Error: return "Error";
+ case DebugType::DeprecatedBehavior: return "Deprecated Behavior";
+ case DebugType::UndefinedBehavior: return "Undefined Behavior";
+ case DebugType::Portability: return "Portability";
+ case DebugType::Performance: return "Performance";
+ case DebugType::Marker: return "Marker";
+ case DebugType::PushGroup: return "Push Group";
+ case DebugType::PopGroup: return "Pop Group";
+ case DebugType::Other: return "Other";
+ }
+ return "<invalid type>";
+}
+
+const char *GetDebugSeverityName(DebugSeverity severity)
+{
+ 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()) UNLIKELY
+ return;
+
+ if(message.length() >= MaxDebugMessageLength) UNLIKELY
+ {
+ ERR("Debug message too long (%zu >= %d):\n-> %.*s\n", message.length(),
+ MaxDebugMessageLength, static_cast<int>(message.length()), 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), static_cast<int>(message.length()),
+ message.data());
+ }
+}
+
+
+FORCE_ALIGN DECL_FUNCEXT2(void, alDebugMessageCallback,EXT, ALDEBUGPROCEXT, void*)
+FORCE_ALIGN void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context,
+ ALDEBUGPROCEXT callback, void *userParam) noexcept
+{
+ std::lock_guard<std::mutex> _{context->mDebugCbLock};
+ context->mDebugCb = callback;
+ context->mDebugParam = userParam;
+}
+
+
+FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageInsert,EXT, ALenum, ALenum, ALuint, ALenum, ALsizei, const ALchar*)
+FORCE_ALIGN void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source,
+ ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) noexcept
+{
+ if(!context->mContextFlags.test(ContextFlags::DebugBit))
+ return;
+
+ if(!message) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "Null message pointer");
+
+ auto msgview = (length < 0) ? std::string_view{message}
+ : std::string_view{message, static_cast<uint>(length)};
+ if(msgview.length() >= MaxDebugMessageLength) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "Debug message too long (%zu >= %d)",
+ msgview.length(), MaxDebugMessageLength);
+
+ auto dsource = GetDebugSource(source);
+ if(!dsource)
+ return context->setError(AL_INVALID_ENUM, "Invalid debug source 0x%04x", source);
+ if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
+ return context->setError(AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source);
+
+ auto dtype = GetDebugType(type);
+ if(!dtype)
+ return context->setError(AL_INVALID_ENUM, "Invalid debug type 0x%04x", type);
+
+ auto dseverity = GetDebugSeverity(severity);
+ if(!dseverity)
+ return context->setError(AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity);
+
+ context->debugMessage(*dsource, *dtype, id, *dseverity, msgview);
+}
+
+
+FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageControl,EXT, ALenum, ALenum, ALenum, ALsizei, const ALuint*, ALboolean)
+FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source,
+ ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) noexcept
+{
+ if(count > 0)
+ {
+ if(!ids)
+ return context->setError(AL_INVALID_VALUE, "IDs is null with non-0 count");
+ if(source == AL_DONT_CARE_EXT)
+ return context->setError(AL_INVALID_OPERATION,
+ "Debug source cannot be AL_DONT_CARE_EXT with IDs");
+ if(type == AL_DONT_CARE_EXT)
+ return context->setError(AL_INVALID_OPERATION,
+ "Debug type cannot be AL_DONT_CARE_EXT with IDs");
+ if(severity != AL_DONT_CARE_EXT)
+ return context->setError(AL_INVALID_OPERATION,
+ "Debug severity must be AL_DONT_CARE_EXT with IDs");
+ }
+
+ if(enable != AL_TRUE && enable != AL_FALSE)
+ return context->setError(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)
+ return context->setError(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)
+ return context->setError(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)
+ return context->setError(AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity);
+ svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1);
+ }
+
+ std::lock_guard<std::mutex> _{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); });
+ }
+}
+
+
+FORCE_ALIGN DECL_FUNCEXT4(void, alPushDebugGroup,EXT, ALenum, ALuint, ALsizei, const ALchar*)
+FORCE_ALIGN void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source,
+ ALuint id, ALsizei length, const ALchar *message) noexcept
+{
+ if(length < 0)
+ {
+ size_t newlen{std::strlen(message)};
+ if(newlen >= MaxDebugMessageLength) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "Debug message too long (%zu >= %d)",
+ newlen, MaxDebugMessageLength);
+ length = static_cast<ALsizei>(newlen);
+ }
+ else if(length >= MaxDebugMessageLength) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "Debug message too long (%d >= %d)", length,
+ MaxDebugMessageLength);
+
+ auto dsource = GetDebugSource(source);
+ if(!dsource)
+ return context->setError(AL_INVALID_ENUM, "Invalid debug source 0x%04x", source);
+ if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application)
+ return context->setError(AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source);
+
+ std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
+ if(context->mDebugGroups.size() >= MaxDebugGroupDepth)
+ {
+ debuglock.unlock();
+ return context->setError(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);
+}
+
+FORCE_ALIGN DECL_FUNCEXT(void, alPopDebugGroup,EXT)
+FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexcept
+{
+ std::unique_lock<std::mutex> debuglock{context->mDebugCbLock};
+ if(context->mDebugGroups.size() <= 1)
+ {
+ debuglock.unlock();
+ return context->setError(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);
+}
+
+
+FORCE_ALIGN DECL_FUNCEXT8(ALuint, alGetDebugMessageLog,EXT, ALuint, ALsizei, ALenum*, ALenum*, ALuint*, ALenum*, ALsizei*, ALchar*)
+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
+{
+ if(logBufSize < 0)
+ {
+ context->setError(AL_INVALID_VALUE, "Negative debug log buffer size");
+ return 0;
+ }
+
+ std::lock_guard<std::mutex> _{context->mDebugCbLock};
+ ALsizei logBufWritten{0};
+ 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(logBuf)
+ {
+ const size_t avail{static_cast<ALuint>(logBufSize - logBufWritten)};
+ if(avail < tocopy)
+ return i;
+ std::copy_n(entry.mMessage.data(), tocopy, logBuf+logBufWritten);
+ logBufWritten += static_cast<ALsizei>(tocopy);
+ }
+
+ if(sources) sources[i] = GetDebugSourceEnum(entry.mSource);
+ if(types) types[i] = GetDebugTypeEnum(entry.mType);
+ if(ids) ids[i] = entry.mId;
+ if(severities) severities[i] = GetDebugSeverityEnum(entry.mSeverity);
+ if(lengths) lengths[i] = static_cast<ALsizei>(tocopy);
+
+ context->mDebugLog.pop_front();
+ }
+
+ return count;
+}
+
+FORCE_ALIGN DECL_FUNCEXT4(void, alObjectLabel,EXT, ALenum, ALuint, ALsizei, const ALchar*)
+FORCE_ALIGN void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
+ ALuint name, ALsizei length, const ALchar *label) noexcept
+{
+ if(!label && length != 0) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "Null label pointer");
+
+ auto objname = (length < 0) ? std::string_view{label}
+ : std::string_view{label, static_cast<uint>(length)};
+ if(objname.length() >= MaxObjectLabelLength) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "Object label length too long (%zu >= %d)",
+ objname.length(), MaxObjectLabelLength);
+
+ if(identifier == AL_SOURCE_EXT)
+ return ALsource::SetName(context, name, objname);
+ if(identifier == AL_BUFFER)
+ return ALbuffer::SetName(context, name, objname);
+ if(identifier == AL_FILTER_EXT)
+ return ALfilter::SetName(context, name, objname);
+ if(identifier == AL_EFFECT_EXT)
+ return ALeffect::SetName(context, name, objname);
+ if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
+ return ALeffectslot::SetName(context, name, objname);
+
+ return context->setError(AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier);
+}
+
+FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum, ALuint, ALsizei, ALsizei*, ALchar*)
+FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier,
+ ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) noexcept
+{
+ if(bufSize < 0) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "Negative label bufSize");
+
+ if(!label && !length) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "Null length and label");
+ if(label && bufSize == 0) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "Zero label bufSize");
+
+ auto copy_name = [name,bufSize,length,label](std::unordered_map<ALuint,std::string> &names)
+ {
+ std::string_view objname;
+
+ auto iter = names.find(name);
+ if(iter != names.end())
+ objname = iter->second;
+
+ if(!label)
+ *length = static_cast<ALsizei>(objname.length());
+ else
+ {
+ const size_t tocopy{minz(objname.length(), static_cast<uint>(bufSize)-1)};
+ std::memcpy(label, objname.data(), tocopy);
+ label[tocopy] = '\0';
+ if(length)
+ *length = static_cast<ALsizei>(tocopy);
+ }
+ };
+
+ if(identifier == AL_SOURCE_EXT)
+ {
+ std::lock_guard _{context->mSourceLock};
+ copy_name(context->mSourceNames);
+ }
+ else if(identifier == AL_BUFFER)
+ {
+ ALCdevice *device{context->mALDevice.get()};
+ std::lock_guard _{device->BufferLock};
+ copy_name(device->mBufferNames);
+ }
+ else if(identifier == AL_FILTER_EXT)
+ {
+ ALCdevice *device{context->mALDevice.get()};
+ std::lock_guard _{device->FilterLock};
+ copy_name(device->mFilterNames);
+ }
+ else if(identifier == AL_EFFECT_EXT)
+ {
+ ALCdevice *device{context->mALDevice.get()};
+ std::lock_guard _{device->EffectLock};
+ copy_name(device->mEffectNames);
+ }
+ else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT)
+ {
+ std::lock_guard _{context->mEffectSlotLock};
+ copy_name(context->mEffectSlotNames);
+ }
+ else
+ context->setError(AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier);
+}
diff --git a/al/debug.h b/al/debug.h
new file mode 100644
index 00000000..351be9c0
--- /dev/null
+++ b/al/debug.h
@@ -0,0 +1,69 @@
+#ifndef AL_DEBUG_H
+#define AL_DEBUG_H
+
+#include <stdint.h>
+#include <string>
+#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 uint8_t MaxDebugLoggedMessages{64};
+inline constexpr uint16_t MaxDebugMessageLength{1024};
+inline constexpr uint8_t MaxDebugGroupDepth{64};
+inline constexpr uint16_t MaxObjectLabelLength{1024};
+
+
+inline constexpr uint DebugSourceBase{0};
+enum class DebugSource : uint8_t {
+ API = 0,
+ System,
+ ThirdParty,
+ Application,
+ Other,
+};
+inline constexpr uint DebugSourceCount{5};
+
+inline constexpr uint DebugTypeBase{DebugSourceBase + DebugSourceCount};
+enum class DebugType : 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 : 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<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 */
diff --git a/al/direct_defs.h b/al/direct_defs.h
new file mode 100644
index 00000000..7526b611
--- /dev/null
+++ b/al/direct_defs.h
@@ -0,0 +1,127 @@
+#ifndef AL_DIRECT_DEFS_H
+#define AL_DIRECT_DEFS_H
+
+namespace detail_ {
+
+template<typename T>
+constexpr T DefaultVal() noexcept { return T{}; }
+
+template<>
+constexpr void DefaultVal() noexcept { }
+
+} // namespace detail_
+
+#define DECL_FUNC(R, Name) \
+R AL_APIENTRY Name(void) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct(context.get()); \
+}
+
+#define DECL_FUNC1(R, Name, T1) \
+R AL_APIENTRY Name(T1 a) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct(context.get(), a); \
+}
+
+#define DECL_FUNC2(R, Name, T1, T2) \
+R AL_APIENTRY Name(T1 a, T2 b) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct(context.get(), a, b); \
+}
+
+#define DECL_FUNC3(R, Name, T1, T2, T3) \
+R AL_APIENTRY Name(T1 a, T2 b, T3 c) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct(context.get(), a, b, c); \
+}
+
+#define DECL_FUNC4(R, Name, T1, T2, T3, T4) \
+R AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct(context.get(), a, b, c, d); \
+}
+
+#define DECL_FUNC5(R, Name, T1, T2, T3, T4, T5) \
+R AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct(context.get(), a, b, c, d, e); \
+}
+
+
+#define DECL_FUNCEXT(R, Name,Ext) \
+R AL_APIENTRY Name##Ext(void) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct##Ext(context.get()); \
+}
+
+#define DECL_FUNCEXT1(R, Name,Ext, T1) \
+R AL_APIENTRY Name##Ext(T1 a) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct##Ext(context.get(), a); \
+}
+
+#define DECL_FUNCEXT2(R, Name,Ext, T1, T2) \
+R AL_APIENTRY Name##Ext(T1 a, T2 b) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct##Ext(context.get(), a, b); \
+}
+
+#define DECL_FUNCEXT3(R, Name,Ext, T1, T2, T3) \
+R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct##Ext(context.get(), a, b, c); \
+}
+
+#define DECL_FUNCEXT4(R, Name,Ext, T1, T2, T3, T4) \
+R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct##Ext(context.get(), a, b, c, d); \
+}
+
+#define DECL_FUNCEXT5(R, Name,Ext, T1, T2, T3, T4, T5) \
+R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct##Ext(context.get(), a, b, c, d, e); \
+}
+
+#define DECL_FUNCEXT6(R, Name,Ext, T1, T2, T3, T4, T5, T6) \
+R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct##Ext(context.get(), a, b, c, d, e, f); \
+}
+
+#define DECL_FUNCEXT8(R, Name,Ext, T1, T2, T3, T4, T5, T6, T7, T8) \
+R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h) noexcept \
+{ \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return detail_::DefaultVal<R>(); \
+ return Name##Direct##Ext(context.get(), a, b, c, d, e, f, g, h); \
+}
+
+#endif /* AL_DIRECT_DEFS_H */
diff --git a/al/eax/api.h b/al/eax/api.h
index d254da1f..18d93ef8 100644
--- a/al/eax/api.h
+++ b/al/eax/api.h
@@ -10,17 +10,18 @@
//
+#include <array>
#include <cfloat>
#include <cstdint>
#include <cstring>
-
-#include <array>
+#ifdef _WIN32
+#include <guiddef.h>
+#endif
#include "AL/al.h"
-#ifndef GUID_DEFINED
-#define GUID_DEFINED
+#ifndef _WIN32
typedef struct _GUID {
std::uint32_t Data1;
std::uint16_t Data2;
@@ -28,16 +29,16 @@ typedef struct _GUID {
std::uint8_t Data4[8];
} GUID;
-#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) \
+friend bool operator==(const T &lhs, const T &rhs) noexcept { return std::memcmp(&lhs, &rhs, sizeof(T)) == 0; } \
+friend bool operator!=(const T &lhs, const T &rhs) noexcept { return !(lhs == rhs); }
extern const GUID DSPROPSETID_EAX_ReverbProperties;
@@ -613,7 +614,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
@@ -836,6 +837,7 @@ 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)
}; // EAXREVERBPROPERTIES
@@ -965,6 +967,7 @@ enum EAXAGCCOMPRESSOR_PROPERTY : unsigned int {
struct EAXAGCCOMPRESSORPROPERTIES {
unsigned long ulOnOff; // Switch Compressor on or off
+ DECL_EQOP(EAXAGCCOMPRESSORPROPERTIES)
}; // EAXAGCCOMPRESSORPROPERTIES
@@ -991,6 +994,7 @@ struct EAXAUTOWAHPROPERTIES {
float flReleaseTime; // Release time (seconds)
long lResonance; // Resonance (mB)
long lPeakLevel; // Peak level (mB)
+ DECL_EQOP(EAXAUTOWAHPROPERTIES)
}; // EAXAUTOWAHPROPERTIES
@@ -1038,6 +1042,7 @@ struct EAXCHORUSPROPERTIES {
float flDepth; // Depth (0 to 1)
float flFeedback; // Feedback (-1 to 1)
float flDelay; // Delay (seconds)
+ DECL_EQOP(EAXCHORUSPROPERTIES)
}; // EAXCHORUSPROPERTIES
@@ -1086,6 +1091,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)
}; // EAXDISTORTIONPROPERTIES
@@ -1130,6 +1136,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)
}; // EAXECHOPROPERTIES
@@ -1184,6 +1191,7 @@ struct EAXEQUALIZERPROPERTIES {
float flMid2Width; // (octaves)
long lHighGain; // (mB)
float flHighCutOff; // (Hz)
+ DECL_EQOP(EAXEQUALIZERPROPERTIES)
}; // EAXEQUALIZERPROPERTIES
@@ -1255,6 +1263,7 @@ struct EAXFLANGERPROPERTIES {
float flDepth; // Depth (0 to 1)
float flFeedback; // Feedback (0 to 1)
float flDelay; // Delay (seconds)
+ DECL_EQOP(EAXFLANGERPROPERTIES)
}; // EAXFLANGERPROPERTIES
@@ -1305,6 +1314,7 @@ struct EAXFREQUENCYSHIFTERPROPERTIES {
float flFrequency; // (Hz)
unsigned long ulLeftDirection; // see enum above
unsigned long ulRightDirection; // see enum above
+ DECL_EQOP(EAXFREQUENCYSHIFTERPROPERTIES)
}; // EAXFREQUENCYSHIFTERPROPERTIES
@@ -1383,6 +1393,7 @@ struct EAXVOCALMORPHERPROPERTIES {
long lPhonemeBCoarseTuning; // (semitones)
unsigned long ulWaveform; // Waveform selector - see enum above
float flRate; // (Hz)
+ DECL_EQOP(EAXVOCALMORPHERPROPERTIES)
}; // EAXVOCALMORPHERPROPERTIES
@@ -1425,6 +1436,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)
}; // EAXPITCHSHIFTERPROPERTIES
@@ -1460,6 +1472,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)
}; // EAXRINGMODULATORPROPERTIES
@@ -1490,4 +1503,5 @@ using LPEAXGET = ALenum(AL_APIENTRY*)(
ALvoid* property_buffer,
ALuint property_size);
+#undef DECL_EQOP
#endif // !EAX_API_INCLUDED
diff --git a/al/eax/call.h b/al/eax/call.h
index 5ec33b0f..45ff328c 100644
--- a/al/eax/call.h
+++ b/al/eax/call.h
@@ -55,13 +55,13 @@ public:
fail_too_small();
const auto count = minz(mPropertyBufferSize / sizeof(TValue), max_count);
- return al::as_span(static_cast<TValue*>(mPropertyBuffer), count);
+ return {static_cast<TValue*>(mPropertyBuffer), count};
}
template<typename TValue>
al::span<TValue> get_values() const
{
- return get_values<TValue>(~size_t{});
+ return get_values<TValue>(~0_uz);
}
template<typename TException, typename TValue>
diff --git a/al/eax/effect.h b/al/eax/effect.h
index a0b4e71b..afe4d94d 100644
--- a/al/eax/effect.h
+++ b/al/eax/effect.h
@@ -4,60 +4,55 @@
#include <cassert>
#include <memory>
+#include <variant>
#include "alnumeric.h"
#include "AL/al.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;
- };
-};
-
-constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props)
+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) noexcept
{
- 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 {
@@ -300,30 +295,30 @@ public:
}
-#define EAXCALL(T, Callable, ...) \
- if(T == EaxEffectType::Reverb) \
+#define EAXCALL(Props, Callable, ...) \
+ if(std::holds_alternative<EAXREVERBPROPERTIES>(Props)) \
return Callable<EaxReverbCommitter>(__VA_ARGS__); \
- if(T == EaxEffectType::Chorus) \
+ if(std::holds_alternative<EAXCHORUSPROPERTIES>(Props)) \
return Callable<EaxChorusCommitter>(__VA_ARGS__); \
- if(T == EaxEffectType::Autowah) \
+ if(std::holds_alternative<EAXAUTOWAHPROPERTIES>(Props)) \
return Callable<EaxAutowahCommitter>(__VA_ARGS__); \
- if(T == EaxEffectType::Compressor) \
+ if(std::holds_alternative<EAXAGCCOMPRESSORPROPERTIES>(Props)) \
return Callable<EaxCompressorCommitter>(__VA_ARGS__); \
- if(T == EaxEffectType::Distortion) \
+ if(std::holds_alternative<EAXDISTORTIONPROPERTIES>(Props)) \
return Callable<EaxDistortionCommitter>(__VA_ARGS__); \
- if(T == EaxEffectType::Echo) \
+ if(std::holds_alternative<EAXECHOPROPERTIES>(Props)) \
return Callable<EaxEchoCommitter>(__VA_ARGS__); \
- if(T == EaxEffectType::Equalizer) \
+ if(std::holds_alternative<EAXEQUALIZERPROPERTIES>(Props)) \
return Callable<EaxEqualizerCommitter>(__VA_ARGS__); \
- if(T == EaxEffectType::Flanger) \
+ if(std::holds_alternative<EAXFLANGERPROPERTIES>(Props)) \
return Callable<EaxFlangerCommitter>(__VA_ARGS__); \
- if(T == EaxEffectType::FrequencyShifter) \
+ if(std::holds_alternative<EAXFREQUENCYSHIFTERPROPERTIES>(Props)) \
return Callable<EaxFrequencyShifterCommitter>(__VA_ARGS__); \
- if(T == EaxEffectType::Modulator) \
+ if(std::holds_alternative<EAXRINGMODULATORPROPERTIES>(Props)) \
return Callable<EaxModulatorCommitter>(__VA_ARGS__); \
- if(T == EaxEffectType::PitchShifter) \
+ if(std::holds_alternative<EAXPITCHSHIFTERPROPERTIES>(Props)) \
return Callable<EaxPitchShifterCommitter>(__VA_ARGS__); \
- if(T == EaxEffectType::VocalMorpher) \
+ if(std::holds_alternative<EAXVOCALMORPHERPROPERTIES>(Props)) \
return Callable<EaxVocalMorpherCommitter>(__VA_ARGS__); \
return Callable<EaxNullCommitter>(__VA_ARGS__)
@@ -332,7 +327,7 @@ public:
{ return T::Set(std::forward<Args>(args)...); }
static void call_set(const EaxCall &call, EaxEffectProps &props)
- { EAXCALL(props.mType, call_set, call, props); }
+ { EAXCALL(props, call_set, call, props); }
void set(const EaxCall &call)
{
@@ -353,7 +348,7 @@ public:
{ return T::Get(std::forward<Args>(args)...); }
static void call_get(const EaxCall &call, const EaxEffectProps &props)
- { EAXCALL(props.mType, call_get, call, props); }
+ { EAXCALL(props, call_get, call, props); }
void get(const EaxCall &call)
{
@@ -373,7 +368,7 @@ public:
{ return T{props_, al_effect_props_}.commit(std::forward<Args>(args)...); }
bool call_commit(const EaxEffectProps &props)
- { EAXCALL(props.mType, call_commit, props); }
+ { EAXCALL(props, call_commit, props); }
bool commit(int eax_version)
{
diff --git a/al/eax/exception.cpp b/al/eax/exception.cpp
index 435e7442..e4945d88 100644
--- a/al/eax/exception.cpp
+++ b/al/eax/exception.cpp
@@ -6,54 +6,27 @@
#include <string>
-EaxException::EaxException(const char *context, const char *message)
+EaxException::EaxException(std::string_view context, std::string_view message)
: std::runtime_error{make_message(context, message)}
{
}
EaxException::~EaxException() = default;
-std::string EaxException::make_message(const char *context, const char *message)
+std::string EaxException::make_message(std::string_view context, std::string_view message)
{
- const auto context_size = (context ? std::string::traits_type::length(context) : 0);
- const auto has_contex = (context_size > 0);
-
- const auto message_size = (message ? std::string::traits_type::length(message) : 0);
- const auto has_message = (message_size > 0);
-
- if (!has_contex && !has_message)
- {
- return std::string{};
- }
-
- static constexpr char left_prefix[] = "[";
- const auto left_prefix_size = std::string::traits_type::length(left_prefix);
-
- static constexpr char right_prefix[] = "] ";
- const auto right_prefix_size = std::string::traits_type::length(right_prefix);
-
- const auto what_size =
- (
- has_contex ?
- left_prefix_size + context_size + right_prefix_size :
- 0) +
- message_size +
- 1;
-
auto what = std::string{};
- what.reserve(what_size);
-
- if (has_contex)
- {
- what.append(left_prefix, left_prefix_size);
- what.append(context, context_size);
- what.append(right_prefix, right_prefix_size);
- }
+ if(context.empty() && message.empty())
+ return what;
- if (has_message)
+ what.reserve((!context.empty() ? context.size() + 3 : 0) + message.length() + 1);
+ if(!context.empty())
{
- what.append(message, message_size);
+ what += "[";
+ what += context;
+ what += "] ";
}
+ what += message;
return what;
}
diff --git a/al/eax/exception.h b/al/eax/exception.h
index 3ae88cdc..336654f0 100644
--- a/al/eax/exception.h
+++ b/al/eax/exception.h
@@ -1,16 +1,16 @@
#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(std::string_view context, std::string_view message);
~EaxException() override;
}; // EaxException
diff --git a/al/eax/fx_slot_index.h b/al/eax/fx_slot_index.h
index 63dba037..9f350d9b 100644
--- a/al/eax/fx_slot_index.h
+++ b/al/eax/fx_slot_index.h
@@ -3,17 +3,16 @@
#include <cstddef>
+#include <optional>
-#include "aloptional.h"
#include "api.h"
using EaxFxSlotIndexValue = std::size_t;
-class EaxFxSlotIndex : public al::optional<EaxFxSlotIndexValue>
-{
+class EaxFxSlotIndex : public std::optional<EaxFxSlotIndexValue> {
public:
- using al::optional<EaxFxSlotIndexValue>::optional;
+ using std::optional<EaxFxSlotIndexValue>::optional;
EaxFxSlotIndex& operator=(const EaxFxSlotIndexValue &value) { set(value); return *this; }
EaxFxSlotIndex& operator=(const GUID &guid) { set(guid); return *this; }
diff --git a/al/eax/globals.cpp b/al/eax/globals.cpp
deleted file mode 100644
index 80e9dbfe..00000000
--- a/al/eax/globals.cpp
+++ /dev/null
@@ -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";
diff --git a/al/eax/globals.h b/al/eax/globals.h
index 1b4d63b8..ff05d009 100644
--- a/al/eax/globals.h
+++ b/al/eax/globals.h
@@ -1,22 +1,20 @@
#ifndef EAX_GLOBALS_INCLUDED
#define EAX_GLOBALS_INCLUDED
+inline bool eax_g_is_enabled{true};
-extern bool eax_g_is_enabled;
+inline constexpr char eax1_ext_name[]{"EAX"};
+inline constexpr char eax2_ext_name[]{"EAX2.0"};
+inline constexpr char eax3_ext_name[]{"EAX3.0"};
+inline constexpr char eax4_ext_name[]{"EAX4.0"};
+inline constexpr char eax5_ext_name[]{"EAX5.0"};
+inline constexpr char eax_x_ram_ext_name[]{"EAX-RAM"};
-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[];
+inline constexpr char eax_eax_set_func_name[]{"EAXSet"};
+inline constexpr char eax_eax_get_func_name[]{"EAXGet"};
-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[];
+inline constexpr char eax_eax_set_buffer_mode_func_name[]{"EAXSetBufferMode"};
+inline constexpr char eax_eax_get_buffer_mode_func_name[]{"EAXGetBufferMode"};
#endif // !EAX_GLOBALS_INCLUDED
diff --git a/al/eax/utils.cpp b/al/eax/utils.cpp
index b3ed6ca1..53599ac5 100644
--- a/al/eax/utils.cpp
+++ b/al/eax/utils.cpp
@@ -8,7 +8,7 @@
#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);
@@ -18,9 +18,9 @@ void eax_log_exception(const char *message) noexcept
}
catch(const std::exception& ex) {
const auto ex_message = ex.what();
- ERR("%s %s\n", message ? message : "", ex_message);
+ ERR("%.*s %s\n", static_cast<int>(message.length()), message.data(), ex_message);
}
catch(...) {
- ERR("%s %s\n", message ? message : "", "Generic exception.");
+ ERR("%.*s %s\n", static_cast<int>(message.length()), message.data(), "Generic exception.");
}
}
diff --git a/al/eax/utils.h b/al/eax/utils.h
index 8ff75a18..8e0f975f 100644
--- a/al/eax/utils.h
+++ b/al/eax/utils.h
@@ -4,8 +4,11 @@
#include <algorithm>
#include <cstdint>
#include <string>
+#include <string_view>
#include <type_traits>
+#include "opthelpers.h"
+
using EaxDirtyFlags = unsigned int;
struct EaxAlLowPassParam {
@@ -13,16 +16,13 @@ struct EaxAlLowPassParam {
float gain_hf;
};
-void eax_log_exception(const char *message) noexcept;
+void eax_log_exception(std::string_view message) noexcept;
template<typename TException, typename TValue>
-void eax_validate_range(
- const char* value_name,
- const TValue& value,
- const TValue& min_value,
+void eax_validate_range(std::string_view value_name, const TValue& value, const TValue& min_value,
const TValue& max_value)
{
- if (value >= min_value && value <= max_value)
+ if(value >= min_value && value <= max_value) LIKELY
return;
const auto message =
diff --git a/al/eax/x_ram.h b/al/eax/x_ram.h
index 438b9916..d10fe697 100644
--- a/al/eax/x_ram.h
+++ b/al/eax/x_ram.h
@@ -25,14 +25,7 @@ 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
diff --git a/al/effect.cpp b/al/effect.cpp
index bde89912..3e48e91b 100644
--- a/al/effect.cpp
+++ b/al/effect.cpp
@@ -31,6 +31,7 @@
#include <new>
#include <numeric>
#include <utility>
+#include <vector>
#include "AL/al.h"
#include "AL/alc.h"
@@ -48,8 +49,8 @@
#include "alstring.h"
#include "core/except.h"
#include "core/logging.h"
+#include "direct_defs.h"
#include "opthelpers.h"
-#include "vector.h"
#ifdef ALSOFT_EAX
#include <cassert>
@@ -73,7 +74,7 @@ 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];
@@ -112,7 +113,7 @@ constexpr EffectPropsItem EffectPropsList[] = {
{ 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 },
+ { AL_EFFECT_CONVOLUTION_SOFT, ConvolutionEffectProps, ConvolutionEffectVtable },
};
@@ -161,7 +162,7 @@ void InitEffectParams(ALeffect *effect, ALenum type)
bool EnsureEffects(ALCdevice *device, size_t needed)
{
- size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), size_t{0},
+ size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), 0_uz,
[](size_t cur, const EffectSubList &sublist) noexcept -> size_t
{ return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
@@ -206,11 +207,13 @@ 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;
}
@@ -230,12 +233,9 @@ inline ALeffect *LookupEffect(ALCdevice *device, ALuint id)
} // namespace
-AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGenEffects, ALsizei, ALuint*)
+FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Generating %d effects", n);
if(n <= 0) UNLIKELY return;
@@ -259,7 +259,7 @@ START_API_FUNC
/* Store the allocated buffer IDs in a separate local list, to avoid
* modifying the user storage in case of failure.
*/
- al::vector<ALuint> ids;
+ std::vector<ALuint> ids;
ids.reserve(static_cast<ALuint>(n));
do {
ALeffect *effect{AllocEffect(device)};
@@ -268,14 +268,11 @@ START_API_FUNC
std::copy(ids.cbegin(), ids.cend(), effects);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei, const ALuint*)
+FORCE_ALIGN void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n,
+ const ALuint *effects) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
if(n <= 0) UNLIKELY return;
@@ -303,29 +300,21 @@ START_API_FUNC
};
std::for_each(effects, effects_end, delete_effect);
}
-END_API_FUNC
-AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint)
+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> _{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
+AL_API DECL_FUNC3(void, alEffecti, ALuint, ALenum, ALint)
+FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
+ ALint value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
@@ -361,21 +350,18 @@ START_API_FUNC
context->setError(e.errorCode(), "%s", e.what());
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alEffectiv, ALuint, ALenum, const ALint*)
+FORCE_ALIGN void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
+ const ALint *values) noexcept
{
switch(param)
{
case AL_EFFECT_TYPE:
- alEffecti(effect, param, values[0]);
+ alEffectiDirect(context, effect, param, values[0]);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
@@ -391,14 +377,11 @@ START_API_FUNC
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
+AL_API DECL_FUNC3(void, alEffectf, ALuint, ALenum, ALfloat)
+FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
+ ALfloat value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
@@ -414,14 +397,11 @@ START_API_FUNC
context->setError(e.errorCode(), "%s", e.what());
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alEffectfv, ALuint, ALenum, const ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
+ const ALfloat *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
@@ -437,14 +417,11 @@ START_API_FUNC
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
+AL_API DECL_FUNC3(void, alGetEffecti, ALuint, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param,
+ ALint *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
@@ -462,21 +439,18 @@ START_API_FUNC
context->setError(e.errorCode(), "%s", e.what());
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetEffectiv, ALuint, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param,
+ ALint *values) noexcept
{
switch(param)
{
case AL_EFFECT_TYPE:
- alGetEffecti(effect, param, values);
+ alGetEffectiDirect(context, effect, param, values);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
@@ -492,14 +466,11 @@ START_API_FUNC
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
+AL_API DECL_FUNC3(void, alGetEffectf, ALuint, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param,
+ ALfloat *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
@@ -515,14 +486,11 @@ START_API_FUNC
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
+AL_API DECL_FUNC3(void, alGetEffectfv, ALuint, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param,
+ ALfloat *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->EffectLock};
@@ -538,7 +506,6 @@ START_API_FUNC
context->setError(e.errorCode(), "%s", e.what());
}
}
-END_API_FUNC
void InitEffect(ALeffect *effect)
@@ -546,13 +513,29 @@ 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> _{device->EffectLock};
+
+ auto effect = LookupEffect(device, id);
+ if(!effect) UNLIKELY
+ return context->setError(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(Effects+idx);
usemask &= ~(1_u64 << idx);
}
FreeMask = ~usemask;
diff --git a/al/effect.h b/al/effect.h
index a1d43313..27e9dd72 100644
--- a/al/effect.h
+++ b/al/effect.h
@@ -1,6 +1,8 @@
#ifndef AL_EFFECT_H
#define AL_EFFECT_H
+#include <string_view>
+
#include "AL/al.h"
#include "AL/efx.h"
@@ -50,6 +52,8 @@ struct ALeffect {
/* Self ID */
ALuint id{0u};
+ static void SetName(ALCcontext *context, ALuint id, std::string_view name);
+
DISABLE_ALLOC()
};
diff --git a/al/effects/autowah.cpp b/al/effects/autowah.cpp
index 129318f4..1a8b43fc 100644
--- a/al/effects/autowah.cpp
+++ b/al/effects/autowah.cpp
@@ -192,19 +192,16 @@ template<>
template<>
bool AutowahCommitter::commit(const EaxEffectProps &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(props == mEaxProps)
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));
+ auto &eaxprops = std::get<EAXAUTOWAHPROPERTIES>(props);
+ mAlProps.Autowah.AttackTime = eaxprops.flAttackTime;
+ mAlProps.Autowah.ReleaseTime = eaxprops.flReleaseTime;
+ mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast<float>(eaxprops.lResonance));
+ mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast<float>(eaxprops.lPeakLevel));
return true;
}
@@ -212,39 +209,46 @@ bool AutowahCommitter::commit(const EaxEffectProps &props)
template<>
void AutowahCommitter::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 AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props_)
{
+ auto &props = std::get<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 AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props_)
{
+ auto &props = std::get<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();
}
}
diff --git a/al/effects/chorus.cpp b/al/effects/chorus.cpp
index 305259a4..90b38e4d 100644
--- a/al/effects/chorus.cpp
+++ b/al/effects/chorus.cpp
@@ -1,13 +1,13 @@
#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"
@@ -27,14 +27,14 @@ 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)
+inline std::optional<ChorusWaveform> WaveformFromEnum(ALenum type)
{
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)
{
@@ -294,9 +294,7 @@ namespace {
struct EaxChorusTraits {
using Props = 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; }
@@ -361,9 +359,7 @@ struct EaxChorusTraits {
struct EaxFlangerTraits {
using Props = 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; }
@@ -431,8 +427,6 @@ struct ChorusFlangerEffect {
using Committer = typename Traits::Committer;
using Exception = typename Committer::Exception;
- static constexpr auto Field = Traits::Field;
-
struct WaveformValidator {
void operator()(unsigned long ulWaveform) const
{
@@ -514,8 +508,7 @@ struct ChorusFlangerEffect {
public:
static void SetDefaults(EaxEffectProps &props)
{
- auto&& all = props.*Field;
- props.mType = Traits::eax_effect_type();
+ auto&& all = props.emplace<typename Traits::Props>();
all.ulWaveform = Traits::eax_default_waveform();
all.lPhase = Traits::eax_default_phase();
all.flRate = Traits::eax_default_rate();
@@ -527,7 +520,7 @@ public:
static void Get(const EaxCall &call, const EaxEffectProps &props)
{
- auto&& all = props.*Field;
+ auto&& all = std::get<typename Traits::Props>(props);
switch(call.get_property_id())
{
case Traits::eax_none_param_id():
@@ -568,7 +561,7 @@ public:
static void Set(const EaxCall &call, EaxEffectProps &props)
{
- auto&& all = props.*Field;
+ auto&& all = std::get<typename Traits::Props>(props);
switch(call.get_property_id())
{
case Traits::eax_none_param_id():
@@ -609,18 +602,11 @@ public:
static bool Commit(const EaxEffectProps &props, EaxEffectProps &props_, EffectProps &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(props == props_)
+ return false;
props_ = props;
- auto&& dst = props.*Field;
+ auto&& dst = std::get<typename Traits::Props>(props);
al_props_.Chorus.Waveform = Traits::eax_waveform(dst.ulWaveform);
al_props_.Chorus.Phase = static_cast<int>(dst.lPhase);
diff --git a/al/effects/compressor.cpp b/al/effects/compressor.cpp
index a4aa8e77..ca8af84f 100644
--- a/al/effects/compressor.cpp
+++ b/al/effects/compressor.cpp
@@ -118,43 +118,43 @@ template<>
template<>
bool CompressorCommitter::commit(const EaxEffectProps &props)
{
- if(props.mType == mEaxProps.mType
- && props.mCompressor.ulOnOff == mEaxProps.mCompressor.ulOnOff)
+ if(props == mEaxProps)
return false;
mEaxProps = props;
- mAlProps.Compressor.OnOff = (props.mCompressor.ulOnOff != 0);
+ mAlProps.Compressor.OnOff = (std::get<EAXAGCCOMPRESSORPROPERTIES>(props).ulOnOff != 0);
return true;
}
template<>
void CompressorCommitter::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 CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_)
{
+ auto &props = std::get<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 CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props_)
{
+ auto &props = std::get<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();
}
}
diff --git a/al/effects/convolution.cpp b/al/effects/convolution.cpp
index 8e850fd3..9c091e53 100644
--- a/al/effects/convolution.cpp
+++ b/al/effects/convolution.cpp
@@ -1,6 +1,8 @@
#include "config.h"
+#include <cmath>
+
#include "AL/al.h"
#include "alc/inprogext.h"
@@ -36,12 +38,25 @@ void Convolution_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/)
param};
}
}
-void Convolution_setParamfv(EffectProps *props, ALenum param, const float *vals)
+void Convolution_setParamfv(EffectProps *props, ALenum param, const float *values)
{
switch(param)
{
+ case AL_CONVOLUTION_ORIENTATION_SOFT:
+ 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])))
+ throw effect_exception{AL_INVALID_VALUE, "Property 0x%04x value out of range", param};
+
+ props->Convolution.OrientAt[0] = values[0];
+ props->Convolution.OrientAt[1] = values[1];
+ props->Convolution.OrientAt[2] = values[2];
+ props->Convolution.OrientUp[0] = values[3];
+ props->Convolution.OrientUp[1] = values[4];
+ props->Convolution.OrientUp[2] = values[5];
+ break;
+
default:
- Convolution_setParamf(props, param, vals[0]);
+ Convolution_setParamf(props, param, values[0]);
}
}
@@ -71,18 +86,29 @@ void Convolution_getParamf(const EffectProps* /*props*/, ALenum param, float* /*
param};
}
}
-void Convolution_getParamfv(const EffectProps *props, ALenum param, float *vals)
+void Convolution_getParamfv(const EffectProps *props, ALenum param, float *values)
{
switch(param)
{
+ case AL_CONVOLUTION_ORIENTATION_SOFT:
+ values[0] = props->Convolution.OrientAt[0];
+ values[1] = props->Convolution.OrientAt[1];
+ values[2] = props->Convolution.OrientAt[2];
+ values[3] = props->Convolution.OrientUp[0];
+ values[4] = props->Convolution.OrientUp[1];
+ values[5] = props->Convolution.OrientUp[2];
+ break;
+
default:
- Convolution_getParamf(props, param, vals);
+ Convolution_getParamf(props, param, values);
}
}
EffectProps genDefaultProps() noexcept
{
EffectProps props{};
+ props.Convolution.OrientAt = {0.0f, 0.0f, -1.0f};
+ props.Convolution.OrientUp = {0.0f, 1.0f, 0.0f};
return props;
}
diff --git a/al/effects/distortion.cpp b/al/effects/distortion.cpp
index ee298ddf..e046d8e7 100644
--- a/al/effects/distortion.cpp
+++ b/al/effects/distortion.cpp
@@ -207,20 +207,17 @@ template<>
template<>
bool DistortionCommitter::commit(const EaxEffectProps &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(props == mEaxProps)
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;
+ auto &eaxprops = std::get<EAXDISTORTIONPROPERTIES>(props);
+ mAlProps.Distortion.Edge = eaxprops.flEdge;
+ mAlProps.Distortion.Gain = level_mb_to_gain(static_cast<float>(eaxprops.lGain));
+ mAlProps.Distortion.LowpassCutoff = eaxprops.flLowPassCutOff;
+ mAlProps.Distortion.EQCenter = eaxprops.flEQCenter;
+ mAlProps.Distortion.EQBandwidth = eaxprops.flEdge;
return true;
}
@@ -228,42 +225,49 @@ bool DistortionCommitter::commit(const EaxEffectProps &props)
template<>
void DistortionCommitter::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 DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props_)
{
+ auto &props = std::get<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 DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props_)
{
+ auto &props = std::get<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();
}
}
diff --git a/al/effects/echo.cpp b/al/effects/echo.cpp
index 2eb37603..48aacef3 100644
--- a/al/effects/echo.cpp
+++ b/al/effects/echo.cpp
@@ -204,20 +204,17 @@ template<>
template<>
bool EchoCommitter::commit(const EaxEffectProps &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(props == mEaxProps)
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;
+ auto &eaxprops = std::get<EAXECHOPROPERTIES>(props);
+ mAlProps.Echo.Delay = eaxprops.flDelay;
+ mAlProps.Echo.LRDelay = eaxprops.flLRDelay;
+ mAlProps.Echo.Damping = eaxprops.flDamping;
+ mAlProps.Echo.Feedback = eaxprops.flFeedback;
+ mAlProps.Echo.Spread = eaxprops.flSpread;
return true;
}
@@ -225,42 +222,49 @@ bool EchoCommitter::commit(const EaxEffectProps &props)
template<>
void EchoCommitter::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 EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props_)
{
+ auto &props = std::get<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 EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props_)
{
+ auto &props = std::get<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();
}
}
diff --git a/al/effects/equalizer.cpp b/al/effects/equalizer.cpp
index 7dc703db..76d5bdef 100644
--- a/al/effects/equalizer.cpp
+++ b/al/effects/equalizer.cpp
@@ -322,30 +322,22 @@ template<>
template<>
bool EqualizerCommitter::commit(const EaxEffectProps &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(props == mEaxProps)
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;
+ auto &eaxprops = std::get<EAXEQUALIZERPROPERTIES>(props);
+ mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast<float>(eaxprops.lLowGain));
+ mAlProps.Equalizer.LowCutoff = eaxprops.flLowCutOff;
+ mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast<float>(eaxprops.lMid1Gain));
+ mAlProps.Equalizer.Mid1Center = eaxprops.flMid1Center;
+ mAlProps.Equalizer.Mid1Width = eaxprops.flMid1Width;
+ mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast<float>(eaxprops.lMid2Gain));
+ mAlProps.Equalizer.Mid2Center = eaxprops.flMid2Center;
+ mAlProps.Equalizer.Mid2Width = eaxprops.flMid2Width;
+ mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast<float>(eaxprops.lHighGain));
+ mAlProps.Equalizer.HighCutoff = eaxprops.flHighCutOff;
return true;
}
@@ -353,57 +345,64 @@ bool EqualizerCommitter::commit(const EaxEffectProps &props)
template<>
void EqualizerCommitter::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 EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props_)
{
+ auto &props = std::get<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 EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props_)
{
+ auto &props = std::get<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();
}
}
diff --git a/al/effects/fshifter.cpp b/al/effects/fshifter.cpp
index 949db203..37c372c3 100644
--- a/al/effects/fshifter.cpp
+++ b/al/effects/fshifter.cpp
@@ -1,13 +1,13 @@
#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
@@ -20,7 +20,7 @@
namespace {
-al::optional<FShifterDirection> DirectionFromEmum(ALenum value)
+std::optional<FShifterDirection> DirectionFromEmum(ALenum value)
{
switch(value)
{
@@ -28,7 +28,7 @@ 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)
{
@@ -200,10 +200,7 @@ template<>
template<>
bool FrequencyShifterCommitter::commit(const EaxEffectProps &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(props == mEaxProps)
return false;
mEaxProps = props;
@@ -217,9 +214,10 @@ 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);
+ auto &eaxprops = std::get<EAXFREQUENCYSHIFTERPROPERTIES>(props);
+ mAlProps.Fshifter.Frequency = eaxprops.flFrequency;
+ mAlProps.Fshifter.LeftDirection = get_direction(eaxprops.ulLeftDirection);
+ mAlProps.Fshifter.RightDirection = get_direction(eaxprops.ulRightDirection);
return true;
}
@@ -227,36 +225,43 @@ bool FrequencyShifterCommitter::commit(const EaxEffectProps &props)
template<>
void FrequencyShifterCommitter::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 FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props_)
{
+ auto &props = std::get<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 FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props_)
{
+ auto &props = std::get<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();
}
}
diff --git a/al/effects/modulator.cpp b/al/effects/modulator.cpp
index 5f37d08f..366e7ef7 100644
--- a/al/effects/modulator.cpp
+++ b/al/effects/modulator.cpp
@@ -1,13 +1,13 @@
#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
@@ -20,7 +20,7 @@
namespace {
-al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
+std::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
{
switch(value)
{
@@ -28,7 +28,7 @@ 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)
{
@@ -206,10 +206,7 @@ template<>
template<>
bool ModulatorCommitter::commit(const EaxEffectProps &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(props == mEaxProps)
return false;
mEaxProps = props;
@@ -225,9 +222,10 @@ 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);
+ auto &eaxprops = std::get<EAXRINGMODULATORPROPERTIES>(props);
+ mAlProps.Modulator.Frequency = eaxprops.flFrequency;
+ mAlProps.Modulator.HighPassCutoff = eaxprops.flHighPassCutOff;
+ mAlProps.Modulator.Waveform = get_waveform(eaxprops.ulWaveform);
return true;
}
@@ -235,36 +233,43 @@ bool ModulatorCommitter::commit(const EaxEffectProps &props)
template<>
void ModulatorCommitter::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 ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_)
{
+ auto &props = std::get<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 ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props_)
{
+ auto &props = std::get<EAXRINGMODULATORPROPERTIES>(props_);
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();
}
}
diff --git a/al/effects/null.cpp b/al/effects/null.cpp
index 0bbc183a..1e8787e7 100644
--- a/al/effects/null.cpp
+++ b/al/effects/null.cpp
@@ -120,7 +120,7 @@ template<>
template<>
bool NullCommitter::commit(const EaxEffectProps &props)
{
- const bool ret{props.mType != mEaxProps.mType};
+ const bool ret{props != mEaxProps};
mEaxProps = props;
return ret;
}
@@ -128,8 +128,7 @@ bool NullCommitter::commit(const EaxEffectProps &props)
template<>
void NullCommitter::SetDefaults(EaxEffectProps &props)
{
- props = EaxEffectProps{};
- props.mType = EaxEffectType::None;
+ props.emplace<std::monostate>();
}
template<>
diff --git a/al/effects/pshifter.cpp b/al/effects/pshifter.cpp
index 634eb186..10824016 100644
--- a/al/effects/pshifter.cpp
+++ b/al/effects/pshifter.cpp
@@ -141,15 +141,14 @@ template<>
template<>
bool PitchShifterCommitter::commit(const EaxEffectProps &props)
{
- if(props.mType == mEaxProps.mType
- && mEaxProps.mPitchShifter.lCoarseTune == props.mPitchShifter.lCoarseTune
- && mEaxProps.mPitchShifter.lFineTune == props.mPitchShifter.lFineTune)
+ if(props == mEaxProps)
return false;
mEaxProps = props;
- mAlProps.Pshifter.CoarseTune = static_cast<int>(mEaxProps.mPitchShifter.lCoarseTune);
- mAlProps.Pshifter.FineTune = static_cast<int>(mEaxProps.mPitchShifter.lFineTune);
+ auto &eaxprops = std::get<EAXPITCHSHIFTERPROPERTIES>(props);
+ mAlProps.Pshifter.CoarseTune = static_cast<int>(eaxprops.lCoarseTune);
+ mAlProps.Pshifter.FineTune = static_cast<int>(eaxprops.lFineTune);
return true;
}
@@ -157,33 +156,34 @@ bool PitchShifterCommitter::commit(const EaxEffectProps &props)
template<>
void PitchShifterCommitter::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 PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props_)
{
+ auto &props = std::get<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 PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props_)
{
+ auto &props = std::get<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();
}
}
diff --git a/al/effects/reverb.cpp b/al/effects/reverb.cpp
index 440d7b4e..b037443f 100644
--- a/al/effects/reverb.cpp
+++ b/al/effects/reverb.cpp
@@ -945,7 +945,7 @@ struct EnvironmentSizeDeferrer2 {
if ((props.dwFlags & EAX2LISTENERFLAGS_DECAYTIMESCALE) != 0)
{
- props.flDecayTime = clamp(
+ props.flDecayTime = std::clamp(
props.flDecayTime * scale,
EAXREVERB_MINDECAYTIME,
EAXREVERB_MAXDECAYTIME);
@@ -954,7 +954,7 @@ struct EnvironmentSizeDeferrer2 {
if ((props.dwFlags & EAX2LISTENERFLAGS_REFLECTIONSSCALE) != 0 &&
(props.dwFlags & EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE) != 0)
{
- props.lReflections = clamp(
+ props.lReflections = std::clamp(
props.lReflections - static_cast<long>(gain_to_level_mb(scale)),
EAXREVERB_MINREFLECTIONS,
EAXREVERB_MAXREFLECTIONS);
@@ -962,7 +962,7 @@ struct EnvironmentSizeDeferrer2 {
if ((props.dwFlags & EAX2LISTENERFLAGS_REFLECTIONSDELAYSCALE) != 0)
{
- props.flReflectionsDelay = clamp(
+ props.flReflectionsDelay = std::clamp(
props.flReflectionsDelay * scale,
EAXREVERB_MINREFLECTIONSDELAY,
EAXREVERB_MAXREFLECTIONSDELAY);
@@ -972,7 +972,7 @@ struct EnvironmentSizeDeferrer2 {
{
const auto log_scalar = ((props.dwFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) ? 2'000.0F : 3'000.0F;
- props.lReverb = clamp(
+ props.lReverb = std::clamp(
props.lReverb - static_cast<long>(std::log10(scale) * log_scalar),
EAXREVERB_MINREVERB,
EAXREVERB_MAXREVERB);
@@ -980,7 +980,7 @@ struct EnvironmentSizeDeferrer2 {
if ((props.dwFlags & EAX2LISTENERFLAGS_REVERBDELAYSCALE) != 0)
{
- props.flReverbDelay = clamp(
+ props.flReverbDelay = std::clamp(
props.flReverbDelay * scale,
EAXREVERB_MINREVERBDELAY,
EAXREVERB_MAXREVERBDELAY);
@@ -1015,7 +1015,7 @@ struct EnvironmentSizeDeferrer3 {
if ((props.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0)
{
- props.flDecayTime = clamp(
+ props.flDecayTime = std::clamp(
props.flDecayTime * scale,
EAXREVERB_MINDECAYTIME,
EAXREVERB_MAXDECAYTIME);
@@ -1024,7 +1024,7 @@ struct EnvironmentSizeDeferrer3 {
if ((props.ulFlags & EAXREVERBFLAGS_REFLECTIONSSCALE) != 0 &&
(props.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0)
{
- props.lReflections = clamp(
+ props.lReflections = std::clamp(
props.lReflections - static_cast<long>(gain_to_level_mb(scale)),
EAXREVERB_MINREFLECTIONS,
EAXREVERB_MAXREFLECTIONS);
@@ -1032,7 +1032,7 @@ struct EnvironmentSizeDeferrer3 {
if ((props.ulFlags & EAXREVERBFLAGS_REFLECTIONSDELAYSCALE) != 0)
{
- props.flReflectionsDelay = clamp(
+ props.flReflectionsDelay = std::clamp(
props.flReflectionsDelay * scale,
EAXREVERB_MINREFLECTIONSDELAY,
EAXREVERB_MAXREFLECTIONSDELAY);
@@ -1041,7 +1041,7 @@ struct EnvironmentSizeDeferrer3 {
if ((props.ulFlags & EAXREVERBFLAGS_REVERBSCALE) != 0)
{
const auto log_scalar = ((props.ulFlags & EAXREVERBFLAGS_DECAYTIMESCALE) != 0) ? 2'000.0F : 3'000.0F;
- props.lReverb = clamp(
+ props.lReverb = std::clamp(
props.lReverb - static_cast<long>(std::log10(scale) * log_scalar),
EAXREVERB_MINREVERB,
EAXREVERB_MAXREVERB);
@@ -1049,7 +1049,7 @@ struct EnvironmentSizeDeferrer3 {
if ((props.ulFlags & EAXREVERBFLAGS_REVERBDELAYSCALE) != 0)
{
- props.flReverbDelay = clamp(
+ props.flReverbDelay = std::clamp(
props.flReverbDelay * scale,
EAXREVERB_MINREVERBDELAY,
EAXREVERB_MAXREVERBDELAY);
@@ -1057,7 +1057,7 @@ struct EnvironmentSizeDeferrer3 {
if ((props.ulFlags & EAXREVERBFLAGS_ECHOTIMESCALE) != 0)
{
- props.flEchoTime = clamp(
+ props.flEchoTime = std::clamp(
props.flEchoTime * scale,
EAXREVERB_MINECHOTIME,
EAXREVERB_MAXECHOTIME);
@@ -1065,7 +1065,7 @@ struct EnvironmentSizeDeferrer3 {
if ((props.ulFlags & EAXREVERBFLAGS_MODULATIONTIMESCALE) != 0)
{
- props.flModulationTime = clamp(
+ props.flModulationTime = std::clamp(
props.flModulationTime * scale,
EAXREVERB_MINMODULATIONTIME,
EAXREVERB_MAXMODULATIONTIME);
@@ -1089,48 +1089,35 @@ struct EaxReverbCommitter::Exception : public EaxReverbEffectException
void EaxReverbCommitter::translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept
{
assert(src.environment <= EAX1REVERB_MAXENVIRONMENT);
- dst.mType = EaxEffectType::Reverb;
- dst.mReverb = EAXREVERB_PRESETS[src.environment];
- dst.mReverb.flDecayTime = src.fDecayTime_sec;
- dst.mReverb.flDecayHFRatio = src.fDamping;
- dst.mReverb.lReverb = mini(static_cast<int>(gain_to_level_mb(src.fVolume)), 0);
+ auto&& eaxprops = dst.emplace<EAXREVERBPROPERTIES>(EAXREVERB_PRESETS[src.environment]);
+ eaxprops.flDecayTime = src.fDecayTime_sec;
+ eaxprops.flDecayHFRatio = src.fDamping;
+ eaxprops.lReverb = mini(static_cast<int>(gain_to_level_mb(src.fVolume)), 0);
}
void EaxReverbCommitter::translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept
{
assert(src.dwEnvironment <= EAX1REVERB_MAXENVIRONMENT);
- const auto& env = EAXREVERB_PRESETS[src.dwEnvironment];
- dst.mType = EaxEffectType::Reverb;
- dst.mReverb.ulEnvironment = src.dwEnvironment;
- dst.mReverb.flEnvironmentSize = src.flEnvironmentSize;
- dst.mReverb.flEnvironmentDiffusion = src.flEnvironmentDiffusion;
- dst.mReverb.lRoom = src.lRoom;
- dst.mReverb.lRoomHF = src.lRoomHF;
- dst.mReverb.lRoomLF = env.lRoomLF;
- dst.mReverb.flDecayTime = src.flDecayTime;
- dst.mReverb.flDecayHFRatio = src.flDecayHFRatio;
- dst.mReverb.flDecayLFRatio = env.flDecayLFRatio;
- dst.mReverb.lReflections = src.lReflections;
- dst.mReverb.flReflectionsDelay = src.flReflectionsDelay;
- dst.mReverb.vReflectionsPan = env.vReflectionsPan;
- dst.mReverb.lReverb = src.lReverb;
- dst.mReverb.flReverbDelay = src.flReverbDelay;
- dst.mReverb.vReverbPan = env.vReverbPan;
- dst.mReverb.flEchoTime = env.flEchoTime;
- dst.mReverb.flEchoDepth = env.flEchoDepth;
- dst.mReverb.flModulationTime = env.flModulationTime;
- dst.mReverb.flModulationDepth = env.flModulationDepth;
- dst.mReverb.flAirAbsorptionHF = src.flAirAbsorptionHF;
- dst.mReverb.flHFReference = env.flHFReference;
- dst.mReverb.flLFReference = env.flLFReference;
- dst.mReverb.flRoomRolloffFactor = src.flRoomRolloffFactor;
- dst.mReverb.ulFlags = src.dwFlags;
+ auto&& eaxprops = dst.emplace<EAXREVERBPROPERTIES>(EAXREVERB_PRESETS[src.dwEnvironment]);
+ eaxprops.ulEnvironment = src.dwEnvironment;
+ eaxprops.flEnvironmentSize = src.flEnvironmentSize;
+ eaxprops.flEnvironmentDiffusion = src.flEnvironmentDiffusion;
+ eaxprops.lRoom = src.lRoom;
+ eaxprops.lRoomHF = src.lRoomHF;
+ eaxprops.flDecayTime = src.flDecayTime;
+ eaxprops.flDecayHFRatio = src.flDecayHFRatio;
+ eaxprops.lReflections = src.lReflections;
+ eaxprops.flReflectionsDelay = src.flReflectionsDelay;
+ eaxprops.lReverb = src.lReverb;
+ eaxprops.flReverbDelay = src.flReverbDelay;
+ eaxprops.flAirAbsorptionHF = src.flAirAbsorptionHF;
+ eaxprops.flRoomRolloffFactor = src.flRoomRolloffFactor;
+ eaxprops.ulFlags = src.dwFlags;
}
void EaxReverbCommitter::translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept
{
- dst.mType = EaxEffectType::Reverb;
- dst.mReverb = src;
+ dst = src;
}
bool EaxReverbCommitter::commit(const EAX_REVERBPROPERTIES &props)
@@ -1156,41 +1143,41 @@ bool EaxReverbCommitter::commit(const EAXREVERBPROPERTIES &props)
bool EaxReverbCommitter::commit(const EaxEffectProps &props)
{
- if(props.mType == mEaxProps.mType
- && memcmp(&props.mReverb, &mEaxProps.mReverb, sizeof(mEaxProps.mReverb)) == 0)
+ if(props == mEaxProps)
return false;
mEaxProps = props;
- const auto size = props.mReverb.flEnvironmentSize;
+ auto &eaxprops = std::get<EAXREVERBPROPERTIES>(props);
+ const auto size = eaxprops.flEnvironmentSize;
const auto density = (size * size * size) / 16.0F;
mAlProps.Reverb.Density = std::min(density, AL_EAXREVERB_MAX_DENSITY);
- mAlProps.Reverb.Diffusion = props.mReverb.flEnvironmentDiffusion;
- mAlProps.Reverb.Gain = level_mb_to_gain(static_cast<float>(props.mReverb.lRoom));
- mAlProps.Reverb.GainHF = level_mb_to_gain(static_cast<float>(props.mReverb.lRoomHF));
- mAlProps.Reverb.GainLF = level_mb_to_gain(static_cast<float>(props.mReverb.lRoomLF));
- mAlProps.Reverb.DecayTime = props.mReverb.flDecayTime;
- mAlProps.Reverb.DecayHFRatio = props.mReverb.flDecayHFRatio;
- mAlProps.Reverb.DecayLFRatio = mEaxProps.mReverb.flDecayLFRatio;
- mAlProps.Reverb.ReflectionsGain = level_mb_to_gain(static_cast<float>(props.mReverb.lReflections));
- mAlProps.Reverb.ReflectionsDelay = props.mReverb.flReflectionsDelay;
- mAlProps.Reverb.ReflectionsPan[0] = props.mReverb.vReflectionsPan.x;
- mAlProps.Reverb.ReflectionsPan[1] = props.mReverb.vReflectionsPan.y;
- mAlProps.Reverb.ReflectionsPan[2] = props.mReverb.vReflectionsPan.z;
- mAlProps.Reverb.LateReverbGain = level_mb_to_gain(static_cast<float>(props.mReverb.lReverb));
- mAlProps.Reverb.LateReverbDelay = props.mReverb.flReverbDelay;
- mAlProps.Reverb.LateReverbPan[0] = props.mReverb.vReverbPan.x;
- mAlProps.Reverb.LateReverbPan[1] = props.mReverb.vReverbPan.y;
- mAlProps.Reverb.LateReverbPan[2] = props.mReverb.vReverbPan.z;
- mAlProps.Reverb.EchoTime = props.mReverb.flEchoTime;
- mAlProps.Reverb.EchoDepth = props.mReverb.flEchoDepth;
- mAlProps.Reverb.ModulationTime = props.mReverb.flModulationTime;
- mAlProps.Reverb.ModulationDepth = props.mReverb.flModulationDepth;
- mAlProps.Reverb.AirAbsorptionGainHF = level_mb_to_gain(props.mReverb.flAirAbsorptionHF);
- mAlProps.Reverb.HFReference = props.mReverb.flHFReference;
- mAlProps.Reverb.LFReference = props.mReverb.flLFReference;
- mAlProps.Reverb.RoomRolloffFactor = props.mReverb.flRoomRolloffFactor;
- mAlProps.Reverb.DecayHFLimit = ((props.mReverb.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0);
+ mAlProps.Reverb.Diffusion = eaxprops.flEnvironmentDiffusion;
+ mAlProps.Reverb.Gain = level_mb_to_gain(static_cast<float>(eaxprops.lRoom));
+ mAlProps.Reverb.GainHF = level_mb_to_gain(static_cast<float>(eaxprops.lRoomHF));
+ mAlProps.Reverb.GainLF = level_mb_to_gain(static_cast<float>(eaxprops.lRoomLF));
+ mAlProps.Reverb.DecayTime = eaxprops.flDecayTime;
+ mAlProps.Reverb.DecayHFRatio = eaxprops.flDecayHFRatio;
+ mAlProps.Reverb.DecayLFRatio = eaxprops.flDecayLFRatio;
+ mAlProps.Reverb.ReflectionsGain = level_mb_to_gain(static_cast<float>(eaxprops.lReflections));
+ mAlProps.Reverb.ReflectionsDelay = eaxprops.flReflectionsDelay;
+ mAlProps.Reverb.ReflectionsPan[0] = eaxprops.vReflectionsPan.x;
+ mAlProps.Reverb.ReflectionsPan[1] = eaxprops.vReflectionsPan.y;
+ mAlProps.Reverb.ReflectionsPan[2] = eaxprops.vReflectionsPan.z;
+ mAlProps.Reverb.LateReverbGain = level_mb_to_gain(static_cast<float>(eaxprops.lReverb));
+ mAlProps.Reverb.LateReverbDelay = eaxprops.flReverbDelay;
+ mAlProps.Reverb.LateReverbPan[0] = eaxprops.vReverbPan.x;
+ mAlProps.Reverb.LateReverbPan[1] = eaxprops.vReverbPan.y;
+ mAlProps.Reverb.LateReverbPan[2] = eaxprops.vReverbPan.z;
+ mAlProps.Reverb.EchoTime = eaxprops.flEchoTime;
+ mAlProps.Reverb.EchoDepth = eaxprops.flEchoDepth;
+ mAlProps.Reverb.ModulationTime = eaxprops.flModulationTime;
+ mAlProps.Reverb.ModulationDepth = eaxprops.flModulationDepth;
+ mAlProps.Reverb.AirAbsorptionGainHF = level_mb_to_gain(eaxprops.flAirAbsorptionHF);
+ mAlProps.Reverb.HFReference = eaxprops.flHFReference;
+ mAlProps.Reverb.LFReference = eaxprops.flLFReference;
+ mAlProps.Reverb.RoomRolloffFactor = eaxprops.flRoomRolloffFactor;
+ mAlProps.Reverb.DecayHFLimit = ((eaxprops.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0);
return true;
}
@@ -1212,8 +1199,7 @@ void EaxReverbCommitter::SetDefaults(EAXREVERBPROPERTIES &props)
void EaxReverbCommitter::SetDefaults(EaxEffectProps &props)
{
- props.mType = EaxEffectType::Reverb;
- SetDefaults(props.mReverb);
+ SetDefaults(props.emplace<EAXREVERBPROPERTIES>());
}
@@ -1290,7 +1276,7 @@ void EaxReverbCommitter::Get(const EaxCall &call, const EAXREVERBPROPERTIES &pro
void EaxReverbCommitter::Get(const EaxCall &call, const EaxEffectProps &props)
{
- Get(call, props.mReverb);
+ Get(call, std::get<EAXREVERBPROPERTIES>(props));
}
@@ -1493,7 +1479,7 @@ void EaxReverbCommitter::Set(const EaxCall &call, EAXREVERBPROPERTIES &props)
void EaxReverbCommitter::Set(const EaxCall &call, EaxEffectProps &props)
{
- Set(call, props.mReverb);
+ Set(call, std::get<EAXREVERBPROPERTIES>(props));
}
#endif // ALSOFT_EAX
diff --git a/al/effects/vmorpher.cpp b/al/effects/vmorpher.cpp
index 21ea3680..f2551229 100644
--- a/al/effects/vmorpher.cpp
+++ b/al/effects/vmorpher.cpp
@@ -1,13 +1,13 @@
#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
@@ -20,7 +20,7 @@
namespace {
-al::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
+std::optional<VMorpherPhenome> PhenomeFromEnum(ALenum val)
{
#define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x: \
return VMorpherPhenome::x
@@ -57,7 +57,7 @@ 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)
@@ -100,7 +100,7 @@ ALenum EnumFromPhenome(VMorpherPhenome phenome)
#undef HANDLE_PHENOME
}
-al::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
+std::optional<VMorpherWaveform> WaveformFromEmum(ALenum value)
{
switch(value)
{
@@ -108,7 +108,7 @@ 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)
{
@@ -355,13 +355,7 @@ template<>
template<>
bool VocalMorpherCommitter::commit(const EaxEffectProps &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(props == mEaxProps)
return false;
mEaxProps = props;
@@ -413,12 +407,13 @@ 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;
+ auto &eaxprops = std::get<EAXVOCALMORPHERPROPERTIES>(props);
+ mAlProps.Vmorpher.PhonemeA = get_phoneme(eaxprops.ulPhonemeA);
+ mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast<int>(eaxprops.lPhonemeACoarseTuning);
+ mAlProps.Vmorpher.PhonemeB = get_phoneme(eaxprops.ulPhonemeB);
+ mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast<int>(eaxprops.lPhonemeBCoarseTuning);
+ mAlProps.Vmorpher.Waveform = get_waveform(eaxprops.ulWaveform);
+ mAlProps.Vmorpher.Rate = eaxprops.flRate;
return true;
}
@@ -426,49 +421,55 @@ bool VocalMorpherCommitter::commit(const EaxEffectProps &props)
template<>
void VocalMorpherCommitter::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 VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props_)
{
+ auto &props = std::get<EAXVOCALMORPHERPROPERTIES>(props_);
switch(call.get_property_id())
{
case EAXVOCALMORPHER_NONE:
break;
case EAXVOCALMORPHER_ALLPARAMETERS:
- call.set_value<Exception>(props.mVocalMorpher);
+ call.set_value<Exception>(props);
break;
case EAXVOCALMORPHER_PHONEMEA:
- call.set_value<Exception>(props.mVocalMorpher.ulPhonemeA);
+ call.set_value<Exception>(props.ulPhonemeA);
break;
case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
- call.set_value<Exception>(props.mVocalMorpher.lPhonemeACoarseTuning);
+ call.set_value<Exception>(props.lPhonemeACoarseTuning);
break;
case EAXVOCALMORPHER_PHONEMEB:
- call.set_value<Exception>(props.mVocalMorpher.ulPhonemeB);
+ call.set_value<Exception>(props.ulPhonemeB);
break;
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
- call.set_value<Exception>(props.mVocalMorpher.lPhonemeBCoarseTuning);
+ call.set_value<Exception>(props.lPhonemeBCoarseTuning);
break;
case EAXVOCALMORPHER_WAVEFORM:
- call.set_value<Exception>(props.mVocalMorpher.ulWaveform);
+ call.set_value<Exception>(props.ulWaveform);
break;
case EAXVOCALMORPHER_RATE:
- call.set_value<Exception>(props.mVocalMorpher.flRate);
+ call.set_value<Exception>(props.flRate);
break;
default:
@@ -477,39 +478,40 @@ void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props
}
template<>
-void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props)
+void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props_)
{
+ auto &props = std::get<EAXVOCALMORPHERPROPERTIES>(props_);
switch(call.get_property_id())
{
case EAXVOCALMORPHER_NONE:
break;
case EAXVOCALMORPHER_ALLPARAMETERS:
- defer<AllValidator>(call, props.mVocalMorpher);
+ defer<AllValidator>(call, props);
break;
case EAXVOCALMORPHER_PHONEMEA:
- defer<PhonemeAValidator>(call, props.mVocalMorpher.ulPhonemeA);
+ defer<PhonemeAValidator>(call, props.ulPhonemeA);
break;
case EAXVOCALMORPHER_PHONEMEACOARSETUNING:
- defer<PhonemeACoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeACoarseTuning);
+ defer<PhonemeACoarseTuningValidator>(call, props.lPhonemeACoarseTuning);
break;
case EAXVOCALMORPHER_PHONEMEB:
- defer<PhonemeBValidator>(call, props.mVocalMorpher.ulPhonemeB);
+ defer<PhonemeBValidator>(call, props.ulPhonemeB);
break;
case EAXVOCALMORPHER_PHONEMEBCOARSETUNING:
- defer<PhonemeBCoarseTuningValidator>(call, props.mVocalMorpher.lPhonemeBCoarseTuning);
+ defer<PhonemeBCoarseTuningValidator>(call, props.lPhonemeBCoarseTuning);
break;
case EAXVOCALMORPHER_WAVEFORM:
- defer<WaveformValidator>(call, props.mVocalMorpher.ulWaveform);
+ defer<WaveformValidator>(call, props.ulWaveform);
break;
case EAXVOCALMORPHER_RATE:
- defer<RateValidator>(call, props.mVocalMorpher.flRate);
+ defer<RateValidator>(call, props.flRate);
break;
default:
diff --git a/al/error.cpp b/al/error.cpp
index afa7019a..c2359477 100644
--- a/al/error.cpp
+++ b/al/error.cpp
@@ -29,25 +29,32 @@
#include <csignal>
#include <cstdarg>
#include <cstdio>
+#include <cstdlib>
#include <cstring>
+#include <limits>
#include <mutex>
+#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 "alstring.h"
#include "core/except.h"
#include "core/logging.h"
+#include "direct_defs.h"
#include "opthelpers.h"
-#include "vector.h"
+#include "strutils.h"
bool TrapALError{false};
void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
{
- auto message = al::vector<char>(256);
+ auto message = std::vector<char>(256);
va_list args, args2;
va_start(args, msg);
@@ -61,8 +68,13 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
va_end(args2);
va_end(args);
- 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);
@@ -79,15 +91,36 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...)
ALenum curerr{AL_NO_ERROR};
mLastError.compare_exchange_strong(curerr, 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 ALenum AL_APIENTRY alGetError(void) noexcept
{
- ContextRef context{GetContextRef()};
+ auto context = GetContextRef();
if(!context) UNLIKELY
{
- static constexpr ALenum deferror{AL_INVALID_OPERATION};
+ static const ALenum deferror{[](const char *envname, const char *optname) -> ALenum
+ {
+ auto optstr = al::getenv(envname);
+ if(!optstr)
+ optstr = ConfigValueStr(nullptr, "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;
+ }("__ALSOFT_DEFAULT_ERROR", "default-error")};
+
WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror);
if(TrapALError)
{
@@ -100,7 +133,10 @@ START_API_FUNC
}
return deferror;
}
+ return alGetErrorDirect(context.get());
+}
+FORCE_ALIGN ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) noexcept
+{
return context->mLastError.exchange(AL_NO_ERROR);
}
-END_API_FUNC
diff --git a/al/event.cpp b/al/event.cpp
index 1bc39d1e..8b76ceff 100644
--- a/al/event.cpp
+++ b/al/event.cpp
@@ -10,14 +10,15 @@
#include <memory>
#include <mutex>
#include <new>
+#include <optional>
#include <string>
+#include <string_view>
#include <thread>
#include <utility>
#include "AL/al.h"
#include "AL/alc.h"
-#include "albyte.h"
#include "alc/context.h"
#include "alc/effects/base.h"
#include "alc/inprogext.h"
@@ -26,12 +27,21 @@
#include "core/except.h"
#include "core/logging.h"
#include "core/voice_change.h"
+#include "debug.h"
+#include "direct_defs.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};
@@ -46,74 +56,101 @@ static int EventThread(ALCcontext *context)
std::lock_guard<std::mutex> _{context->mEventCbLock};
do {
- auto *evt_ptr = reinterpret_cast<AsyncEvent*>(evt_data.buf);
+ auto *evt_ptr = std::launder(reinterpret_cast<AsyncEvent*>(evt_data.buf));
evt_data.buf += sizeof(AsyncEvent);
evt_data.len -= 1;
- AsyncEvent evt{*evt_ptr};
- al::destroy_at(evt_ptr);
+ AsyncEvent event{std::move(*evt_ptr)};
+ std::destroy_at(evt_ptr);
ring->readAdvance(1);
- quitnow = evt.EnumType == AsyncEvent::KillThread;
+ 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);
- }
+ const std::string_view message{evt.msg};
+
+ context->debugMessage(DebugSource::System, DebugType::Error, 0,
+ DebugSeverity::High, message);
+
+ if(context->mEventCb
+ && enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected)))
+ context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0,
+ static_cast<ALsizei>(message.length()), message.data(),
+ context->mEventParam);
+ };
+
+ std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect,
+ proc_killthread}, event);
} while(evt_data.len != 0);
}
return 0;
}
+constexpr std::optional<AsyncEnableBits> GetEventType(ALenum etype) noexcept
+{
+ switch(etype)
+ {
+ case AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT: return AsyncEnableBits::BufferCompleted;
+ case AL_EVENT_TYPE_DISCONNECTED_SOFT: return AsyncEnableBits::Disconnected;
+ case AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT: return AsyncEnableBits::SourceState;
+ }
+ return std::nullopt;
+}
+
+} // namespace
+
+
void StartEventThrd(ALCcontext *ctx)
{
try {
@@ -138,7 +175,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 +183,22 @@ void StopEventThrd(ALCcontext *ctx)
ctx->mEventThread.join();
}
-AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable)
-START_API_FUNC
+AL_API DECL_FUNCEXT3(void, alEventControl,SOFT, ALsizei, const ALenum*, ALboolean)
+FORCE_ALIGN void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count,
+ const ALenum *types, ALboolean enable) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) 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");
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)
+ return context->setError(AL_INVALID_ENUM, "Invalid event type 0x%04x", evttype);
+ flags.set(al::to_underlying(*etype));
+ }
if(enable)
{
@@ -199,17 +224,12 @@ START_API_FUNC
std::lock_guard<std::mutex> _{context->mEventCbLock};
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam)
-START_API_FUNC
+AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT, void*)
+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> _{context->mEventCbLock};
context->mEventCb = callback;
context->mEventParam = userParam;
}
-END_API_FUNC
diff --git a/al/extension.cpp b/al/extension.cpp
index 3ead0af8..6d1ac327 100644
--- a/al/extension.cpp
+++ b/al/extension.cpp
@@ -30,15 +30,13 @@
#include "alc/context.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*)
+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");
@@ -46,37 +44,36 @@ START_API_FUNC
}
size_t len{strlen(extName)};
- const char *ptr{context->mExtensionList};
- while(ptr && *ptr)
+ for(std::string_view ext : context->mExtensions)
{
- if(al::strncasecmp(ptr, extName, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len])))
+ if(len == ext.length() && al::strncasecmp(ext.data(), extName, len) == 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);
+}
+
+FORCE_ALIGN ALvoid* AL_APIENTRY alGetProcAddressDirect(ALCcontext*, 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
+AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) noexcept
+{
+ if(!enumName) return static_cast<ALenum>(0);
+ return alcGetEnumValue(nullptr, enumName);
+}
+
+FORCE_ALIGN ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext*, const ALchar *enumName) noexcept
{
if(!enumName) return static_cast<ALenum>(0);
return alcGetEnumValue(nullptr, enumName);
}
-END_API_FUNC
diff --git a/al/filter.cpp b/al/filter.cpp
index 73efa01f..f0a078b7 100644
--- a/al/filter.cpp
+++ b/al/filter.cpp
@@ -31,6 +31,7 @@
#include <mutex>
#include <new>
#include <numeric>
+#include <vector>
#include "AL/al.h"
#include "AL/alc.h"
@@ -42,8 +43,8 @@
#include "almalloc.h"
#include "alnumeric.h"
#include "core/except.h"
+#include "direct_defs.h"
#include "opthelpers.h"
-#include "vector.h"
namespace {
@@ -73,20 +74,157 @@ filter_exception::filter_exception(ALenum code, const char* msg, ...) : mErrorCo
filter_exception::~filter_exception() = default;
-#define DEFINE_ALFILTER_VTABLE(T) \
-const ALfilter::Vtable T##_vtable = { \
- T##_setParami, T##_setParamiv, T##_setParamf, T##_setParamfv, \
- T##_getParami, T##_getParamiv, T##_getParamf, T##_getParamfv, \
+void InitFilterParams(ALfilter *filter, ALenum type)
+{
+ if(type == AL_FILTER_LOWPASS)
+ {
+ filter->Gain = AL_LOWPASS_DEFAULT_GAIN;
+ filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF;
+ filter->HFReference = LOWPASSFREQREF;
+ filter->GainLF = 1.0f;
+ filter->LFReference = HIGHPASSFREQREF;
+ filter->mTypeVariant.emplace<LowpassFilterTable>();
+ }
+ else if(type == AL_FILTER_HIGHPASS)
+ {
+ filter->Gain = AL_HIGHPASS_DEFAULT_GAIN;
+ filter->GainHF = 1.0f;
+ filter->HFReference = LOWPASSFREQREF;
+ filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
+ filter->LFReference = HIGHPASSFREQREF;
+ filter->mTypeVariant.emplace<HighpassFilterTable>();
+ }
+ else if(type == AL_FILTER_BANDPASS)
+ {
+ filter->Gain = AL_BANDPASS_DEFAULT_GAIN;
+ filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF;
+ filter->HFReference = LOWPASSFREQREF;
+ filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
+ filter->LFReference = HIGHPASSFREQREF;
+ filter->mTypeVariant.emplace<BandpassFilterTable>();
+ }
+ else
+ {
+ filter->Gain = 1.0f;
+ filter->GainHF = 1.0f;
+ filter->HFReference = LOWPASSFREQREF;
+ filter->GainLF = 1.0f;
+ filter->LFReference = HIGHPASSFREQREF;
+ filter->mTypeVariant.emplace<NullFilterTable>();
+ }
+ filter->type = type;
}
-void ALlowpass_setParami(ALfilter*, ALenum param, int)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
-void ALlowpass_setParamiv(ALfilter*, ALenum param, const int*)
+bool EnsureFilters(ALCdevice *device, size_t needed)
+{
+ size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), 0_uz,
+ [](size_t cur, const FilterSubList &sublist) noexcept -> size_t
+ { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
+
+ while(needed > count)
+ {
+ if(device->FilterList.size() >= 1<<25) UNLIKELY
+ return false;
+
+ device->FilterList.emplace_back();
+ auto sublist = device->FilterList.end() - 1;
+ sublist->FreeMask = ~0_u64;
+ sublist->Filters = static_cast<ALfilter*>(al_calloc(alignof(ALfilter), sizeof(ALfilter)*64));
+ if(!sublist->Filters) UNLIKELY
+ {
+ device->FilterList.pop_back();
+ return false;
+ }
+ count += 64;
+ }
+ return true;
+}
+
+
+ALfilter *AllocFilter(ALCdevice *device)
+{
+ auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(),
+ [](const FilterSubList &entry) noexcept -> bool
+ { return entry.FreeMask != 0; });
+ auto lidx = static_cast<ALuint>(std::distance(device->FilterList.begin(), sublist));
+ auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
+ ASSUME(slidx < 64);
+
+ ALfilter *filter{al::construct_at(sublist->Filters + slidx)};
+ InitFilterParams(filter, AL_FILTER_NULL);
+
+ /* Add 1 to avoid filter ID 0. */
+ filter->id = ((lidx<<6) | slidx) + 1;
+
+ sublist->FreeMask &= ~(1_u64 << slidx);
+
+ return filter;
+}
+
+void FreeFilter(ALCdevice *device, ALfilter *filter)
{
- throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x",
- param};
+ device->mFilterNames.erase(filter->id);
+
+ const ALuint id{filter->id - 1};
+ const size_t lidx{id >> 6};
+ const ALuint slidx{id & 0x3f};
+
+ std::destroy_at(filter);
+
+ device->FilterList[lidx].FreeMask |= 1_u64 << slidx;
}
-void ALlowpass_setParamf(ALfilter *filter, ALenum param, float val)
+
+
+inline ALfilter *LookupFilter(ALCdevice *device, ALuint id)
+{
+ const size_t lidx{(id-1) >> 6};
+ const ALuint slidx{(id-1) & 0x3f};
+
+ if(lidx >= device->FilterList.size()) UNLIKELY
+ return nullptr;
+ FilterSubList &sublist = device->FilterList[lidx];
+ if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
+ return nullptr;
+ return sublist.Filters + slidx;
+}
+
+} // namespace
+
+/* Null filter parameter handlers */
+template<>
+void FilterTable<NullFilterTable>::setParami(ALfilter*, ALenum param, int)
+{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::setParamiv(ALfilter*, ALenum param, const int*)
+{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::setParamf(ALfilter*, ALenum param, float)
+{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::setParamfv(ALfilter*, ALenum param, const float*)
+{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::getParami(const ALfilter*, ALenum param, int*)
+{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::getParamiv(const ALfilter*, ALenum param, int*)
+{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::getParamf(const ALfilter*, ALenum param, float*)
+{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+template<>
+void FilterTable<NullFilterTable>::getParamfv(const ALfilter*, ALenum param, float*)
+{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
+
+/* Lowpass parameter handlers */
+template<>
+void FilterTable<LowpassFilterTable>::setParami(ALfilter*, ALenum param, int)
+{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
+template<>
+void FilterTable<LowpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
+{ setParami(filter, param, values[0]); }
+template<>
+void FilterTable<LowpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
{
switch(param)
{
@@ -106,17 +244,17 @@ void ALlowpass_setParamf(ALfilter *filter, ALenum param, float val)
throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
}
}
-void ALlowpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
-{ ALlowpass_setParamf(filter, param, vals[0]); }
-
-void ALlowpass_getParami(const ALfilter*, ALenum param, int*)
+template<>
+void FilterTable<LowpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
+{ setParamf(filter, param, vals[0]); }
+template<>
+void FilterTable<LowpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; }
-void ALlowpass_getParamiv(const ALfilter*, ALenum param, int*)
-{
- throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer-vector property 0x%04x",
- param};
-}
-void ALlowpass_getParamf(const ALfilter *filter, ALenum param, float *val)
+template<>
+void FilterTable<LowpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
+{ getParami(filter, param, values); }
+template<>
+void FilterTable<LowpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
{
switch(param)
{
@@ -132,20 +270,19 @@ void ALlowpass_getParamf(const ALfilter *filter, ALenum param, float *val)
throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param};
}
}
-void ALlowpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
-{ ALlowpass_getParamf(filter, param, vals); }
-
-DEFINE_ALFILTER_VTABLE(ALlowpass);
-
+template<>
+void FilterTable<LowpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
+{ getParamf(filter, param, vals); }
-void ALhighpass_setParami(ALfilter*, ALenum param, int)
+/* Highpass parameter handlers */
+template<>
+void FilterTable<HighpassFilterTable>::setParami(ALfilter*, ALenum param, int)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
-void ALhighpass_setParamiv(ALfilter*, ALenum param, const int*)
-{
- throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x",
- param};
-}
-void ALhighpass_setParamf(ALfilter *filter, ALenum param, float val)
+template<>
+void FilterTable<HighpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
+{ setParami(filter, param, values[0]); }
+template<>
+void FilterTable<HighpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
{
switch(param)
{
@@ -165,17 +302,17 @@ void ALhighpass_setParamf(ALfilter *filter, ALenum param, float val)
throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
}
}
-void ALhighpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
-{ ALhighpass_setParamf(filter, param, vals[0]); }
-
-void ALhighpass_getParami(const ALfilter*, ALenum param, int*)
+template<>
+void FilterTable<HighpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
+{ setParamf(filter, param, vals[0]); }
+template<>
+void FilterTable<HighpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; }
-void ALhighpass_getParamiv(const ALfilter*, ALenum param, int*)
-{
- throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer-vector property 0x%04x",
- param};
-}
-void ALhighpass_getParamf(const ALfilter *filter, ALenum param, float *val)
+template<>
+void FilterTable<HighpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
+{ getParami(filter, param, values); }
+template<>
+void FilterTable<HighpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
{
switch(param)
{
@@ -191,20 +328,19 @@ void ALhighpass_getParamf(const ALfilter *filter, ALenum param, float *val)
throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param};
}
}
-void ALhighpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
-{ ALhighpass_getParamf(filter, param, vals); }
-
-DEFINE_ALFILTER_VTABLE(ALhighpass);
-
+template<>
+void FilterTable<HighpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
+{ getParamf(filter, param, vals); }
-void ALbandpass_setParami(ALfilter*, ALenum param, int)
+/* Bandpass parameter handlers */
+template<>
+void FilterTable<BandpassFilterTable>::setParami(ALfilter*, ALenum param, int)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
-void ALbandpass_setParamiv(ALfilter*, ALenum param, const int*)
-{
- throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x",
- param};
-}
-void ALbandpass_setParamf(ALfilter *filter, ALenum param, float val)
+template<>
+void FilterTable<BandpassFilterTable>::setParamiv(ALfilter *filter, ALenum param, const int *values)
+{ setParami(filter, param, values[0]); }
+template<>
+void FilterTable<BandpassFilterTable>::setParamf(ALfilter *filter, ALenum param, float val)
{
switch(param)
{
@@ -230,17 +366,17 @@ void ALbandpass_setParamf(ALfilter *filter, ALenum param, float val)
throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
}
}
-void ALbandpass_setParamfv(ALfilter *filter, ALenum param, const float *vals)
-{ ALbandpass_setParamf(filter, param, vals[0]); }
-
-void ALbandpass_getParami(const ALfilter*, ALenum param, int*)
+template<>
+void FilterTable<BandpassFilterTable>::setParamfv(ALfilter *filter, ALenum param, const float *vals)
+{ setParamf(filter, param, vals[0]); }
+template<>
+void FilterTable<BandpassFilterTable>::getParami(const ALfilter*, ALenum param, int*)
{ throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; }
-void ALbandpass_getParamiv(const ALfilter*, ALenum param, int*)
-{
- throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer-vector property 0x%04x",
- param};
-}
-void ALbandpass_getParamf(const ALfilter *filter, ALenum param, float *val)
+template<>
+void FilterTable<BandpassFilterTable>::getParamiv(const ALfilter *filter, ALenum param, int *values)
+{ getParami(filter, param, values); }
+template<>
+void FilterTable<BandpassFilterTable>::getParamf(const ALfilter *filter, ALenum param, float *val)
{
switch(param)
{
@@ -260,153 +396,14 @@ void ALbandpass_getParamf(const ALfilter *filter, ALenum param, float *val)
throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param};
}
}
-void ALbandpass_getParamfv(const ALfilter *filter, ALenum param, float *vals)
-{ ALbandpass_getParamf(filter, param, vals); }
-
-DEFINE_ALFILTER_VTABLE(ALbandpass);
-
-
-void ALnullfilter_setParami(ALfilter*, ALenum param, int)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_setParamiv(ALfilter*, ALenum param, const int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_setParamf(ALfilter*, ALenum param, float)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_setParamfv(ALfilter*, ALenum param, const float*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-
-void ALnullfilter_getParami(const ALfilter*, ALenum param, int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_getParamiv(const ALfilter*, ALenum param, int*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_getParamf(const ALfilter*, ALenum param, float*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-void ALnullfilter_getParamfv(const ALfilter*, ALenum param, float*)
-{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; }
-
-DEFINE_ALFILTER_VTABLE(ALnullfilter);
-
-
-void InitFilterParams(ALfilter *filter, ALenum type)
-{
- if(type == AL_FILTER_LOWPASS)
- {
- filter->Gain = AL_LOWPASS_DEFAULT_GAIN;
- filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF;
- filter->HFReference = LOWPASSFREQREF;
- filter->GainLF = 1.0f;
- filter->LFReference = HIGHPASSFREQREF;
- filter->vtab = &ALlowpass_vtable;
- }
- else if(type == AL_FILTER_HIGHPASS)
- {
- filter->Gain = AL_HIGHPASS_DEFAULT_GAIN;
- filter->GainHF = 1.0f;
- filter->HFReference = LOWPASSFREQREF;
- filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF;
- filter->LFReference = HIGHPASSFREQREF;
- filter->vtab = &ALhighpass_vtable;
- }
- else if(type == AL_FILTER_BANDPASS)
- {
- filter->Gain = AL_BANDPASS_DEFAULT_GAIN;
- filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF;
- filter->HFReference = LOWPASSFREQREF;
- filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF;
- filter->LFReference = HIGHPASSFREQREF;
- filter->vtab = &ALbandpass_vtable;
- }
- else
- {
- filter->Gain = 1.0f;
- filter->GainHF = 1.0f;
- filter->HFReference = LOWPASSFREQREF;
- filter->GainLF = 1.0f;
- filter->LFReference = HIGHPASSFREQREF;
- filter->vtab = &ALnullfilter_vtable;
- }
- filter->type = type;
-}
-
-bool EnsureFilters(ALCdevice *device, size_t needed)
-{
- size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), size_t{0},
- [](size_t cur, const FilterSubList &sublist) noexcept -> size_t
- { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
-
- while(needed > count)
- {
- if(device->FilterList.size() >= 1<<25) UNLIKELY
- return false;
-
- device->FilterList.emplace_back();
- auto sublist = device->FilterList.end() - 1;
- sublist->FreeMask = ~0_u64;
- sublist->Filters = static_cast<ALfilter*>(al_calloc(alignof(ALfilter), sizeof(ALfilter)*64));
- if(!sublist->Filters) UNLIKELY
- {
- device->FilterList.pop_back();
- return false;
- }
- count += 64;
- }
- return true;
-}
-
-
-ALfilter *AllocFilter(ALCdevice *device)
-{
- auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(),
- [](const FilterSubList &entry) noexcept -> bool
- { return entry.FreeMask != 0; });
- auto lidx = static_cast<ALuint>(std::distance(device->FilterList.begin(), sublist));
- auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
- ASSUME(slidx < 64);
-
- ALfilter *filter{al::construct_at(sublist->Filters + slidx)};
- InitFilterParams(filter, AL_FILTER_NULL);
-
- /* Add 1 to avoid filter ID 0. */
- filter->id = ((lidx<<6) | slidx) + 1;
-
- sublist->FreeMask &= ~(1_u64 << slidx);
-
- return filter;
-}
-
-void FreeFilter(ALCdevice *device, ALfilter *filter)
-{
- const ALuint id{filter->id - 1};
- const size_t lidx{id >> 6};
- const ALuint slidx{id & 0x3f};
-
- al::destroy_at(filter);
-
- device->FilterList[lidx].FreeMask |= 1_u64 << slidx;
-}
-
-
-inline ALfilter *LookupFilter(ALCdevice *device, ALuint id)
-{
- const size_t lidx{(id-1) >> 6};
- const ALuint slidx{(id-1) & 0x3f};
-
- if(lidx >= device->FilterList.size()) UNLIKELY
- return nullptr;
- FilterSubList &sublist = device->FilterList[lidx];
- if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
- return nullptr;
- return sublist.Filters + slidx;
-}
+template<>
+void FilterTable<BandpassFilterTable>::getParamfv(const ALfilter *filter, ALenum param, float *vals)
+{ getParamf(filter, param, vals); }
-} // namespace
-AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGenFilters, ALsizei, ALuint*)
+FORCE_ALIGN void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Generating %d filters", n);
if(n <= 0) UNLIKELY return;
@@ -430,7 +427,7 @@ START_API_FUNC
/* Store the allocated buffer IDs in a separate local list, to avoid
* modifying the user storage in case of failure.
*/
- al::vector<ALuint> ids;
+ std::vector<ALuint> ids;
ids.reserve(static_cast<ALuint>(n));
do {
ALfilter *filter{AllocFilter(device)};
@@ -439,14 +436,11 @@ START_API_FUNC
std::copy(ids.begin(), ids.end(), filters);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alDeleteFilters, ALsizei, const ALuint*)
+FORCE_ALIGN void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n,
+ const ALuint *filters) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Deleting %d filters", n);
if(n <= 0) UNLIKELY return;
@@ -474,71 +468,58 @@ START_API_FUNC
};
std::for_each(filters, filters_end, delete_filter);
}
-END_API_FUNC
-AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsFilter, ALuint)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) noexcept
{
- ContextRef context{GetContextRef()};
- if(context) LIKELY
- {
- ALCdevice *device{context->mALDevice.get()};
- std::lock_guard<std::mutex> _{device->FilterLock};
- if(!filter || LookupFilter(device, filter))
- return AL_TRUE;
- }
+ ALCdevice *device{context->mALDevice.get()};
+ std::lock_guard<std::mutex> _{device->FilterLock};
+ if(!filter || LookupFilter(device, filter))
+ return AL_TRUE;
return AL_FALSE;
}
-END_API_FUNC
-AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alFilteri, ALuint, ALenum, ALint)
+FORCE_ALIGN void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
+ ALint value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
ALfilter *alfilt{LookupFilter(device, filter)};
if(!alfilt) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
- else
+ else if(param == AL_FILTER_TYPE)
{
- if(param == AL_FILTER_TYPE)
- {
- if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS
- || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)
- InitFilterParams(alfilt, value);
- else
- context->setError(AL_INVALID_VALUE, "Invalid filter type 0x%04x", value);
- }
- else try
- {
- /* Call the appropriate handler */
- alfilt->setParami(param, value);
- }
- catch(filter_exception &e) {
- context->setError(e.errorCode(), "%s", e.what());
- }
+ if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS
+ || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)
+ InitFilterParams(alfilt, value);
+ else
+ context->setError(AL_INVALID_VALUE, "Invalid filter type 0x%04x", value);
+ }
+ else try
+ {
+ /* Call the appropriate handler */
+ std::visit([alfilt,param,value](auto&& thunk){thunk.setParami(alfilt, param, value);},
+ alfilt->mTypeVariant);
+ }
+ catch(filter_exception &e) {
+ context->setError(e.errorCode(), "%s", e.what());
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alFilteriv, ALuint, ALenum, const ALint*)
+FORCE_ALIGN void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param,
+ const ALint *values) noexcept
{
switch(param)
{
case AL_FILTER_TYPE:
- alFilteri(filter, param, values[0]);
+ alFilteriDirect(context, filter, param, values[0]);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
@@ -548,20 +529,18 @@ START_API_FUNC
else try
{
/* Call the appropriate handler */
- alfilt->setParamiv(param, values);
+ std::visit([alfilt,param,values](auto&& thunk){thunk.setParamiv(alfilt, param, values);},
+ alfilt->mTypeVariant);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alFilterf, ALuint, ALenum, ALfloat)
+FORCE_ALIGN void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
+ ALfloat value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
@@ -571,20 +550,18 @@ START_API_FUNC
else try
{
/* Call the appropriate handler */
- alfilt->setParamf(param, value);
+ std::visit([alfilt,param,value](auto&& thunk){thunk.setParamf(alfilt, param, value);},
+ alfilt->mTypeVariant);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alFilterfv, ALuint, ALenum, const ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
+ const ALfloat *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
@@ -594,55 +571,48 @@ START_API_FUNC
else try
{
/* Call the appropriate handler */
- alfilt->setParamfv(param, values);
+ std::visit([alfilt,param,values](auto&& thunk){thunk.setParamfv(alfilt, param, values);},
+ alfilt->mTypeVariant);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetFilteri, ALuint, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param,
+ ALint *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
const ALfilter *alfilt{LookupFilter(device, filter)};
if(!alfilt) UNLIKELY
context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter);
- else
+ else if(param == AL_FILTER_TYPE)
+ *value = alfilt->type;
+ else try
{
- if(param == AL_FILTER_TYPE)
- *value = alfilt->type;
- else try
- {
- /* Call the appropriate handler */
- alfilt->getParami(param, value);
- }
- catch(filter_exception &e) {
- context->setError(e.errorCode(), "%s", e.what());
- }
+ /* Call the appropriate handler */
+ std::visit([alfilt,param,value](auto&& thunk){thunk.getParami(alfilt, param, value);},
+ alfilt->mTypeVariant);
+ }
+ catch(filter_exception &e) {
+ context->setError(e.errorCode(), "%s", e.what());
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetFilteriv, ALuint, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param,
+ ALint *values) noexcept
{
switch(param)
{
case AL_FILTER_TYPE:
- alGetFilteri(filter, param, values);
+ alGetFilteriDirect(context, filter, param, values);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
@@ -652,20 +622,18 @@ START_API_FUNC
else try
{
/* Call the appropriate handler */
- alfilt->getParamiv(param, values);
+ std::visit([alfilt,param,values](auto&& thunk){thunk.getParamiv(alfilt, param, values);},
+ alfilt->mTypeVariant);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetFilterf, ALuint, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param,
+ ALfloat *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
@@ -675,20 +643,18 @@ START_API_FUNC
else try
{
/* Call the appropriate handler */
- alfilt->getParamf(param, value);
+ std::visit([alfilt,param,value](auto&& thunk){thunk.getParamf(alfilt, param, value);},
+ alfilt->mTypeVariant);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetFilterfv, ALuint, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param,
+ ALfloat *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALCdevice *device{context->mALDevice.get()};
std::lock_guard<std::mutex> _{device->FilterLock};
@@ -698,22 +664,38 @@ START_API_FUNC
else try
{
/* Call the appropriate handler */
- alfilt->getParamfv(param, values);
+ std::visit([alfilt,param,values](auto&& thunk){thunk.getParamfv(alfilt, param, values);},
+ alfilt->mTypeVariant);
}
catch(filter_exception &e) {
context->setError(e.errorCode(), "%s", e.what());
}
}
-END_API_FUNC
+
+
+void ALfilter::SetName(ALCcontext *context, ALuint id, std::string_view name)
+{
+ ALCdevice *device{context->mALDevice.get()};
+ std::lock_guard<std::mutex> _{device->FilterLock};
+
+ auto filter = LookupFilter(device, id);
+ if(!filter) UNLIKELY
+ return context->setError(AL_INVALID_NAME, "Invalid filter ID %u", id);
+
+ device->mFilterNames.insert_or_assign(id, name);
+}
FilterSubList::~FilterSubList()
{
+ if(!Filters)
+ return;
+
uint64_t usemask{~FreeMask};
while(usemask)
{
const int idx{al::countr_zero(usemask)};
- al::destroy_at(Filters+idx);
+ std::destroy_at(Filters+idx);
usemask &= ~(1_u64 << idx);
}
FreeMask = ~usemask;
diff --git a/al/filter.h b/al/filter.h
index 65a9e30f..505900d4 100644
--- a/al/filter.h
+++ b/al/filter.h
@@ -1,6 +1,8 @@
#ifndef AL_FILTER_H
#define AL_FILTER_H
+#include <string_view>
+#include <variant>
#include "AL/al.h"
#include "AL/alc.h"
@@ -12,6 +14,25 @@
#define 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 {
ALenum type{AL_FILTER_NULL};
@@ -21,30 +42,14 @@ struct ALfilter {
float GainLF{1.0f};
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()
};
diff --git a/al/listener.cpp b/al/listener.cpp
index 06d7c370..ea2ebb3f 100644
--- a/al/listener.cpp
+++ b/al/listener.cpp
@@ -33,6 +33,7 @@
#include "almalloc.h"
#include "atomic.h"
#include "core/except.h"
+#include "direct_defs.h"
#include "opthelpers.h"
@@ -68,12 +69,9 @@ inline void CommitAndUpdateProps(ALCcontext *context)
} // namespace
-AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alListenerf, ALenum, ALfloat)
+FORCE_ALIGN void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{context->mPropLock};
switch(param)
@@ -82,28 +80,25 @@ START_API_FUNC
if(!(value >= 0.0f && std::isfinite(value)))
return context->setError(AL_INVALID_VALUE, "Listener gain out of range");
listener.Gain = value;
- UpdateProps(context.get());
+ UpdateProps(context);
break;
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");
listener.mMetersPerUnit = value;
- UpdateProps(context.get());
+ UpdateProps(context);
break;
default:
context->setError(AL_INVALID_ENUM, "Invalid listener float property");
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
-START_API_FUNC
+AL_API DECL_FUNC4(void, alListener3f, ALenum, ALfloat, ALfloat, ALfloat)
+FORCE_ALIGN void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1,
+ ALfloat value2, ALfloat value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{context->mPropLock};
switch(param)
@@ -114,7 +109,7 @@ START_API_FUNC
listener.Position[0] = value1;
listener.Position[1] = value2;
listener.Position[2] = value3;
- CommitAndUpdateProps(context.get());
+ CommitAndUpdateProps(context);
break;
case AL_VELOCITY:
@@ -123,40 +118,34 @@ START_API_FUNC
listener.Velocity[0] = value1;
listener.Velocity[1] = value2;
listener.Velocity[2] = value3;
- CommitAndUpdateProps(context.get());
+ CommitAndUpdateProps(context);
break;
default:
context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alListenerfv, ALenum, const ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param,
+ const ALfloat *values) noexcept
{
- if(values)
+ if(!values) UNLIKELY
+ return context->setError(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[0]);
+ return;
- case AL_POSITION:
- case AL_VELOCITY:
- alListener3f(param, values[0], values[1], values[2]);
- return;
- }
+ case AL_POSITION:
+ case AL_VELOCITY:
+ alListener3fDirect(context, param, values[0], values[1], values[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};
switch(param)
@@ -172,22 +161,18 @@ START_API_FUNC
listener.OrientUp[0] = values[3];
listener.OrientUp[1] = values[4];
listener.OrientUp[2] = values[5];
- CommitAndUpdateProps(context.get());
+ CommitAndUpdateProps(context);
break;
default:
context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alListeneri(ALenum param, ALint /*value*/)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alListeneri, ALenum, ALint)
+FORCE_ALIGN void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint /*value*/) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
switch(param)
{
@@ -195,23 +180,20 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
-START_API_FUNC
+AL_API DECL_FUNC4(void, alListener3i, ALenum, ALint, ALint, ALint)
+FORCE_ALIGN void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1,
+ ALint value2, ALint value3) noexcept
{
switch(param)
{
case AL_POSITION:
case AL_VELOCITY:
- alListener3f(param, static_cast<ALfloat>(value1), static_cast<ALfloat>(value2),
- static_cast<ALfloat>(value3));
+ alListener3fDirect(context, param, static_cast<ALfloat>(value1),
+ static_cast<ALfloat>(value2), static_cast<ALfloat>(value3));
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
switch(param)
{
@@ -219,55 +201,48 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alListeneriv, ALenum, const ALint*)
+FORCE_ALIGN void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param,
+ const ALint *values) noexcept
{
- if(values)
+ if(!values) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+
+ switch(param)
{
- ALfloat fvals[6];
- switch(param)
- {
- case AL_POSITION:
- case AL_VELOCITY:
- alListener3f(param, static_cast<ALfloat>(values[0]), static_cast<ALfloat>(values[1]),
- static_cast<ALfloat>(values[2]));
- return;
+ case AL_POSITION:
+ case AL_VELOCITY:
+ alListener3fDirect(context, 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;
- }
+ case AL_ORIENTATION:
+ const ALfloat fvals[6]{
+ static_cast<ALfloat>(values[0]),
+ static_cast<ALfloat>(values[1]),
+ static_cast<ALfloat>(values[2]),
+ static_cast<ALfloat>(values[3]),
+ static_cast<ALfloat>(values[4]),
+ static_cast<ALfloat>(values[5]),
+ };
+ alListenerfvDirect(context, 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)
+ switch(param)
{
default:
context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGetListenerf, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param,
+ ALfloat *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{context->mPropLock};
if(!value)
@@ -286,14 +261,11 @@ START_API_FUNC
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
+AL_API DECL_FUNC4(void, alGetListener3f, ALenum, ALfloat*, ALfloat*, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param,
+ ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{context->mPropLock};
if(!value1 || !value2 || !value3)
@@ -316,27 +288,24 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property");
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGetListenerfv, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param,
+ ALfloat *values) noexcept
{
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);
+ alGetListener3fDirect(context, param, values+0, values+1, values+2);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{context->mPropLock};
if(!values)
@@ -357,15 +326,11 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property");
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGetListeneri, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
if(!value)
context->setError(AL_INVALID_VALUE, "NULL pointer");
@@ -375,14 +340,11 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid listener integer property");
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
-START_API_FUNC
+AL_API DECL_FUNC4(void, alGetListener3i, ALenum, ALint*, ALint*, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param,
+ ALint *value1, ALint *value2, ALint *value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{context->mPropLock};
if(!value1 || !value2 || !value3)
@@ -405,22 +367,19 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property");
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGetListeneriv, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param,
+ ALint *values) noexcept
{
switch(param)
{
case AL_POSITION:
case AL_VELOCITY:
- alGetListener3i(param, values+0, values+1, values+2);
+ alGetListener3iDirect(context, param, values+0, values+1, values+2);
return;
}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
ALlistener &listener = context->mListener;
std::lock_guard<std::mutex> _{context->mPropLock};
if(!values)
@@ -441,4 +400,3 @@ START_API_FUNC
context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property");
}
}
-END_API_FUNC
diff --git a/al/source.cpp b/al/source.cpp
index cba33862..fe5bba40 100644
--- a/al/source.cpp
+++ b/al/source.cpp
@@ -27,20 +27,22 @@
#include <atomic>
#include <cassert>
#include <chrono>
+#include <cinttypes>
#include <climits>
#include <cmath>
#include <cstdint>
#include <functional>
-#include <inttypes.h>
#include <iterator>
#include <limits>
#include <memory>
#include <mutex>
#include <new>
#include <numeric>
+#include <optional>
#include <stdexcept>
#include <thread>
#include <utility>
+#include <vector>
#include "AL/al.h"
#include "AL/alc.h"
@@ -55,7 +57,6 @@
#include "alc/inprogext.h"
#include "almalloc.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "alspan.h"
#include "atomic.h"
#include "auxeffectslot.h"
@@ -67,22 +68,21 @@
#include "core/filters/splitter.h"
#include "core/logging.h"
#include "core/voice_change.h"
+#include "direct_defs.h"
#include "event.h"
#include "filter.h"
#include "opthelpers.h"
#include "ringbuffer.h"
-#include "threads.h"
#ifdef ALSOFT_EAX
#include <cassert>
#endif // ALSOFT_EAX
-bool sBufferSubDataCompat{false};
-
namespace {
using namespace std::placeholders;
using std::chrono::nanoseconds;
+using seconds_d = std::chrono::duration<double>;
Voice *GetSourceVoice(ALsource *source, ALCcontext *context)
{
@@ -95,7 +95,7 @@ Voice *GetSourceVoice(ALsource *source, ALCcontext *context)
if(voice->mSourceID.load(std::memory_order_acquire) == sid)
return voice;
}
- source->VoiceIdx = INVALID_VOICE_IDX;
+ source->VoiceIdx = InvalidVoiceIndex;
return nullptr;
}
@@ -281,7 +281,8 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl
* (Bytes, Samples or Seconds). The offset is relative to the start of the
* queue (not the start of the current buffer).
*/
-double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
+template<typename T>
+NOINLINE T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
{
ALCdevice *device{context->mALDevice.get()};
const VoiceBufferItem *Current{};
@@ -304,7 +305,7 @@ double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
} while(refcount != device->MixCount.load(std::memory_order_relaxed));
if(!voice)
- return 0.0;
+ return T{0};
const ALbuffer *BufferFmt{nullptr};
auto BufferList = Source->mQueue.cbegin();
@@ -321,24 +322,48 @@ double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
}
ASSUME(BufferFmt != nullptr);
- double offset{};
+ T offset{};
switch(name)
{
case AL_SEC_OFFSET:
- offset = static_cast<double>(readPos) + readPosFrac/double{MixerFracOne};
- offset /= BufferFmt->mSampleRate;
+ if constexpr(std::is_floating_point_v<T>)
+ {
+ offset = static_cast<T>(readPos) + static_cast<T>(readPosFrac)/T{MixerFracOne};
+ offset /= static_cast<T>(BufferFmt->mSampleRate);
+ }
+ else
+ {
+ readPos /= BufferFmt->mSampleRate;
+ offset = static_cast<T>(clampi64(readPos, std::numeric_limits<T>::min(),
+ std::numeric_limits<T>::max()));
+ }
break;
case AL_SAMPLE_OFFSET:
- offset = static_cast<double>(readPos) + readPosFrac/double{MixerFracOne};
+ if constexpr(std::is_floating_point_v<T>)
+ offset = static_cast<T>(readPos) + static_cast<T>(readPosFrac)/T{MixerFracOne};
+ else
+ offset = static_cast<T>(clampi64(readPos, std::numeric_limits<T>::min(),
+ std::numeric_limits<T>::max()));
break;
case AL_BYTE_OFFSET:
const ALuint BlockSamples{BufferFmt->mBlockAlign};
const ALuint BlockSize{BufferFmt->blockSizeFromFmt()};
-
/* Round down to the block boundary. */
- offset = static_cast<double>(readPos / BlockSamples) * BlockSize;
+ readPos = readPos / BlockSamples * BlockSize;
+
+ if constexpr(std::is_floating_point_v<T>)
+ offset = static_cast<T>(readPos);
+ else
+ {
+ if(readPos > std::numeric_limits<T>::max())
+ offset = RoundDown(std::numeric_limits<T>::max(), static_cast<T>(BlockSize));
+ else if(readPos < std::numeric_limits<T>::min())
+ offset = RoundUp(std::numeric_limits<T>::min(), static_cast<T>(BlockSize));
+ else
+ offset = static_cast<T>(readPos);
+ }
break;
}
return offset;
@@ -349,7 +374,8 @@ double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
* Gets the length of the given Source's buffer queue, in the appropriate
* format (Bytes, Samples or Seconds).
*/
-double GetSourceLength(const ALsource *source, ALenum name)
+template<typename T>
+NOINLINE T GetSourceLength(const ALsource *source, ALenum name)
{
uint64_t length{0};
const ALbuffer *BufferFmt{nullptr};
@@ -360,25 +386,40 @@ double GetSourceLength(const ALsource *source, ALenum name)
length += listitem.mSampleLen;
}
if(length == 0)
- return 0.0;
+ return T{0};
ASSUME(BufferFmt != nullptr);
switch(name)
{
case AL_SEC_LENGTH_SOFT:
- return static_cast<double>(length) / BufferFmt->mSampleRate;
+ if constexpr(std::is_floating_point_v<T>)
+ return static_cast<T>(length) / static_cast<T>(BufferFmt->mSampleRate);
+ else
+ return static_cast<T>(minu64(length/BufferFmt->mSampleRate,
+ std::numeric_limits<T>::max()));
case AL_SAMPLE_LENGTH_SOFT:
- return static_cast<double>(length);
+ if constexpr(std::is_floating_point_v<T>)
+ return static_cast<T>(length);
+ else
+ return static_cast<T>(minu64(length, std::numeric_limits<T>::max()));
case AL_BYTE_LENGTH_SOFT:
const ALuint BlockSamples{BufferFmt->mBlockAlign};
const ALuint BlockSize{BufferFmt->blockSizeFromFmt()};
-
/* Round down to the block boundary. */
- return static_cast<double>(length / BlockSamples) * BlockSize;
+ length = length / BlockSamples * BlockSize;
+
+ if constexpr(std::is_floating_point_v<T>)
+ return static_cast<T>(length);
+ else
+ {
+ if(length > std::numeric_limits<T>::max())
+ return RoundDown(std::numeric_limits<T>::max(), static_cast<T>(BlockSize));
+ return static_cast<T>(length);
+ }
}
- return 0.0;
+ return T{0};
}
@@ -392,11 +433,11 @@ struct VoicePos {
* GetSampleOffset
*
* Retrieves the voice position, fixed-point fraction, and bufferlist item
- * using the givem offset type and offset. If the offset is out of range,
+ * using the given offset type and offset. If the offset is out of range,
* returns an empty optional.
*/
-al::optional<VoicePos> GetSampleOffset(al::deque<ALbufferQueueItem> &BufferList, ALenum OffsetType,
- double Offset)
+std::optional<VoicePos> GetSampleOffset(std::deque<ALbufferQueueItem> &BufferList,
+ ALenum OffsetType, double Offset)
{
/* Find the first valid Buffer in the Queue */
const ALbuffer *BufferFmt{nullptr};
@@ -406,7 +447,7 @@ al::optional<VoicePos> GetSampleOffset(al::deque<ALbufferQueueItem> &BufferList,
if(BufferFmt) break;
}
if(!BufferFmt) UNLIKELY
- return al::nullopt;
+ return std::nullopt;
/* Get sample frame offset */
int64_t offset{};
@@ -452,12 +493,12 @@ al::optional<VoicePos> GetSampleOffset(al::deque<ALbufferQueueItem> &BufferList,
if(offset < 0)
{
if(offset < std::numeric_limits<int>::min())
- return al::nullopt;
+ return std::nullopt;
return VoicePos{static_cast<int>(offset), frac, &BufferList.front()};
}
if(BufferFmt->mCallback)
- return al::nullopt;
+ return std::nullopt;
int64_t totalBufferLen{0};
for(auto &item : BufferList)
@@ -473,7 +514,7 @@ al::optional<VoicePos> GetSampleOffset(al::deque<ALbufferQueueItem> &BufferList,
}
/* Offset is out of range of the queue */
- return al::nullopt;
+ return std::nullopt;
}
@@ -676,8 +717,7 @@ inline ALenum GetSourceState(ALsource *source, Voice *voice)
bool EnsureSources(ALCcontext *context, size_t needed)
{
- size_t count{std::accumulate(context->mSourceList.cbegin(), context->mSourceList.cend(),
- size_t{0},
+ size_t count{std::accumulate(context->mSourceList.cbegin(), context->mSourceList.cend(), 0_uz,
[](size_t cur, const SourceSubList &sublist) noexcept -> size_t
{ return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
@@ -722,6 +762,8 @@ ALsource *AllocSource(ALCcontext *context)
void FreeSource(ALCcontext *context, ALsource *source)
{
+ context->mSourceNames.erase(source->id);
+
const ALuint id{source->id - 1};
const size_t lidx{id >> 6};
const ALuint slidx{id & 0x3f};
@@ -738,7 +780,7 @@ void FreeSource(ALCcontext *context, ALsource *source)
SendVoiceChanges(context, vchg);
}
- al::destroy_at(source);
+ std::destroy_at(source);
context->mSourceList[lidx].FreeMask |= 1_u64 << slidx;
context->mNumSources--;
@@ -758,56 +800,55 @@ inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept
return sublist.Sources + slidx;
}
-inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept
+auto LookupBuffer = [](ALCdevice *device, auto id) noexcept -> ALbuffer*
{
- const size_t lidx{(id-1) >> 6};
- const ALuint slidx{(id-1) & 0x3f};
+ const auto lidx{(id-1) >> 6};
+ const auto slidx{(id-1) & 0x3f};
if(lidx >= device->BufferList.size()) UNLIKELY
return nullptr;
- BufferSubList &sublist = device->BufferList[lidx];
+ BufferSubList &sublist = device->BufferList[static_cast<size_t>(lidx)];
if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
return nullptr;
- return sublist.Buffers + slidx;
-}
+ return sublist.Buffers + static_cast<size_t>(slidx);
+};
-inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) noexcept
+auto LookupFilter = [](ALCdevice *device, auto id) noexcept -> ALfilter*
{
- const size_t lidx{(id-1) >> 6};
- const ALuint slidx{(id-1) & 0x3f};
+ const auto lidx{(id-1) >> 6};
+ const auto slidx{(id-1) & 0x3f};
if(lidx >= device->FilterList.size()) UNLIKELY
return nullptr;
- FilterSubList &sublist = device->FilterList[lidx];
+ FilterSubList &sublist = device->FilterList[static_cast<size_t>(lidx)];
if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
return nullptr;
- return sublist.Filters + slidx;
-}
+ return sublist.Filters + static_cast<size_t>(slidx);
+};
-inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept
+auto LookupEffectSlot = [](ALCcontext *context, auto id) noexcept -> ALeffectslot*
{
- const size_t lidx{(id-1) >> 6};
- const ALuint slidx{(id-1) & 0x3f};
+ const auto lidx{(id-1) >> 6};
+ const auto slidx{(id-1) & 0x3f};
if(lidx >= context->mEffectSlotList.size()) UNLIKELY
return nullptr;
- EffectSlotSubList &sublist{context->mEffectSlotList[lidx]};
+ EffectSlotSubList &sublist{context->mEffectSlotList[static_cast<size_t>(lidx)]};
if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
return nullptr;
- return sublist.EffectSlots + slidx;
-}
+ return sublist.EffectSlots + static_cast<size_t>(slidx);
+};
-al::optional<SourceStereo> StereoModeFromEnum(ALenum mode)
+auto StereoModeFromEnum = [](auto mode) noexcept -> std::optional<SourceStereo>
{
switch(mode)
{
case AL_NORMAL_SOFT: return SourceStereo::Normal;
case AL_SUPER_STEREO_SOFT: return SourceStereo::Enhanced;
}
- WARN("Unsupported stereo mode: 0x%04x\n", mode);
- return al::nullopt;
-}
+ return std::nullopt;
+};
ALenum EnumFromStereoMode(SourceStereo mode)
{
switch(mode)
@@ -818,7 +859,7 @@ ALenum EnumFromStereoMode(SourceStereo mode)
throw std::runtime_error{"Invalid SourceStereo: "+std::to_string(int(mode))};
}
-al::optional<SpatializeMode> SpatializeModeFromEnum(ALenum mode)
+auto SpatializeModeFromEnum = [](auto mode) noexcept -> std::optional<SpatializeMode>
{
switch(mode)
{
@@ -826,9 +867,8 @@ al::optional<SpatializeMode> SpatializeModeFromEnum(ALenum mode)
case AL_TRUE: return SpatializeMode::On;
case AL_AUTO_SOFT: return SpatializeMode::Auto;
}
- WARN("Unsupported spatialize mode: 0x%04x\n", mode);
- return al::nullopt;
-}
+ return std::nullopt;
+};
ALenum EnumFromSpatializeMode(SpatializeMode mode)
{
switch(mode)
@@ -840,7 +880,7 @@ ALenum EnumFromSpatializeMode(SpatializeMode mode)
throw std::runtime_error{"Invalid SpatializeMode: "+std::to_string(int(mode))};
}
-al::optional<DirectMode> DirectModeFromEnum(ALenum mode)
+auto DirectModeFromEnum = [](auto mode) noexcept -> std::optional<DirectMode>
{
switch(mode)
{
@@ -848,9 +888,8 @@ al::optional<DirectMode> DirectModeFromEnum(ALenum mode)
case AL_DROP_UNMATCHED_SOFT: return DirectMode::DropMismatch;
case AL_REMIX_UNMATCHED_SOFT: return DirectMode::RemixMismatch;
}
- WARN("Unsupported direct mode: 0x%04x\n", mode);
- return al::nullopt;
-}
+ return std::nullopt;
+};
ALenum EnumFromDirectMode(DirectMode mode)
{
switch(mode)
@@ -862,7 +901,7 @@ ALenum EnumFromDirectMode(DirectMode mode)
throw std::runtime_error{"Invalid DirectMode: "+std::to_string(int(mode))};
}
-al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
+auto DistanceModelFromALenum = [](auto model) noexcept -> std::optional<DistanceModel>
{
switch(model)
{
@@ -874,8 +913,8 @@ al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
case AL_EXPONENT_DISTANCE: return DistanceModel::Exponent;
case AL_EXPONENT_DISTANCE_CLAMPED: return DistanceModel::ExponentClamped;
}
- return al::nullopt;
-}
+ return std::nullopt;
+};
ALenum ALenumFromDistanceModel(DistanceModel model)
{
switch(model)
@@ -973,8 +1012,6 @@ enum SourceProp : ALenum {
};
-constexpr size_t MaxValues{6u};
-
constexpr ALuint IntValsByProp(ALenum prop)
{
switch(static_cast<SourceProp>(prop))
@@ -1276,10 +1313,6 @@ constexpr ALuint DoubleValsByProp(ALenum prop)
}
-void SetSourcefv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, const al::span<const float> values);
-void SetSourceiv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, const al::span<const int> values);
-void SetSourcei64v(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, const al::span<const int64_t> values);
-
struct check_exception : std::exception {
};
struct check_size_exception final : check_exception {
@@ -1327,10 +1360,39 @@ inline void CommitAndUpdateSourceProps(ALsource *source, ALCcontext *context)
#endif
+template<typename T>
+struct PropType { };
+template<>
+struct PropType<ALint> { static const char *Name() { return "integer"; } };
+template<>
+struct PropType<ALint64SOFT> { static const char *Name() { return "int64"; } };
+template<>
+struct PropType<ALfloat> { static const char *Name() { return "float"; } };
+template<>
+struct PropType<ALdouble> { static const char *Name() { return "double"; } };
+
+template<typename T>
+struct HexPrinter {
+ char mStr[sizeof(T)*2 + 3]{};
+ HexPrinter(T value)
+ {
+ using ST = std::make_signed_t<std::remove_cv_t<T>>;
+ if constexpr(std::is_same_v<ST,int>)
+ std::snprintf(mStr, std::size(mStr), "0x%x", value);
+ else if constexpr(std::is_same_v<ST,long>)
+ std::snprintf(mStr, std::size(mStr), "0x%lx", value);
+ else if constexpr(std::is_same_v<ST,long long>)
+ std::snprintf(mStr, std::size(mStr), "0x%llx", value);
+ }
+
+ const char *c_str() const noexcept { return mStr; }
+};
+
+
/**
- * Returns a pair of lambdas to check the following setters and getters.
+ * Returns a pair of lambdas to check the following setter.
*
- * The first lambda checks the size of the span is valid for its given size,
+ * The first lambda checks the size of the span is valid for the required size,
* setting the proper context error and throwing a check_size_exception if it
* fails.
*
@@ -1357,19 +1419,33 @@ auto GetCheckers(ALCcontext *const Context, const SourceProp prop, const al::spa
);
}
-void SetSourcefv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop,
- const al::span<const float> values)
-try {
- /* Structured bindings would be nice (C++17). */
- auto Checkers = GetCheckers(Context, prop, values);
- auto &CheckSize = Checkers.first;
- auto &CheckValue = Checkers.second;
- int ival;
+template<typename T>
+NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, const SourceProp prop,
+ const al::span<const T> values) try
+{
+ auto&& [CheckSize, CheckValue] = GetCheckers(Context, prop, values);
+ ALCdevice *device{Context->mALDevice.get()};
switch(prop)
{
+ case AL_SOURCE_STATE:
+ case AL_SOURCE_TYPE:
+ case AL_BUFFERS_QUEUED:
+ case AL_BUFFERS_PROCESSED:
+ if constexpr(std::is_integral_v<T>)
+ {
+ /* Query only */
+ return Context->setError(AL_INVALID_OPERATION,
+ "Setting read-only source property 0x%04x", prop);
+ }
+ break;
+
+ case AL_BYTE_LENGTH_SOFT:
+ case AL_SAMPLE_LENGTH_SOFT:
case AL_SEC_LENGTH_SOFT:
+ case AL_SAMPLE_OFFSET_LATENCY_SOFT:
case AL_SEC_OFFSET_LATENCY_SOFT:
+ case AL_SAMPLE_OFFSET_CLOCK_SOFT:
case AL_SEC_OFFSET_CLOCK_SOFT:
/* Query only */
return Context->setError(AL_INVALID_OPERATION,
@@ -1377,712 +1453,540 @@ try {
case AL_PITCH:
CheckSize(1);
- CheckValue(values[0] >= 0.0f);
+ CheckValue(values[0] >= T{0});
- Source->Pitch = values[0];
+ Source->Pitch = static_cast<float>(values[0]);
return UpdateSourceProps(Source, Context);
case AL_CONE_INNER_ANGLE:
CheckSize(1);
- CheckValue(values[0] >= 0.0f && values[0] <= 360.0f);
+ CheckValue(values[0] >= T{0} && values[0] <= T{360});
- Source->InnerAngle = values[0];
+ Source->InnerAngle = static_cast<float>(values[0]);
return CommitAndUpdateSourceProps(Source, Context);
case AL_CONE_OUTER_ANGLE:
CheckSize(1);
- CheckValue(values[0] >= 0.0f && values[0] <= 360.0f);
+ CheckValue(values[0] >= T{0} && values[0] <= T{360});
- Source->OuterAngle = values[0];
+ Source->OuterAngle = static_cast<float>(values[0]);
return CommitAndUpdateSourceProps(Source, Context);
case AL_GAIN:
CheckSize(1);
- CheckValue(values[0] >= 0.0f);
+ CheckValue(values[0] >= T{0});
- Source->Gain = values[0];
+ Source->Gain = static_cast<float>(values[0]);
return UpdateSourceProps(Source, Context);
case AL_MAX_DISTANCE:
CheckSize(1);
- CheckValue(values[0] >= 0.0f);
+ CheckValue(values[0] >= T{0});
- Source->MaxDistance = values[0];
+ Source->MaxDistance = static_cast<float>(values[0]);
return CommitAndUpdateSourceProps(Source, Context);
case AL_ROLLOFF_FACTOR:
CheckSize(1);
- CheckValue(values[0] >= 0.0f);
+ CheckValue(values[0] >= T{0});
- Source->RolloffFactor = values[0];
+ Source->RolloffFactor = static_cast<float>(values[0]);
return CommitAndUpdateSourceProps(Source, Context);
case AL_REFERENCE_DISTANCE:
CheckSize(1);
- CheckValue(values[0] >= 0.0f);
+ CheckValue(values[0] >= T{0});
- Source->RefDistance = values[0];
+ Source->RefDistance = static_cast<float>(values[0]);
return CommitAndUpdateSourceProps(Source, Context);
case AL_MIN_GAIN:
CheckSize(1);
- CheckValue(values[0] >= 0.0f);
+ CheckValue(values[0] >= T{0});
- Source->MinGain = values[0];
+ Source->MinGain = static_cast<float>(values[0]);
return UpdateSourceProps(Source, Context);
case AL_MAX_GAIN:
CheckSize(1);
- CheckValue(values[0] >= 0.0f);
+ CheckValue(values[0] >= T{0});
- Source->MaxGain = values[0];
+ Source->MaxGain = static_cast<float>(values[0]);
return UpdateSourceProps(Source, Context);
case AL_CONE_OUTER_GAIN:
CheckSize(1);
- CheckValue(values[0] >= 0.0f && values[0] <= 1.0f);
+ CheckValue(values[0] >= T{0} && values[0] <= T{1});
- Source->OuterGain = values[0];
+ Source->OuterGain = static_cast<float>(values[0]);
return UpdateSourceProps(Source, Context);
case AL_CONE_OUTER_GAINHF:
CheckSize(1);
- CheckValue(values[0] >= 0.0f && values[0] <= 1.0f);
+ CheckValue(values[0] >= T{0} && values[0] <= T{1});
- Source->OuterGainHF = values[0];
+ Source->OuterGainHF = static_cast<float>(values[0]);
return UpdateSourceProps(Source, Context);
case AL_AIR_ABSORPTION_FACTOR:
CheckSize(1);
- CheckValue(values[0] >= 0.0f && values[0] <= 10.0f);
+ CheckValue(values[0] >= T{0} && values[0] <= T{10});
- Source->AirAbsorptionFactor = values[0];
+ Source->AirAbsorptionFactor = static_cast<float>(values[0]);
return UpdateSourceProps(Source, Context);
case AL_ROOM_ROLLOFF_FACTOR:
CheckSize(1);
- CheckValue(values[0] >= 0.0f && values[0] <= 10.0f);
+ CheckValue(values[0] >= T{0} && values[0] <= T{1});
- Source->RoomRolloffFactor = values[0];
+ Source->RoomRolloffFactor = static_cast<float>(values[0]);
return UpdateSourceProps(Source, Context);
case AL_DOPPLER_FACTOR:
CheckSize(1);
- CheckValue(values[0] >= 0.0f && values[0] <= 1.0f);
+ CheckValue(values[0] >= T{0} && values[0] <= T{1});
- Source->DopplerFactor = values[0];
+ Source->DopplerFactor = static_cast<float>(values[0]);
return UpdateSourceProps(Source, Context);
+
+ case AL_SOURCE_RELATIVE:
+ if constexpr(std::is_integral_v<T>)
+ {
+ CheckSize(1);
+ CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE);
+
+ Source->HeadRelative = values[0] != AL_FALSE;
+ return CommitAndUpdateSourceProps(Source, Context);
+ }
+ break;
+
+ case AL_LOOPING:
+ if constexpr(std::is_integral_v<T>)
+ {
+ CheckSize(1);
+ CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE);
+
+ Source->Looping = values[0] != AL_FALSE;
+ if(Voice *voice{GetSourceVoice(Source, Context)})
+ {
+ if(Source->Looping)
+ voice->mLoopBuffer.store(&Source->mQueue.front(), std::memory_order_release);
+ else
+ voice->mLoopBuffer.store(nullptr, std::memory_order_release);
+
+ /* If the source is playing, wait for the current mix to finish
+ * to ensure it isn't currently looping back or reaching the
+ * end.
+ */
+ device->waitForMix();
+ }
+ return;
+ }
+ break;
+
+ case AL_BUFFER:
+ if constexpr(std::is_integral_v<T>)
+ {
+ CheckSize(1);
+ {
+ const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))};
+ if(state == AL_PLAYING || state == AL_PAUSED)
+ return Context->setError(AL_INVALID_OPERATION,
+ "Setting buffer on playing or paused source %u", Source->id);
+ }
+ std::deque<ALbufferQueueItem> oldlist;
+ if(values[0])
+ {
+ using UT = std::make_unsigned_t<T>;
+ std::lock_guard<std::mutex> _{device->BufferLock};
+ ALbuffer *buffer{LookupBuffer(device, static_cast<UT>(values[0]))};
+ if(!buffer) UNLIKELY
+ return Context->setError(AL_INVALID_VALUE, "Invalid buffer ID %s",
+ std::to_string(values[0]).c_str());
+ if(buffer->MappedAccess && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY
+ return Context->setError(AL_INVALID_OPERATION,
+ "Setting non-persistently mapped buffer %u", buffer->id);
+ if(buffer->mCallback && ReadRef(buffer->ref) != 0) UNLIKELY
+ return Context->setError(AL_INVALID_OPERATION,
+ "Setting already-set callback buffer %u", buffer->id);
+
+ /* Add the selected buffer to a one-item queue */
+ std::deque<ALbufferQueueItem> newlist;
+ newlist.emplace_back();
+ newlist.back().mCallback = buffer->mCallback;
+ newlist.back().mUserData = buffer->mUserData;
+ newlist.back().mBlockAlign = buffer->mBlockAlign;
+ newlist.back().mSampleLen = buffer->mSampleLen;
+ newlist.back().mLoopStart = buffer->mLoopStart;
+ newlist.back().mLoopEnd = buffer->mLoopEnd;
+ newlist.back().mSamples = buffer->mData.data();
+ newlist.back().mBuffer = buffer;
+ IncrementRef(buffer->ref);
+
+ /* Source is now Static */
+ Source->SourceType = AL_STATIC;
+ Source->mQueue.swap(oldlist);
+ Source->mQueue.swap(newlist);
+ }
+ else
+ {
+ /* Source is now Undetermined */
+ Source->SourceType = AL_UNDETERMINED;
+ Source->mQueue.swap(oldlist);
+ }
+
+ /* Delete all elements in the previous queue */
+ for(auto &item : oldlist)
+ {
+ if(ALbuffer *buffer{item.mBuffer})
+ DecrementRef(buffer->ref);
+ }
+ return;
+ }
+ break;
+
+
case AL_SEC_OFFSET:
case AL_SAMPLE_OFFSET:
case AL_BYTE_OFFSET:
CheckSize(1);
- CheckValue(std::isfinite(values[0]));
+ if constexpr(std::is_floating_point_v<T>)
+ CheckValue(std::isfinite(values[0]));
if(Voice *voice{GetSourceVoice(Source, Context)})
{
- auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]);
+ auto vpos = GetSampleOffset(Source->mQueue, prop, static_cast<double>(values[0]));
if(!vpos) return Context->setError(AL_INVALID_VALUE, "Invalid offset");
if(SetVoiceOffset(voice, *vpos, Source, Context, Context->mALDevice.get()))
return;
}
Source->OffsetType = prop;
- Source->Offset = values[0];
+ Source->Offset = static_cast<double>(values[0]);
return;
case AL_SAMPLE_RW_OFFSETS_SOFT:
+ if(sBufferSubDataCompat)
+ {
+ if constexpr(std::is_integral_v<T>)
+ {
+ /* Query only */
+ return Context->setError(AL_INVALID_OPERATION,
+ "Setting read-only source property 0x%04x", prop);
+ }
+ }
break;
case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/
if(sBufferSubDataCompat)
+ {
+ if constexpr(std::is_integral_v<T>)
+ {
+ /* Query only */
+ return Context->setError(AL_INVALID_OPERATION,
+ "Setting read-only source property 0x%04x", prop);
+ }
break;
+ }
CheckSize(1);
- CheckValue(values[0] >= 0.0f && std::isfinite(values[0]));
+ if constexpr(std::is_floating_point_v<T>)
+ CheckValue(values[0] >= T{0} && std::isfinite(static_cast<float>(values[0])));
+ else
+ CheckValue(values[0] >= T{0});
- Source->Radius = values[0];
+ Source->Radius = static_cast<float>(values[0]);
return UpdateSourceProps(Source, Context);
case AL_SUPER_STEREO_WIDTH_SOFT:
CheckSize(1);
- CheckValue(values[0] >= 0.0f && values[0] <= 1.0f);
+ CheckValue(values[0] >= T{0} && values[0] <= T{1});
- Source->EnhWidth = values[0];
+ Source->EnhWidth = static_cast<float>(values[0]);
return UpdateSourceProps(Source, Context);
case AL_STEREO_ANGLES:
CheckSize(2);
- CheckValue(std::isfinite(values[0]) && std::isfinite(values[1]));
+ if constexpr(std::is_floating_point_v<T>)
+ CheckValue(std::isfinite(static_cast<float>(values[0]))
+ && std::isfinite(static_cast<float>(values[1])));
- Source->StereoPan[0] = values[0];
- Source->StereoPan[1] = values[1];
+ Source->StereoPan[0] = static_cast<float>(values[0]);
+ Source->StereoPan[1] = static_cast<float>(values[1]);
return UpdateSourceProps(Source, Context);
case AL_POSITION:
CheckSize(3);
- CheckValue(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
-
- Source->Position[0] = values[0];
- Source->Position[1] = values[1];
- Source->Position[2] = values[2];
+ if constexpr(std::is_floating_point_v<T>)
+ CheckValue(std::isfinite(static_cast<float>(values[0]))
+ && std::isfinite(static_cast<float>(values[1]))
+ && std::isfinite(static_cast<float>(values[2])));
+
+ Source->Position[0] = static_cast<float>(values[0]);
+ Source->Position[1] = static_cast<float>(values[1]);
+ Source->Position[2] = static_cast<float>(values[2]);
return CommitAndUpdateSourceProps(Source, Context);
case AL_VELOCITY:
CheckSize(3);
- CheckValue(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
-
- Source->Velocity[0] = values[0];
- Source->Velocity[1] = values[1];
- Source->Velocity[2] = values[2];
+ if constexpr(std::is_floating_point_v<T>)
+ CheckValue(std::isfinite(static_cast<float>(values[0]))
+ && std::isfinite(static_cast<float>(values[1]))
+ && std::isfinite(static_cast<float>(values[2])));
+
+ Source->Velocity[0] = static_cast<float>(values[0]);
+ Source->Velocity[1] = static_cast<float>(values[1]);
+ Source->Velocity[2] = static_cast<float>(values[2]);
return CommitAndUpdateSourceProps(Source, Context);
case AL_DIRECTION:
CheckSize(3);
- CheckValue(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]));
-
- Source->Direction[0] = values[0];
- Source->Direction[1] = values[1];
- Source->Direction[2] = values[2];
+ if constexpr(std::is_floating_point_v<T>)
+ CheckValue(std::isfinite(static_cast<float>(values[0]))
+ && std::isfinite(static_cast<float>(values[1]))
+ && std::isfinite(static_cast<float>(values[2])));
+
+ Source->Direction[0] = static_cast<float>(values[0]);
+ Source->Direction[1] = static_cast<float>(values[1]);
+ Source->Direction[2] = static_cast<float>(values[2]);
return CommitAndUpdateSourceProps(Source, Context);
case AL_ORIENTATION:
CheckSize(6);
- CheckValue(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2])
- && std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5]));
-
- Source->OrientAt[0] = values[0];
- Source->OrientAt[1] = values[1];
- Source->OrientAt[2] = values[2];
- Source->OrientUp[0] = values[3];
- Source->OrientUp[1] = values[4];
- Source->OrientUp[2] = values[5];
+ if constexpr(std::is_floating_point_v<T>)
+ CheckValue(std::isfinite(static_cast<float>(values[0]))
+ && std::isfinite(static_cast<float>(values[1]))
+ && std::isfinite(static_cast<float>(values[2]))
+ && std::isfinite(static_cast<float>(values[3]))
+ && std::isfinite(static_cast<float>(values[4]))
+ && std::isfinite(static_cast<float>(values[5])));
+
+ Source->OrientAt[0] = static_cast<float>(values[0]);
+ Source->OrientAt[1] = static_cast<float>(values[1]);
+ Source->OrientAt[2] = static_cast<float>(values[2]);
+ Source->OrientUp[0] = static_cast<float>(values[3]);
+ Source->OrientUp[1] = static_cast<float>(values[4]);
+ Source->OrientUp[2] = static_cast<float>(values[5]);
return UpdateSourceProps(Source, Context);
- case AL_SOURCE_RELATIVE:
- case AL_LOOPING:
- case AL_SOURCE_STATE:
- case AL_SOURCE_TYPE:
- case AL_DISTANCE_MODEL:
- case AL_DIRECT_FILTER_GAINHF_AUTO:
- case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
- case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
- case AL_DIRECT_CHANNELS_SOFT:
- case AL_SOURCE_RESAMPLER_SOFT:
- case AL_SOURCE_SPATIALIZE_SOFT:
- case AL_BYTE_LENGTH_SOFT:
- case AL_SAMPLE_LENGTH_SOFT:
- case AL_STEREO_MODE_SOFT:
- CheckSize(1);
- ival = static_cast<int>(values[0]);
- return SetSourceiv(Source, Context, prop, {&ival, 1u});
-
- case AL_BUFFERS_QUEUED:
- case AL_BUFFERS_PROCESSED:
- CheckSize(1);
- ival = static_cast<int>(static_cast<ALuint>(values[0]));
- return SetSourceiv(Source, Context, prop, {&ival, 1u});
-
- case AL_BUFFER:
case AL_DIRECT_FILTER:
- case AL_AUXILIARY_SEND_FILTER:
- case AL_SAMPLE_OFFSET_LATENCY_SOFT:
- case AL_SAMPLE_OFFSET_CLOCK_SOFT:
- break;
- }
-
- ERR("Unexpected property: 0x%04x\n", prop);
- Context->setError(AL_INVALID_ENUM, "Invalid source float property 0x%04x", prop);
-}
-catch(check_exception&) {
-}
-
-void SetSourceiv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop,
- const al::span<const int> values)
-try {
- auto Checkers = GetCheckers(Context, prop, values);
- auto &CheckSize = Checkers.first;
- auto &CheckValue = Checkers.second;
- ALCdevice *device{Context->mALDevice.get()};
- ALeffectslot *slot{nullptr};
- al::deque<ALbufferQueueItem> oldlist;
- std::unique_lock<std::mutex> slotlock;
- float fvals[6];
-
- switch(prop)
- {
- case AL_SOURCE_STATE:
- case AL_SOURCE_TYPE:
- case AL_BUFFERS_QUEUED:
- case AL_BUFFERS_PROCESSED:
- case AL_BYTE_LENGTH_SOFT:
- case AL_SAMPLE_LENGTH_SOFT:
- /* Query only */
- return Context->setError(AL_INVALID_OPERATION,
- "Setting read-only source property 0x%04x", prop);
-
- case AL_SOURCE_RELATIVE:
- CheckSize(1);
- CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE);
-
- Source->HeadRelative = values[0] != AL_FALSE;
- return CommitAndUpdateSourceProps(Source, Context);
-
- case AL_LOOPING:
- CheckSize(1);
- CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE);
-
- Source->Looping = values[0] != AL_FALSE;
- if(Voice *voice{GetSourceVoice(Source, Context)})
+ if constexpr(std::is_integral_v<T>)
{
- if(Source->Looping)
- voice->mLoopBuffer.store(&Source->mQueue.front(), std::memory_order_release);
+ CheckSize(1);
+ const auto filterid = static_cast<std::make_unsigned_t<T>>(values[0]);
+ if(values[0])
+ {
+ std::lock_guard<std::mutex> _{device->FilterLock};
+ ALfilter *filter{LookupFilter(device, filterid)};
+ if(!filter) UNLIKELY
+ return Context->setError(AL_INVALID_VALUE, "Invalid filter ID %s",
+ std::to_string(filterid).c_str());
+ Source->Direct.Gain = filter->Gain;
+ Source->Direct.GainHF = filter->GainHF;
+ Source->Direct.HFReference = filter->HFReference;
+ Source->Direct.GainLF = filter->GainLF;
+ Source->Direct.LFReference = filter->LFReference;
+ }
else
- voice->mLoopBuffer.store(nullptr, std::memory_order_release);
-
- /* If the source is playing, wait for the current mix to finish to
- * ensure it isn't currently looping back or reaching the end.
- */
- device->waitForMix();
+ {
+ Source->Direct.Gain = 1.0f;
+ Source->Direct.GainHF = 1.0f;
+ Source->Direct.HFReference = LOWPASSFREQREF;
+ Source->Direct.GainLF = 1.0f;
+ Source->Direct.LFReference = HIGHPASSFREQREF;
+ }
+ return UpdateSourceProps(Source, Context);
}
- return;
+ break;
- case AL_BUFFER:
- CheckSize(1);
- {
- const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))};
- if(state == AL_PLAYING || state == AL_PAUSED)
- return Context->setError(AL_INVALID_OPERATION,
- "Setting buffer on playing or paused source %u", Source->id);
- }
- if(values[0])
- {
- std::lock_guard<std::mutex> _{device->BufferLock};
- ALbuffer *buffer{LookupBuffer(device, static_cast<ALuint>(values[0]))};
- if(!buffer)
- return Context->setError(AL_INVALID_VALUE, "Invalid buffer ID %u",
- static_cast<ALuint>(values[0]));
- if(buffer->MappedAccess && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
- return Context->setError(AL_INVALID_OPERATION,
- "Setting non-persistently mapped buffer %u", buffer->id);
- if(buffer->mCallback && ReadRef(buffer->ref) != 0)
- return Context->setError(AL_INVALID_OPERATION,
- "Setting already-set callback buffer %u", buffer->id);
-
- /* Add the selected buffer to a one-item queue */
- al::deque<ALbufferQueueItem> newlist;
- newlist.emplace_back();
- newlist.back().mCallback = buffer->mCallback;
- newlist.back().mUserData = buffer->mUserData;
- newlist.back().mBlockAlign = buffer->mBlockAlign;
- newlist.back().mSampleLen = buffer->mSampleLen;
- newlist.back().mLoopStart = buffer->mLoopStart;
- newlist.back().mLoopEnd = buffer->mLoopEnd;
- newlist.back().mSamples = buffer->mData.data();
- newlist.back().mBuffer = buffer;
- IncrementRef(buffer->ref);
-
- /* Source is now Static */
- Source->SourceType = AL_STATIC;
- Source->mQueue.swap(oldlist);
- Source->mQueue.swap(newlist);
- }
- else
+ case AL_DIRECT_FILTER_GAINHF_AUTO:
+ if constexpr(std::is_integral_v<T>)
{
- /* Source is now Undetermined */
- Source->SourceType = AL_UNDETERMINED;
- Source->mQueue.swap(oldlist);
- }
+ CheckSize(1);
+ CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE);
- /* Delete all elements in the previous queue */
- for(auto &item : oldlist)
- {
- if(ALbuffer *buffer{item.mBuffer})
- DecrementRef(buffer->ref);
+ Source->DryGainHFAuto = values[0] != AL_FALSE;
+ return UpdateSourceProps(Source, Context);
}
- return;
-
- case AL_SEC_OFFSET:
- case AL_SAMPLE_OFFSET:
- case AL_BYTE_OFFSET:
- CheckSize(1);
+ break;
- if(Voice *voice{GetSourceVoice(Source, Context)})
+ case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
+ if constexpr(std::is_integral_v<T>)
{
- auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]);
- if(!vpos) return Context->setError(AL_INVALID_VALUE, "Invalid source offset");
+ CheckSize(1);
+ CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE);
- if(SetVoiceOffset(voice, *vpos, Source, Context, device))
- return;
- }
- Source->OffsetType = prop;
- Source->Offset = values[0];
- return;
-
- case AL_DIRECT_FILTER:
- CheckSize(1);
- if(values[0])
- {
- std::lock_guard<std::mutex> _{device->FilterLock};
- ALfilter *filter{LookupFilter(device, static_cast<ALuint>(values[0]))};
- if(!filter)
- return Context->setError(AL_INVALID_VALUE, "Invalid filter ID %u",
- static_cast<ALuint>(values[0]));
- Source->Direct.Gain = filter->Gain;
- Source->Direct.GainHF = filter->GainHF;
- Source->Direct.HFReference = filter->HFReference;
- Source->Direct.GainLF = filter->GainLF;
- Source->Direct.LFReference = filter->LFReference;
- }
- else
- {
- Source->Direct.Gain = 1.0f;
- Source->Direct.GainHF = 1.0f;
- Source->Direct.HFReference = LOWPASSFREQREF;
- Source->Direct.GainLF = 1.0f;
- Source->Direct.LFReference = HIGHPASSFREQREF;
+ Source->WetGainAuto = values[0] != AL_FALSE;
+ return UpdateSourceProps(Source, Context);
}
- return UpdateSourceProps(Source, Context);
-
- case AL_DIRECT_FILTER_GAINHF_AUTO:
- CheckSize(1);
- CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE);
-
- Source->DryGainHFAuto = values[0] != AL_FALSE;
- return UpdateSourceProps(Source, Context);
-
- case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
- CheckSize(1);
- CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE);
-
- Source->WetGainAuto = values[0] != AL_FALSE;
- return UpdateSourceProps(Source, Context);
+ break;
case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
- CheckSize(1);
- CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE);
+ if constexpr(std::is_integral_v<T>)
+ {
+ CheckSize(1);
+ CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE);
- Source->WetGainHFAuto = values[0] != AL_FALSE;
- return UpdateSourceProps(Source, Context);
+ Source->WetGainHFAuto = values[0] != AL_FALSE;
+ return UpdateSourceProps(Source, Context);
+ }
+ break;
case AL_DIRECT_CHANNELS_SOFT:
- CheckSize(1);
- if(auto mode = DirectModeFromEnum(values[0]))
+ if constexpr(std::is_integral_v<T>)
{
- Source->DirectChannels = *mode;
- return UpdateSourceProps(Source, Context);
+ CheckSize(1);
+ if(auto mode = DirectModeFromEnum(values[0]))
+ {
+ Source->DirectChannels = *mode;
+ return UpdateSourceProps(Source, Context);
+ }
+ return Context->setError(AL_INVALID_VALUE, "Invalid direct channels mode: %s\n",
+ HexPrinter{values[0]}.c_str());
}
- Context->setError(AL_INVALID_VALUE, "Unsupported AL_DIRECT_CHANNELS_SOFT: 0x%04x\n",
- values[0]);
- return;
+ break;
case AL_DISTANCE_MODEL:
- CheckSize(1);
- if(auto model = DistanceModelFromALenum(values[0]))
+ if constexpr(std::is_integral_v<T>)
{
- Source->mDistanceModel = *model;
- if(Context->mSourceDistanceModel)
- UpdateSourceProps(Source, Context);
- return;
+ CheckSize(1);
+ if(auto model = DistanceModelFromALenum(values[0]))
+ {
+ Source->mDistanceModel = *model;
+ if(Context->mSourceDistanceModel)
+ UpdateSourceProps(Source, Context);
+ return;
+ }
+ return Context->setError(AL_INVALID_VALUE, "Invalid distance model: %s\n",
+ HexPrinter{values[0]}.c_str());
}
- Context->setError(AL_INVALID_VALUE, "Distance model out of range: 0x%04x", values[0]);
- return;
+ break;
case AL_SOURCE_RESAMPLER_SOFT:
- CheckSize(1);
- CheckValue(values[0] >= 0 && values[0] <= static_cast<int>(Resampler::Max));
-
- Source->mResampler = static_cast<Resampler>(values[0]);
- return UpdateSourceProps(Source, Context);
-
- case AL_SOURCE_SPATIALIZE_SOFT:
- CheckSize(1);
- if(auto mode = SpatializeModeFromEnum(values[0]))
+ if constexpr(std::is_integral_v<T>)
{
- Source->mSpatialize = *mode;
- return UpdateSourceProps(Source, Context);
- }
- Context->setError(AL_INVALID_VALUE, "Unsupported AL_SOURCE_SPATIALIZE_SOFT: 0x%04x\n",
- values[0]);
- return;
+ CheckSize(1);
+ CheckValue(values[0] >= 0 && values[0] <= static_cast<int>(Resampler::Max));
- case AL_STEREO_MODE_SOFT:
- CheckSize(1);
- {
- const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))};
- if(state == AL_PLAYING || state == AL_PAUSED)
- return Context->setError(AL_INVALID_OPERATION,
- "Modifying stereo mode on playing or paused source %u", Source->id);
- }
- if(auto mode = StereoModeFromEnum(values[0]))
- {
- Source->mStereoMode = *mode;
- return;
- }
- Context->setError(AL_INVALID_VALUE, "Unsupported AL_STEREO_MODE_SOFT: 0x%04x\n",
- values[0]);
- return;
-
- case AL_AUXILIARY_SEND_FILTER:
- CheckSize(3);
- slotlock = std::unique_lock<std::mutex>{Context->mEffectSlotLock};
- if(values[0] && (slot=LookupEffectSlot(Context, static_cast<ALuint>(values[0]))) == nullptr)
- return Context->setError(AL_INVALID_VALUE, "Invalid effect ID %u", values[0]);
- if(static_cast<ALuint>(values[1]) >= device->NumAuxSends)
- return Context->setError(AL_INVALID_VALUE, "Invalid send %u", values[1]);
-
- if(values[2])
- {
- std::lock_guard<std::mutex> _{device->FilterLock};
- ALfilter *filter{LookupFilter(device, static_cast<ALuint>(values[2]))};
- if(!filter)
- return Context->setError(AL_INVALID_VALUE, "Invalid filter ID %u", values[2]);
-
- auto &send = Source->Send[static_cast<ALuint>(values[1])];
- send.Gain = filter->Gain;
- send.GainHF = filter->GainHF;
- send.HFReference = filter->HFReference;
- send.GainLF = filter->GainLF;
- send.LFReference = filter->LFReference;
- }
- else
- {
- /* Disable filter */
- auto &send = Source->Send[static_cast<ALuint>(values[1])];
- send.Gain = 1.0f;
- send.GainHF = 1.0f;
- send.HFReference = LOWPASSFREQREF;
- send.GainLF = 1.0f;
- send.LFReference = HIGHPASSFREQREF;
+ Source->mResampler = static_cast<Resampler>(values[0]);
+ return UpdateSourceProps(Source, Context);
}
+ break;
- /* We must force an update if the current auxiliary slot is valid and
- * about to be changed on an active source, in case the old slot is
- * about to be deleted.
- */
- if(Source->Send[static_cast<ALuint>(values[1])].Slot
- && slot != Source->Send[static_cast<ALuint>(values[1])].Slot
- && IsPlayingOrPaused(Source))
- {
- /* Add refcount on the new slot, and release the previous slot */
- if(slot) IncrementRef(slot->ref);
- if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot)
- DecrementRef(oldslot->ref);
- Source->Send[static_cast<ALuint>(values[1])].Slot = slot;
-
- Voice *voice{GetSourceVoice(Source, Context)};
- if(voice) UpdateSourceProps(Source, voice, Context);
- else Source->mPropsDirty = true;
- }
- else
+ case AL_SOURCE_SPATIALIZE_SOFT:
+ if constexpr(std::is_integral_v<T>)
{
- if(slot) IncrementRef(slot->ref);
- if(auto *oldslot = Source->Send[static_cast<ALuint>(values[1])].Slot)
- DecrementRef(oldslot->ref);
- Source->Send[static_cast<ALuint>(values[1])].Slot = slot;
- UpdateSourceProps(Source, Context);
+ CheckSize(1);
+ if(auto mode = SpatializeModeFromEnum(values[0]))
+ {
+ Source->mSpatialize = *mode;
+ return UpdateSourceProps(Source, Context);
+ }
+ return Context->setError(AL_INVALID_VALUE, "Invalid source spatialize mode: %s\n",
+ HexPrinter{values[0]}.c_str());
}
- return;
-
-
- case AL_SAMPLE_RW_OFFSETS_SOFT:
- if(sBufferSubDataCompat)
- /* Query only */
- return Context->setError(AL_INVALID_OPERATION,
- "Setting read-only source property 0x%04x", prop);
- break;
-
- case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/
- if(sBufferSubDataCompat)
- return Context->setError(AL_INVALID_OPERATION,
- "Setting read-only source property 0x%04x", prop);
- /*fall-through*/
-
- /* 1x float */
- case AL_CONE_INNER_ANGLE:
- case AL_CONE_OUTER_ANGLE:
- case AL_PITCH:
- case AL_GAIN:
- case AL_MIN_GAIN:
- case AL_MAX_GAIN:
- case AL_REFERENCE_DISTANCE:
- case AL_ROLLOFF_FACTOR:
- case AL_CONE_OUTER_GAIN:
- case AL_MAX_DISTANCE:
- case AL_DOPPLER_FACTOR:
- case AL_CONE_OUTER_GAINHF:
- case AL_AIR_ABSORPTION_FACTOR:
- case AL_ROOM_ROLLOFF_FACTOR:
- case AL_SEC_LENGTH_SOFT:
- case AL_SUPER_STEREO_WIDTH_SOFT:
- CheckSize(1);
- fvals[0] = static_cast<float>(values[0]);
- return SetSourcefv(Source, Context, prop, {fvals, 1u});
-
- /* 3x float */
- case AL_POSITION:
- case AL_VELOCITY:
- case AL_DIRECTION:
- CheckSize(3);
- fvals[0] = static_cast<float>(values[0]);
- fvals[1] = static_cast<float>(values[1]);
- fvals[2] = static_cast<float>(values[2]);
- return SetSourcefv(Source, Context, prop, {fvals, 3u});
-
- /* 6x float */
- case AL_ORIENTATION:
- CheckSize(6);
- fvals[0] = static_cast<float>(values[0]);
- fvals[1] = static_cast<float>(values[1]);
- fvals[2] = static_cast<float>(values[2]);
- fvals[3] = static_cast<float>(values[3]);
- fvals[4] = static_cast<float>(values[4]);
- fvals[5] = static_cast<float>(values[5]);
- return SetSourcefv(Source, Context, prop, {fvals, 6u});
-
- case AL_SAMPLE_OFFSET_LATENCY_SOFT:
- case AL_SEC_OFFSET_LATENCY_SOFT:
- case AL_SEC_OFFSET_CLOCK_SOFT:
- case AL_SAMPLE_OFFSET_CLOCK_SOFT:
- case AL_STEREO_ANGLES:
break;
- }
- ERR("Unexpected property: 0x%04x\n", prop);
- Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop);
-}
-catch(check_exception&) {
-}
-
-void SetSourcei64v(ALsource *const Source, ALCcontext *const Context, const SourceProp prop,
- const al::span<const int64_t> values)
-try {
- auto Checkers = GetCheckers(Context, prop, values);
- auto &CheckSize = Checkers.first;
- auto &CheckValue = Checkers.second;
- float fvals[MaxValues];
- int ivals[MaxValues];
-
- switch(prop)
- {
- case AL_SOURCE_TYPE:
- case AL_BUFFERS_QUEUED:
- case AL_BUFFERS_PROCESSED:
- case AL_SOURCE_STATE:
- case AL_BYTE_LENGTH_SOFT:
- case AL_SAMPLE_LENGTH_SOFT:
- case AL_SAMPLE_OFFSET_LATENCY_SOFT:
- case AL_SAMPLE_OFFSET_CLOCK_SOFT:
- /* Query only */
- return Context->setError(AL_INVALID_OPERATION,
- "Setting read-only source property 0x%04x", prop);
-
- /* 1x int */
- case AL_SOURCE_RELATIVE:
- case AL_LOOPING:
- case AL_SEC_OFFSET:
- case AL_SAMPLE_OFFSET:
- case AL_BYTE_OFFSET:
- case AL_DIRECT_FILTER_GAINHF_AUTO:
- case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
- case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
- case AL_DIRECT_CHANNELS_SOFT:
- case AL_DISTANCE_MODEL:
- case AL_SOURCE_RESAMPLER_SOFT:
- case AL_SOURCE_SPATIALIZE_SOFT:
case AL_STEREO_MODE_SOFT:
- CheckSize(1);
- CheckValue(values[0] <= INT_MAX && values[0] >= INT_MIN);
-
- ivals[0] = static_cast<int>(values[0]);
- return SetSourceiv(Source, Context, prop, {ivals, 1u});
-
- /* 1x uint */
- case AL_BUFFER:
- case AL_DIRECT_FILTER:
- CheckSize(1);
- CheckValue(values[0] <= UINT_MAX && values[0] >= 0);
-
- ivals[0] = static_cast<int>(values[0]);
- return SetSourceiv(Source, Context, prop, {ivals, 1u});
-
- /* 3x uint */
- case AL_AUXILIARY_SEND_FILTER:
- CheckSize(3);
- CheckValue(values[0] <= UINT_MAX && values[0] >= 0 && values[1] <= UINT_MAX
- && values[1] >= 0 && values[2] <= UINT_MAX && values[2] >= 0);
-
- ivals[0] = static_cast<int>(values[0]);
- ivals[1] = static_cast<int>(values[1]);
- ivals[2] = static_cast<int>(values[2]);
- return SetSourceiv(Source, Context, prop, {ivals, 3u});
-
- case AL_SAMPLE_RW_OFFSETS_SOFT:
- if(sBufferSubDataCompat)
+ if constexpr(std::is_integral_v<T>)
{
- /* Query only */
- return Context->setError(AL_INVALID_OPERATION,
- "Setting read-only source property 0x%04x", prop);
+ CheckSize(1);
+ {
+ const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))};
+ if(state == AL_PLAYING || state == AL_PAUSED)
+ return Context->setError(AL_INVALID_OPERATION,
+ "Modifying stereo mode on playing or paused source %u", Source->id);
+ }
+ if(auto mode = StereoModeFromEnum(values[0]))
+ {
+ Source->mStereoMode = *mode;
+ return;
+ }
+ return Context->setError(AL_INVALID_VALUE, "Invalid stereo mode: %s\n",
+ HexPrinter{values[0]}.c_str());
}
break;
- case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/
- if(sBufferSubDataCompat)
- return Context->setError(AL_INVALID_OPERATION,
- "Setting read-only source property 0x%04x", prop);
- /*fall-through*/
-
- /* 1x float */
- case AL_CONE_INNER_ANGLE:
- case AL_CONE_OUTER_ANGLE:
- case AL_PITCH:
- case AL_GAIN:
- case AL_MIN_GAIN:
- case AL_MAX_GAIN:
- case AL_REFERENCE_DISTANCE:
- case AL_ROLLOFF_FACTOR:
- case AL_CONE_OUTER_GAIN:
- case AL_MAX_DISTANCE:
- case AL_DOPPLER_FACTOR:
- case AL_CONE_OUTER_GAINHF:
- case AL_AIR_ABSORPTION_FACTOR:
- case AL_ROOM_ROLLOFF_FACTOR:
- case AL_SEC_LENGTH_SOFT:
- case AL_SUPER_STEREO_WIDTH_SOFT:
- CheckSize(1);
- fvals[0] = static_cast<float>(values[0]);
- return SetSourcefv(Source, Context, prop, {fvals, 1u});
+ case AL_AUXILIARY_SEND_FILTER:
+ if constexpr(std::is_integral_v<T>)
+ {
+ CheckSize(3);
+ const auto slotid = static_cast<std::make_unsigned_t<T>>(values[0]);
+ const auto sendidx = static_cast<std::make_unsigned_t<T>>(values[1]);
+ const auto filterid = static_cast<std::make_unsigned_t<T>>(values[2]);
+
+ std::unique_lock slotlock{Context->mEffectSlotLock};
+ ALeffectslot *slot{};
+ if(values[0])
+ {
+ if((slot=LookupEffectSlot(Context, slotid)) == nullptr) UNLIKELY
+ return Context->setError(AL_INVALID_VALUE, "Invalid effect ID %s",
+ std::to_string(slotid).c_str());
+ }
- /* 3x float */
- case AL_POSITION:
- case AL_VELOCITY:
- case AL_DIRECTION:
- CheckSize(3);
- fvals[0] = static_cast<float>(values[0]);
- fvals[1] = static_cast<float>(values[1]);
- fvals[2] = static_cast<float>(values[2]);
- return SetSourcefv(Source, Context, prop, {fvals, 3u});
+ if(sendidx >= device->NumAuxSends) UNLIKELY
+ return Context->setError(AL_INVALID_VALUE, "Invalid send %s",
+ std::to_string(sendidx).c_str());
+ auto &send = Source->Send[static_cast<size_t>(sendidx)];
- /* 6x float */
- case AL_ORIENTATION:
- CheckSize(6);
- fvals[0] = static_cast<float>(values[0]);
- fvals[1] = static_cast<float>(values[1]);
- fvals[2] = static_cast<float>(values[2]);
- fvals[3] = static_cast<float>(values[3]);
- fvals[4] = static_cast<float>(values[4]);
- fvals[5] = static_cast<float>(values[5]);
- return SetSourcefv(Source, Context, prop, {fvals, 6u});
+ if(values[2])
+ {
+ std::lock_guard<std::mutex> _{device->FilterLock};
+ ALfilter *filter{LookupFilter(device, filterid)};
+ if(!filter) UNLIKELY
+ return Context->setError(AL_INVALID_VALUE, "Invalid filter ID %s",
+ std::to_string(filterid).c_str());
+
+ send.Gain = filter->Gain;
+ send.GainHF = filter->GainHF;
+ send.HFReference = filter->HFReference;
+ send.GainLF = filter->GainLF;
+ send.LFReference = filter->LFReference;
+ }
+ else
+ {
+ /* Disable filter */
+ send.Gain = 1.0f;
+ send.GainHF = 1.0f;
+ send.HFReference = LOWPASSFREQREF;
+ send.GainLF = 1.0f;
+ send.LFReference = HIGHPASSFREQREF;
+ }
- case AL_SEC_OFFSET_LATENCY_SOFT:
- case AL_SEC_OFFSET_CLOCK_SOFT:
- case AL_STEREO_ANGLES:
+ /* We must force an update if the current auxiliary slot is valid
+ * and about to be changed on an active source, in case the old
+ * slot is about to be deleted.
+ */
+ if(send.Slot && slot != send.Slot && IsPlayingOrPaused(Source))
+ {
+ /* Add refcount on the new slot, and release the previous slot */
+ if(slot) IncrementRef(slot->ref);
+ if(auto *oldslot = send.Slot)
+ DecrementRef(oldslot->ref);
+ send.Slot = slot;
+
+ Voice *voice{GetSourceVoice(Source, Context)};
+ if(voice) UpdateSourceProps(Source, voice, Context);
+ else Source->mPropsDirty = true;
+ }
+ else
+ {
+ if(slot) IncrementRef(slot->ref);
+ if(auto *oldslot = send.Slot)
+ DecrementRef(oldslot->ref);
+ send.Slot = slot;
+ UpdateSourceProps(Source, Context);
+ }
+ return;
+ }
break;
}
- ERR("Unexpected property: 0x%04x\n", prop);
- Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop);
+ ERR("Unexpected %s property: 0x%04x\n", PropType<T>::Name(), prop);
+ Context->setError(AL_INVALID_ENUM, "Invalid source %s property 0x%04x", PropType<T>::Name(),
+ prop);
}
catch(check_exception&) {
}
@@ -2100,245 +2004,292 @@ auto GetSizeChecker(ALCcontext *const Context, const SourceProp prop, const al::
};
}
-bool GetSourcedv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, const al::span<double> values);
-bool GetSourceiv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, const al::span<int> values);
-bool GetSourcei64v(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, const al::span<int64_t> values);
-
-bool GetSourcedv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop,
- const al::span<double> values)
-try {
+template<typename T>
+[[nodiscard]] NOINLINE
+bool GetProperty(ALsource *const Source, ALCcontext *const Context, const SourceProp prop,
+ const al::span<T> values) try
+{
+ using std::chrono::duration_cast;
auto CheckSize = GetSizeChecker(Context, prop, values);
ALCdevice *device{Context->mALDevice.get()};
- ClockLatency clocktime;
- nanoseconds srcclock;
- int ivals[MaxValues];
- bool err;
switch(prop)
{
case AL_GAIN:
CheckSize(1);
- values[0] = Source->Gain;
+ values[0] = static_cast<T>(Source->Gain);
return true;
case AL_PITCH:
CheckSize(1);
- values[0] = Source->Pitch;
+ values[0] = static_cast<T>(Source->Pitch);
return true;
case AL_MAX_DISTANCE:
CheckSize(1);
- values[0] = Source->MaxDistance;
+ values[0] = static_cast<T>(Source->MaxDistance);
return true;
case AL_ROLLOFF_FACTOR:
CheckSize(1);
- values[0] = Source->RolloffFactor;
+ values[0] = static_cast<T>(Source->RolloffFactor);
return true;
case AL_REFERENCE_DISTANCE:
CheckSize(1);
- values[0] = Source->RefDistance;
+ values[0] = static_cast<T>(Source->RefDistance);
return true;
case AL_CONE_INNER_ANGLE:
CheckSize(1);
- values[0] = Source->InnerAngle;
+ values[0] = static_cast<T>(Source->InnerAngle);
return true;
case AL_CONE_OUTER_ANGLE:
CheckSize(1);
- values[0] = Source->OuterAngle;
+ values[0] = static_cast<T>(Source->OuterAngle);
return true;
case AL_MIN_GAIN:
CheckSize(1);
- values[0] = Source->MinGain;
+ values[0] = static_cast<T>(Source->MinGain);
return true;
case AL_MAX_GAIN:
CheckSize(1);
- values[0] = Source->MaxGain;
+ values[0] = static_cast<T>(Source->MaxGain);
return true;
case AL_CONE_OUTER_GAIN:
CheckSize(1);
- values[0] = Source->OuterGain;
+ values[0] = static_cast<T>(Source->OuterGain);
return true;
case AL_SEC_OFFSET:
case AL_SAMPLE_OFFSET:
case AL_BYTE_OFFSET:
CheckSize(1);
- values[0] = GetSourceOffset(Source, prop, Context);
+ values[0] = GetSourceOffset<T>(Source, prop, Context);
return true;
case AL_CONE_OUTER_GAINHF:
CheckSize(1);
- values[0] = Source->OuterGainHF;
+ values[0] = static_cast<T>(Source->OuterGainHF);
return true;
case AL_AIR_ABSORPTION_FACTOR:
CheckSize(1);
- values[0] = Source->AirAbsorptionFactor;
+ values[0] = static_cast<T>(Source->AirAbsorptionFactor);
return true;
case AL_ROOM_ROLLOFF_FACTOR:
CheckSize(1);
- values[0] = Source->RoomRolloffFactor;
+ values[0] = static_cast<T>(Source->RoomRolloffFactor);
return true;
case AL_DOPPLER_FACTOR:
CheckSize(1);
- values[0] = Source->DopplerFactor;
+ values[0] = static_cast<T>(Source->DopplerFactor);
return true;
case AL_SAMPLE_RW_OFFSETS_SOFT:
+ if constexpr(std::is_integral_v<T>)
+ {
+ if(sBufferSubDataCompat)
+ {
+ CheckSize(2);
+ values[0] = GetSourceOffset<T>(Source, AL_SAMPLE_OFFSET, Context);
+ /* FIXME: values[1] should be ahead of values[0] by the device
+ * update time. It needs to clamp or wrap the length of the
+ * buffer queue.
+ */
+ values[1] = values[0];
+ return true;
+ }
+ }
break;
case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/
- if(sBufferSubDataCompat)
- break;
+ if constexpr(std::is_floating_point_v<T>)
+ {
+ if(sBufferSubDataCompat)
+ break;
- CheckSize(1);
- values[0] = Source->Radius;
- return true;
+ CheckSize(1);
+ values[0] = static_cast<T>(Source->Radius);
+ return true;
+ }
+ else
+ {
+ if(sBufferSubDataCompat)
+ {
+ CheckSize(2);
+ values[0] = GetSourceOffset<T>(Source, AL_BYTE_OFFSET, Context);
+ /* FIXME: values[1] should be ahead of values[0] by the device
+ * update time. It needs to clamp or wrap the length of the
+ * buffer queue.
+ */
+ values[1] = values[0];
+ return true;
+ }
+ break;
+ }
case AL_SUPER_STEREO_WIDTH_SOFT:
CheckSize(1);
- values[0] = Source->EnhWidth;
+ values[0] = static_cast<T>(Source->EnhWidth);
return true;
case AL_BYTE_LENGTH_SOFT:
case AL_SAMPLE_LENGTH_SOFT:
case AL_SEC_LENGTH_SOFT:
CheckSize(1);
- values[0] = GetSourceLength(Source, prop);
+ values[0] = GetSourceLength<T>(Source, prop);
return true;
case AL_STEREO_ANGLES:
- CheckSize(2);
- values[0] = Source->StereoPan[0];
- values[1] = Source->StereoPan[1];
- return true;
+ if constexpr(std::is_floating_point_v<T>)
+ {
+ CheckSize(2);
+ values[0] = static_cast<T>(Source->StereoPan[0]);
+ values[1] = static_cast<T>(Source->StereoPan[1]);
+ return true;
+ }
+ break;
- case AL_SEC_OFFSET_LATENCY_SOFT:
- CheckSize(2);
- /* Get the source offset with the clock time first. Then get the clock
- * time with the device latency. Order is important.
- */
- values[0] = GetSourceSecOffset(Source, Context, &srcclock);
+ case AL_SAMPLE_OFFSET_LATENCY_SOFT:
+ if constexpr(std::is_same_v<T,int64_t>)
{
- std::lock_guard<std::mutex> _{device->StateLock};
- clocktime = GetClockLatency(device, device->Backend.get());
+ CheckSize(2);
+ /* Get the source offset with the clock time first. Then get the
+ * clock time with the device latency. Order is important.
+ */
+ ClockLatency clocktime{};
+ nanoseconds srcclock{};
+ values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
+ {
+ std::lock_guard<std::mutex> _{device->StateLock};
+ clocktime = GetClockLatency(device, device->Backend.get());
+ }
+ if(srcclock == clocktime.ClockTime)
+ values[1] = nanoseconds{clocktime.Latency}.count();
+ else
+ {
+ /* If the clock time incremented, reduce the latency by that
+ * much since it's that much closer to the source offset it got
+ * earlier.
+ */
+ const auto diff = std::min(clocktime.Latency, clocktime.ClockTime-srcclock);
+ values[1] = nanoseconds{clocktime.Latency - diff}.count();
+ }
+ return true;
}
- if(srcclock == clocktime.ClockTime)
- values[1] = static_cast<double>(clocktime.Latency.count()) / 1000000000.0;
- else
+ break;
+
+ case AL_SAMPLE_OFFSET_CLOCK_SOFT:
+ if constexpr(std::is_same_v<T,int64_t>)
+ {
+ CheckSize(2);
+ nanoseconds srcclock{};
+ values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
+ values[1] = srcclock.count();
+ return true;
+ }
+ break;
+
+ case AL_SEC_OFFSET_LATENCY_SOFT:
+ if constexpr(std::is_same_v<T,double>)
{
- /* If the clock time incremented, reduce the latency by that much
- * since it's that much closer to the source offset it got earlier.
+ CheckSize(2);
+ /* Get the source offset with the clock time first. Then get the
+ * clock time with the device latency. Order is important.
*/
- const nanoseconds diff{clocktime.ClockTime - srcclock};
- const nanoseconds latency{clocktime.Latency - std::min(clocktime.Latency, diff)};
- values[1] = static_cast<double>(latency.count()) / 1000000000.0;
+ ClockLatency clocktime{};
+ nanoseconds srcclock{};
+ values[0] = GetSourceSecOffset(Source, Context, &srcclock);
+ {
+ std::lock_guard<std::mutex> _{device->StateLock};
+ clocktime = GetClockLatency(device, device->Backend.get());
+ }
+ if(srcclock == clocktime.ClockTime)
+ values[1] = duration_cast<seconds_d>(clocktime.Latency).count();
+ else
+ {
+ /* If the clock time incremented, reduce the latency by that
+ * much since it's that much closer to the source offset it got
+ * earlier.
+ */
+ const auto diff = std::min(clocktime.Latency, clocktime.ClockTime-srcclock);
+ values[1] = duration_cast<seconds_d>(clocktime.Latency - diff).count();
+ }
+ return true;
}
- return true;
+ break;
case AL_SEC_OFFSET_CLOCK_SOFT:
- CheckSize(2);
- values[0] = GetSourceSecOffset(Source, Context, &srcclock);
- values[1] = static_cast<double>(srcclock.count()) / 1000000000.0;
- return true;
+ if constexpr(std::is_same_v<T,double>)
+ {
+ CheckSize(2);
+ nanoseconds srcclock{};
+ values[0] = GetSourceSecOffset(Source, Context, &srcclock);
+ values[1] = duration_cast<seconds_d>(srcclock).count();
+ return true;
+ }
+ break;
case AL_POSITION:
CheckSize(3);
- values[0] = Source->Position[0];
- values[1] = Source->Position[1];
- values[2] = Source->Position[2];
+ values[0] = static_cast<T>(Source->Position[0]);
+ values[1] = static_cast<T>(Source->Position[1]);
+ values[2] = static_cast<T>(Source->Position[2]);
return true;
case AL_VELOCITY:
CheckSize(3);
- values[0] = Source->Velocity[0];
- values[1] = Source->Velocity[1];
- values[2] = Source->Velocity[2];
+ values[0] = static_cast<T>(Source->Velocity[0]);
+ values[1] = static_cast<T>(Source->Velocity[1]);
+ values[2] = static_cast<T>(Source->Velocity[2]);
return true;
case AL_DIRECTION:
CheckSize(3);
- values[0] = Source->Direction[0];
- values[1] = Source->Direction[1];
- values[2] = Source->Direction[2];
+ values[0] = static_cast<T>(Source->Direction[0]);
+ values[1] = static_cast<T>(Source->Direction[1]);
+ values[2] = static_cast<T>(Source->Direction[2]);
return true;
case AL_ORIENTATION:
CheckSize(6);
- values[0] = Source->OrientAt[0];
- values[1] = Source->OrientAt[1];
- values[2] = Source->OrientAt[2];
- values[3] = Source->OrientUp[0];
- values[4] = Source->OrientUp[1];
- values[5] = Source->OrientUp[2];
+ values[0] = static_cast<T>(Source->OrientAt[0]);
+ values[1] = static_cast<T>(Source->OrientAt[1]);
+ values[2] = static_cast<T>(Source->OrientAt[2]);
+ values[3] = static_cast<T>(Source->OrientUp[0]);
+ values[4] = static_cast<T>(Source->OrientUp[1]);
+ values[5] = static_cast<T>(Source->OrientUp[2]);
return true;
- /* 1x int */
- case AL_SOURCE_RELATIVE:
- case AL_LOOPING:
- case AL_SOURCE_STATE:
- case AL_BUFFERS_QUEUED:
- case AL_BUFFERS_PROCESSED:
- case AL_SOURCE_TYPE:
- case AL_DIRECT_FILTER_GAINHF_AUTO:
- case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
- case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
- case AL_DIRECT_CHANNELS_SOFT:
- case AL_DISTANCE_MODEL:
- case AL_SOURCE_RESAMPLER_SOFT:
- case AL_SOURCE_SPATIALIZE_SOFT:
- case AL_STEREO_MODE_SOFT:
- CheckSize(1);
- if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
- values[0] = static_cast<double>(ivals[0]);
- return err;
-
- case AL_BUFFER:
- case AL_DIRECT_FILTER:
- case AL_AUXILIARY_SEND_FILTER:
- case AL_SAMPLE_OFFSET_LATENCY_SOFT:
- case AL_SAMPLE_OFFSET_CLOCK_SOFT:
- break;
- }
-
- ERR("Unexpected property: 0x%04x\n", prop);
- Context->setError(AL_INVALID_ENUM, "Invalid source double property 0x%04x", prop);
- return false;
-}
-catch(check_exception&) {
- return false;
-}
-
-bool GetSourceiv(ALsource *const Source, ALCcontext *const Context, const SourceProp prop,
- const al::span<int> values)
-try {
- auto CheckSize = GetSizeChecker(Context, prop, values);
- double dvals[MaxValues];
- bool err;
- switch(prop)
- {
case AL_SOURCE_RELATIVE:
- CheckSize(1);
- values[0] = Source->HeadRelative;
- return true;
+ if constexpr(std::is_integral_v<T>)
+ {
+ CheckSize(1);
+ values[0] = Source->HeadRelative;
+ return true;
+ }
+ break;
case AL_LOOPING:
- CheckSize(1);
- values[0] = Source->Looping;
- return true;
+ if constexpr(std::is_integral_v<T>)
+ {
+ CheckSize(1);
+ values[0] = Source->Looping;
+ return true;
+ }
+ break;
case AL_BUFFER:
- CheckSize(1);
+ if constexpr(std::is_integral_v<T>)
{
+ CheckSize(1);
ALbufferQueueItem *BufferList{};
/* HACK: This query should technically only return the buffer set
* on a static source. However, some apps had used it to detect
@@ -2356,377 +2307,150 @@ try {
BufferList = static_cast<ALbufferQueueItem*>(Current);
}
ALbuffer *buffer{BufferList ? BufferList->mBuffer : nullptr};
- values[0] = buffer ? static_cast<int>(buffer->id) : 0;
+ values[0] = buffer ? static_cast<T>(buffer->id) : T{0};
+ return true;
}
- return true;
+ break;
case AL_SOURCE_STATE:
- CheckSize(1);
- values[0] = GetSourceState(Source, GetSourceVoice(Source, Context));
- return true;
+ if constexpr(std::is_integral_v<T>)
+ {
+ CheckSize(1);
+ values[0] = GetSourceState(Source, GetSourceVoice(Source, Context));
+ return true;
+ }
+ break;
case AL_BUFFERS_QUEUED:
- CheckSize(1);
- values[0] = static_cast<int>(Source->mQueue.size());
- return true;
-
- case AL_BUFFERS_PROCESSED:
- CheckSize(1);
- if(Source->Looping || Source->SourceType != AL_STREAMING)
+ if constexpr(std::is_integral_v<T>)
{
- /* Buffers on a looping source are in a perpetual state of PENDING,
- * so don't report any as PROCESSED
- */
- values[0] = 0;
+ CheckSize(1);
+ values[0] = static_cast<T>(Source->mQueue.size());
+ return true;
}
- else
+ break;
+
+ case AL_BUFFERS_PROCESSED:
+ if constexpr(std::is_integral_v<T>)
{
- int played{0};
- if(Source->state != AL_INITIAL)
+ CheckSize(1);
+ if(Source->Looping || Source->SourceType != AL_STREAMING)
+ {
+ /* Buffers on a looping source are in a perpetual state of
+ * PENDING, so don't report any as PROCESSED
+ */
+ values[0] = 0;
+ }
+ else
{
- const VoiceBufferItem *Current{nullptr};
- if(Voice *voice{GetSourceVoice(Source, Context)})
- Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
- for(auto &item : Source->mQueue)
+ int played{0};
+ if(Source->state != AL_INITIAL)
{
- if(&item == Current)
- break;
- ++played;
+ const VoiceBufferItem *Current{nullptr};
+ if(Voice *voice{GetSourceVoice(Source, Context)})
+ Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
+ for(auto &item : Source->mQueue)
+ {
+ if(&item == Current)
+ break;
+ ++played;
+ }
}
+ values[0] = played;
}
- values[0] = played;
+ return true;
}
- return true;
+ break;
case AL_SOURCE_TYPE:
- CheckSize(1);
- values[0] = Source->SourceType;
- return true;
-
- case AL_DIRECT_FILTER_GAINHF_AUTO:
- CheckSize(1);
- values[0] = Source->DryGainHFAuto;
- return true;
-
- case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
- CheckSize(1);
- values[0] = Source->WetGainAuto;
- return true;
-
- case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
- CheckSize(1);
- values[0] = Source->WetGainHFAuto;
- return true;
-
- case AL_DIRECT_CHANNELS_SOFT:
- CheckSize(1);
- values[0] = EnumFromDirectMode(Source->DirectChannels);
- return true;
-
- case AL_DISTANCE_MODEL:
- CheckSize(1);
- values[0] = ALenumFromDistanceModel(Source->mDistanceModel);
- return true;
-
- case AL_BYTE_LENGTH_SOFT:
- case AL_SAMPLE_LENGTH_SOFT:
- case AL_SEC_LENGTH_SOFT:
- CheckSize(1);
- values[0] = static_cast<int>(mind(GetSourceLength(Source, prop),
- std::numeric_limits<int>::max()));
- return true;
-
- case AL_SOURCE_RESAMPLER_SOFT:
- CheckSize(1);
- values[0] = static_cast<int>(Source->mResampler);
- return true;
-
- case AL_SOURCE_SPATIALIZE_SOFT:
- CheckSize(1);
- values[0] = EnumFromSpatializeMode(Source->mSpatialize);
- return true;
-
- case AL_STEREO_MODE_SOFT:
- CheckSize(1);
- values[0] = EnumFromStereoMode(Source->mStereoMode);
- return true;
-
- case AL_SAMPLE_RW_OFFSETS_SOFT:
- if(sBufferSubDataCompat)
+ if constexpr(std::is_integral_v<T>)
{
- CheckSize(2);
- const auto offset = GetSourceOffset(Source, AL_SAMPLE_OFFSET, Context);
- /* FIXME: values[1] should be ahead of values[0] by the device
- * update time. It needs to clamp or wrap the length of the buffer
- * queue.
- */
- values[0] = static_cast<int>(mind(offset, std::numeric_limits<int>::max()));
- values[1] = values[0];
+ CheckSize(1);
+ values[0] = Source->SourceType;
return true;
}
break;
- case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/
- if(sBufferSubDataCompat)
+
+ case AL_DIRECT_FILTER_GAINHF_AUTO:
+ if constexpr(std::is_integral_v<T>)
{
- CheckSize(2);
- const auto offset = GetSourceOffset(Source, AL_BYTE_OFFSET, Context);
- /* FIXME: values[1] should be ahead of values[0] by the device
- * update time. It needs to clamp or wrap the length of the buffer
- * queue.
- */
- values[0] = static_cast<int>(mind(offset, std::numeric_limits<int>::max()));
- values[1] = values[0];
+ CheckSize(1);
+ values[0] = Source->DryGainHFAuto;
return true;
}
- /*fall-through*/
-
- /* 1x float/double */
- case AL_CONE_INNER_ANGLE:
- case AL_CONE_OUTER_ANGLE:
- case AL_PITCH:
- case AL_GAIN:
- case AL_MIN_GAIN:
- case AL_MAX_GAIN:
- case AL_REFERENCE_DISTANCE:
- case AL_ROLLOFF_FACTOR:
- case AL_CONE_OUTER_GAIN:
- case AL_MAX_DISTANCE:
- case AL_SEC_OFFSET:
- case AL_SAMPLE_OFFSET:
- case AL_BYTE_OFFSET:
- case AL_DOPPLER_FACTOR:
- case AL_AIR_ABSORPTION_FACTOR:
- case AL_ROOM_ROLLOFF_FACTOR:
- case AL_CONE_OUTER_GAINHF:
- case AL_SUPER_STEREO_WIDTH_SOFT:
- CheckSize(1);
- if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false)
- values[0] = static_cast<int>(dvals[0]);
- return err;
+ break;
- /* 3x float/double */
- case AL_POSITION:
- case AL_VELOCITY:
- case AL_DIRECTION:
- CheckSize(3);
- if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false)
+ case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
+ if constexpr(std::is_integral_v<T>)
{
- values[0] = static_cast<int>(dvals[0]);
- values[1] = static_cast<int>(dvals[1]);
- values[2] = static_cast<int>(dvals[2]);
+ CheckSize(1);
+ values[0] = Source->WetGainAuto;
+ return true;
}
- return err;
+ break;
- /* 6x float/double */
- case AL_ORIENTATION:
- CheckSize(6);
- if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false)
+ case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
+ if constexpr(std::is_integral_v<T>)
{
- values[0] = static_cast<int>(dvals[0]);
- values[1] = static_cast<int>(dvals[1]);
- values[2] = static_cast<int>(dvals[2]);
- values[3] = static_cast<int>(dvals[3]);
- values[4] = static_cast<int>(dvals[4]);
- values[5] = static_cast<int>(dvals[5]);
+ CheckSize(1);
+ values[0] = Source->WetGainHFAuto;
+ return true;
}
- return err;
-
- case AL_SAMPLE_OFFSET_LATENCY_SOFT:
- case AL_SAMPLE_OFFSET_CLOCK_SOFT:
- break; /* i64 only */
- case AL_SEC_OFFSET_LATENCY_SOFT:
- case AL_SEC_OFFSET_CLOCK_SOFT:
- break; /* Double only */
- case AL_STEREO_ANGLES:
- break; /* Float/double only */
-
- case AL_DIRECT_FILTER:
- case AL_AUXILIARY_SEND_FILTER:
- break; /* ??? */
- }
-
- ERR("Unexpected property: 0x%04x\n", prop);
- Context->setError(AL_INVALID_ENUM, "Invalid source integer property 0x%04x", prop);
- return false;
-}
-catch(check_exception&) {
- return false;
-}
-
-bool GetSourcei64v(ALsource *const Source, ALCcontext *const Context, const SourceProp prop,
- const al::span<int64_t> values)
-try {
- auto CheckSize = GetSizeChecker(Context, prop, values);
- ALCdevice *device{Context->mALDevice.get()};
- ClockLatency clocktime;
- nanoseconds srcclock;
- double dvals[MaxValues];
- int ivals[MaxValues];
- bool err;
-
- switch(prop)
- {
- case AL_BYTE_LENGTH_SOFT:
- case AL_SAMPLE_LENGTH_SOFT:
- case AL_SEC_LENGTH_SOFT:
- CheckSize(1);
- values[0] = static_cast<int64_t>(GetSourceLength(Source, prop));
- return true;
+ break;
- case AL_SAMPLE_OFFSET_LATENCY_SOFT:
- CheckSize(2);
- /* Get the source offset with the clock time first. Then get the clock
- * time with the device latency. Order is important.
- */
- values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
- {
- std::lock_guard<std::mutex> _{device->StateLock};
- clocktime = GetClockLatency(device, device->Backend.get());
- }
- if(srcclock == clocktime.ClockTime)
- values[1] = clocktime.Latency.count();
- else
+ case AL_DIRECT_CHANNELS_SOFT:
+ if constexpr(std::is_integral_v<T>)
{
- /* If the clock time incremented, reduce the latency by that much
- * since it's that much closer to the source offset it got earlier.
- */
- const nanoseconds diff{clocktime.ClockTime - srcclock};
- values[1] = nanoseconds{clocktime.Latency - std::min(clocktime.Latency, diff)}.count();
+ CheckSize(1);
+ values[0] = EnumFromDirectMode(Source->DirectChannels);
+ return true;
}
- return true;
-
- case AL_SAMPLE_OFFSET_CLOCK_SOFT:
- CheckSize(2);
- values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
- values[1] = srcclock.count();
- return true;
+ break;
- case AL_SAMPLE_RW_OFFSETS_SOFT:
- if(sBufferSubDataCompat)
+ case AL_DISTANCE_MODEL:
+ if constexpr(std::is_integral_v<T>)
{
- CheckSize(2);
- /* FIXME: values[1] should be ahead of values[0] by the device
- * update time. It needs to clamp or wrap the length of the buffer
- * queue.
- */
- values[0] = static_cast<int64_t>(GetSourceOffset(Source, AL_SAMPLE_OFFSET, Context));
- values[1] = values[0];
+ CheckSize(1);
+ values[0] = ALenumFromDistanceModel(Source->mDistanceModel);
return true;
}
break;
- case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/
- if(sBufferSubDataCompat)
+
+ case AL_SOURCE_RESAMPLER_SOFT:
+ if constexpr(std::is_integral_v<T>)
{
- CheckSize(2);
- /* FIXME: values[1] should be ahead of values[0] by the device
- * update time. It needs to clamp or wrap the length of the buffer
- * queue.
- */
- values[0] = static_cast<int64_t>(GetSourceOffset(Source, AL_BYTE_OFFSET, Context));
- values[1] = values[0];
+ CheckSize(1);
+ values[0] = static_cast<T>(Source->mResampler);
return true;
}
- /*fall-through*/
-
- /* 1x float/double */
- case AL_CONE_INNER_ANGLE:
- case AL_CONE_OUTER_ANGLE:
- case AL_PITCH:
- case AL_GAIN:
- case AL_MIN_GAIN:
- case AL_MAX_GAIN:
- case AL_REFERENCE_DISTANCE:
- case AL_ROLLOFF_FACTOR:
- case AL_CONE_OUTER_GAIN:
- case AL_MAX_DISTANCE:
- case AL_SEC_OFFSET:
- case AL_SAMPLE_OFFSET:
- case AL_BYTE_OFFSET:
- case AL_DOPPLER_FACTOR:
- case AL_AIR_ABSORPTION_FACTOR:
- case AL_ROOM_ROLLOFF_FACTOR:
- case AL_CONE_OUTER_GAINHF:
- case AL_SUPER_STEREO_WIDTH_SOFT:
- CheckSize(1);
- if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false)
- values[0] = static_cast<int64_t>(dvals[0]);
- return err;
+ break;
- /* 3x float/double */
- case AL_POSITION:
- case AL_VELOCITY:
- case AL_DIRECTION:
- CheckSize(3);
- if((err=GetSourcedv(Source, Context, prop, {dvals, 3u})) != false)
+ case AL_SOURCE_SPATIALIZE_SOFT:
+ if constexpr(std::is_integral_v<T>)
{
- values[0] = static_cast<int64_t>(dvals[0]);
- values[1] = static_cast<int64_t>(dvals[1]);
- values[2] = static_cast<int64_t>(dvals[2]);
+ CheckSize(1);
+ values[0] = EnumFromSpatializeMode(Source->mSpatialize);
+ return true;
}
- return err;
+ break;
- /* 6x float/double */
- case AL_ORIENTATION:
- CheckSize(6);
- if((err=GetSourcedv(Source, Context, prop, {dvals, 6u})) != false)
+ case AL_STEREO_MODE_SOFT:
+ if constexpr(std::is_integral_v<T>)
{
- values[0] = static_cast<int64_t>(dvals[0]);
- values[1] = static_cast<int64_t>(dvals[1]);
- values[2] = static_cast<int64_t>(dvals[2]);
- values[3] = static_cast<int64_t>(dvals[3]);
- values[4] = static_cast<int64_t>(dvals[4]);
- values[5] = static_cast<int64_t>(dvals[5]);
+ CheckSize(1);
+ values[0] = EnumFromStereoMode(Source->mStereoMode);
+ return true;
}
- return err;
-
- /* 1x int */
- case AL_SOURCE_RELATIVE:
- case AL_LOOPING:
- case AL_SOURCE_STATE:
- case AL_BUFFERS_QUEUED:
- case AL_BUFFERS_PROCESSED:
- case AL_SOURCE_TYPE:
- case AL_DIRECT_FILTER_GAINHF_AUTO:
- case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
- case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
- case AL_DIRECT_CHANNELS_SOFT:
- case AL_DISTANCE_MODEL:
- case AL_SOURCE_RESAMPLER_SOFT:
- case AL_SOURCE_SPATIALIZE_SOFT:
- case AL_STEREO_MODE_SOFT:
- CheckSize(1);
- if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
- values[0] = ivals[0];
- return err;
+ break;
- /* 1x uint */
- case AL_BUFFER:
case AL_DIRECT_FILTER:
- CheckSize(1);
- if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false)
- values[0] = static_cast<ALuint>(ivals[0]);
- return err;
-
- /* 3x uint */
case AL_AUXILIARY_SEND_FILTER:
- CheckSize(3);
- if((err=GetSourceiv(Source, Context, prop, {ivals, 3u})) != false)
- {
- values[0] = static_cast<ALuint>(ivals[0]);
- values[1] = static_cast<ALuint>(ivals[1]);
- values[2] = static_cast<ALuint>(ivals[2]);
- }
- return err;
-
- case AL_SEC_OFFSET_LATENCY_SOFT:
- case AL_SEC_OFFSET_CLOCK_SOFT:
- break; /* Double only */
- case AL_STEREO_ANGLES:
- break; /* Float/double only */
+ break;
}
- ERR("Unexpected property: 0x%04x\n", prop);
- Context->setError(AL_INVALID_ENUM, "Invalid source integer64 property 0x%04x", prop);
+ ERR("Unexpected %s query property: 0x%04x\n", PropType<T>::Name(), prop);
+ Context->setError(AL_INVALID_ENUM, "Invalid source %s query property 0x%04x",
+ PropType<T>::Name(), prop);
return false;
}
catch(check_exception&) {
@@ -2905,12 +2629,9 @@ void StartSources(ALCcontext *const context, const al::span<ALsource*> srchandle
} // namespace
-AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGenSources, ALsizei, ALuint*)
+FORCE_ALIGN void AL_APIENTRY alGenSourcesDirect(ALCcontext *context, ALsizei n, ALuint *sources) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Generating %d sources", n);
if(n <= 0) UNLIKELY return;
@@ -2923,7 +2644,7 @@ START_API_FUNC
device->SourcesMax, context->mNumSources, n);
return;
}
- if(!EnsureSources(context.get(), static_cast<ALuint>(n)))
+ if(!EnsureSources(context, static_cast<ALuint>(n)))
{
context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d source%s", n, (n==1)?"":"s");
return;
@@ -2931,36 +2652,33 @@ START_API_FUNC
if(n == 1)
{
- ALsource *source{AllocSource(context.get())};
+ ALsource *source{AllocSource(context)};
sources[0] = source->id;
#ifdef ALSOFT_EAX
- source->eaxInitialize(context.get());
+ source->eaxInitialize(context);
#endif // ALSOFT_EAX
}
else
{
- al::vector<ALuint> ids;
+ std::vector<ALuint> ids;
ids.reserve(static_cast<ALuint>(n));
do {
- ALsource *source{AllocSource(context.get())};
+ ALsource *source{AllocSource(context)};
ids.emplace_back(source->id);
#ifdef ALSOFT_EAX
- source->eaxInitialize(context.get());
+ source->eaxInitialize(context);
#endif // ALSOFT_EAX
} while(--n);
std::copy(ids.cbegin(), ids.cend(), sources);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alDeleteSources, ALsizei, const ALuint*)
+FORCE_ALIGN void AL_APIENTRY alDeleteSourcesDirect(ALCcontext *context, ALsizei n,
+ const ALuint *sources) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Deleting %d sources", n);
if(n <= 0) UNLIKELY return;
@@ -2968,8 +2686,8 @@ START_API_FUNC
std::lock_guard<std::mutex> _{context->mSourceLock};
/* Check that all Sources are valid */
- auto validate_source = [&context](const ALuint sid) -> bool
- { return LookupSource(context.get(), sid) != nullptr; };
+ auto validate_source = [context](const ALuint sid) -> bool
+ { return LookupSource(context, sid) != nullptr; };
const ALuint *sources_end = sources + n;
auto invsrc = std::find_if_not(sources, sources_end, validate_source);
@@ -2979,577 +2697,462 @@ START_API_FUNC
/* All good. Delete source IDs. */
auto delete_source = [&context](const ALuint sid) -> void
{
- ALsource *src{LookupSource(context.get(), sid)};
- if(src) FreeSource(context.get(), src);
+ ALsource *src{LookupSource(context, sid)};
+ if(src) FreeSource(context, src);
};
std::for_each(sources, sources_end, delete_source);
}
-END_API_FUNC
-AL_API ALboolean AL_APIENTRY alIsSource(ALuint source)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsSource, ALuint)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsSourceDirect(ALCcontext *context, ALuint source) noexcept
{
- ContextRef context{GetContextRef()};
- if(context) LIKELY
- {
- std::lock_guard<std::mutex> _{context->mSourceLock};
- if(LookupSource(context.get(), source) != nullptr)
- return AL_TRUE;
- }
+ std::lock_guard<std::mutex> _{context->mSourceLock};
+ if(LookupSource(context, source) != nullptr)
+ return AL_TRUE;
return AL_FALSE;
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alSourcef, ALuint, ALenum, ALfloat)
+FORCE_ALIGN void AL_APIENTRY alSourcefDirect(ALCcontext *context, ALuint source, ALenum param,
+ ALfloat value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mSourceLock};
- ALsource *Source = LookupSource(context.get(), source);
+ ALsource *Source = LookupSource(context, source);
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else
- SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+
+ SetProperty<float>(Source, context, static_cast<SourceProp>(param), al::span{&value, 1u});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
-START_API_FUNC
+AL_API DECL_FUNC5(void, alSource3f, ALuint, ALenum, ALfloat, ALfloat, ALfloat)
+FORCE_ALIGN void AL_APIENTRY alSource3fDirect(ALCcontext *context, ALuint source, ALenum param,
+ ALfloat value1, ALfloat value2, ALfloat value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mSourceLock};
- ALsource *Source = LookupSource(context.get(), source);
+ ALsource *Source = LookupSource(context, source);
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else
- {
- const float fvals[3]{ value1, value2, value3 };
- SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
- }
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+
+ const float fvals[3]{ value1, value2, value3 };
+ SetProperty<float>(Source, context, static_cast<SourceProp>(param), al::span{fvals});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alSourcefv, ALuint, ALenum, const ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alSourcefvDirect(ALCcontext *context, ALuint source, ALenum param,
+ const ALfloat *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mSourceLock};
- ALsource *Source = LookupSource(context.get(), source);
+ ALsource *Source = LookupSource(context, source);
if(!Source) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
if(!values) UNLIKELY
return context->setError(AL_INVALID_VALUE, "NULL pointer");
const ALuint count{FloatValsByProp(param)};
- SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {values, count});
+ SetProperty(Source, context, static_cast<SourceProp>(param), al::span{values, count});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value)
-START_API_FUNC
+AL_API DECL_FUNCEXT3(void, alSourced,SOFT, ALuint, ALenum, ALdouble)
+FORCE_ALIGN void AL_APIENTRY alSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param,
+ ALdouble value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mSourceLock};
- ALsource *Source = LookupSource(context.get(), source);
+ ALsource *Source = LookupSource(context, source);
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else
- {
- const float fval[1]{static_cast<float>(value)};
- SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fval);
- }
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+
+ SetProperty<double>(Source, context, static_cast<SourceProp>(param), al::span{&value, 1});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3)
-START_API_FUNC
+AL_API DECL_FUNCEXT5(void, alSource3d,SOFT, ALuint, ALenum, ALdouble, ALdouble, ALdouble)
+FORCE_ALIGN void AL_APIENTRY alSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param,
+ ALdouble value1, ALdouble value2, ALdouble value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mSourceLock};
- ALsource *Source = LookupSource(context.get(), source);
+ ALsource *Source = LookupSource(context, source);
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else
- {
- const float fvals[3]{static_cast<float>(value1), static_cast<float>(value2),
- static_cast<float>(value3)};
- SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), fvals);
- }
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+
+ const double dvals[3]{value1, value2, value3};
+ SetProperty<double>(Source, context, static_cast<SourceProp>(param), al::span{dvals});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values)
-START_API_FUNC
+AL_API DECL_FUNCEXT3(void, alSourcedv,SOFT, ALuint, ALenum, const ALdouble*)
+FORCE_ALIGN void AL_APIENTRY alSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param,
+ const ALdouble *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mSourceLock};
- ALsource *Source = LookupSource(context.get(), source);
+ ALsource *Source = LookupSource(context, source);
if(!Source) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
if(!values) UNLIKELY
return context->setError(AL_INVALID_VALUE, "NULL pointer");
const ALuint count{DoubleValsByProp(param)};
- float fvals[MaxValues];
- std::copy_n(values, count, fvals);
- SetSourcefv(Source, context.get(), static_cast<SourceProp>(param), {fvals, count});
+ SetProperty(Source, context, static_cast<SourceProp>(param), al::span{values, count});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alSourcei, ALuint, ALenum, ALint)
+FORCE_ALIGN void AL_APIENTRY alSourceiDirect(ALCcontext *context, ALuint source, ALenum param,
+ ALint value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mSourceLock};
- ALsource *Source = LookupSource(context.get(), source);
+ ALsource *Source = LookupSource(context, source);
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else
- SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+
+ SetProperty<int>(Source, context, static_cast<SourceProp>(param), al::span{&value, 1u});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3)
-START_API_FUNC
+AL_API DECL_FUNC5(void, alSource3i, ALuint, ALenum, ALint, ALint, ALint)
+FORCE_ALIGN void AL_APIENTRY alSource3iDirect(ALCcontext *context, ALuint source, ALenum param,
+ ALint value1, ALint value2, ALint value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mSourceLock};
- ALsource *Source = LookupSource(context.get(), source);
+ ALsource *Source = LookupSource(context, source);
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else
- {
- const int ivals[3]{ value1, value2, value3 };
- SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals);
- }
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+
+ const int ivals[3]{ value1, value2, value3 };
+ SetProperty<int>(Source, context, static_cast<SourceProp>(param), al::span{ivals});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alSourceiv, ALuint, ALenum, const ALint*)
+FORCE_ALIGN void AL_APIENTRY alSourceivDirect(ALCcontext *context, ALuint source, ALenum param,
+ const ALint *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mSourceLock};
- ALsource *Source = LookupSource(context.get(), source);
+ ALsource *Source = LookupSource(context, source);
if(!Source) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
if(!values) UNLIKELY
return context->setError(AL_INVALID_VALUE, "NULL pointer");
const ALuint count{IntValsByProp(param)};
- SetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, count});
+ SetProperty(Source, context, static_cast<SourceProp>(param), al::span{values, count});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value)
-START_API_FUNC
+AL_API DECL_FUNCEXT3(void, alSourcei64,SOFT, ALuint, ALenum, ALint64SOFT)
+FORCE_ALIGN void AL_APIENTRY alSourcei64DirectSOFT(ALCcontext *context, ALuint source,
+ ALenum param, ALint64SOFT value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else
- SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {&value, 1u});
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+
+ SetProperty<int64_t>(Source, context, static_cast<SourceProp>(param), al::span{&value, 1u});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3)
-START_API_FUNC
+AL_API DECL_FUNCEXT5(void, alSource3i64,SOFT, ALuint, ALenum, ALint64SOFT, ALint64SOFT, ALint64SOFT)
+FORCE_ALIGN void AL_APIENTRY alSource3i64DirectSOFT(ALCcontext *context, ALuint source,
+ ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else
- {
- const int64_t i64vals[3]{ value1, value2, value3 };
- SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals);
- }
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+
+ const int64_t i64vals[3]{ value1, value2, value3 };
+ SetProperty<int64_t>(Source, context, static_cast<SourceProp>(param), al::span{i64vals});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values)
-START_API_FUNC
+AL_API DECL_FUNCEXT3(void, alSourcei64v,SOFT, ALuint, ALenum, const ALint64SOFT*)
+FORCE_ALIGN void AL_APIENTRY alSourcei64vDirectSOFT(ALCcontext *context, ALuint source,
+ ALenum param, const ALint64SOFT *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
std::lock_guard<std::mutex> __{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
if(!values) UNLIKELY
return context->setError(AL_INVALID_VALUE, "NULL pointer");
const ALuint count{Int64ValsByProp(param)};
- SetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, count});
+ SetProperty(Source, context, static_cast<SourceProp>(param), al::span{values, count});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetSourcef, ALuint, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetSourcefDirect(ALCcontext *context, ALuint source, ALenum param,
+ ALfloat *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else if(!value) UNLIKELY
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else
- {
- double dval[1];
- if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dval))
- *value = static_cast<float>(dval[0]);
- }
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+ if(!value) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+
+ std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param), al::span{value, 1});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
-START_API_FUNC
+AL_API DECL_FUNC5(void, alGetSource3f, ALuint, ALenum, ALfloat*, ALfloat*, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetSource3fDirect(ALCcontext *context, ALuint source, ALenum param,
+ ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else if(!(value1 && value2 && value3)) UNLIKELY
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+ if(!(value1 && value2 && value3)) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+
+ float fvals[3];
+ if(GetProperty<float>(Source, context, static_cast<SourceProp>(param), al::span{fvals}))
{
- double dvals[3];
- if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
- {
- *value1 = static_cast<float>(dvals[0]);
- *value2 = static_cast<float>(dvals[1]);
- *value3 = static_cast<float>(dvals[2]);
- }
+ *value1 = fvals[0];
+ *value2 = fvals[1];
+ *value3 = fvals[2];
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetSourcefv, ALuint, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetSourcefvDirect(ALCcontext *context, ALuint source, ALenum param,
+ ALfloat *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
if(!values) UNLIKELY
return context->setError(AL_INVALID_VALUE, "NULL pointer");
const ALuint count{FloatValsByProp(param)};
- double dvals[MaxValues];
- if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {dvals, count}))
- std::copy_n(dvals, count, values);
+ std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param),
+ al::span{values, count});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value)
-START_API_FUNC
+AL_API DECL_FUNCEXT3(void, alGetSourced,SOFT, ALuint, ALenum, ALdouble*)
+FORCE_ALIGN void AL_APIENTRY alGetSourcedDirectSOFT(ALCcontext *context, ALuint source,
+ ALenum param, ALdouble *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else if(!value) UNLIKELY
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else
- GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+ if(!value) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+
+ std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param), al::span{value, 1});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3)
-START_API_FUNC
+AL_API DECL_FUNCEXT5(void, alGetSource3d,SOFT, ALuint, ALenum, ALdouble*, ALdouble*, ALdouble*)
+FORCE_ALIGN void AL_APIENTRY alGetSource3dDirectSOFT(ALCcontext *context, ALuint source,
+ ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else if(!(value1 && value2 && value3)) UNLIKELY
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+ if(!(value1 && value2 && value3)) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+
+ double dvals[3];
+ if(GetProperty<double>(Source, context, static_cast<SourceProp>(param), al::span{dvals}))
{
- double dvals[3];
- if(GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), dvals))
- {
- *value1 = dvals[0];
- *value2 = dvals[1];
- *value3 = dvals[2];
- }
+ *value1 = dvals[0];
+ *value2 = dvals[1];
+ *value3 = dvals[2];
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values)
-START_API_FUNC
+AL_API DECL_FUNCEXT3(void, alGetSourcedv,SOFT, ALuint, ALenum, ALdouble*)
+FORCE_ALIGN void AL_APIENTRY alGetSourcedvDirectSOFT(ALCcontext *context, ALuint source,
+ ALenum param, ALdouble *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
if(!values) UNLIKELY
return context->setError(AL_INVALID_VALUE, "NULL pointer");
const ALuint count{DoubleValsByProp(param)};
- GetSourcedv(Source, context.get(), static_cast<SourceProp>(param), {values, count});
+ std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param),
+ al::span{values, count});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetSourcei, ALuint, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetSourceiDirect(ALCcontext *context, ALuint source, ALenum param,
+ ALint *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else if(!value) UNLIKELY
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else
- GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+ if(!value) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+
+ std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param), al::span{value, 1});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3)
-START_API_FUNC
+AL_API DECL_FUNC5(void, alGetSource3i, ALuint, ALenum, ALint*, ALint*, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetSource3iDirect(ALCcontext *context, ALuint source, ALenum param,
+ ALint *value1, ALint *value2, ALint *value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else if(!(value1 && value2 && value3)) UNLIKELY
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+ if(!(value1 && value2 && value3)) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+
+ int ivals[3];
+ if(GetProperty<int>(Source, context, static_cast<SourceProp>(param), al::span{ivals}))
{
- int ivals[3];
- if(GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), ivals))
- {
- *value1 = ivals[0];
- *value2 = ivals[1];
- *value3 = ivals[2];
- }
+ *value1 = ivals[0];
+ *value2 = ivals[1];
+ *value3 = ivals[2];
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alGetSourceiv, ALuint, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetSourceivDirect(ALCcontext *context, ALuint source, ALenum param,
+ ALint *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
if(!values) UNLIKELY
return context->setError(AL_INVALID_VALUE, "NULL pointer");
const ALuint count{IntValsByProp(param)};
- GetSourceiv(Source, context.get(), static_cast<SourceProp>(param), {values, count});
+ std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param),
+ al::span{values, count});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value)
-START_API_FUNC
+AL_API DECL_FUNCEXT3(void, alGetSourcei64,SOFT, ALuint, ALenum, ALint64SOFT*)
+FORCE_ALIGN void AL_APIENTRY alGetSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else if(!value) UNLIKELY
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else
- GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {value, 1u});
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+ if(!value) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+
+ std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param), al::span{value, 1});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3)
-START_API_FUNC
+AL_API DECL_FUNCEXT5(void, alGetSource3i64,SOFT, ALuint, ALenum, ALint64SOFT*, ALint64SOFT*, ALint64SOFT*)
+FORCE_ALIGN void AL_APIENTRY alGetSource3i64DirectSOFT(ALCcontext *context, ALuint source,
+ ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
- context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- else if(!(value1 && value2 && value3)) UNLIKELY
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
+ if(!(value1 && value2 && value3)) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+
+ int64_t i64vals[3];
+ if(GetProperty<int64_t>(Source, context, static_cast<SourceProp>(param), al::span{i64vals}))
{
- int64_t i64vals[3];
- if(GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), i64vals))
- {
- *value1 = i64vals[0];
- *value2 = i64vals[1];
- *value3 = i64vals[2];
- }
+ *value1 = i64vals[0];
+ *value2 = i64vals[1];
+ *value3 = i64vals[2];
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values)
-START_API_FUNC
+AL_API DECL_FUNCEXT3(void, alGetSourcei64v,SOFT, ALuint, ALenum, ALint64SOFT*)
+FORCE_ALIGN void AL_APIENTRY alGetSourcei64vDirectSOFT(ALCcontext *context, ALuint source,
+ ALenum param, ALint64SOFT *values) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *Source{LookupSource(context.get(), source)};
+ ALsource *Source{LookupSource(context, source)};
if(!Source) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
if(!values) UNLIKELY
return context->setError(AL_INVALID_VALUE, "NULL pointer");
const ALuint count{Int64ValsByProp(param)};
- GetSourcei64v(Source, context.get(), static_cast<SourceProp>(param), {values, count});
+ std::ignore = GetProperty(Source, context, static_cast<SourceProp>(param),
+ al::span{values, count});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourcePlay(ALuint source)
-START_API_FUNC
+AL_API DECL_FUNC1(void, alSourcePlay, ALuint)
+FORCE_ALIGN void AL_APIENTRY alSourcePlayDirect(ALCcontext *context, ALuint source) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *srchandle{LookupSource(context.get(), source)};
+ ALsource *srchandle{LookupSource(context, source)};
if(!srchandle)
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- StartSources(context.get(), {&srchandle, 1});
+ StartSources(context, {&srchandle, 1});
}
-END_API_FUNC
-void AL_APIENTRY alSourcePlayAtTimeSOFT(ALuint source, ALint64SOFT start_time)
-START_API_FUNC
+FORCE_ALIGN DECL_FUNCEXT2(void, alSourcePlayAtTime,SOFT, ALuint, ALint64SOFT)
+FORCE_ALIGN void AL_APIENTRY alSourcePlayAtTimeDirectSOFT(ALCcontext *context, ALuint source,
+ ALint64SOFT start_time) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(start_time < 0) UNLIKELY
return context->setError(AL_INVALID_VALUE, "Invalid time point %" PRId64, start_time);
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *srchandle{LookupSource(context.get(), source)};
+ ALsource *srchandle{LookupSource(context, source)};
if(!srchandle)
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source);
- StartSources(context.get(), {&srchandle, 1}, nanoseconds{start_time});
+ StartSources(context, {&srchandle, 1}, nanoseconds{start_time});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alSourcePlayv, ALsizei, const ALuint*)
+FORCE_ALIGN void AL_APIENTRY alSourcePlayvDirect(ALCcontext *context, ALsizei n,
+ const ALuint *sources) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Playing %d sources", n);
if(n <= 0) UNLIKELY return;
- al::vector<ALsource*> extra_sources;
+ std::vector<ALsource*> extra_sources;
std::array<ALsource*,8> source_storage;
al::span<ALsource*> srchandles;
if(static_cast<ALuint>(n) <= source_storage.size()) LIKELY
- srchandles = {source_storage.data(), static_cast<ALuint>(n)};
+ srchandles = al::span{source_storage}.first(static_cast<ALuint>(n));
else
{
extra_sources.resize(static_cast<ALuint>(n));
- srchandles = {extra_sources.data(), extra_sources.size()};
+ srchandles = extra_sources;
}
std::lock_guard<std::mutex> _{context->mSourceLock};
for(auto &srchdl : srchandles)
{
- srchdl = LookupSource(context.get(), *sources);
+ srchdl = LookupSource(context, *sources);
if(!srchdl) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources);
++sources;
}
- StartSources(context.get(), srchandles);
+ StartSources(context, srchandles);
}
-END_API_FUNC
-void AL_APIENTRY alSourcePlayAtTimevSOFT(ALsizei n, const ALuint *sources, ALint64SOFT start_time)
-START_API_FUNC
+FORCE_ALIGN DECL_FUNCEXT3(void, alSourcePlayAtTimev,SOFT, ALsizei, const ALuint*, ALint64SOFT)
+FORCE_ALIGN void AL_APIENTRY alSourcePlayAtTimevDirectSOFT(ALCcontext *context, ALsizei n,
+ const ALuint *sources, ALint64SOFT start_time) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Playing %d sources", n);
if(n <= 0) UNLIKELY return;
@@ -3557,61 +3160,57 @@ START_API_FUNC
if(start_time < 0) UNLIKELY
return context->setError(AL_INVALID_VALUE, "Invalid time point %" PRId64, start_time);
- al::vector<ALsource*> extra_sources;
+ std::vector<ALsource*> extra_sources;
std::array<ALsource*,8> source_storage;
al::span<ALsource*> srchandles;
if(static_cast<ALuint>(n) <= source_storage.size()) LIKELY
- srchandles = {source_storage.data(), static_cast<ALuint>(n)};
+ srchandles = al::span{source_storage}.first(static_cast<ALuint>(n));
else
{
extra_sources.resize(static_cast<ALuint>(n));
- srchandles = {extra_sources.data(), extra_sources.size()};
+ srchandles = extra_sources;
}
std::lock_guard<std::mutex> _{context->mSourceLock};
for(auto &srchdl : srchandles)
{
- srchdl = LookupSource(context.get(), *sources);
+ srchdl = LookupSource(context, *sources);
if(!srchdl)
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources);
++sources;
}
- StartSources(context.get(), srchandles, nanoseconds{start_time});
+ StartSources(context, srchandles, nanoseconds{start_time});
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourcePause(ALuint source)
-START_API_FUNC
-{ alSourcePausev(1, &source); }
-END_API_FUNC
+AL_API DECL_FUNC1(void, alSourcePause, ALuint)
+FORCE_ALIGN void AL_APIENTRY alSourcePauseDirect(ALCcontext *context, ALuint source) noexcept
+{ alSourcePausevDirect(context, 1, &source); }
-AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alSourcePausev, ALsizei, const ALuint*)
+FORCE_ALIGN void AL_APIENTRY alSourcePausevDirect(ALCcontext *context, ALsizei n,
+ const ALuint *sources) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Pausing %d sources", n);
if(n <= 0) UNLIKELY return;
- al::vector<ALsource*> extra_sources;
+ std::vector<ALsource*> extra_sources;
std::array<ALsource*,8> source_storage;
al::span<ALsource*> srchandles;
if(static_cast<ALuint>(n) <= source_storage.size()) LIKELY
- srchandles = {source_storage.data(), static_cast<ALuint>(n)};
+ srchandles = al::span{source_storage}.first(static_cast<ALuint>(n));
else
{
extra_sources.resize(static_cast<ALuint>(n));
- srchandles = {extra_sources.data(), extra_sources.size()};
+ srchandles = extra_sources;
}
std::lock_guard<std::mutex> _{context->mSourceLock};
for(auto &srchdl : srchandles)
{
- srchdl = LookupSource(context.get(), *sources);
+ srchdl = LookupSource(context, *sources);
if(!srchdl)
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources);
++sources;
@@ -3624,14 +3223,14 @@ START_API_FUNC
VoiceChange *tail{}, *cur{};
for(ALsource *source : srchandles)
{
- Voice *voice{GetSourceVoice(source, context.get())};
+ Voice *voice{GetSourceVoice(source, context)};
if(GetSourceState(source, voice) == AL_PLAYING)
{
if(!cur)
- cur = tail = GetVoiceChanger(context.get());
+ cur = tail = GetVoiceChanger(context);
else
{
- cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
+ cur->mNext.store(GetVoiceChanger(context), std::memory_order_relaxed);
cur = cur->mNext.load(std::memory_order_relaxed);
}
cur->mVoice = voice;
@@ -3641,7 +3240,7 @@ START_API_FUNC
}
if(tail) LIKELY
{
- SendVoiceChanges(context.get(), tail);
+ SendVoiceChanges(context, tail);
/* Second, now that the voice changes have been sent, because it's
* possible that the voice stopped after it was detected playing and
* before the voice got paused, recheck that the source is still
@@ -3649,45 +3248,41 @@ START_API_FUNC
*/
for(ALsource *source : srchandles)
{
- Voice *voice{GetSourceVoice(source, context.get())};
+ Voice *voice{GetSourceVoice(source, context)};
if(GetSourceState(source, voice) == AL_PLAYING)
source->state = AL_PAUSED;
}
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourceStop(ALuint source)
-START_API_FUNC
-{ alSourceStopv(1, &source); }
-END_API_FUNC
+AL_API DECL_FUNC1(void, alSourceStop, ALuint)
+FORCE_ALIGN void AL_APIENTRY alSourceStopDirect(ALCcontext *context, ALuint source) noexcept
+{ alSourceStopvDirect(context, 1, &source); }
-AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alSourceStopv, ALsizei, const ALuint*)
+FORCE_ALIGN void AL_APIENTRY alSourceStopvDirect(ALCcontext *context, ALsizei n,
+ const ALuint *sources) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Stopping %d sources", n);
if(n <= 0) UNLIKELY return;
- al::vector<ALsource*> extra_sources;
+ std::vector<ALsource*> extra_sources;
std::array<ALsource*,8> source_storage;
al::span<ALsource*> srchandles;
if(static_cast<ALuint>(n) <= source_storage.size()) LIKELY
- srchandles = {source_storage.data(), static_cast<ALuint>(n)};
+ srchandles = al::span{source_storage}.first(static_cast<ALuint>(n));
else
{
extra_sources.resize(static_cast<ALuint>(n));
- srchandles = {extra_sources.data(), extra_sources.size()};
+ srchandles = extra_sources;
}
std::lock_guard<std::mutex> _{context->mSourceLock};
for(auto &srchdl : srchandles)
{
- srchdl = LookupSource(context.get(), *sources);
+ srchdl = LookupSource(context, *sources);
if(!srchdl)
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources);
++sources;
@@ -3696,13 +3291,13 @@ START_API_FUNC
VoiceChange *tail{}, *cur{};
for(ALsource *source : srchandles)
{
- if(Voice *voice{GetSourceVoice(source, context.get())})
+ if(Voice *voice{GetSourceVoice(source, context)})
{
if(!cur)
- cur = tail = GetVoiceChanger(context.get());
+ cur = tail = GetVoiceChanger(context);
else
{
- cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
+ cur->mNext.store(GetVoiceChanger(context), std::memory_order_relaxed);
cur = cur->mNext.load(std::memory_order_relaxed);
}
voice->mPendingChange.store(true, std::memory_order_relaxed);
@@ -3713,44 +3308,40 @@ START_API_FUNC
}
source->Offset = 0.0;
source->OffsetType = AL_NONE;
- source->VoiceIdx = INVALID_VOICE_IDX;
+ source->VoiceIdx = InvalidVoiceIndex;
}
if(tail) LIKELY
- SendVoiceChanges(context.get(), tail);
+ SendVoiceChanges(context, tail);
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourceRewind(ALuint source)
-START_API_FUNC
-{ alSourceRewindv(1, &source); }
-END_API_FUNC
+AL_API DECL_FUNC1(void, alSourceRewind, ALuint)
+FORCE_ALIGN void AL_APIENTRY alSourceRewindDirect(ALCcontext *context, ALuint source) noexcept
+{ alSourceRewindvDirect(context, 1, &source); }
-AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alSourceRewindv, ALsizei, const ALuint*)
+FORCE_ALIGN void AL_APIENTRY alSourceRewindvDirect(ALCcontext *context, ALsizei n,
+ const ALuint *sources) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(n < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Rewinding %d sources", n);
if(n <= 0) UNLIKELY return;
- al::vector<ALsource*> extra_sources;
+ std::vector<ALsource*> extra_sources;
std::array<ALsource*,8> source_storage;
al::span<ALsource*> srchandles;
if(static_cast<ALuint>(n) <= source_storage.size()) LIKELY
- srchandles = {source_storage.data(), static_cast<ALuint>(n)};
+ srchandles = al::span{source_storage}.first(static_cast<ALuint>(n));
else
{
extra_sources.resize(static_cast<ALuint>(n));
- srchandles = {extra_sources.data(), extra_sources.size()};
+ srchandles = extra_sources;
}
std::lock_guard<std::mutex> _{context->mSourceLock};
for(auto &srchdl : srchandles)
{
- srchdl = LookupSource(context.get(), *sources);
+ srchdl = LookupSource(context, *sources);
if(!srchdl)
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources);
++sources;
@@ -3759,14 +3350,14 @@ START_API_FUNC
VoiceChange *tail{}, *cur{};
for(ALsource *source : srchandles)
{
- Voice *voice{GetSourceVoice(source, context.get())};
+ Voice *voice{GetSourceVoice(source, context)};
if(source->state != AL_INITIAL)
{
if(!cur)
- cur = tail = GetVoiceChanger(context.get());
+ cur = tail = GetVoiceChanger(context);
else
{
- cur->mNext.store(GetVoiceChanger(context.get()), std::memory_order_relaxed);
+ cur->mNext.store(GetVoiceChanger(context), std::memory_order_relaxed);
cur = cur->mNext.load(std::memory_order_relaxed);
}
if(voice)
@@ -3778,26 +3369,23 @@ START_API_FUNC
}
source->Offset = 0.0;
source->OffsetType = AL_NONE;
- source->VoiceIdx = INVALID_VOICE_IDX;
+ source->VoiceIdx = InvalidVoiceIndex;
}
if(tail) LIKELY
- SendVoiceChanges(context.get(), tail);
+ SendVoiceChanges(context, tail);
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alSourceQueueBuffers, ALuint, ALsizei, const ALuint*)
+FORCE_ALIGN void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALuint src,
+ ALsizei nb, const ALuint *buffers) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(nb < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Queueing %d buffers", nb);
if(nb <= 0) UNLIKELY return;
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *source{LookupSource(context.get(),src)};
+ ALsource *source{LookupSource(context,src)};
if(!source) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", src);
@@ -3912,20 +3500,17 @@ START_API_FUNC
(iter-1)->mNext.store(al::to_address(iter), std::memory_order_release);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
-START_API_FUNC
+AL_API DECL_FUNC3(void, alSourceUnqueueBuffers, ALuint, ALsizei, ALuint*)
+FORCE_ALIGN void AL_APIENTRY alSourceUnqueueBuffersDirect(ALCcontext *context, ALuint src,
+ ALsizei nb, ALuint *buffers) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(nb < 0) UNLIKELY
context->setError(AL_INVALID_VALUE, "Unqueueing %d buffers", nb);
if(nb <= 0) UNLIKELY return;
std::lock_guard<std::mutex> _{context->mSourceLock};
- ALsource *source{LookupSource(context.get(),src)};
+ ALsource *source{LookupSource(context,src)};
if(!source) UNLIKELY
return context->setError(AL_INVALID_NAME, "Invalid source ID %u", src);
@@ -3940,7 +3525,7 @@ START_API_FUNC
if(source->state != AL_INITIAL) LIKELY
{
VoiceBufferItem *Current{nullptr};
- if(Voice *voice{GetSourceVoice(source, context.get())})
+ if(Voice *voice{GetSourceVoice(source, context)})
Current = voice->mCurrentBuffer.load(std::memory_order_relaxed);
for(auto &item : source->mQueue)
{
@@ -3965,18 +3550,15 @@ START_API_FUNC
source->mQueue.pop_front();
} while(--nb);
}
-END_API_FUNC
-AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint, ALsizei, const ALuint*)
-START_API_FUNC
+AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint, ALsizei, const ALuint*) noexcept
{
ContextRef context{GetContextRef()};
if(!context) UNLIKELY return;
context->setError(AL_INVALID_OPERATION, "alSourceQueueBufferLayersSOFT not supported");
}
-END_API_FUNC
ALsource::ALsource()
@@ -4028,14 +3610,29 @@ void UpdateAllSourceProps(ALCcontext *context)
}
}
+void ALsource::SetName(ALCcontext *context, ALuint id, std::string_view name)
+{
+ std::lock_guard<std::mutex> _{context->mSourceLock};
+
+ auto source = LookupSource(context, id);
+ if(!source) UNLIKELY
+ return context->setError(AL_INVALID_NAME, "Invalid source ID %u", id);
+
+ context->mSourceNames.insert_or_assign(id, name);
+}
+
+
SourceSubList::~SourceSubList()
{
+ if(!Sources)
+ return;
+
uint64_t usemask{~FreeMask};
while(usemask)
{
const int idx{al::countr_zero(usemask)};
usemask &= ~(1_u64 << idx);
- al::destroy_at(Sources+idx);
+ std::destroy_at(Sources+idx);
}
FreeMask = ~usemask;
al_free(Sources);
@@ -4097,7 +3694,8 @@ ALsource* ALsource::EaxLookupSource(ALCcontext& al_context, ALuint source_id) no
void ALsource::eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept
{
- for (auto i = size_t{}; i < EAX_MAX_FXSLOTS; ++i) {
+ for(size_t i{0};i < EAX_MAX_FXSLOTS;++i)
+ {
auto& send = sends[i];
send.guidReceivingFXSlotID = *(ids[i]);
send.lSend = EAXSOURCE_DEFAULTSEND;
@@ -4209,7 +3807,8 @@ void ALsource::eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noex
void ALsource::eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept
{
- for (auto i = size_t{}; i < eax_max_speakers; ++i) {
+ for(size_t i{0};i < eax_max_speakers;++i)
+ {
auto& speaker_level = speaker_levels[i];
speaker_level.lSpeakerID = static_cast<long>(EAXSPEAKER_FRONT_LEFT + i);
speaker_level.lLevel = EAXSOURCE_DEFAULTSPEAKERLEVEL;
@@ -4251,7 +3850,7 @@ void ALsource::eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept
else
{
dst.source.ulFlags &= ~EAXSOURCEFLAGS_ROOMAUTO;
- dst.sends[0].lSend = clamp(static_cast<long>(gain_to_level_mb(src.fMix)),
+ dst.sends[0].lSend = std::clamp(static_cast<long>(gain_to_level_mb(src.fMix)),
EAXSOURCE_MINSEND, EAXSOURCE_MAXSEND);
}
}
@@ -4280,7 +3879,7 @@ void ALsource::eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept
dst.source.ulFlags = src.dwFlags;
dst.source.flMacroFXFactor = EAXSOURCE_DEFAULTMACROFXFACTOR;
- // Set everyting else to defaults.
+ // Set everything else to defaults.
//
eax5_set_sends_defaults(dst.sends);
eax5_set_active_fx_slots_defaults(dst.active_fx_slots);
@@ -4294,7 +3893,7 @@ void ALsource::eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept
static_cast<Eax3Props&>(dst.source) = src;
dst.source.flMacroFXFactor = EAXSOURCE_DEFAULTMACROFXFACTOR;
- // Set everyting else to defaults.
+ // Set everything else to defaults.
//
eax5_set_sends_defaults(dst.sends);
eax5_set_active_fx_slots_defaults(dst.active_fx_slots);
@@ -4312,7 +3911,7 @@ void ALsource::eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept
//
dst.sends = src.sends;
- for (auto i = size_t{}; i < EAX_MAX_FXSLOTS; ++i)
+ for(size_t i{0};i < EAX_MAX_FXSLOTS;++i)
dst.sends[i].guidReceivingFXSlotID = *(eax5_fx_slot_ids[i]);
// Active FX slots.
@@ -4374,19 +3973,21 @@ EaxAlLowPassParam ALsource::eax_create_direct_filter_param() const noexcept
static_cast<float>(mEax.source.lDirectHF) +
static_cast<float>(mEax.source.lObstruction);
- for (auto i = std::size_t{}; i < EAX_MAX_FXSLOTS; ++i)
+ for(size_t i{0};i < EAX_MAX_FXSLOTS;++i)
{
if(!mEaxActiveFxSlots[i])
continue;
- if(has_source_occlusion) {
+ if(has_source_occlusion)
+ {
const auto& fx_slot = mEaxAlContext->eaxGetFxSlot(i);
const auto& fx_slot_eax = fx_slot.eax_get_eax_fx_slot();
const auto is_environmental_fx = ((fx_slot_eax.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0);
const auto is_primary = (mEaxPrimaryFxSlotId.value_or(-1) == fx_slot.eax_get_index());
const auto is_listener_environment = (is_environmental_fx && is_primary);
- if(is_listener_environment) {
+ if(is_listener_environment)
+ {
gain_mb += eax_calculate_dst_occlusion_mb(
mEax.source.lOcclusion,
mEax.source.flOcclusionDirectRatio,
@@ -4398,7 +3999,8 @@ EaxAlLowPassParam ALsource::eax_create_direct_filter_param() const noexcept
const auto& send = mEax.sends[i];
- if(send.lOcclusion != 0) {
+ if(send.lOcclusion != 0)
+ {
gain_mb += eax_calculate_dst_occlusion_mb(
send.lOcclusion,
send.flOcclusionDirectRatio,
@@ -4473,8 +4075,9 @@ void ALsource::eax_update_direct_filter()
void ALsource::eax_update_room_filters()
{
- for (auto i = size_t{}; i < EAX_MAX_FXSLOTS; ++i) {
- if (!mEaxActiveFxSlots[i])
+ for(size_t i{0};i < EAX_MAX_FXSLOTS;++i)
+ {
+ if(!mEaxActiveFxSlots[i])
continue;
auto& fx_slot = mEaxAlContext->eaxGetFxSlot(i);
@@ -4486,7 +4089,7 @@ void ALsource::eax_update_room_filters()
void ALsource::eax_set_efx_outer_gain_hf()
{
- OuterGainHF = clamp(
+ OuterGainHF = std::clamp(
level_mb_to_gain(static_cast<float>(mEax.source.lOutsideVolumeHF)),
AL_MIN_CONE_OUTER_GAINHF,
AL_MAX_CONE_OUTER_GAINHF);
@@ -5280,7 +4883,7 @@ void ALsource::eax_commit_active_fx_slots()
// Deactivate EFX auxiliary effect slots for inactive slots. Active slots
// will be updated with the room filters.
- for(auto i = size_t{}; i < EAX_MAX_FXSLOTS; ++i)
+ for(size_t i{0};i < EAX_MAX_FXSLOTS;++i)
{
if(!mEaxActiveFxSlots[i])
eax_set_al_source_send(nullptr, i, EaxAlLowPassParam{1.0f, 1.0f});
diff --git a/al/source.h b/al/source.h
index ac97c8a7..c7694f83 100644
--- a/al/source.h
+++ b/al/source.h
@@ -4,9 +4,10 @@
#include <array>
#include <atomic>
#include <cstddef>
+#include <deque>
#include <iterator>
#include <limits>
-#include <deque>
+#include <string_view>
#include "AL/al.h"
#include "AL/alc.h"
@@ -14,7 +15,6 @@
#include "alc/alu.h"
#include "alc/context.h"
#include "alc/inprogext.h"
-#include "aldeque.h"
#include "almalloc.h"
#include "alnumeric.h"
#include "atomic.h"
@@ -39,9 +39,9 @@ enum class SourceStereo : bool {
#define DEFAULT_SENDS 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};
@@ -138,14 +138,14 @@ 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};
@@ -157,6 +157,8 @@ struct ALsource {
ALsource(const ALsource&) = delete;
ALsource& operator=(const ALsource&) = delete;
+ static void SetName(ALCcontext *context, ALuint id, std::string_view name);
+
DISABLE_ALLOC()
#ifdef ALSOFT_EAX
diff --git a/al/state.cpp b/al/state.cpp
index 86d81b13..5131edd9 100644
--- a/al/state.cpp
+++ b/al/state.cpp
@@ -24,7 +24,9 @@
#include <atomic>
#include <cmath>
+#include <cstring>
#include <mutex>
+#include <optional>
#include <stdexcept>
#include <string>
@@ -32,16 +34,18 @@
#include "AL/alc.h"
#include "AL/alext.h"
+#include "al/debug.h"
+#include "albit.h"
#include "alc/alu.h"
#include "alc/context.h"
#include "alc/inprogext.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "atomic.h"
#include "core/context.h"
#include "core/except.h"
#include "core/mixer/defs.h"
#include "core/voice.h"
+#include "direct_defs.h"
#include "intrusive_ptr.h"
#include "opthelpers.h"
#include "strutils.h"
@@ -67,6 +71,8 @@ constexpr ALchar alErrInvalidEnum[] = "Invalid Enum";
constexpr ALchar alErrInvalidValue[] = "Invalid Value";
constexpr ALchar alErrInvalidOp[] = "Invalid Operation";
constexpr ALchar alErrOutOfMemory[] = "Out of Memory";
+constexpr ALchar alStackOverflow[] = "Stack Overflow";
+constexpr ALchar alStackUnderflow[] = "Stack Underflow";
/* Resampler strings */
template<Resampler rtype> struct ResamplerName { };
@@ -103,7 +109,7 @@ const ALchar *GetResamplerName(const Resampler rtype)
throw std::runtime_error{"Unexpected resampler index"};
}
-al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
+std::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
{
switch(model)
{
@@ -115,7 +121,7 @@ al::optional<DistanceModel> DistanceModelFromALenum(ALenum model)
case AL_EXPONENT_DISTANCE: return DistanceModel::Exponent;
case AL_EXPONENT_DISTANCE_CLAMPED: return DistanceModel::ExponentClamped;
}
- return al::nullopt;
+ return std::nullopt;
}
ALenum ALenumFromDistanceModel(DistanceModel model)
{
@@ -132,626 +138,352 @@ ALenum ALenumFromDistanceModel(DistanceModel model)
throw std::runtime_error{"Unexpected distance model "+std::to_string(static_cast<int>(model))};
}
-} // namespace
-
-/* WARNING: Non-standard export! Not part of any extension, or exposed in the
- * alcFunctions list.
- */
-AL_API const ALchar* AL_APIENTRY alsoft_get_version(void)
-START_API_FUNC
-{
- static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION");
- if(spoof) return spoof->c_str();
- return ALSOFT_VERSION;
-}
-END_API_FUNC
-
-#define DO_UPDATEPROPS() do { \
- if(!context->mDeferUpdates) \
- UpdateContextProps(context.get()); \
- else \
- context->mPropsDirty = true; \
-} while(0)
-
-
-AL_API void AL_APIENTRY alEnable(ALenum capability)
-START_API_FUNC
-{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
- switch(capability)
- {
- case AL_SOURCE_DISTANCE_MODEL:
- {
- std::lock_guard<std::mutex> _{context->mPropLock};
- context->mSourceDistanceModel = true;
- DO_UPDATEPROPS();
- }
- break;
-
- case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
- context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported");
- break;
-
- default:
- context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability);
- }
-}
-END_API_FUNC
-
-AL_API void AL_APIENTRY alDisable(ALenum capability)
-START_API_FUNC
-{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
- switch(capability)
- {
- case AL_SOURCE_DISTANCE_MODEL:
- {
- std::lock_guard<std::mutex> _{context->mPropLock};
- context->mSourceDistanceModel = false;
- DO_UPDATEPROPS();
- }
- break;
-
- case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
- context->mStopVoicesOnDisconnect = false;
- break;
-
- default:
- context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability);
- }
-}
-END_API_FUNC
-
-AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability)
-START_API_FUNC
-{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return AL_FALSE;
-
- std::lock_guard<std::mutex> _{context->mPropLock};
- ALboolean value{AL_FALSE};
- switch(capability)
- {
- case AL_SOURCE_DISTANCE_MODEL:
- value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE;
- break;
-
- case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
- value = context->mStopVoicesOnDisconnect ? AL_TRUE : AL_FALSE;
- break;
-
- default:
- context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
- }
-
- return value;
-}
-END_API_FUNC
-
-AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum pname)
-START_API_FUNC
+enum PropertyValue : ALenum {
+ DopplerFactor = AL_DOPPLER_FACTOR,
+ DopplerVelocity = AL_DOPPLER_VELOCITY,
+ DistanceModel = AL_DISTANCE_MODEL,
+ SpeedOfSound = AL_SPEED_OF_SOUND,
+ DeferredUpdates = AL_DEFERRED_UPDATES_SOFT,
+ GainLimit = AL_GAIN_LIMIT_SOFT,
+ NumResamplers = AL_NUM_RESAMPLERS_SOFT,
+ DefaultResampler = AL_DEFAULT_RESAMPLER_SOFT,
+ DebugLoggedMessages = AL_DEBUG_LOGGED_MESSAGES_EXT,
+ DebugNextLoggedMessageLength = AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT,
+ MaxDebugMessageLength = AL_MAX_DEBUG_MESSAGE_LENGTH_EXT,
+ MaxDebugLoggedMessages = AL_MAX_DEBUG_LOGGED_MESSAGES_EXT,
+ MaxDebugGroupDepth = AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT,
+ MaxLabelLength = AL_MAX_LABEL_LENGTH_EXT,
+ ContextFlags = AL_CONTEXT_FLAGS_EXT,
+#ifdef ALSOFT_EAX
+ EaxRamSize = AL_EAX_RAM_SIZE,
+ EaxRamFree = AL_EAX_RAM_FREE,
+#endif
+};
+
+template<typename T>
+struct PropertyCastType {
+ template<typename U>
+ constexpr auto operator()(U&& value) const noexcept
+ { return static_cast<T>(std::forward<U>(value)); }
+};
+/* Special-case ALboolean to be an actual bool instead of a char type. */
+template<>
+struct PropertyCastType<ALboolean> {
+ template<typename U>
+ constexpr ALboolean operator()(U&& value) const noexcept
+ { return static_cast<bool>(std::forward<U>(value)) ? AL_TRUE : AL_FALSE; }
+};
+
+
+template<typename T>
+void GetValue(ALCcontext *context, ALenum pname, T *values)
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return AL_FALSE;
+ auto cast_value = PropertyCastType<T>{};
- std::lock_guard<std::mutex> _{context->mPropLock};
- ALboolean value{AL_FALSE};
- switch(pname)
+ switch(static_cast<PropertyValue>(pname))
{
case AL_DOPPLER_FACTOR:
- if(context->mDopplerFactor != 0.0f)
- value = AL_TRUE;
- break;
+ *values = cast_value(context->mDopplerFactor);
+ return;
case AL_DOPPLER_VELOCITY:
- if(context->mDopplerVelocity != 0.0f)
- value = AL_TRUE;
- break;
-
- case AL_DISTANCE_MODEL:
- if(context->mDistanceModel == DistanceModel::Default)
- value = AL_TRUE;
- break;
+ if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY
+ context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 0,
+ DebugSeverity::Medium,
+ "AL_DOPPLER_VELOCITY is deprecated in AL 1.1, use AL_SPEED_OF_SOUND; "
+ "AL_DOPPLER_VELOCITY -> AL_SPEED_OF_SOUND / 343.3f");
+ *values = cast_value(context->mDopplerVelocity);
+ return;
case AL_SPEED_OF_SOUND:
- if(context->mSpeedOfSound != 0.0f)
- value = AL_TRUE;
- break;
-
- case AL_DEFERRED_UPDATES_SOFT:
- if(context->mDeferUpdates)
- value = AL_TRUE;
- break;
+ *values = cast_value(context->mSpeedOfSound);
+ return;
case AL_GAIN_LIMIT_SOFT:
- if(GainMixMax/context->mGainBoost != 0.0f)
- value = AL_TRUE;
- break;
-
- case AL_NUM_RESAMPLERS_SOFT:
- /* Always non-0. */
- value = AL_TRUE;
- break;
-
- case AL_DEFAULT_RESAMPLER_SOFT:
- value = static_cast<int>(ResamplerDefault) ? AL_TRUE : AL_FALSE;
- break;
-
- default:
- context->setError(AL_INVALID_VALUE, "Invalid boolean property 0x%04x", pname);
- }
-
- return value;
-}
-END_API_FUNC
-
-AL_API ALdouble AL_APIENTRY alGetDouble(ALenum pname)
-START_API_FUNC
-{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return 0.0;
-
- std::lock_guard<std::mutex> _{context->mPropLock};
- ALdouble value{0.0};
- switch(pname)
- {
- case AL_DOPPLER_FACTOR:
- value = context->mDopplerFactor;
- break;
-
- case AL_DOPPLER_VELOCITY:
- value = context->mDopplerVelocity;
- break;
-
- case AL_DISTANCE_MODEL:
- value = static_cast<ALdouble>(ALenumFromDistanceModel(context->mDistanceModel));
- break;
-
- case AL_SPEED_OF_SOUND:
- value = context->mSpeedOfSound;
- break;
+ *values = cast_value(GainMixMax / context->mGainBoost);
+ return;
case AL_DEFERRED_UPDATES_SOFT:
- if(context->mDeferUpdates)
- value = static_cast<ALdouble>(AL_TRUE);
- break;
-
- case AL_GAIN_LIMIT_SOFT:
- value = ALdouble{GainMixMax}/context->mGainBoost;
- break;
-
- case AL_NUM_RESAMPLERS_SOFT:
- value = static_cast<ALdouble>(Resampler::Max) + 1.0;
- break;
-
- case AL_DEFAULT_RESAMPLER_SOFT:
- value = static_cast<ALdouble>(ResamplerDefault);
- break;
-
- default:
- context->setError(AL_INVALID_VALUE, "Invalid double property 0x%04x", pname);
- }
-
- return value;
-}
-END_API_FUNC
-
-AL_API ALfloat AL_APIENTRY alGetFloat(ALenum pname)
-START_API_FUNC
-{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return 0.0f;
-
- std::lock_guard<std::mutex> _{context->mPropLock};
- ALfloat value{0.0f};
- switch(pname)
- {
- case AL_DOPPLER_FACTOR:
- value = context->mDopplerFactor;
- break;
-
- case AL_DOPPLER_VELOCITY:
- value = context->mDopplerVelocity;
- break;
+ *values = cast_value(context->mDeferUpdates ? AL_TRUE : AL_FALSE);
+ return;
case AL_DISTANCE_MODEL:
- value = static_cast<ALfloat>(ALenumFromDistanceModel(context->mDistanceModel));
- break;
-
- case AL_SPEED_OF_SOUND:
- value = context->mSpeedOfSound;
- break;
-
- case AL_DEFERRED_UPDATES_SOFT:
- if(context->mDeferUpdates)
- value = static_cast<ALfloat>(AL_TRUE);
- break;
-
- case AL_GAIN_LIMIT_SOFT:
- value = GainMixMax/context->mGainBoost;
- break;
+ *values = cast_value(ALenumFromDistanceModel(context->mDistanceModel));
+ return;
case AL_NUM_RESAMPLERS_SOFT:
- value = static_cast<ALfloat>(Resampler::Max) + 1.0f;
- break;
+ *values = cast_value(al::to_underlying(Resampler::Max) + 1);
+ return;
case AL_DEFAULT_RESAMPLER_SOFT:
- value = static_cast<ALfloat>(ResamplerDefault);
- break;
+ *values = cast_value(al::to_underlying(ResamplerDefault));
+ return;
- default:
- context->setError(AL_INVALID_VALUE, "Invalid float property 0x%04x", pname);
+ case AL_DEBUG_LOGGED_MESSAGES_EXT:
+ {
+ std::lock_guard<std::mutex> _{context->mDebugCbLock};
+ *values = cast_value(context->mDebugLog.size());
+ return;
}
- return value;
-}
-END_API_FUNC
-
-AL_API ALint AL_APIENTRY alGetInteger(ALenum pname)
-START_API_FUNC
-{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return 0;
-
- std::lock_guard<std::mutex> _{context->mPropLock};
- ALint value{0};
- switch(pname)
+ case AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT:
{
- case AL_DOPPLER_FACTOR:
- value = static_cast<ALint>(context->mDopplerFactor);
- break;
+ std::lock_guard<std::mutex> _{context->mDebugCbLock};
+ *values = cast_value(context->mDebugLog.empty() ? 0_uz
+ : (context->mDebugLog.front().mMessage.size()+1));
+ return;
+ }
- case AL_DOPPLER_VELOCITY:
- value = static_cast<ALint>(context->mDopplerVelocity);
- break;
+ case AL_MAX_DEBUG_MESSAGE_LENGTH_EXT:
+ *values = cast_value(MaxDebugMessageLength);
+ return;
- case AL_DISTANCE_MODEL:
- value = ALenumFromDistanceModel(context->mDistanceModel);
- break;
+ case AL_MAX_DEBUG_LOGGED_MESSAGES_EXT:
+ *values = cast_value(MaxDebugLoggedMessages);
+ return;
- case AL_SPEED_OF_SOUND:
- value = static_cast<ALint>(context->mSpeedOfSound);
- break;
+ case AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT:
+ *values = cast_value(MaxDebugGroupDepth);
+ return;
- case AL_DEFERRED_UPDATES_SOFT:
- if(context->mDeferUpdates)
- value = AL_TRUE;
- break;
+ case AL_MAX_LABEL_LENGTH_EXT:
+ *values = cast_value(MaxObjectLabelLength);
+ return;
- case AL_GAIN_LIMIT_SOFT:
- value = static_cast<ALint>(GainMixMax/context->mGainBoost);
- break;
-
- case AL_NUM_RESAMPLERS_SOFT:
- value = static_cast<int>(Resampler::Max) + 1;
- break;
-
- case AL_DEFAULT_RESAMPLER_SOFT:
- value = static_cast<int>(ResamplerDefault);
- break;
+ case AL_CONTEXT_FLAGS_EXT:
+ *values = cast_value(context->mContextFlags.to_ulong());
+ return;
#ifdef ALSOFT_EAX
#define EAX_ERROR "[alGetInteger] EAX not enabled."
case AL_EAX_RAM_SIZE:
- if (eax_g_is_enabled)
- {
- value = eax_x_ram_max_size;
- }
- else
+ if(eax_g_is_enabled)
{
- context->setError(AL_INVALID_VALUE, EAX_ERROR);
+ *values = cast_value(eax_x_ram_max_size);
+ return;
}
-
- break;
+ context->setError(AL_INVALID_ENUM, EAX_ERROR);
+ return;
case AL_EAX_RAM_FREE:
- if (eax_g_is_enabled)
+ if(eax_g_is_enabled)
{
auto device = context->mALDevice.get();
std::lock_guard<std::mutex> device_lock{device->BufferLock};
-
- value = static_cast<ALint>(device->eax_x_ram_free_size);
+ *values = cast_value(device->eax_x_ram_free_size);
+ return;
}
- else
- {
- context->setError(AL_INVALID_VALUE, EAX_ERROR);
- }
-
- break;
+ context->setError(AL_INVALID_ENUM, EAX_ERROR);
+ return;
#undef EAX_ERROR
#endif // ALSOFT_EAX
-
- default:
- context->setError(AL_INVALID_VALUE, "Invalid integer property 0x%04x", pname);
}
+ context->setError(AL_INVALID_ENUM, "Invalid context property 0x%04x", pname);
+}
- return value;
+
+inline void UpdateProps(ALCcontext *context)
+{
+ if(!context->mDeferUpdates)
+ UpdateContextProps(context);
+ else
+ context->mPropsDirty = true;
}
-END_API_FUNC
-AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname)
-START_API_FUNC
+} // namespace
+
+/* WARNING: Non-standard export! Not part of any extension, or exposed in the
+ * alcFunctions list.
+ */
+AL_API const ALchar* AL_APIENTRY alsoft_get_version(void) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return 0_i64;
+ static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION");
+ if(spoof) return spoof->c_str();
+ return ALSOFT_VERSION;
+}
- std::lock_guard<std::mutex> _{context->mPropLock};
- ALint64SOFT value{0};
- switch(pname)
- {
- case AL_DOPPLER_FACTOR:
- value = static_cast<ALint64SOFT>(context->mDopplerFactor);
- break;
- case AL_DOPPLER_VELOCITY:
- value = static_cast<ALint64SOFT>(context->mDopplerVelocity);
+AL_API DECL_FUNC1(void, alEnable, ALenum)
+FORCE_ALIGN void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capability) noexcept
+{
+ switch(capability)
+ {
+ case AL_SOURCE_DISTANCE_MODEL:
+ {
+ std::lock_guard<std::mutex> _{context->mPropLock};
+ context->mSourceDistanceModel = true;
+ UpdateProps(context);
+ }
break;
- case AL_DISTANCE_MODEL:
- value = ALenumFromDistanceModel(context->mDistanceModel);
+ case AL_DEBUG_OUTPUT_EXT:
+ context->mDebugEnabled = true;
break;
- case AL_SPEED_OF_SOUND:
- value = static_cast<ALint64SOFT>(context->mSpeedOfSound);
+ case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
+ context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported");
break;
- case AL_DEFERRED_UPDATES_SOFT:
- if(context->mDeferUpdates)
- value = AL_TRUE;
- break;
+ default:
+ context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability);
+ }
+}
- case AL_GAIN_LIMIT_SOFT:
- value = static_cast<ALint64SOFT>(GainMixMax/context->mGainBoost);
+AL_API DECL_FUNC1(void, alDisable, ALenum)
+FORCE_ALIGN void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capability) noexcept
+{
+ switch(capability)
+ {
+ case AL_SOURCE_DISTANCE_MODEL:
+ {
+ std::lock_guard<std::mutex> _{context->mPropLock};
+ context->mSourceDistanceModel = false;
+ UpdateProps(context);
+ }
break;
- case AL_NUM_RESAMPLERS_SOFT:
- value = static_cast<ALint64SOFT>(Resampler::Max) + 1;
+ case AL_DEBUG_OUTPUT_EXT:
+ context->mDebugEnabled = false;
break;
- case AL_DEFAULT_RESAMPLER_SOFT:
- value = static_cast<ALint64SOFT>(ResamplerDefault);
+ case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
+ context->mStopVoicesOnDisconnect = false;
break;
default:
- context->setError(AL_INVALID_VALUE, "Invalid integer64 property 0x%04x", pname);
+ context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability);
}
-
- return value;
}
-END_API_FUNC
-AL_API ALvoid* AL_APIENTRY alGetPointerSOFT(ALenum pname)
-START_API_FUNC
+AL_API DECL_FUNC1(ALboolean, alIsEnabled, ALenum)
+FORCE_ALIGN ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum capability) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return nullptr;
-
std::lock_guard<std::mutex> _{context->mPropLock};
- void *value{nullptr};
- switch(pname)
+ ALboolean value{AL_FALSE};
+ switch(capability)
{
- case AL_EVENT_CALLBACK_FUNCTION_SOFT:
- value = reinterpret_cast<void*>(context->mEventCb);
+ case AL_SOURCE_DISTANCE_MODEL:
+ value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE;
break;
- case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
- value = context->mEventParam;
+ case AL_DEBUG_OUTPUT_EXT:
+ value = context->mDebugEnabled ? AL_TRUE : AL_FALSE;
+ break;
+
+ case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
+ value = context->mStopVoicesOnDisconnect ? AL_TRUE : AL_FALSE;
break;
default:
- context->setError(AL_INVALID_VALUE, "Invalid pointer property 0x%04x", pname);
+ context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
}
return value;
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetBooleanv(ALenum pname, ALboolean *values)
-START_API_FUNC
-{
- if(values)
- {
- switch(pname)
- {
- case AL_DOPPLER_FACTOR:
- case AL_DOPPLER_VELOCITY:
- case AL_DISTANCE_MODEL:
- case AL_SPEED_OF_SOUND:
- case AL_DEFERRED_UPDATES_SOFT:
- case AL_GAIN_LIMIT_SOFT:
- case AL_NUM_RESAMPLERS_SOFT:
- case AL_DEFAULT_RESAMPLER_SOFT:
- values[0] = alGetBoolean(pname);
- return;
- }
- }
+#define DECL_GETFUNC(R, Name, Ext) \
+AL_API R AL_APIENTRY Name##Ext(ALenum pname) noexcept \
+{ \
+ R value{}; \
+ auto context = GetContextRef(); \
+ if(!context) UNLIKELY return value; \
+ Name##vDirect##Ext(GetContextRef().get(), pname, &value); \
+ return value; \
+} \
+FORCE_ALIGN R AL_APIENTRY Name##Direct##Ext(ALCcontext *context, ALenum pname) noexcept \
+{ \
+ R value{}; \
+ Name##vDirect##Ext(context, pname, &value); \
+ return value; \
+}
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
+DECL_GETFUNC(ALboolean, alGetBoolean,)
+DECL_GETFUNC(ALdouble, alGetDouble,)
+DECL_GETFUNC(ALfloat, alGetFloat,)
+DECL_GETFUNC(ALint, alGetInteger,)
- if(!values)
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else switch(pname)
- {
- default:
- context->setError(AL_INVALID_VALUE, "Invalid boolean-vector property 0x%04x", pname);
- }
-}
-END_API_FUNC
+DECL_GETFUNC(ALint64SOFT, alGetInteger64,SOFT)
+DECL_GETFUNC(ALvoid*, alGetPointer,SOFT)
-AL_API void AL_APIENTRY alGetDoublev(ALenum pname, ALdouble *values)
-START_API_FUNC
-{
- if(values)
- {
- switch(pname)
- {
- case AL_DOPPLER_FACTOR:
- case AL_DOPPLER_VELOCITY:
- case AL_DISTANCE_MODEL:
- case AL_SPEED_OF_SOUND:
- case AL_DEFERRED_UPDATES_SOFT:
- case AL_GAIN_LIMIT_SOFT:
- case AL_NUM_RESAMPLERS_SOFT:
- case AL_DEFAULT_RESAMPLER_SOFT:
- values[0] = alGetDouble(pname);
- return;
- }
- }
+#undef DECL_GETFUNC
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
- if(!values)
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else switch(pname)
- {
- default:
- context->setError(AL_INVALID_VALUE, "Invalid double-vector property 0x%04x", pname);
- }
+AL_API DECL_FUNC2(void, alGetBooleanv, ALenum, ALboolean*)
+FORCE_ALIGN void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum pname, ALboolean *values) noexcept
+{
+ if(!values) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+ GetValue(context, pname, values);
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetFloatv(ALenum pname, ALfloat *values)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGetDoublev, ALenum, ALdouble*)
+FORCE_ALIGN void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum pname, ALdouble *values) noexcept
{
- if(values)
- {
- switch(pname)
- {
- case AL_DOPPLER_FACTOR:
- case AL_DOPPLER_VELOCITY:
- case AL_DISTANCE_MODEL:
- case AL_SPEED_OF_SOUND:
- case AL_DEFERRED_UPDATES_SOFT:
- case AL_GAIN_LIMIT_SOFT:
- case AL_NUM_RESAMPLERS_SOFT:
- case AL_DEFAULT_RESAMPLER_SOFT:
- values[0] = alGetFloat(pname);
- return;
- }
- }
-
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
- if(!values)
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else switch(pname)
- {
- default:
- context->setError(AL_INVALID_VALUE, "Invalid float-vector property 0x%04x", pname);
- }
+ if(!values) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+ GetValue(context, pname, values);
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetIntegerv(ALenum pname, ALint *values)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGetFloatv, ALenum, ALfloat*)
+FORCE_ALIGN void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum pname, ALfloat *values) noexcept
{
- if(values)
- {
- switch(pname)
- {
- case AL_DOPPLER_FACTOR:
- case AL_DOPPLER_VELOCITY:
- case AL_DISTANCE_MODEL:
- case AL_SPEED_OF_SOUND:
- case AL_DEFERRED_UPDATES_SOFT:
- case AL_GAIN_LIMIT_SOFT:
- case AL_NUM_RESAMPLERS_SOFT:
- case AL_DEFAULT_RESAMPLER_SOFT:
- values[0] = alGetInteger(pname);
- return;
- }
- }
-
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
- if(!values)
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else switch(pname)
- {
- default:
- context->setError(AL_INVALID_VALUE, "Invalid integer-vector property 0x%04x", pname);
- }
+ if(!values) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+ GetValue(context, pname, values);
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values)
-START_API_FUNC
+AL_API DECL_FUNC2(void, alGetIntegerv, ALenum, ALint*)
+FORCE_ALIGN void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum pname, ALint *values) noexcept
{
- if(values)
- {
- switch(pname)
- {
- case AL_DOPPLER_FACTOR:
- case AL_DOPPLER_VELOCITY:
- case AL_DISTANCE_MODEL:
- case AL_SPEED_OF_SOUND:
- case AL_DEFERRED_UPDATES_SOFT:
- case AL_GAIN_LIMIT_SOFT:
- case AL_NUM_RESAMPLERS_SOFT:
- case AL_DEFAULT_RESAMPLER_SOFT:
- values[0] = alGetInteger64SOFT(pname);
- return;
- }
- }
-
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
+ if(!values) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+ GetValue(context, pname, values);
+}
- if(!values)
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else switch(pname)
- {
- default:
- context->setError(AL_INVALID_VALUE, "Invalid integer64-vector property 0x%04x", pname);
- }
+AL_API DECL_FUNCEXT2(void, alGetInteger64v,SOFT, ALenum, ALint64SOFT*)
+FORCE_ALIGN void AL_APIENTRY alGetInteger64vDirectSOFT(ALCcontext *context, ALenum pname, ALint64SOFT *values) noexcept
+{
+ if(!values) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+ GetValue(context, pname, values);
}
-END_API_FUNC
-AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, ALvoid **values)
-START_API_FUNC
+AL_API DECL_FUNCEXT2(void, alGetPointerv,SOFT, ALenum, ALvoid**)
+FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, ALvoid **values) noexcept
{
- if(values)
+ if(!values) UNLIKELY
+ return context->setError(AL_INVALID_VALUE, "NULL pointer");
+
+ switch(pname)
{
- switch(pname)
- {
- case AL_EVENT_CALLBACK_FUNCTION_SOFT:
- case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
- values[0] = alGetPointerSOFT(pname);
- return;
- }
- }
+ case AL_EVENT_CALLBACK_FUNCTION_SOFT:
+ *values = al::bit_cast<void*>(context->mEventCb);
+ break;
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
+ case AL_EVENT_CALLBACK_USER_PARAM_SOFT:
+ *values = context->mEventParam;
+ break;
+
+ case AL_DEBUG_CALLBACK_FUNCTION_EXT:
+ *values = al::bit_cast<void*>(context->mDebugCb);
+ break;
+
+ case AL_DEBUG_CALLBACK_USER_PARAM_EXT:
+ *values = context->mDebugParam;
+ break;
- if(!values)
- context->setError(AL_INVALID_VALUE, "NULL pointer");
- else switch(pname)
- {
default:
- context->setError(AL_INVALID_VALUE, "Invalid pointer-vector property 0x%04x", pname);
+ context->setError(AL_INVALID_ENUM, "Invalid context pointer property 0x%04x", pname);
}
}
-END_API_FUNC
-AL_API const ALchar* AL_APIENTRY alGetString(ALenum pname)
-START_API_FUNC
+AL_API DECL_FUNC1(const ALchar*, alGetString, ALenum)
+FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALenum pname) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return nullptr;
-
const ALchar *value{nullptr};
switch(pname)
{
@@ -768,7 +500,7 @@ START_API_FUNC
break;
case AL_EXTENSIONS:
- value = context->mExtensionList;
+ value = context->mExtensionsString.c_str();
break;
case AL_NO_ERROR:
@@ -795,112 +527,79 @@ START_API_FUNC
value = alErrOutOfMemory;
break;
+ case AL_STACK_OVERFLOW_EXT:
+ value = alStackOverflow;
+ break;
+
+ case AL_STACK_UNDERFLOW_EXT:
+ value = alStackUnderflow;
+ break;
+
default:
context->setError(AL_INVALID_VALUE, "Invalid string property 0x%04x", pname);
}
return value;
}
-END_API_FUNC
-AL_API void AL_APIENTRY alDopplerFactor(ALfloat value)
-START_API_FUNC
+AL_API DECL_FUNC1(void, alDopplerFactor, ALfloat)
+FORCE_ALIGN void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(!(value >= 0.0f && std::isfinite(value)))
context->setError(AL_INVALID_VALUE, "Doppler factor %f out of range", value);
else
{
std::lock_guard<std::mutex> _{context->mPropLock};
context->mDopplerFactor = value;
- DO_UPDATEPROPS();
+ UpdateProps(context);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value)
-START_API_FUNC
+AL_API DECL_FUNC1(void, alSpeedOfSound, ALfloat)
+FORCE_ALIGN void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
- if(!(value >= 0.0f && std::isfinite(value)))
- context->setError(AL_INVALID_VALUE, "Doppler velocity %f out of range", value);
- else
- {
- std::lock_guard<std::mutex> _{context->mPropLock};
- context->mDopplerVelocity = value;
- DO_UPDATEPROPS();
- }
-}
-END_API_FUNC
-
-AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value)
-START_API_FUNC
-{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(!(value > 0.0f && std::isfinite(value)))
context->setError(AL_INVALID_VALUE, "Speed of sound %f out of range", value);
else
{
std::lock_guard<std::mutex> _{context->mPropLock};
context->mSpeedOfSound = value;
- DO_UPDATEPROPS();
+ UpdateProps(context);
}
}
-END_API_FUNC
-AL_API void AL_APIENTRY alDistanceModel(ALenum value)
-START_API_FUNC
+AL_API DECL_FUNC1(void, alDistanceModel, ALenum)
+FORCE_ALIGN void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum value) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
if(auto model = DistanceModelFromALenum(value))
{
std::lock_guard<std::mutex> _{context->mPropLock};
context->mDistanceModel = *model;
if(!context->mSourceDistanceModel)
- DO_UPDATEPROPS();
+ UpdateProps(context);
}
else
context->setError(AL_INVALID_VALUE, "Distance model 0x%04x out of range", value);
}
-END_API_FUNC
-AL_API void AL_APIENTRY alDeferUpdatesSOFT(void)
-START_API_FUNC
+AL_API DECL_FUNCEXT(void, alDeferUpdates,SOFT)
+FORCE_ALIGN void AL_APIENTRY alDeferUpdatesDirectSOFT(ALCcontext *context) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
context->deferUpdates();
}
-END_API_FUNC
-AL_API void AL_APIENTRY alProcessUpdatesSOFT(void)
-START_API_FUNC
+AL_API DECL_FUNCEXT(void, alProcessUpdates,SOFT)
+FORCE_ALIGN void AL_APIENTRY alProcessUpdatesDirectSOFT(ALCcontext *context) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return;
-
std::lock_guard<std::mutex> _{context->mPropLock};
context->processUpdates();
}
-END_API_FUNC
-AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index)
-START_API_FUNC
+AL_API DECL_FUNCEXT2(const ALchar*, alGetStringi,SOFT, ALenum,ALsizei)
+FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context, ALenum pname, ALsizei index) noexcept
{
- ContextRef context{GetContextRef()};
- if(!context) UNLIKELY return nullptr;
-
const ALchar *value{nullptr};
switch(pname)
{
@@ -916,7 +615,28 @@ START_API_FUNC
}
return value;
}
-END_API_FUNC
+
+
+AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) noexcept
+{
+ ContextRef context{GetContextRef()};
+ if(!context) UNLIKELY return;
+
+ if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY
+ context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 0,
+ DebugSeverity::Medium,
+ "alDopplerVelocity is deprecated in AL 1.1, use alSpeedOfSound; "
+ "alDopplerVelocity(x) -> alSpeedOfSound(343.3f * x)");
+
+ if(!(value >= 0.0f && std::isfinite(value)))
+ context->setError(AL_INVALID_VALUE, "Doppler velocity %f out of range", value);
+ else
+ {
+ std::lock_guard<std::mutex> _{context->mPropLock};
+ context->mDopplerVelocity = value;
+ UpdateProps(context.get());
+ }
+}
void UpdateContextProps(ALCcontext *context)
diff --git a/alc/alc.cpp b/alc/alc.cpp
index af8ff55d..6017e743 100644
--- a/alc/alc.cpp
+++ b/alc/alc.cpp
@@ -38,6 +38,7 @@
#include <climits>
#include <cmath>
#include <csignal>
+#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
@@ -48,11 +49,13 @@
#include <memory>
#include <mutex>
#include <new>
+#include <optional>
#include <stddef.h>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
+#include <vector>
#include "AL/al.h"
#include "AL/alc.h"
@@ -61,16 +64,16 @@
#include "al/auxeffectslot.h"
#include "al/buffer.h"
+#include "al/debug.h"
#include "al/effect.h"
#include "al/filter.h"
#include "al/listener.h"
#include "al/source.h"
+#include "alc/events.h"
#include "albit.h"
-#include "albyte.h"
#include "alconfig.h"
#include "almalloc.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "alspan.h"
#include "alstring.h"
#include "alu.h"
@@ -96,12 +99,11 @@
#include "core/voice_change.h"
#include "device.h"
#include "effects/base.h"
+#include "export_list.h"
#include "inprogext.h"
#include "intrusive_ptr.h"
#include "opthelpers.h"
#include "strutils.h"
-#include "threads.h"
-#include "vector.h"
#include "backends/base.h"
#include "backends/null.h"
@@ -161,13 +163,6 @@
#endif // ALSOFT_EAX
-FILE *gLogFile{stderr};
-#ifdef _DEBUG
-LogLevel gLogLevel{LogLevel::Warning};
-#else
-LogLevel gLogLevel{LogLevel::Error};
-#endif
-
/************************************************
* Library initialization
************************************************/
@@ -179,7 +174,7 @@ BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/)
case DLL_PROCESS_ATTACH:
/* Pin the DLL so we won't get unloaded until the process terminates */
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
- reinterpret_cast<WCHAR*>(module), &module);
+ al::bit_cast<WCHAR*>(module), &module);
break;
}
return TRUE;
@@ -261,673 +256,6 @@ BackendFactory *PlaybackFactory{};
BackendFactory *CaptureFactory{};
-/************************************************
- * Functions, enums, and errors
- ************************************************/
-#define DECL(x) { #x, reinterpret_cast<void*>(x) }
-const struct {
- const char *funcName;
- void *address;
-} alcFunctions[] = {
- DECL(alcCreateContext),
- DECL(alcMakeContextCurrent),
- DECL(alcProcessContext),
- DECL(alcSuspendContext),
- DECL(alcDestroyContext),
- DECL(alcGetCurrentContext),
- DECL(alcGetContextsDevice),
- DECL(alcOpenDevice),
- DECL(alcCloseDevice),
- DECL(alcGetError),
- DECL(alcIsExtensionPresent),
- DECL(alcGetProcAddress),
- DECL(alcGetEnumValue),
- DECL(alcGetString),
- DECL(alcGetIntegerv),
- DECL(alcCaptureOpenDevice),
- DECL(alcCaptureCloseDevice),
- DECL(alcCaptureStart),
- DECL(alcCaptureStop),
- DECL(alcCaptureSamples),
-
- DECL(alcSetThreadContext),
- DECL(alcGetThreadContext),
-
- DECL(alcLoopbackOpenDeviceSOFT),
- DECL(alcIsRenderFormatSupportedSOFT),
- DECL(alcRenderSamplesSOFT),
-
- DECL(alcDevicePauseSOFT),
- DECL(alcDeviceResumeSOFT),
-
- DECL(alcGetStringiSOFT),
- DECL(alcResetDeviceSOFT),
-
- DECL(alcGetInteger64vSOFT),
-
- DECL(alcReopenDeviceSOFT),
-
- DECL(alEnable),
- DECL(alDisable),
- DECL(alIsEnabled),
- DECL(alGetString),
- DECL(alGetBooleanv),
- DECL(alGetIntegerv),
- DECL(alGetFloatv),
- DECL(alGetDoublev),
- DECL(alGetBoolean),
- DECL(alGetInteger),
- DECL(alGetFloat),
- DECL(alGetDouble),
- DECL(alGetError),
- DECL(alIsExtensionPresent),
- DECL(alGetProcAddress),
- DECL(alGetEnumValue),
- DECL(alListenerf),
- DECL(alListener3f),
- DECL(alListenerfv),
- DECL(alListeneri),
- DECL(alListener3i),
- DECL(alListeneriv),
- DECL(alGetListenerf),
- DECL(alGetListener3f),
- DECL(alGetListenerfv),
- DECL(alGetListeneri),
- DECL(alGetListener3i),
- DECL(alGetListeneriv),
- DECL(alGenSources),
- DECL(alDeleteSources),
- DECL(alIsSource),
- DECL(alSourcef),
- DECL(alSource3f),
- DECL(alSourcefv),
- DECL(alSourcei),
- DECL(alSource3i),
- DECL(alSourceiv),
- DECL(alGetSourcef),
- DECL(alGetSource3f),
- DECL(alGetSourcefv),
- DECL(alGetSourcei),
- DECL(alGetSource3i),
- DECL(alGetSourceiv),
- DECL(alSourcePlayv),
- DECL(alSourceStopv),
- DECL(alSourceRewindv),
- DECL(alSourcePausev),
- DECL(alSourcePlay),
- DECL(alSourceStop),
- DECL(alSourceRewind),
- DECL(alSourcePause),
- DECL(alSourceQueueBuffers),
- DECL(alSourceUnqueueBuffers),
- DECL(alGenBuffers),
- DECL(alDeleteBuffers),
- DECL(alIsBuffer),
- DECL(alBufferData),
- DECL(alBufferf),
- DECL(alBuffer3f),
- DECL(alBufferfv),
- DECL(alBufferi),
- DECL(alBuffer3i),
- DECL(alBufferiv),
- DECL(alGetBufferf),
- DECL(alGetBuffer3f),
- DECL(alGetBufferfv),
- DECL(alGetBufferi),
- DECL(alGetBuffer3i),
- DECL(alGetBufferiv),
- DECL(alDopplerFactor),
- DECL(alDopplerVelocity),
- DECL(alSpeedOfSound),
- DECL(alDistanceModel),
-
- DECL(alGenFilters),
- DECL(alDeleteFilters),
- DECL(alIsFilter),
- DECL(alFilteri),
- DECL(alFilteriv),
- DECL(alFilterf),
- DECL(alFilterfv),
- DECL(alGetFilteri),
- DECL(alGetFilteriv),
- DECL(alGetFilterf),
- DECL(alGetFilterfv),
- DECL(alGenEffects),
- DECL(alDeleteEffects),
- DECL(alIsEffect),
- DECL(alEffecti),
- DECL(alEffectiv),
- DECL(alEffectf),
- DECL(alEffectfv),
- DECL(alGetEffecti),
- DECL(alGetEffectiv),
- DECL(alGetEffectf),
- DECL(alGetEffectfv),
- DECL(alGenAuxiliaryEffectSlots),
- DECL(alDeleteAuxiliaryEffectSlots),
- DECL(alIsAuxiliaryEffectSlot),
- DECL(alAuxiliaryEffectSloti),
- DECL(alAuxiliaryEffectSlotiv),
- DECL(alAuxiliaryEffectSlotf),
- DECL(alAuxiliaryEffectSlotfv),
- DECL(alGetAuxiliaryEffectSloti),
- DECL(alGetAuxiliaryEffectSlotiv),
- DECL(alGetAuxiliaryEffectSlotf),
- DECL(alGetAuxiliaryEffectSlotfv),
-
- DECL(alDeferUpdatesSOFT),
- DECL(alProcessUpdatesSOFT),
-
- DECL(alSourcedSOFT),
- DECL(alSource3dSOFT),
- DECL(alSourcedvSOFT),
- DECL(alGetSourcedSOFT),
- DECL(alGetSource3dSOFT),
- DECL(alGetSourcedvSOFT),
- DECL(alSourcei64SOFT),
- DECL(alSource3i64SOFT),
- DECL(alSourcei64vSOFT),
- DECL(alGetSourcei64SOFT),
- DECL(alGetSource3i64SOFT),
- DECL(alGetSourcei64vSOFT),
-
- DECL(alGetStringiSOFT),
-
- DECL(alBufferStorageSOFT),
- DECL(alMapBufferSOFT),
- DECL(alUnmapBufferSOFT),
- DECL(alFlushMappedBufferSOFT),
-
- DECL(alEventControlSOFT),
- DECL(alEventCallbackSOFT),
- DECL(alGetPointerSOFT),
- DECL(alGetPointervSOFT),
-
- DECL(alBufferCallbackSOFT),
- DECL(alGetBufferPtrSOFT),
- DECL(alGetBuffer3PtrSOFT),
- DECL(alGetBufferPtrvSOFT),
-
- DECL(alAuxiliaryEffectSlotPlaySOFT),
- DECL(alAuxiliaryEffectSlotPlayvSOFT),
- DECL(alAuxiliaryEffectSlotStopSOFT),
- DECL(alAuxiliaryEffectSlotStopvSOFT),
-
- DECL(alSourcePlayAtTimeSOFT),
- DECL(alSourcePlayAtTimevSOFT),
-
- DECL(alBufferSubDataSOFT),
-
- DECL(alBufferDataStatic),
-#ifdef ALSOFT_EAX
-}, eaxFunctions[] = {
- DECL(EAXGet),
- DECL(EAXSet),
- DECL(EAXGetBufferMode),
- DECL(EAXSetBufferMode),
-#endif
-};
-#undef DECL
-
-#define DECL(x) { #x, (x) }
-constexpr struct {
- const ALCchar *enumName;
- ALCenum value;
-} alcEnumerations[] = {
- DECL(ALC_INVALID),
- DECL(ALC_FALSE),
- DECL(ALC_TRUE),
-
- DECL(ALC_MAJOR_VERSION),
- DECL(ALC_MINOR_VERSION),
- DECL(ALC_ATTRIBUTES_SIZE),
- DECL(ALC_ALL_ATTRIBUTES),
- DECL(ALC_DEFAULT_DEVICE_SPECIFIER),
- DECL(ALC_DEVICE_SPECIFIER),
- DECL(ALC_ALL_DEVICES_SPECIFIER),
- DECL(ALC_DEFAULT_ALL_DEVICES_SPECIFIER),
- DECL(ALC_EXTENSIONS),
- DECL(ALC_FREQUENCY),
- DECL(ALC_REFRESH),
- DECL(ALC_SYNC),
- DECL(ALC_MONO_SOURCES),
- DECL(ALC_STEREO_SOURCES),
- DECL(ALC_CAPTURE_DEVICE_SPECIFIER),
- DECL(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER),
- DECL(ALC_CAPTURE_SAMPLES),
- DECL(ALC_CONNECTED),
-
- DECL(ALC_EFX_MAJOR_VERSION),
- DECL(ALC_EFX_MINOR_VERSION),
- DECL(ALC_MAX_AUXILIARY_SENDS),
-
- DECL(ALC_FORMAT_CHANNELS_SOFT),
- DECL(ALC_FORMAT_TYPE_SOFT),
-
- DECL(ALC_MONO_SOFT),
- DECL(ALC_STEREO_SOFT),
- DECL(ALC_QUAD_SOFT),
- DECL(ALC_5POINT1_SOFT),
- DECL(ALC_6POINT1_SOFT),
- DECL(ALC_7POINT1_SOFT),
- DECL(ALC_BFORMAT3D_SOFT),
-
- DECL(ALC_BYTE_SOFT),
- DECL(ALC_UNSIGNED_BYTE_SOFT),
- DECL(ALC_SHORT_SOFT),
- DECL(ALC_UNSIGNED_SHORT_SOFT),
- DECL(ALC_INT_SOFT),
- DECL(ALC_UNSIGNED_INT_SOFT),
- DECL(ALC_FLOAT_SOFT),
-
- DECL(ALC_HRTF_SOFT),
- DECL(ALC_DONT_CARE_SOFT),
- DECL(ALC_HRTF_STATUS_SOFT),
- DECL(ALC_HRTF_DISABLED_SOFT),
- DECL(ALC_HRTF_ENABLED_SOFT),
- DECL(ALC_HRTF_DENIED_SOFT),
- DECL(ALC_HRTF_REQUIRED_SOFT),
- DECL(ALC_HRTF_HEADPHONES_DETECTED_SOFT),
- DECL(ALC_HRTF_UNSUPPORTED_FORMAT_SOFT),
- DECL(ALC_NUM_HRTF_SPECIFIERS_SOFT),
- DECL(ALC_HRTF_SPECIFIER_SOFT),
- DECL(ALC_HRTF_ID_SOFT),
-
- DECL(ALC_AMBISONIC_LAYOUT_SOFT),
- DECL(ALC_AMBISONIC_SCALING_SOFT),
- DECL(ALC_AMBISONIC_ORDER_SOFT),
- DECL(ALC_ACN_SOFT),
- DECL(ALC_FUMA_SOFT),
- DECL(ALC_N3D_SOFT),
- DECL(ALC_SN3D_SOFT),
-
- DECL(ALC_OUTPUT_LIMITER_SOFT),
-
- DECL(ALC_DEVICE_CLOCK_SOFT),
- DECL(ALC_DEVICE_LATENCY_SOFT),
- DECL(ALC_DEVICE_CLOCK_LATENCY_SOFT),
- DECL(AL_SAMPLE_OFFSET_CLOCK_SOFT),
- DECL(AL_SEC_OFFSET_CLOCK_SOFT),
-
- DECL(ALC_OUTPUT_MODE_SOFT),
- DECL(ALC_ANY_SOFT),
- DECL(ALC_STEREO_BASIC_SOFT),
- DECL(ALC_STEREO_UHJ_SOFT),
- DECL(ALC_STEREO_HRTF_SOFT),
- DECL(ALC_SURROUND_5_1_SOFT),
- DECL(ALC_SURROUND_6_1_SOFT),
- DECL(ALC_SURROUND_7_1_SOFT),
-
- DECL(ALC_NO_ERROR),
- DECL(ALC_INVALID_DEVICE),
- DECL(ALC_INVALID_CONTEXT),
- DECL(ALC_INVALID_ENUM),
- DECL(ALC_INVALID_VALUE),
- DECL(ALC_OUT_OF_MEMORY),
-
-
- DECL(AL_INVALID),
- DECL(AL_NONE),
- DECL(AL_FALSE),
- DECL(AL_TRUE),
-
- DECL(AL_SOURCE_RELATIVE),
- DECL(AL_CONE_INNER_ANGLE),
- DECL(AL_CONE_OUTER_ANGLE),
- DECL(AL_PITCH),
- DECL(AL_POSITION),
- DECL(AL_DIRECTION),
- DECL(AL_VELOCITY),
- DECL(AL_LOOPING),
- DECL(AL_BUFFER),
- DECL(AL_GAIN),
- DECL(AL_MIN_GAIN),
- DECL(AL_MAX_GAIN),
- DECL(AL_ORIENTATION),
- DECL(AL_REFERENCE_DISTANCE),
- DECL(AL_ROLLOFF_FACTOR),
- DECL(AL_CONE_OUTER_GAIN),
- DECL(AL_MAX_DISTANCE),
- DECL(AL_SEC_OFFSET),
- DECL(AL_SAMPLE_OFFSET),
- DECL(AL_BYTE_OFFSET),
- DECL(AL_SOURCE_TYPE),
- DECL(AL_STATIC),
- DECL(AL_STREAMING),
- DECL(AL_UNDETERMINED),
- DECL(AL_METERS_PER_UNIT),
- DECL(AL_LOOP_POINTS_SOFT),
- DECL(AL_DIRECT_CHANNELS_SOFT),
-
- DECL(AL_DIRECT_FILTER),
- DECL(AL_AUXILIARY_SEND_FILTER),
- DECL(AL_AIR_ABSORPTION_FACTOR),
- DECL(AL_ROOM_ROLLOFF_FACTOR),
- DECL(AL_CONE_OUTER_GAINHF),
- DECL(AL_DIRECT_FILTER_GAINHF_AUTO),
- DECL(AL_AUXILIARY_SEND_FILTER_GAIN_AUTO),
- DECL(AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO),
-
- DECL(AL_SOURCE_STATE),
- DECL(AL_INITIAL),
- DECL(AL_PLAYING),
- DECL(AL_PAUSED),
- DECL(AL_STOPPED),
-
- DECL(AL_BUFFERS_QUEUED),
- DECL(AL_BUFFERS_PROCESSED),
-
- DECL(AL_FORMAT_MONO8),
- DECL(AL_FORMAT_MONO16),
- DECL(AL_FORMAT_MONO_FLOAT32),
- DECL(AL_FORMAT_MONO_DOUBLE_EXT),
- DECL(AL_FORMAT_STEREO8),
- DECL(AL_FORMAT_STEREO16),
- DECL(AL_FORMAT_STEREO_FLOAT32),
- DECL(AL_FORMAT_STEREO_DOUBLE_EXT),
- DECL(AL_FORMAT_MONO_IMA4),
- DECL(AL_FORMAT_STEREO_IMA4),
- DECL(AL_FORMAT_MONO_MSADPCM_SOFT),
- DECL(AL_FORMAT_STEREO_MSADPCM_SOFT),
- DECL(AL_FORMAT_QUAD8_LOKI),
- DECL(AL_FORMAT_QUAD16_LOKI),
- DECL(AL_FORMAT_QUAD8),
- DECL(AL_FORMAT_QUAD16),
- DECL(AL_FORMAT_QUAD32),
- DECL(AL_FORMAT_51CHN8),
- DECL(AL_FORMAT_51CHN16),
- DECL(AL_FORMAT_51CHN32),
- DECL(AL_FORMAT_61CHN8),
- DECL(AL_FORMAT_61CHN16),
- DECL(AL_FORMAT_61CHN32),
- DECL(AL_FORMAT_71CHN8),
- DECL(AL_FORMAT_71CHN16),
- DECL(AL_FORMAT_71CHN32),
- DECL(AL_FORMAT_REAR8),
- DECL(AL_FORMAT_REAR16),
- DECL(AL_FORMAT_REAR32),
- DECL(AL_FORMAT_MONO_MULAW),
- DECL(AL_FORMAT_MONO_MULAW_EXT),
- DECL(AL_FORMAT_STEREO_MULAW),
- DECL(AL_FORMAT_STEREO_MULAW_EXT),
- DECL(AL_FORMAT_QUAD_MULAW),
- DECL(AL_FORMAT_51CHN_MULAW),
- DECL(AL_FORMAT_61CHN_MULAW),
- DECL(AL_FORMAT_71CHN_MULAW),
- DECL(AL_FORMAT_REAR_MULAW),
- DECL(AL_FORMAT_MONO_ALAW_EXT),
- DECL(AL_FORMAT_STEREO_ALAW_EXT),
-
- DECL(AL_FORMAT_BFORMAT2D_8),
- DECL(AL_FORMAT_BFORMAT2D_16),
- DECL(AL_FORMAT_BFORMAT2D_FLOAT32),
- DECL(AL_FORMAT_BFORMAT2D_MULAW),
- DECL(AL_FORMAT_BFORMAT3D_8),
- DECL(AL_FORMAT_BFORMAT3D_16),
- DECL(AL_FORMAT_BFORMAT3D_FLOAT32),
- DECL(AL_FORMAT_BFORMAT3D_MULAW),
-
- DECL(AL_FREQUENCY),
- DECL(AL_BITS),
- DECL(AL_CHANNELS),
- DECL(AL_SIZE),
- DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT),
- DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT),
-
- DECL(AL_SOURCE_RADIUS),
-
- DECL(AL_SAMPLE_OFFSET_LATENCY_SOFT),
- DECL(AL_SEC_OFFSET_LATENCY_SOFT),
-
- DECL(AL_STEREO_ANGLES),
-
- DECL(AL_UNUSED),
- DECL(AL_PENDING),
- DECL(AL_PROCESSED),
-
- DECL(AL_NO_ERROR),
- DECL(AL_INVALID_NAME),
- DECL(AL_INVALID_ENUM),
- DECL(AL_INVALID_VALUE),
- DECL(AL_INVALID_OPERATION),
- DECL(AL_OUT_OF_MEMORY),
-
- DECL(AL_VENDOR),
- DECL(AL_VERSION),
- DECL(AL_RENDERER),
- DECL(AL_EXTENSIONS),
-
- DECL(AL_DOPPLER_FACTOR),
- DECL(AL_DOPPLER_VELOCITY),
- DECL(AL_DISTANCE_MODEL),
- DECL(AL_SPEED_OF_SOUND),
- DECL(AL_SOURCE_DISTANCE_MODEL),
- DECL(AL_DEFERRED_UPDATES_SOFT),
- DECL(AL_GAIN_LIMIT_SOFT),
-
- DECL(AL_INVERSE_DISTANCE),
- DECL(AL_INVERSE_DISTANCE_CLAMPED),
- DECL(AL_LINEAR_DISTANCE),
- DECL(AL_LINEAR_DISTANCE_CLAMPED),
- DECL(AL_EXPONENT_DISTANCE),
- DECL(AL_EXPONENT_DISTANCE_CLAMPED),
-
- DECL(AL_FILTER_TYPE),
- DECL(AL_FILTER_NULL),
- DECL(AL_FILTER_LOWPASS),
- DECL(AL_FILTER_HIGHPASS),
- DECL(AL_FILTER_BANDPASS),
-
- DECL(AL_LOWPASS_GAIN),
- DECL(AL_LOWPASS_GAINHF),
-
- DECL(AL_HIGHPASS_GAIN),
- DECL(AL_HIGHPASS_GAINLF),
-
- DECL(AL_BANDPASS_GAIN),
- DECL(AL_BANDPASS_GAINHF),
- DECL(AL_BANDPASS_GAINLF),
-
- DECL(AL_EFFECT_TYPE),
- DECL(AL_EFFECT_NULL),
- DECL(AL_EFFECT_REVERB),
- DECL(AL_EFFECT_EAXREVERB),
- DECL(AL_EFFECT_CHORUS),
- DECL(AL_EFFECT_DISTORTION),
- DECL(AL_EFFECT_ECHO),
- DECL(AL_EFFECT_FLANGER),
- DECL(AL_EFFECT_PITCH_SHIFTER),
- DECL(AL_EFFECT_FREQUENCY_SHIFTER),
- DECL(AL_EFFECT_VOCAL_MORPHER),
- DECL(AL_EFFECT_RING_MODULATOR),
- DECL(AL_EFFECT_AUTOWAH),
- DECL(AL_EFFECT_COMPRESSOR),
- DECL(AL_EFFECT_EQUALIZER),
- DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT),
- DECL(AL_EFFECT_DEDICATED_DIALOGUE),
-
- DECL(AL_EFFECTSLOT_EFFECT),
- DECL(AL_EFFECTSLOT_GAIN),
- DECL(AL_EFFECTSLOT_AUXILIARY_SEND_AUTO),
- DECL(AL_EFFECTSLOT_NULL),
-
- DECL(AL_EAXREVERB_DENSITY),
- DECL(AL_EAXREVERB_DIFFUSION),
- DECL(AL_EAXREVERB_GAIN),
- DECL(AL_EAXREVERB_GAINHF),
- DECL(AL_EAXREVERB_GAINLF),
- DECL(AL_EAXREVERB_DECAY_TIME),
- DECL(AL_EAXREVERB_DECAY_HFRATIO),
- DECL(AL_EAXREVERB_DECAY_LFRATIO),
- DECL(AL_EAXREVERB_REFLECTIONS_GAIN),
- DECL(AL_EAXREVERB_REFLECTIONS_DELAY),
- DECL(AL_EAXREVERB_REFLECTIONS_PAN),
- DECL(AL_EAXREVERB_LATE_REVERB_GAIN),
- DECL(AL_EAXREVERB_LATE_REVERB_DELAY),
- DECL(AL_EAXREVERB_LATE_REVERB_PAN),
- DECL(AL_EAXREVERB_ECHO_TIME),
- DECL(AL_EAXREVERB_ECHO_DEPTH),
- DECL(AL_EAXREVERB_MODULATION_TIME),
- DECL(AL_EAXREVERB_MODULATION_DEPTH),
- DECL(AL_EAXREVERB_AIR_ABSORPTION_GAINHF),
- DECL(AL_EAXREVERB_HFREFERENCE),
- DECL(AL_EAXREVERB_LFREFERENCE),
- DECL(AL_EAXREVERB_ROOM_ROLLOFF_FACTOR),
- DECL(AL_EAXREVERB_DECAY_HFLIMIT),
-
- DECL(AL_REVERB_DENSITY),
- DECL(AL_REVERB_DIFFUSION),
- DECL(AL_REVERB_GAIN),
- DECL(AL_REVERB_GAINHF),
- DECL(AL_REVERB_DECAY_TIME),
- DECL(AL_REVERB_DECAY_HFRATIO),
- DECL(AL_REVERB_REFLECTIONS_GAIN),
- DECL(AL_REVERB_REFLECTIONS_DELAY),
- DECL(AL_REVERB_LATE_REVERB_GAIN),
- DECL(AL_REVERB_LATE_REVERB_DELAY),
- DECL(AL_REVERB_AIR_ABSORPTION_GAINHF),
- DECL(AL_REVERB_ROOM_ROLLOFF_FACTOR),
- DECL(AL_REVERB_DECAY_HFLIMIT),
-
- DECL(AL_CHORUS_WAVEFORM),
- DECL(AL_CHORUS_PHASE),
- DECL(AL_CHORUS_RATE),
- DECL(AL_CHORUS_DEPTH),
- DECL(AL_CHORUS_FEEDBACK),
- DECL(AL_CHORUS_DELAY),
-
- DECL(AL_DISTORTION_EDGE),
- DECL(AL_DISTORTION_GAIN),
- DECL(AL_DISTORTION_LOWPASS_CUTOFF),
- DECL(AL_DISTORTION_EQCENTER),
- DECL(AL_DISTORTION_EQBANDWIDTH),
-
- DECL(AL_ECHO_DELAY),
- DECL(AL_ECHO_LRDELAY),
- DECL(AL_ECHO_DAMPING),
- DECL(AL_ECHO_FEEDBACK),
- DECL(AL_ECHO_SPREAD),
-
- DECL(AL_FLANGER_WAVEFORM),
- DECL(AL_FLANGER_PHASE),
- DECL(AL_FLANGER_RATE),
- DECL(AL_FLANGER_DEPTH),
- DECL(AL_FLANGER_FEEDBACK),
- DECL(AL_FLANGER_DELAY),
-
- DECL(AL_FREQUENCY_SHIFTER_FREQUENCY),
- DECL(AL_FREQUENCY_SHIFTER_LEFT_DIRECTION),
- DECL(AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION),
-
- DECL(AL_RING_MODULATOR_FREQUENCY),
- DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF),
- DECL(AL_RING_MODULATOR_WAVEFORM),
-
- DECL(AL_PITCH_SHIFTER_COARSE_TUNE),
- DECL(AL_PITCH_SHIFTER_FINE_TUNE),
-
- DECL(AL_COMPRESSOR_ONOFF),
-
- DECL(AL_EQUALIZER_LOW_GAIN),
- DECL(AL_EQUALIZER_LOW_CUTOFF),
- DECL(AL_EQUALIZER_MID1_GAIN),
- DECL(AL_EQUALIZER_MID1_CENTER),
- DECL(AL_EQUALIZER_MID1_WIDTH),
- DECL(AL_EQUALIZER_MID2_GAIN),
- DECL(AL_EQUALIZER_MID2_CENTER),
- DECL(AL_EQUALIZER_MID2_WIDTH),
- DECL(AL_EQUALIZER_HIGH_GAIN),
- DECL(AL_EQUALIZER_HIGH_CUTOFF),
-
- DECL(AL_DEDICATED_GAIN),
-
- DECL(AL_AUTOWAH_ATTACK_TIME),
- DECL(AL_AUTOWAH_RELEASE_TIME),
- DECL(AL_AUTOWAH_RESONANCE),
- DECL(AL_AUTOWAH_PEAK_GAIN),
-
- DECL(AL_VOCAL_MORPHER_PHONEMEA),
- DECL(AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING),
- DECL(AL_VOCAL_MORPHER_PHONEMEB),
- DECL(AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING),
- DECL(AL_VOCAL_MORPHER_WAVEFORM),
- DECL(AL_VOCAL_MORPHER_RATE),
-
- DECL(AL_EFFECTSLOT_TARGET_SOFT),
-
- DECL(AL_NUM_RESAMPLERS_SOFT),
- DECL(AL_DEFAULT_RESAMPLER_SOFT),
- DECL(AL_SOURCE_RESAMPLER_SOFT),
- DECL(AL_RESAMPLER_NAME_SOFT),
-
- DECL(AL_SOURCE_SPATIALIZE_SOFT),
- DECL(AL_AUTO_SOFT),
-
- DECL(AL_MAP_READ_BIT_SOFT),
- DECL(AL_MAP_WRITE_BIT_SOFT),
- DECL(AL_MAP_PERSISTENT_BIT_SOFT),
- DECL(AL_PRESERVE_DATA_BIT_SOFT),
-
- DECL(AL_EVENT_CALLBACK_FUNCTION_SOFT),
- DECL(AL_EVENT_CALLBACK_USER_PARAM_SOFT),
- DECL(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT),
- DECL(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT),
- DECL(AL_EVENT_TYPE_DISCONNECTED_SOFT),
-
- DECL(AL_DROP_UNMATCHED_SOFT),
- DECL(AL_REMIX_UNMATCHED_SOFT),
-
- DECL(AL_AMBISONIC_LAYOUT_SOFT),
- DECL(AL_AMBISONIC_SCALING_SOFT),
- DECL(AL_FUMA_SOFT),
- DECL(AL_ACN_SOFT),
- DECL(AL_SN3D_SOFT),
- DECL(AL_N3D_SOFT),
-
- DECL(AL_BUFFER_CALLBACK_FUNCTION_SOFT),
- DECL(AL_BUFFER_CALLBACK_USER_PARAM_SOFT),
-
- DECL(AL_UNPACK_AMBISONIC_ORDER_SOFT),
-
- DECL(AL_EFFECT_CONVOLUTION_REVERB_SOFT),
- DECL(AL_EFFECTSLOT_STATE_SOFT),
-
- DECL(AL_FORMAT_UHJ2CHN8_SOFT),
- DECL(AL_FORMAT_UHJ2CHN16_SOFT),
- DECL(AL_FORMAT_UHJ2CHN_FLOAT32_SOFT),
- DECL(AL_FORMAT_UHJ3CHN8_SOFT),
- DECL(AL_FORMAT_UHJ3CHN16_SOFT),
- DECL(AL_FORMAT_UHJ3CHN_FLOAT32_SOFT),
- DECL(AL_FORMAT_UHJ4CHN8_SOFT),
- DECL(AL_FORMAT_UHJ4CHN16_SOFT),
- DECL(AL_FORMAT_UHJ4CHN_FLOAT32_SOFT),
- DECL(AL_STEREO_MODE_SOFT),
- DECL(AL_NORMAL_SOFT),
- DECL(AL_SUPER_STEREO_SOFT),
- DECL(AL_SUPER_STEREO_WIDTH_SOFT),
-
- DECL(AL_FORMAT_UHJ2CHN_MULAW_SOFT),
- DECL(AL_FORMAT_UHJ2CHN_ALAW_SOFT),
- DECL(AL_FORMAT_UHJ2CHN_IMA4_SOFT),
- DECL(AL_FORMAT_UHJ2CHN_MSADPCM_SOFT),
- DECL(AL_FORMAT_UHJ3CHN_MULAW_SOFT),
- DECL(AL_FORMAT_UHJ3CHN_ALAW_SOFT),
- DECL(AL_FORMAT_UHJ4CHN_MULAW_SOFT),
- DECL(AL_FORMAT_UHJ4CHN_ALAW_SOFT),
-
- DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT),
-
-#ifdef ALSOFT_EAX
-}, eaxEnumerations[] = {
- DECL(AL_EAX_RAM_SIZE),
- DECL(AL_EAX_RAM_FREE),
- DECL(AL_STORAGE_AUTOMATIC),
- DECL(AL_STORAGE_HARDWARE),
- DECL(AL_STORAGE_ACCESSIBLE),
-#endif // ALSOFT_EAX
-};
-#undef DECL
-
constexpr ALCchar alcNoError[] = "No Error";
constexpr ALCchar alcErrInvalidDevice[] = "Invalid Device";
constexpr ALCchar alcErrInvalidContext[] = "Invalid Context";
@@ -974,16 +302,20 @@ constexpr ALCchar alcNoDeviceExtList[] =
"ALC_ENUMERATE_ALL_EXT "
"ALC_ENUMERATION_EXT "
"ALC_EXT_CAPTURE "
+ "ALC_EXTX_direct_context "
"ALC_EXT_EFX "
"ALC_EXT_thread_local_context "
"ALC_SOFT_loopback "
"ALC_SOFT_loopback_bformat "
- "ALC_SOFT_reopen_device";
+ "ALC_SOFT_reopen_device "
+ "ALC_SOFT_system_events";
constexpr ALCchar alcExtensionList[] =
"ALC_ENUMERATE_ALL_EXT "
"ALC_ENUMERATION_EXT "
"ALC_EXT_CAPTURE "
+ "ALC_EXT_debug "
"ALC_EXT_DEDICATED "
+ "ALC_EXTX_direct_context "
"ALC_EXT_disconnect "
"ALC_EXT_EFX "
"ALC_EXT_thread_local_context "
@@ -994,7 +326,8 @@ constexpr ALCchar alcExtensionList[] =
"ALC_SOFT_output_limiter "
"ALC_SOFT_output_mode "
"ALC_SOFT_pause_device "
- "ALC_SOFT_reopen_device";
+ "ALC_SOFT_reopen_device "
+ "ALC_SOFT_system_events";
constexpr int alcMajorVersion{1};
constexpr int alcMinorVersion{1};
@@ -1008,8 +341,8 @@ using DeviceRef = al::intrusive_ptr<ALCdevice>;
/************************************************
* Device lists
************************************************/
-al::vector<ALCdevice*> DeviceList;
-al::vector<ALCcontext*> ContextList;
+std::vector<ALCdevice*> DeviceList;
+std::vector<ALCcontext*> ContextList;
std::recursive_mutex ListLock;
@@ -1034,7 +367,7 @@ void alc_initconfig(void)
if(logf) gLogFile = logf;
else
{
- auto u8name = wstr_to_utf8(logfile->c_str());
+ auto u8name = wstr_to_utf8(*logfile);
ERR("Failed to open log file '%s'\n", u8name.c_str());
}
}
@@ -1051,7 +384,7 @@ void alc_initconfig(void)
ALSOFT_GIT_BRANCH);
{
std::string names;
- if(al::size(BackendList) < 1)
+ if(std::size(BackendList) < 1)
names = "(none)";
else
{
@@ -1171,13 +504,7 @@ void alc_initconfig(void)
}
Voice::InitMixer(ConfigValueStr(nullptr, nullptr, "resampler"));
- auto uhjfiltopt = ConfigValueStr(nullptr, "uhj", "decode-filter");
- if(!uhjfiltopt)
- {
- if((uhjfiltopt = ConfigValueStr(nullptr, "uhj", "filter")))
- WARN("uhj/filter is deprecated, please use uhj/decode-filter\n");
- }
- if(uhjfiltopt)
+ if(auto uhjfiltopt = ConfigValueStr(nullptr, "uhj", "decode-filter"))
{
if(al::strcasecmp(uhjfiltopt->c_str(), "fir256") == 0)
UhjDecodeQuality = UhjQualityType::FIR256;
@@ -1188,7 +515,7 @@ void alc_initconfig(void)
else
WARN("Unsupported uhj/decode-filter: %s\n", uhjfiltopt->c_str());
}
- if((uhjfiltopt = ConfigValueStr(nullptr, "uhj", "encode-filter")))
+ if(auto uhjfiltopt = ConfigValueStr(nullptr, "uhj", "encode-filter"))
{
if(al::strcasecmp(uhjfiltopt->c_str(), "fir256") == 0)
UhjEncodeQuality = UhjQualityType::FIR256;
@@ -1407,48 +734,58 @@ void ProbeCaptureDeviceList()
struct DevFmtPair { DevFmtChannels chans; DevFmtType type; };
-al::optional<DevFmtPair> DecomposeDevFormat(ALenum format)
+std::optional<DevFmtPair> DecomposeDevFormat(ALenum format)
{
static const struct {
ALenum format;
DevFmtChannels channels;
DevFmtType type;
} list[] = {
- { AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte },
- { AL_FORMAT_MONO16, DevFmtMono, DevFmtShort },
+ { AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte },
+ { AL_FORMAT_MONO16, DevFmtMono, DevFmtShort },
+ { AL_FORMAT_MONO_I32, DevFmtMono, DevFmtInt },
{ AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat },
- { AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte },
- { AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort },
+ { AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte },
+ { AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort },
+ { AL_FORMAT_STEREO_I32, DevFmtStereo, DevFmtInt },
{ AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat },
- { AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte },
- { AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort },
- { AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat },
-
- { AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte },
- { AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort },
- { AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat },
-
- { AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte },
- { AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort },
- { AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat },
-
- { AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte },
- { AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort },
- { AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat },
+ { AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte },
+ { AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort },
+ { AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat },
+ { AL_FORMAT_QUAD_I32, DevFmtQuad, DevFmtInt },
+ { AL_FORMAT_QUAD_FLOAT32, DevFmtQuad, DevFmtFloat },
+
+ { AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte },
+ { AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort },
+ { AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat },
+ { AL_FORMAT_51CHN_I32, DevFmtX51, DevFmtInt },
+ { AL_FORMAT_51CHN_FLOAT32, DevFmtX51, DevFmtFloat },
+
+ { AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte },
+ { AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort },
+ { AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat },
+ { AL_FORMAT_61CHN_I32, DevFmtX61, DevFmtInt },
+ { AL_FORMAT_61CHN_FLOAT32, DevFmtX61, DevFmtFloat },
+
+ { AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte },
+ { AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort },
+ { AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat },
+ { AL_FORMAT_71CHN_I32, DevFmtX71, DevFmtInt },
+ { AL_FORMAT_71CHN_FLOAT32, DevFmtX71, DevFmtFloat },
};
for(const auto &item : list)
{
if(item.format == format)
- return al::make_optional<DevFmtPair>({item.channels, item.type});
+ return DevFmtPair{item.channels, item.type};
}
- return al::nullopt;
+ return std::nullopt;
}
-al::optional<DevFmtType> DevFmtTypeFromEnum(ALCenum type)
+std::optional<DevFmtType> DevFmtTypeFromEnum(ALCenum type)
{
switch(type)
{
@@ -1461,7 +798,7 @@ al::optional<DevFmtType> DevFmtTypeFromEnum(ALCenum type)
case ALC_FLOAT_SOFT: return DevFmtFloat;
}
WARN("Unsupported format type: 0x%04x\n", type);
- return al::nullopt;
+ return std::nullopt;
}
ALCenum EnumFromDevFmt(DevFmtType type)
{
@@ -1478,7 +815,7 @@ ALCenum EnumFromDevFmt(DevFmtType type)
throw std::runtime_error{"Invalid DevFmtType: "+std::to_string(int(type))};
}
-al::optional<DevFmtChannels> DevFmtChannelsFromEnum(ALCenum channels)
+std::optional<DevFmtChannels> DevFmtChannelsFromEnum(ALCenum channels)
{
switch(channels)
{
@@ -1491,7 +828,7 @@ al::optional<DevFmtChannels> DevFmtChannelsFromEnum(ALCenum channels)
case ALC_BFORMAT3D_SOFT: return DevFmtAmbi3D;
}
WARN("Unsupported format channels: 0x%04x\n", channels);
- return al::nullopt;
+ return std::nullopt;
}
ALCenum EnumFromDevFmt(DevFmtChannels channels)
{
@@ -1511,7 +848,7 @@ ALCenum EnumFromDevFmt(DevFmtChannels channels)
throw std::runtime_error{"Invalid DevFmtChannels: "+std::to_string(int(channels))};
}
-al::optional<DevAmbiLayout> DevAmbiLayoutFromEnum(ALCenum layout)
+std::optional<DevAmbiLayout> DevAmbiLayoutFromEnum(ALCenum layout)
{
switch(layout)
{
@@ -1519,7 +856,7 @@ al::optional<DevAmbiLayout> DevAmbiLayoutFromEnum(ALCenum layout)
case ALC_ACN_SOFT: return DevAmbiLayout::ACN;
}
WARN("Unsupported ambisonic layout: 0x%04x\n", layout);
- return al::nullopt;
+ return std::nullopt;
}
ALCenum EnumFromDevAmbi(DevAmbiLayout layout)
{
@@ -1531,7 +868,7 @@ ALCenum EnumFromDevAmbi(DevAmbiLayout layout)
throw std::runtime_error{"Invalid DevAmbiLayout: "+std::to_string(int(layout))};
}
-al::optional<DevAmbiScaling> DevAmbiScalingFromEnum(ALCenum scaling)
+std::optional<DevAmbiScaling> DevAmbiScalingFromEnum(ALCenum scaling)
{
switch(scaling)
{
@@ -1540,7 +877,7 @@ al::optional<DevAmbiScaling> DevAmbiScalingFromEnum(ALCenum scaling)
case ALC_N3D_SOFT: return DevAmbiScaling::N3D;
}
WARN("Unsupported ambisonic scaling: 0x%04x\n", scaling);
- return al::nullopt;
+ return std::nullopt;
}
ALCenum EnumFromDevAmbi(DevAmbiScaling scaling)
{
@@ -1554,95 +891,60 @@ ALCenum EnumFromDevAmbi(DevAmbiScaling scaling)
}
-/* Downmixing channel arrays, to map the given format's missing channels to
- * existing ones. Based on Wine's DSound downmix values, which are based on
- * PulseAudio's.
+/* Downmixing channel arrays, to map a device format's missing channels to
+ * existing ones. Based on what PipeWire does, though simplified.
*/
-constexpr std::array<InputRemixMap::TargetMix,2> FrontStereoSplit{{
- {FrontLeft, 0.5f}, {FrontRight, 0.5f}
-}};
-constexpr std::array<InputRemixMap::TargetMix,1> FrontLeft9{{
- {FrontLeft, 1.0f/9.0f}
-}};
-constexpr std::array<InputRemixMap::TargetMix,1> FrontRight9{{
- {FrontRight, 1.0f/9.0f}
-}};
-constexpr std::array<InputRemixMap::TargetMix,2> BackMonoToFrontSplit{{
- {FrontLeft, 0.5f/9.0f}, {FrontRight, 0.5f/9.0f}
-}};
-constexpr std::array<InputRemixMap::TargetMix,2> LeftStereoSplit{{
- {FrontLeft, 0.5f}, {BackLeft, 0.5f}
-}};
-constexpr std::array<InputRemixMap::TargetMix,2> RightStereoSplit{{
- {FrontRight, 0.5f}, {BackRight, 0.5f}
-}};
-constexpr std::array<InputRemixMap::TargetMix,2> BackStereoSplit{{
- {BackLeft, 0.5f}, {BackRight, 0.5f}
-}};
-constexpr std::array<InputRemixMap::TargetMix,2> SideStereoSplit{{
- {SideLeft, 0.5f}, {SideRight, 0.5f}
-}};
-constexpr std::array<InputRemixMap::TargetMix,1> ToSideLeft{{
- {SideLeft, 1.0f}
-}};
-constexpr std::array<InputRemixMap::TargetMix,1> ToSideRight{{
- {SideRight, 1.0f}
-}};
-constexpr std::array<InputRemixMap::TargetMix,2> BackLeftSplit{{
- {SideLeft, 0.5f}, {BackCenter, 0.5f}
-}};
-constexpr std::array<InputRemixMap::TargetMix,2> BackRightSplit{{
- {SideRight, 0.5f}, {BackCenter, 0.5f}
-}};
-
-const std::array<InputRemixMap,6> StereoDownmix{{
- { FrontCenter, FrontStereoSplit },
- { SideLeft, FrontLeft9 },
- { SideRight, FrontRight9 },
- { BackLeft, FrontLeft9 },
- { BackRight, FrontRight9 },
- { BackCenter, BackMonoToFrontSplit },
-}};
-const std::array<InputRemixMap,4> QuadDownmix{{
- { FrontCenter, FrontStereoSplit },
- { SideLeft, LeftStereoSplit },
- { SideRight, RightStereoSplit },
- { BackCenter, BackStereoSplit },
-}};
-const std::array<InputRemixMap,3> X51Downmix{{
- { BackLeft, ToSideLeft },
- { BackRight, ToSideRight },
- { BackCenter, SideStereoSplit },
-}};
-const std::array<InputRemixMap,2> X61Downmix{{
- { BackLeft, BackLeftSplit },
- { BackRight, BackRightSplit },
-}};
-const std::array<InputRemixMap,1> X71Downmix{{
- { BackCenter, BackStereoSplit },
-}};
-
-
-/** Stores the latest ALC device error. */
-void alcSetError(ALCdevice *device, ALCenum errorCode)
-{
- WARN("Error generated on device %p, code 0x%04x\n", voidp{device}, errorCode);
- if(TrapALCError)
- {
-#ifdef _WIN32
- /* DebugBreak() will cause an exception if there is no debugger */
- if(IsDebuggerPresent())
- DebugBreak();
-#elif defined(SIGTRAP)
- raise(SIGTRAP);
-#endif
- }
-
- if(device)
- device->LastError.store(errorCode);
- else
- LastNullDeviceError.store(errorCode);
-}
+constexpr float inv_sqrt2f{static_cast<float>(1.0 / al::numbers::sqrt2)};
+constexpr std::array FrontStereo3dB{
+ InputRemixMap::TargetMix{FrontLeft, inv_sqrt2f},
+ InputRemixMap::TargetMix{FrontRight, inv_sqrt2f}
+};
+constexpr std::array FrontStereo6dB{
+ InputRemixMap::TargetMix{FrontLeft, 0.5f},
+ InputRemixMap::TargetMix{FrontRight, 0.5f}
+};
+constexpr std::array SideStereo3dB{
+ InputRemixMap::TargetMix{SideLeft, inv_sqrt2f},
+ InputRemixMap::TargetMix{SideRight, inv_sqrt2f}
+};
+constexpr std::array BackStereo3dB{
+ InputRemixMap::TargetMix{BackLeft, inv_sqrt2f},
+ InputRemixMap::TargetMix{BackRight, inv_sqrt2f}
+};
+constexpr std::array FrontLeft3dB{InputRemixMap::TargetMix{FrontLeft, inv_sqrt2f}};
+constexpr std::array FrontRight3dB{InputRemixMap::TargetMix{FrontRight, inv_sqrt2f}};
+constexpr std::array SideLeft0dB{InputRemixMap::TargetMix{SideLeft, 1.0f}};
+constexpr std::array SideRight0dB{InputRemixMap::TargetMix{SideRight, 1.0f}};
+constexpr std::array BackLeft0dB{InputRemixMap::TargetMix{BackLeft, 1.0f}};
+constexpr std::array BackRight0dB{InputRemixMap::TargetMix{BackRight, 1.0f}};
+constexpr std::array BackCenter3dB{InputRemixMap::TargetMix{BackCenter, inv_sqrt2f}};
+
+constexpr std::array StereoDownmix{
+ InputRemixMap{FrontCenter, FrontStereo3dB},
+ InputRemixMap{SideLeft, FrontLeft3dB},
+ InputRemixMap{SideRight, FrontRight3dB},
+ InputRemixMap{BackLeft, FrontLeft3dB},
+ InputRemixMap{BackRight, FrontRight3dB},
+ InputRemixMap{BackCenter, FrontStereo6dB},
+};
+constexpr std::array QuadDownmix{
+ InputRemixMap{FrontCenter, FrontStereo3dB},
+ InputRemixMap{SideLeft, BackLeft0dB},
+ InputRemixMap{SideRight, BackRight0dB},
+ InputRemixMap{BackCenter, BackStereo3dB},
+};
+constexpr std::array X51Downmix{
+ InputRemixMap{BackLeft, SideLeft0dB},
+ InputRemixMap{BackRight, SideRight0dB},
+ InputRemixMap{BackCenter, SideStereo3dB},
+};
+constexpr std::array X61Downmix{
+ InputRemixMap{BackLeft, BackCenter3dB},
+ InputRemixMap{BackRight, BackCenter3dB},
+};
+constexpr std::array X71Downmix{
+ InputRemixMap{BackCenter, BackStereo3dB},
+};
std::unique_ptr<Compressor> CreateDeviceLimiter(const ALCdevice *device, const float threshold)
@@ -1695,13 +997,13 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList)
uint numMono{device->NumMonoSources};
uint numStereo{device->NumStereoSources};
uint numSends{device->NumAuxSends};
- al::optional<StereoEncoding> stereomode;
- al::optional<bool> optlimit;
- al::optional<uint> optsrate;
- al::optional<DevFmtChannels> optchans;
- al::optional<DevFmtType> opttype;
- al::optional<DevAmbiLayout> optlayout;
- al::optional<DevAmbiScaling> optscale;
+ std::optional<StereoEncoding> stereomode;
+ std::optional<bool> optlimit;
+ std::optional<uint> optsrate;
+ std::optional<DevFmtChannels> optchans;
+ std::optional<DevFmtType> opttype;
+ std::optional<DevAmbiLayout> optlayout;
+ std::optional<DevAmbiScaling> optscale;
uint period_size{DEFAULT_UPDATE_SIZE};
uint buffer_size{DEFAULT_UPDATE_SIZE * DEFAULT_NUM_UPDATES};
int hrtf_id{-1};
@@ -1844,7 +1146,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList)
if(attrList && attrList[0])
{
ALenum outmode{ALC_ANY_SOFT};
- al::optional<bool> opthrtf;
+ std::optional<bool> opthrtf;
int freqAttr{};
#define ATTRIBUTE(a) a: TRACE("%s = %d\n", #a, attrList[attrIdx + 1]);
@@ -1904,7 +1206,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList)
else if(attrList[attrIdx + 1] == ALC_TRUE)
opthrtf = true;
else if(attrList[attrIdx + 1] == ALC_DONT_CARE_SOFT)
- opthrtf = al::nullopt;
+ opthrtf = std::nullopt;
break;
case ATTRIBUTE(ALC_HRTF_ID_SOFT)
@@ -1917,7 +1219,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList)
else if(attrList[attrIdx + 1] == ALC_TRUE)
optlimit = true;
else if(attrList[attrIdx + 1] == ALC_DONT_CARE_SOFT)
- optlimit = al::nullopt;
+ optlimit = std::nullopt;
break;
case ATTRIBUTE(ALC_OUTPUT_MODE_SOFT)
@@ -2542,6 +1844,11 @@ ContextRef VerifyContext(ALCcontext *context)
} // namespace
+FORCE_ALIGN void ALC_APIENTRY alsoft_set_log_callback(LPALSOFTLOGCALLBACK callback, void *userptr) noexcept
+{
+ al_set_log_callback(callback, userptr);
+}
+
/** Returns a new reference to the currently active context for this thread. */
ContextRef GetContextRef(void)
{
@@ -2562,58 +1869,86 @@ ContextRef GetContextRef(void)
return ContextRef{context};
}
+void alcSetError(ALCdevice *device, ALCenum errorCode)
+{
+ WARN("Error generated on device %p, code 0x%04x\n", voidp{device}, errorCode);
+ if(TrapALCError)
+ {
+#ifdef _WIN32
+ /* DebugBreak() will cause an exception if there is no debugger */
+ if(IsDebuggerPresent())
+ DebugBreak();
+#elif defined(SIGTRAP)
+ raise(SIGTRAP);
+#endif
+ }
+
+ if(device)
+ device->LastError.store(errorCode);
+ else
+ LastNullDeviceError.store(errorCode);
+}
/************************************************
* Standard ALC functions
************************************************/
-ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)
-START_API_FUNC
+ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) noexcept
{
DeviceRef dev{VerifyDevice(device)};
if(dev) return dev->LastError.exchange(ALC_NO_ERROR);
return LastNullDeviceError.exchange(ALC_NO_ERROR);
}
-END_API_FUNC
-ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context)
-START_API_FUNC
+ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) noexcept
{
- if(!SuspendDefers)
- return;
-
ContextRef ctx{VerifyContext(context)};
if(!ctx)
+ {
alcSetError(nullptr, ALC_INVALID_CONTEXT);
- else
+ return;
+ }
+
+ if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY
+ ctx->debugMessage(DebugSource::API, DebugType::Portability, 0, DebugSeverity::Medium,
+ "alcSuspendContext behavior is not portable -- some implementations suspend all "
+ "rendering, some only defer property changes, and some are completely no-op; consider "
+ "using alcDevicePauseSOFT to suspend all rendering, or alDeferUpdatesSOFT to only "
+ "defer property changes");
+
+ if(SuspendDefers)
{
std::lock_guard<std::mutex> _{ctx->mPropLock};
ctx->deferUpdates();
}
}
-END_API_FUNC
-ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context)
-START_API_FUNC
+ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) noexcept
{
- if(!SuspendDefers)
- return;
-
ContextRef ctx{VerifyContext(context)};
if(!ctx)
+ {
alcSetError(nullptr, ALC_INVALID_CONTEXT);
- else
+ return;
+ }
+
+ if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY
+ ctx->debugMessage(DebugSource::API, DebugType::Portability, 0, DebugSeverity::Medium,
+ "alcProcessContext behavior is not portable -- some implementations resume rendering, "
+ "some apply deferred property changes, and some are completely no-op; consider using "
+ "alcDeviceResumeSOFT to resume rendering, or alProcessUpdatesSOFT to apply deferred "
+ "property changes");
+
+ if(SuspendDefers)
{
std::lock_guard<std::mutex> _{ctx->mPropLock};
ctx->processUpdates();
}
}
-END_API_FUNC
-ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param)
-START_API_FUNC
+ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param) noexcept
{
const ALCchar *value{nullptr};
@@ -2732,7 +2067,6 @@ START_API_FUNC
return value;
}
-END_API_FUNC
static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span<int> values)
@@ -3056,8 +2390,7 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span<int>
return 0;
}
-ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
-START_API_FUNC
+ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) noexcept
{
DeviceRef dev{VerifyDevice(device)};
if(size <= 0 || values == nullptr)
@@ -3065,10 +2398,8 @@ START_API_FUNC
else
GetIntegerv(dev.get(), param, {values, static_cast<uint>(size)});
}
-END_API_FUNC
-ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALCsizei size, ALCint64SOFT *values)
-START_API_FUNC
+ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALCsizei size, ALCint64SOFT *values) noexcept
{
DeviceRef dev{VerifyDevice(device)};
if(size <= 0 || values == nullptr)
@@ -3078,7 +2409,7 @@ START_API_FUNC
}
if(!dev || dev->Type == DeviceType::Capture)
{
- auto ivals = al::vector<int>(static_cast<uint>(size));
+ auto ivals = std::vector<int>(static_cast<uint>(size));
if(size_t got{GetIntegerv(dev.get(), pname, ivals)})
std::copy_n(ivals.begin(), got, values);
return;
@@ -3197,17 +2528,15 @@ START_API_FUNC
break;
default:
- auto ivals = al::vector<int>(static_cast<uint>(size));
+ auto ivals = std::vector<int>(static_cast<uint>(size));
if(size_t got{GetIntegerv(dev.get(), pname, ivals)})
std::copy_n(ivals.begin(), got, values);
break;
}
}
-END_API_FUNC
-ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extName)
-START_API_FUNC
+ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extName) noexcept
{
DeviceRef dev{VerifyDevice(device)};
if(!extName)
@@ -3231,11 +2560,12 @@ START_API_FUNC
}
return ALC_FALSE;
}
-END_API_FUNC
-ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName)
-START_API_FUNC
+ALCvoid* ALC_APIENTRY alcGetProcAddress2(ALCdevice *device, const ALCchar *funcName) noexcept
+{ return alcGetProcAddress(device, funcName); }
+
+ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName) noexcept
{
if(!funcName)
{
@@ -3243,6 +2573,7 @@ START_API_FUNC
alcSetError(dev.get(), ALC_INVALID_VALUE);
return nullptr;
}
+
#ifdef ALSOFT_EAX
if(eax_g_is_enabled)
{
@@ -3260,11 +2591,9 @@ START_API_FUNC
}
return nullptr;
}
-END_API_FUNC
-ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName)
-START_API_FUNC
+ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName) noexcept
{
if(!enumName)
{
@@ -3272,6 +2601,7 @@ START_API_FUNC
alcSetError(dev.get(), ALC_INVALID_VALUE);
return 0;
}
+
#ifdef ALSOFT_EAX
if(eax_g_is_enabled)
{
@@ -3290,11 +2620,9 @@ START_API_FUNC
return 0;
}
-END_API_FUNC
-ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList)
-START_API_FUNC
+ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList) noexcept
{
/* Explicitly hold the list lock while taking the StateLock in case the
* device is asynchronously destroyed, to ensure this new context is
@@ -3320,7 +2648,34 @@ START_API_FUNC
return nullptr;
}
- ContextRef context{new ALCcontext{dev}};
+ ContextFlagBitset ctxflags{0};
+ if(attrList)
+ {
+ for(size_t i{0};attrList[i];i+=2)
+ {
+ if(attrList[i] == ALC_CONTEXT_FLAGS_EXT)
+ {
+ ctxflags = static_cast<ALuint>(attrList[i+1]);
+ break;
+ }
+ }
+ }
+
+ ContextRef context{[](auto&& ...args) -> ContextRef
+ {
+ try {
+ return ContextRef{new ALCcontext{std::forward<decltype(args)>(args)...}};
+ }
+ catch(std::exception& e) {
+ ERR("Failed to create ALCcontext: %s\n", e.what());
+ return ContextRef{};
+ }
+ }(dev, ctxflags)};
+ if(!context)
+ {
+ alcSetError(dev.get(), ALC_OUT_OF_MEMORY);
+ return nullptr;
+ }
context->init();
if(auto volopt = dev->configValue<float>(nullptr, "volume-adjust"))
@@ -3374,7 +2729,7 @@ START_API_FUNC
if(ALeffectslot *slot{context->mDefaultSlot.get()})
{
- ALenum sloterr{slot->initEffect(ALCcontext::sDefaultEffect.type,
+ ALenum sloterr{slot->initEffect(0, ALCcontext::sDefaultEffect.type,
ALCcontext::sDefaultEffect.Props, context.get())};
if(sloterr == AL_NO_ERROR)
slot->updateProps(context.get());
@@ -3385,10 +2740,8 @@ START_API_FUNC
TRACE("Created context %p\n", voidp{context.get()});
return context.release();
}
-END_API_FUNC
-ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context)
-START_API_FUNC
+ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) noexcept
{
std::unique_lock<std::recursive_mutex> listlock{ListLock};
auto iter = std::lower_bound(ContextList.begin(), ContextList.end(), context);
@@ -3414,26 +2767,20 @@ START_API_FUNC
Device->Flags.reset(DeviceRunning);
}
}
-END_API_FUNC
-ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void)
-START_API_FUNC
+ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) noexcept
{
ALCcontext *Context{ALCcontext::getThreadContext()};
if(!Context) Context = ALCcontext::sGlobalContext.load();
return Context;
}
-END_API_FUNC
/** Returns the currently active thread-local context. */
-ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void)
-START_API_FUNC
+ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) noexcept
{ return ALCcontext::getThreadContext(); }
-END_API_FUNC
-ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context)
-START_API_FUNC
+ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) noexcept
{
/* context must be valid or nullptr */
ContextRef ctx;
@@ -3467,11 +2814,9 @@ START_API_FUNC
return ALC_TRUE;
}
-END_API_FUNC
/** Makes the given context the active context for the current thread. */
-ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context)
-START_API_FUNC
+ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) noexcept
{
/* context must be valid or nullptr */
ContextRef ctx;
@@ -3490,11 +2835,9 @@ START_API_FUNC
return ALC_TRUE;
}
-END_API_FUNC
-ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context)
-START_API_FUNC
+ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context) noexcept
{
ContextRef ctx{VerifyContext(Context)};
if(!ctx)
@@ -3504,11 +2847,9 @@ START_API_FUNC
}
return ctx->mALDevice.get();
}
-END_API_FUNC
-ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
-START_API_FUNC
+ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) noexcept
{
InitConfig();
@@ -3565,9 +2906,26 @@ START_API_FUNC
device->NumAuxSends = DefaultSends;
try {
+ /* We need to ensure the device name isn't too long. The string_view is
+ * printed using the "%.*s" formatter, which uses an int for the
+ * precision/length. It wouldn't be a significant problem if larger
+ * values simply printed fewer characters due to truncation, but
+ * negative values are ignored, treating it like a normal null-
+ * terminated string, and string_views don't need to be null-
+ * terminated.
+ *
+ * Other than the annoyance of checking, this shouldn't be a problem.
+ * Two billion bytes is enough for a device name.
+ */
+ const std::string_view devname{deviceName ? deviceName : ""};
+ if(devname.length() >= std::numeric_limits<int>::max())
+ throw al::backend_exception{al::backend_error::NoDevice,
+ "Device name too long (%zu >= %d)", devname.length(),
+ std::numeric_limits<int>::max()};
+
auto backend = PlaybackFactory->createBackend(device.get(), BackendType::Playback);
std::lock_guard<std::recursive_mutex> _{ListLock};
- backend->open(deviceName);
+ backend->open(devname);
device->Backend = std::move(backend);
}
catch(al::backend_exception &e) {
@@ -3586,10 +2944,8 @@ START_API_FUNC
TRACE("Created device %p, \"%s\"\n", voidp{device.get()}, device->DeviceName.c_str());
return device.release();
}
-END_API_FUNC
-ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
-START_API_FUNC
+ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept
{
std::unique_lock<std::recursive_mutex> listlock{ListLock};
auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device);
@@ -3611,7 +2967,7 @@ START_API_FUNC
DeviceList.erase(iter);
std::unique_lock<std::mutex> statelock{dev->StateLock};
- al::vector<ContextRef> orphanctxs;
+ std::vector<ContextRef> orphanctxs;
for(ContextBase *ctx : *dev->mContexts.load())
{
auto ctxiter = std::lower_bound(ContextList.begin(), ContextList.end(), ctx);
@@ -3636,14 +2992,12 @@ START_API_FUNC
return ALC_TRUE;
}
-END_API_FUNC
/************************************************
* ALC capture functions
************************************************/
-ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples)
-START_API_FUNC
+ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples) noexcept
{
InitConfig();
@@ -3688,14 +3042,20 @@ START_API_FUNC
device->UpdateSize = static_cast<uint>(samples);
device->BufferSize = static_cast<uint>(samples);
+ TRACE("Capture format: %s, %s, %uhz, %u / %u buffer\n", DevFmtChannelsString(device->FmtChans),
+ DevFmtTypeString(device->FmtType), device->Frequency, device->UpdateSize,
+ device->BufferSize);
+
try {
- TRACE("Capture format: %s, %s, %uhz, %u / %u buffer\n",
- DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
- device->Frequency, device->UpdateSize, device->BufferSize);
+ const std::string_view devname{deviceName ? deviceName : ""};
+ if(devname.length() >= std::numeric_limits<int>::max())
+ throw al::backend_exception{al::backend_error::NoDevice,
+ "Device name too long (%zu >= %d)", devname.length(),
+ std::numeric_limits<int>::max()};
auto backend = CaptureFactory->createBackend(device.get(), BackendType::Capture);
std::lock_guard<std::recursive_mutex> _{ListLock};
- backend->open(deviceName);
+ backend->open(devname);
device->Backend = std::move(backend);
}
catch(al::backend_exception &e) {
@@ -3714,10 +3074,8 @@ START_API_FUNC
TRACE("Created capture device %p, \"%s\"\n", voidp{device.get()}, device->DeviceName.c_str());
return device.release();
}
-END_API_FUNC
-ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
-START_API_FUNC
+ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) noexcept
{
std::unique_lock<std::recursive_mutex> listlock{ListLock};
auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device);
@@ -3743,10 +3101,8 @@ START_API_FUNC
return ALC_TRUE;
}
-END_API_FUNC
-ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
-START_API_FUNC
+ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) noexcept
{
DeviceRef dev{VerifyDevice(device)};
if(!dev || dev->Type != DeviceType::Capture)
@@ -3772,10 +3128,8 @@ START_API_FUNC
}
}
}
-END_API_FUNC
-ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
-START_API_FUNC
+ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) noexcept
{
DeviceRef dev{VerifyDevice(device)};
if(!dev || dev->Type != DeviceType::Capture)
@@ -3788,10 +3142,8 @@ START_API_FUNC
dev->Flags.reset(DeviceRunning);
}
}
-END_API_FUNC
-ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
-START_API_FUNC
+ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) noexcept
{
DeviceRef dev{VerifyDevice(device)};
if(!dev || dev->Type != DeviceType::Capture)
@@ -3818,9 +3170,8 @@ START_API_FUNC
return;
}
- backend->captureSamples(static_cast<al::byte*>(buffer), usamples);
+ backend->captureSamples(static_cast<std::byte*>(buffer), usamples);
}
-END_API_FUNC
/************************************************
@@ -3828,8 +3179,7 @@ END_API_FUNC
************************************************/
/** Open a loopback device, for manual rendering. */
-ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName)
-START_API_FUNC
+ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName) noexcept
{
InitConfig();
@@ -3886,13 +3236,11 @@ START_API_FUNC
TRACE("Created loopback device %p\n", voidp{device.get()});
return device.release();
}
-END_API_FUNC
/**
* Determines if the loopback device supports the given format for rendering.
*/
-ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type)
-START_API_FUNC
+ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type) noexcept
{
DeviceRef dev{VerifyDevice(device)};
if(!dev || dev->Type != DeviceType::Loopback)
@@ -3908,23 +3256,26 @@ START_API_FUNC
return ALC_FALSE;
}
-END_API_FUNC
/**
* Renders some samples into a buffer, using the format last set by the
* attributes given to alcCreateContext.
*/
-FORCE_ALIGN ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
-START_API_FUNC
+#if defined(__GNUC__) && defined(__i386__)
+/* Needed on x86-32 even without SSE codegen, since the mixer may still use SSE
+ * and GCC assumes the stack is aligned (x86-64 ABI guarantees alignment).
+ */
+[[gnu::force_align_arg_pointer]]
+#endif
+ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) noexcept
{
- if(!device || device->Type != DeviceType::Loopback)
+ if(!device || device->Type != DeviceType::Loopback) UNLIKELY
alcSetError(device, ALC_INVALID_DEVICE);
- else if(samples < 0 || (samples > 0 && buffer == nullptr))
+ else if(samples < 0 || (samples > 0 && buffer == nullptr)) UNLIKELY
alcSetError(device, ALC_INVALID_VALUE);
else
device->renderSamples(buffer, static_cast<uint>(samples), device->channelsFromFmt());
}
-END_API_FUNC
/************************************************
@@ -3932,8 +3283,7 @@ END_API_FUNC
************************************************/
/** Pause the DSP to stop audio processing. */
-ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device)
-START_API_FUNC
+ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) noexcept
{
DeviceRef dev{VerifyDevice(device)};
if(!dev || dev->Type != DeviceType::Playback)
@@ -3947,11 +3297,9 @@ START_API_FUNC
dev->Flags.set(DevicePaused);
}
}
-END_API_FUNC
/** Resume the DSP to restart audio processing. */
-ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device)
-START_API_FUNC
+ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept
{
DeviceRef dev{VerifyDevice(device)};
if(!dev || dev->Type != DeviceType::Playback)
@@ -3982,7 +3330,6 @@ START_API_FUNC
DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
device->Frequency, device->UpdateSize, device->BufferSize);
}
-END_API_FUNC
/************************************************
@@ -3990,8 +3337,7 @@ END_API_FUNC
************************************************/
/** Gets a string parameter at the given index. */
-ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index)
-START_API_FUNC
+ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index) noexcept
{
DeviceRef dev{VerifyDevice(device)};
if(!dev || dev->Type == DeviceType::Capture)
@@ -4011,11 +3357,9 @@ START_API_FUNC
return nullptr;
}
-END_API_FUNC
/** Resets the given device output, using the specified attribute list. */
-ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs)
-START_API_FUNC
+ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs) noexcept
{
std::unique_lock<std::recursive_mutex> listlock{ListLock};
DeviceRef dev{VerifyDevice(device)};
@@ -4037,7 +3381,6 @@ START_API_FUNC
return ResetDeviceParams(dev.get(), attribs) ? ALC_TRUE : ALC_FALSE;
}
-END_API_FUNC
/************************************************
@@ -4046,8 +3389,7 @@ END_API_FUNC
/** Reopens the given device output, using the specified name and attribute list. */
FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device,
- const ALCchar *deviceName, const ALCint *attribs)
-START_API_FUNC
+ const ALCchar *deviceName, const ALCint *attribs) noexcept
{
if(deviceName)
{
@@ -4075,8 +3417,14 @@ START_API_FUNC
BackendPtr newbackend;
try {
+ const std::string_view devname{deviceName ? deviceName : ""};
+ if(devname.length() >= std::numeric_limits<int>::max())
+ throw al::backend_exception{al::backend_error::NoDevice,
+ "Device name too long (%zu >= %d)", devname.length(),
+ std::numeric_limits<int>::max()};
+
newbackend = PlaybackFactory->createBackend(dev.get(), BackendType::Playback);
- newbackend->open(deviceName);
+ newbackend->open(devname);
}
catch(al::backend_exception &e) {
listlock.unlock();
@@ -4122,4 +3470,37 @@ START_API_FUNC
ResetDeviceParams(dev.get(), attribs);
return ALC_TRUE;
}
-END_API_FUNC
+
+/************************************************
+ * ALC event query functions
+ ************************************************/
+
+FORCE_ALIGN ALCenum ALC_APIENTRY alcEventIsSupportedSOFT(ALCenum eventType, ALCenum deviceType) noexcept
+{
+ auto etype = alc::GetEventType(eventType);
+ if(!etype)
+ {
+ WARN("Invalid event type: 0x%04x\n", eventType);
+ alcSetError(nullptr, ALC_INVALID_ENUM);
+ return ALC_EVENT_NOT_SUPPORTED_SOFT;
+ }
+
+ auto supported = alc::EventSupport::NoSupport;
+ switch(deviceType)
+ {
+ case ALC_PLAYBACK_DEVICE_SOFT:
+ if(PlaybackFactory)
+ supported = PlaybackFactory->queryEventSupport(*etype, BackendType::Playback);
+ break;
+
+ case ALC_CAPTURE_DEVICE_SOFT:
+ if(CaptureFactory)
+ supported = CaptureFactory->queryEventSupport(*etype, BackendType::Capture);
+ break;
+
+ default:
+ WARN("Invalid device type: 0x%04x\n", deviceType);
+ alcSetError(nullptr, ALC_INVALID_ENUM);
+ }
+ return al::to_underlying(supported);
+}
diff --git a/alc/alconfig.cpp b/alc/alconfig.cpp
index b0544b89..cfa178fb 100644
--- a/alc/alconfig.cpp
+++ b/alc/alconfig.cpp
@@ -45,6 +45,13 @@
#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 {
@@ -52,7 +59,7 @@ struct ConfigEntry {
std::string key;
std::string value;
};
-al::vector<ConfigEntry> ConfOpts;
+std::vector<ConfigEntry> ConfOpts;
std::string &lstrip(std::string &line)
@@ -258,7 +265,7 @@ void LoadConfigFromFile(std::istream &f)
}
}
- TRACE(" found '%s' = '%s'\n", fullKey.c_str(), value.c_str());
+ TRACE(" setting '%s' = '%s'\n", fullKey.c_str(), value.c_str());
/* Check if we already have this option set */
auto find_key = [&fullKey](const ConfigEntry &entry) -> bool
@@ -309,17 +316,14 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha
{ 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());
return nullptr;
- }
return GetConfigValue(nullptr, blockName, keyName);
}
@@ -329,9 +333,16 @@ 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)
+#if !defined(_GAMING_XBOX)
{
+#if !defined(ALSOFT_UWP)
+ WCHAR buffer[MAX_PATH];
+ if (!SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE))
+ return;
+#else
+ winrt::Windows::Storage::ApplicationDataContainer localSettings = winrt::Windows::Storage::ApplicationData::Current().LocalSettings();
+ auto buffer = Windows::Storage::ApplicationData::Current().RoamingFolder().Path();
+#endif
std::string filepath{wstr_to_utf8(buffer)};
filepath += "\\alsoft.ini";
@@ -340,6 +351,8 @@ void ReadALConfig()
if(f.is_open())
LoadConfigFromFile(f);
}
+#endif
+
std::string ppath{GetProcBinary().path};
if(!ppath.empty())
@@ -483,40 +496,40 @@ void ReadALConfig()
}
#endif
-al::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *keyName)
+std::optional<std::string> ConfigValueStr(const char *devName, const char *blockName, const char *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 char *devName, const char *blockName, const char *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 char *devName, const char *blockName, const char *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 char *devName, const char *blockName, const char *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 char *devName, const char *blockName, const char *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;
+ return std::nullopt;
}
bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def)
diff --git a/alc/alconfig.h b/alc/alconfig.h
index df2830cc..1eb44405 100644
--- a/alc/alconfig.h
+++ b/alc/alconfig.h
@@ -1,18 +1,18 @@
#ifndef ALCONFIG_H
#define ALCONFIG_H
+#include <optional>
#include <string>
-#include "aloptional.h"
void ReadALConfig();
bool GetConfigValueBool(const char *devName, const char *blockName, const char *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 char *devName, const char *blockName, const char *keyName);
+std::optional<int> ConfigValueInt(const char *devName, const char *blockName, const char *keyName);
+std::optional<unsigned int> ConfigValueUInt(const char *devName, const char *blockName, const char *keyName);
+std::optional<float> ConfigValueFloat(const char *devName, const char *blockName, const char *keyName);
+std::optional<bool> ConfigValueBool(const char *devName, const char *blockName, const char *keyName);
#endif /* ALCONFIG_H */
diff --git a/alc/alu.cpp b/alc/alu.cpp
index e9ad68b1..e0858b18 100644
--- a/alc/alu.cpp
+++ b/alc/alu.cpp
@@ -36,6 +36,7 @@
#include <limits>
#include <memory>
#include <new>
+#include <optional>
#include <stdint.h>
#include <utility>
@@ -76,7 +77,6 @@
#include "opthelpers.h"
#include "ringbuffer.h"
#include "strutils.h"
-#include "threads.h"
#include "vecmat.h"
#include "vector.h"
@@ -135,12 +135,6 @@ float ZScale{1.0f};
float NfcScale{1.0f};
-struct ChanMap {
- Channel channel;
- float angle;
- float elevation;
-};
-
using HrtfDirectMixerFunc = void(*)(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut,
const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples, float *TempBuf,
HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize);
@@ -285,8 +279,8 @@ ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState
void DeviceBase::ProcessHrtf(const size_t SamplesToDo)
{
/* HRTF is stereo output only. */
- const uint lidx{RealOut.ChannelIndex[FrontLeft]};
- const uint ridx{RealOut.ChannelIndex[FrontRight]};
+ const size_t lidx{RealOut.ChannelIndex[FrontLeft]};
+ const size_t ridx{RealOut.ChannelIndex[FrontRight]};
MixDirectHrtf(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer, HrtfAccumData,
mHrtfState->mTemp.data(), mHrtfState->mChannels.data(), mHrtfState->mIrSize, SamplesToDo);
@@ -300,9 +294,9 @@ void DeviceBase::ProcessAmbiDec(const size_t SamplesToDo)
void DeviceBase::ProcessAmbiDecStablized(const size_t SamplesToDo)
{
/* Decode with front image stablization. */
- const uint lidx{RealOut.ChannelIndex[FrontLeft]};
- const uint ridx{RealOut.ChannelIndex[FrontRight]};
- const uint cidx{RealOut.ChannelIndex[FrontCenter]};
+ const size_t lidx{RealOut.ChannelIndex[FrontLeft]};
+ const size_t ridx{RealOut.ChannelIndex[FrontRight]};
+ const size_t cidx{RealOut.ChannelIndex[FrontCenter]};
AmbiDecoder->processStablize(RealOut.Buffer, Dry.Buffer.data(), lidx, ridx, cidx,
SamplesToDo);
@@ -311,8 +305,8 @@ void DeviceBase::ProcessAmbiDecStablized(const size_t SamplesToDo)
void DeviceBase::ProcessUhj(const size_t SamplesToDo)
{
/* UHJ is stereo output only. */
- const uint lidx{RealOut.ChannelIndex[FrontLeft]};
- const uint ridx{RealOut.ChannelIndex[FrontRight]};
+ const size_t lidx{RealOut.ChannelIndex[FrontLeft]};
+ const size_t ridx{RealOut.ChannelIndex[FrontRight]};
/* Encode to stereo-compatible 2-channel UHJ output. */
mUhjEncoder->encode(RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(),
@@ -325,8 +319,8 @@ void DeviceBase::ProcessBs2b(const size_t SamplesToDo)
AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo);
/* BS2B is stereo output only. */
- const uint lidx{RealOut.ChannelIndex[FrontLeft]};
- const uint ridx{RealOut.ChannelIndex[FrontRight]};
+ const size_t lidx{RealOut.ChannelIndex[FrontLeft]};
+ const size_t ridx{RealOut.ChannelIndex[FrontRight]};
/* Now apply the BS2B binaural/crossfeed filter. */
bs2b_cross_feed(Bs2b.get(), RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(),
@@ -376,28 +370,28 @@ void UpsampleBFormatTransform(
}
-inline auto& GetAmbiScales(AmbiScaling scaletype) noexcept
+constexpr auto GetAmbiScales(AmbiScaling scaletype) noexcept
{
switch(scaletype)
{
- case AmbiScaling::FuMa: return AmbiScale::FromFuMa();
- case AmbiScaling::SN3D: return AmbiScale::FromSN3D();
- case AmbiScaling::UHJ: return AmbiScale::FromUHJ();
+ case AmbiScaling::FuMa: return al::span{AmbiScale::FromFuMa};
+ case AmbiScaling::SN3D: return al::span{AmbiScale::FromSN3D};
+ case AmbiScaling::UHJ: return al::span{AmbiScale::FromUHJ};
case AmbiScaling::N3D: break;
}
- return AmbiScale::FromN3D();
+ return al::span{AmbiScale::FromN3D};
}
-inline auto& GetAmbiLayout(AmbiLayout layouttype) noexcept
+constexpr auto GetAmbiLayout(AmbiLayout layouttype) noexcept
{
- if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa();
- return AmbiIndex::FromACN();
+ if(layouttype == AmbiLayout::FuMa) return al::span{AmbiIndex::FromFuMa};
+ return al::span{AmbiIndex::FromACN};
}
-inline auto& GetAmbi2DLayout(AmbiLayout layouttype) noexcept
+constexpr auto GetAmbi2DLayout(AmbiLayout layouttype) noexcept
{
- if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa2D();
- return AmbiIndex::FromACN2D();
+ if(layouttype == AmbiLayout::FuMa) return al::span{AmbiIndex::FromFuMa2D};
+ return al::span{AmbiIndex::FromACN2D};
}
@@ -490,9 +484,8 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa
auto evt_vec = ring->getWriteVector();
if(evt_vec.first.len > 0) LIKELY
{
- AsyncEvent *evt{al::construct_at(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf),
- AsyncEvent::ReleaseEffectState)};
- evt->u.mEffectState = oldstate;
+ auto &evt = InitAsyncEvent<AsyncEffectReleaseEvent>(evt_vec.first.buf);
+ evt.mEffectState = oldstate;
ring->writeAdvance(1);
}
else
@@ -521,27 +514,76 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa
}
-/* Scales the given azimuth toward the side (+/- pi/2 radians) for positions in
- * front.
+/* Scales the azimuth of the given vector by 3 if it's in front. Effectively
+ * scales +/-30 degrees to +/-90 degrees, leaving > +90 and < -90 alone.
*/
-inline float ScaleAzimuthFront(float azimuth, float scale)
+inline std::array<float,3> ScaleAzimuthFront3(std::array<float,3> pos)
{
- const float abs_azi{std::fabs(azimuth)};
- if(!(abs_azi >= al::numbers::pi_v<float>*0.5f))
- return std::copysign(minf(abs_azi*scale, al::numbers::pi_v<float>*0.5f), azimuth);
- return azimuth;
+ if(pos[2] < 0.0f)
+ {
+ /* Normalize the length of the x,z components for a 2D vector of the
+ * azimuth angle. Negate Z since {0,0,-1} is angle 0.
+ */
+ const float len2d{std::sqrt(pos[0]*pos[0] + pos[2]*pos[2])};
+ float x{pos[0] / len2d};
+ float z{-pos[2] / len2d};
+
+ /* Z > cos(pi/6) = -30 < azimuth < 30 degrees. */
+ if(z > 0.866025403785f)
+ {
+ /* Triple the angle represented by x,z. */
+ x = x*3.0f - x*x*x*4.0f;
+ z = z*z*z*4.0f - z*3.0f;
+
+ /* Scale the vector back to fit in 3D. */
+ pos[0] = x * len2d;
+ pos[2] = -z * len2d;
+ }
+ else
+ {
+ /* If azimuth >= 30 degrees, clamp to 90 degrees. */
+ pos[0] = std::copysign(len2d, pos[0]);
+ pos[2] = 0.0f;
+ }
+ }
+ return pos;
}
-/* Wraps the given value in radians to stay between [-pi,+pi] */
-inline float WrapRadians(float r)
+/* Scales the azimuth of the given vector by 1.5 (3/2) if it's in front. */
+inline std::array<float,3> ScaleAzimuthFront3_2(std::array<float,3> pos)
{
- static constexpr float Pi{al::numbers::pi_v<float>};
- static constexpr float Pi2{Pi*2.0f};
- if(r > Pi) return std::fmod(Pi+r, Pi2) - Pi;
- if(r < -Pi) return Pi - std::fmod(Pi-r, Pi2);
- return r;
+ if(pos[2] < 0.0f)
+ {
+ const float len2d{std::sqrt(pos[0]*pos[0] + pos[2]*pos[2])};
+ float x{pos[0] / len2d};
+ float z{-pos[2] / len2d};
+
+ /* Z > cos(pi/3) = -60 < azimuth < 60 degrees. */
+ if(z > 0.5f)
+ {
+ /* Halve the angle represented by x,z. */
+ x = std::copysign(std::sqrt((1.0f - z) * 0.5f), x);
+ z = std::sqrt((1.0f + z) * 0.5f);
+
+ /* Triple the angle represented by x,z. */
+ x = x*3.0f - x*x*x*4.0f;
+ z = z*z*z*4.0f - z*3.0f;
+
+ /* Scale the vector back to fit in 3D. */
+ pos[0] = x * len2d;
+ pos[2] = -z * len2d;
+ }
+ else
+ {
+ /* If azimuth >= 60 degrees, clamp to 90 degrees. */
+ pos[0] = std::copysign(len2d, pos[0]);
+ pos[2] = 0.0f;
+ }
+ }
+ return pos;
}
+
/* Begin ambisonic rotation helpers.
*
* Rotating first-order B-Format just needs a straight-forward X/Y/Z rotation
@@ -561,19 +603,18 @@ inline float WrapRadians(float r)
* precomputed since they're constant. The second-order coefficients are
* followed by the third-order coefficients, etc.
*/
-template<size_t L>
-constexpr size_t CalcRotatorSize()
-{ return (L*2 + 1)*(L*2 + 1) + CalcRotatorSize<L-1>(); }
-
-template<> constexpr size_t CalcRotatorSize<0>() = delete;
-template<> constexpr size_t CalcRotatorSize<1>() = delete;
-template<> constexpr size_t CalcRotatorSize<2>() { return 5*5; }
+constexpr size_t CalcRotatorSize(size_t l) noexcept
+{
+ if(l >= 2)
+ return (l*2 + 1)*(l*2 + 1) + CalcRotatorSize(l-1);
+ return 0;
+}
struct RotatorCoeffs {
struct CoeffValues {
float u, v, w;
};
- std::array<CoeffValues,CalcRotatorSize<MaxAmbiOrder>()> mCoeffs{};
+ std::array<CoeffValues,CalcRotatorSize(MaxAmbiOrder)> mCoeffs{};
RotatorCoeffs()
{
@@ -585,17 +626,38 @@ struct RotatorCoeffs {
{
for(int m{-l};m <= l;++m)
{
- // compute u,v,w terms of Eq.8.1 (Table I)
- const bool d{m == 0}; // the delta function d_m0
- const float denom{static_cast<float>((std::abs(n) == l) ?
- (2*l) * (2*l - 1) : (l*l - n*n))};
-
- const int abs_m{std::abs(m)};
- coeffs->u = std::sqrt(static_cast<float>(l*l - m*m)/denom);
- coeffs->v = std::sqrt(static_cast<float>(l+abs_m-1) *
- static_cast<float>(l+abs_m) / denom) * (1.0f+d) * (1.0f - 2.0f*d) * 0.5f;
- coeffs->w = std::sqrt(static_cast<float>(l-abs_m-1) *
- static_cast<float>(l-abs_m) / denom) * (1.0f-d) * -0.5f;
+ /* compute u,v,w terms of Eq.8.1 (Table I)
+ *
+ * const bool d{m == 0}; // the delta function d_m0
+ * const double denom{(std::abs(n) == l) ?
+ * (2*l) * (2*l - 1) : (l*l - n*n)};
+ *
+ * const int abs_m{std::abs(m)};
+ * coeffs->u = std::sqrt((l*l - m*m) / denom);
+ * coeffs->v = std::sqrt((l+abs_m-1) * (l+abs_m) / denom) *
+ * (1.0+d) * (1.0 - 2.0*d) * 0.5;
+ * coeffs->w = std::sqrt((l-abs_m-1) * (l-abs_m) / denom) *
+ * (1.0-d) * -0.5;
+ */
+
+ const double denom{static_cast<double>((std::abs(n) == l) ?
+ (2*l) * (2*l - 1) : (l*l - n*n))};
+
+ if(m == 0)
+ {
+ coeffs->u = static_cast<float>(std::sqrt(l * l / denom));
+ coeffs->v = static_cast<float>(std::sqrt((l-1) * l / denom) * -1.0);
+ coeffs->w = 0.0f;
+ }
+ else
+ {
+ const int abs_m{std::abs(m)};
+ coeffs->u = static_cast<float>(std::sqrt((l*l - m*m) / denom));
+ coeffs->v = static_cast<float>(std::sqrt((l+abs_m-1) * (l+abs_m) / denom) *
+ 0.5);
+ coeffs->w = static_cast<float>(std::sqrt((l-abs_m-1) * (l-abs_m) / denom) *
+ -0.5);
+ }
++coeffs;
}
}
@@ -679,26 +741,36 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order)
float r{0.0f};
// computes Eq.8.1
- const float u{coeffs->u};
- if(u != 0.0f) r += u * U(l, m, n, last_band, matrix);
- const float v{coeffs->v};
- if(v != 0.0f) r += v * V(l, m, n, last_band, matrix);
- const float w{coeffs->w};
- if(w != 0.0f) r += w * W(l, m, n, last_band, matrix);
+ if(const float u{coeffs->u}; u != 0.0f)
+ r += u * U(l, m, n, last_band, matrix);
+ if(const float v{coeffs->v}; v != 0.0f)
+ r += v * V(l, m, n, last_band, matrix);
+ if(const float w{coeffs->w}; w != 0.0f)
+ r += w * W(l, m, n, last_band, matrix);
matrix[y][x] = r;
++coeffs;
}
}
last_band = band_idx;
- band_idx += static_cast<uint>(l)*size_t{2} + 1;
+ band_idx += static_cast<uint>(l)*2_uz + 1;
}
}
/* End ambisonic rotation helpers. */
-constexpr float Deg2Rad(float x) noexcept
-{ return static_cast<float>(al::numbers::pi / 180.0 * x); }
+constexpr float sin30{0.5f};
+constexpr float cos30{0.866025403785f};
+constexpr float sin45{al::numbers::sqrt2_v<float>*0.5f};
+constexpr float cos45{al::numbers::sqrt2_v<float>*0.5f};
+constexpr float sin110{ 0.939692620786f};
+constexpr float cos110{-0.342020143326f};
+
+struct ChanPosMap {
+ Channel channel;
+ std::array<float,3> pos;
+};
+
struct GainTriplet { float Base, HF, LF; };
@@ -707,45 +779,45 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
const al::span<const GainTriplet,MAX_SENDS> WetGain, EffectSlot *(&SendSlots)[MAX_SENDS],
const VoiceProps *props, const ContextParams &Context, DeviceBase *Device)
{
- static constexpr ChanMap MonoMap[1]{
- { FrontCenter, 0.0f, 0.0f }
+ static constexpr ChanPosMap MonoMap[1]{
+ { FrontCenter, std::array{0.0f, 0.0f, -1.0f} }
}, RearMap[2]{
- { BackLeft, Deg2Rad(-150.0f), Deg2Rad(0.0f) },
- { BackRight, Deg2Rad( 150.0f), Deg2Rad(0.0f) }
+ { BackLeft, std::array{-sin30, 0.0f, cos30} },
+ { BackRight, std::array{ sin30, 0.0f, cos30} },
}, QuadMap[4]{
- { FrontLeft, Deg2Rad( -45.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 45.0f), Deg2Rad(0.0f) },
- { BackLeft, Deg2Rad(-135.0f), Deg2Rad(0.0f) },
- { BackRight, Deg2Rad( 135.0f), Deg2Rad(0.0f) }
+ { FrontLeft, std::array{-sin45, 0.0f, -cos45} },
+ { FrontRight, std::array{ sin45, 0.0f, -cos45} },
+ { BackLeft, std::array{-sin45, 0.0f, cos45} },
+ { BackRight, std::array{ sin45, 0.0f, cos45} },
}, X51Map[6]{
- { FrontLeft, Deg2Rad( -30.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) },
- { FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) },
- { LFE, 0.0f, 0.0f },
- { SideLeft, Deg2Rad(-110.0f), Deg2Rad(0.0f) },
- { SideRight, Deg2Rad( 110.0f), Deg2Rad(0.0f) }
+ { FrontLeft, std::array{-sin30, 0.0f, -cos30} },
+ { FrontRight, std::array{ sin30, 0.0f, -cos30} },
+ { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} },
+ { LFE, {} },
+ { SideLeft, std::array{-sin110, 0.0f, -cos110} },
+ { SideRight, std::array{ sin110, 0.0f, -cos110} },
}, X61Map[7]{
- { FrontLeft, Deg2Rad(-30.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) },
- { FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) },
- { LFE, 0.0f, 0.0f },
- { BackCenter, Deg2Rad(180.0f), Deg2Rad(0.0f) },
- { SideLeft, Deg2Rad(-90.0f), Deg2Rad(0.0f) },
- { SideRight, Deg2Rad( 90.0f), Deg2Rad(0.0f) }
+ { FrontLeft, std::array{-sin30, 0.0f, -cos30} },
+ { FrontRight, std::array{ sin30, 0.0f, -cos30} },
+ { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} },
+ { LFE, {} },
+ { BackCenter, std::array{ 0.0f, 0.0f, 1.0f} },
+ { SideLeft, std::array{-1.0f, 0.0f, 0.0f} },
+ { SideRight, std::array{ 1.0f, 0.0f, 0.0f} },
}, X71Map[8]{
- { FrontLeft, Deg2Rad( -30.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) },
- { FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) },
- { LFE, 0.0f, 0.0f },
- { BackLeft, Deg2Rad(-150.0f), Deg2Rad(0.0f) },
- { BackRight, Deg2Rad( 150.0f), Deg2Rad(0.0f) },
- { SideLeft, Deg2Rad( -90.0f), Deg2Rad(0.0f) },
- { SideRight, Deg2Rad( 90.0f), Deg2Rad(0.0f) }
+ { FrontLeft, std::array{-sin30, 0.0f, -cos30} },
+ { FrontRight, std::array{ sin30, 0.0f, -cos30} },
+ { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} },
+ { LFE, {} },
+ { BackLeft, std::array{-sin30, 0.0f, cos30} },
+ { BackRight, std::array{ sin30, 0.0f, cos30} },
+ { SideLeft, std::array{ -1.0f, 0.0f, 0.0f} },
+ { SideRight, std::array{ 1.0f, 0.0f, 0.0f} },
};
- ChanMap StereoMap[2]{
- { FrontLeft, Deg2Rad(-30.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) }
+ ChanPosMap StereoMap[2]{
+ { FrontLeft, std::array{-sin30, 0.0f, -cos30} },
+ { FrontRight, std::array{ sin30, 0.0f, -cos30} },
};
const auto Frequency = static_cast<float>(Device->Frequency);
@@ -763,7 +835,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
}
DirectMode DirectChannels{props->DirectChannels};
- const ChanMap *chans{nullptr};
+ const ChanPosMap *chans{nullptr};
switch(voice->mFmtChannels)
{
case FmtMono:
@@ -775,11 +847,13 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
case FmtStereo:
if(DirectChannels == DirectMode::Off)
{
- /* Convert counter-clockwise to clock-wise, and wrap between
- * [-pi,+pi].
- */
- StereoMap[0].angle = WrapRadians(-props->StereoPan[0]);
- StereoMap[1].angle = WrapRadians(-props->StereoPan[1]);
+ for(size_t i{0};i < 2;++i)
+ {
+ /* StereoPan is counter-clockwise in radians. */
+ const float a{props->StereoPan[i]};
+ StereoMap[i].pos[0] = -std::sin(a);
+ StereoMap[i].pos[2] = -std::cos(a);
+ }
}
chans = StereoMap;
break;
@@ -845,32 +919,21 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
auto calc_coeffs = [xpos,ypos,zpos](RenderMode mode)
{
if(mode != RenderMode::Pairwise)
- return CalcDirectionCoeffs({xpos, ypos, zpos});
-
- /* Clamp Y, in case rounding errors caused it to end up outside
- * of -1...+1.
- */
- const float ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
- /* Negate Z for right-handed coords with -Z in front. */
- const float az{std::atan2(xpos, -zpos)};
-
- /* A scalar of 1.5 for plain stereo results in +/-60 degrees
- * being moved to +/-90 degrees for direct right and left
- * speaker responses.
- */
- return CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, 0.0f);
+ return CalcDirectionCoeffs(std::array{xpos, ypos, zpos}, 0.0f);
+ const auto pos = ScaleAzimuthFront3_2(std::array{xpos, ypos, zpos});
+ return CalcDirectionCoeffs(pos, 0.0f);
};
- auto&& scales = GetAmbiScales(voice->mAmbiScaling);
+ const auto scales = GetAmbiScales(voice->mAmbiScaling);
auto coeffs = calc_coeffs(Device->mRenderMode);
if(!(coverage > 0.0f))
{
- ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base*scales[0],
+ ComputePanGains(&Device->Dry, coeffs, DryGain.Base*scales[0],
voice->mChans[0].mDryParams.Gains.Target);
for(uint i{0};i < NumSends;i++)
{
if(const EffectSlot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base*scales[0],
+ ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base*scales[0],
voice->mChans[0].mWetParams[i].Gains.Target);
}
}
@@ -922,25 +985,25 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
{
if(voice->mAmbiOrder == 1)
{
- auto&& upsampler = Is2DAmbisonic(voice->mFmtChannels) ?
- AmbiScale::FirstOrder2DUp : AmbiScale::FirstOrderUp;
+ const auto upsampler = Is2DAmbisonic(voice->mFmtChannels) ?
+ al::span{AmbiScale::FirstOrder2DUp} : al::span{AmbiScale::FirstOrderUp};
UpsampleBFormatTransform(mixmatrix, upsampler, shrot, Device->mAmbiOrder);
}
else if(voice->mAmbiOrder == 2)
{
- auto&& upsampler = Is2DAmbisonic(voice->mFmtChannels) ?
- AmbiScale::SecondOrder2DUp : AmbiScale::SecondOrderUp;
+ const auto upsampler = Is2DAmbisonic(voice->mFmtChannels) ?
+ al::span{AmbiScale::SecondOrder2DUp} : al::span{AmbiScale::SecondOrderUp};
UpsampleBFormatTransform(mixmatrix, upsampler, shrot, Device->mAmbiOrder);
}
else if(voice->mAmbiOrder == 3)
{
- auto&& upsampler = Is2DAmbisonic(voice->mFmtChannels) ?
- AmbiScale::ThirdOrder2DUp : AmbiScale::ThirdOrderUp;
+ const auto upsampler = Is2DAmbisonic(voice->mFmtChannels) ?
+ al::span{AmbiScale::ThirdOrder2DUp} : al::span{AmbiScale::ThirdOrderUp};
UpsampleBFormatTransform(mixmatrix, upsampler, shrot, Device->mAmbiOrder);
}
else if(voice->mAmbiOrder == 4)
{
- auto&& upsampler = AmbiScale::FourthOrder2DUp;
+ const auto upsampler = al::span{AmbiScale::FourthOrder2DUp};
UpsampleBFormatTransform(mixmatrix, upsampler, shrot, Device->mAmbiOrder);
}
else
@@ -974,13 +1037,13 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
for(size_t x{0};x < MaxAmbiChannels;++x)
coeffs[x] += mixmatrix[acn][x] * scale;
- ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base,
+ ComputePanGains(&Device->Dry, coeffs, DryGain.Base,
voice->mChans[c].mDryParams.Gains.Target);
for(uint i{0};i < NumSends;i++)
{
if(const EffectSlot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
+ ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base,
voice->mChans[c].mWetParams[i].Gains.Target);
}
@@ -1028,12 +1091,12 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
if(chans[c].channel == LFE)
continue;
- const auto coeffs = CalcAngleCoeffs(chans[c].angle, chans[c].elevation, 0.0f);
+ const auto coeffs = CalcDirectionCoeffs(chans[c].pos, 0.0f);
for(uint i{0};i < NumSends;i++)
{
if(const EffectSlot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
+ ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base,
voice->mChans[c].mWetParams[i].Gains.Target);
}
}
@@ -1047,21 +1110,21 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
if(Distance > std::numeric_limits<float>::epsilon())
{
- const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
- const float src_az{std::atan2(xpos, -zpos)};
-
if(voice->mFmtChannels == FmtMono)
{
+ const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
+ const float src_az{std::atan2(xpos, -zpos)};
+
Device->mHrtf->getCoeffs(src_ev, src_az, Distance*NfcScale, Spread,
voice->mChans[0].mDryParams.Hrtf.Target.Coeffs,
voice->mChans[0].mDryParams.Hrtf.Target.Delay);
voice->mChans[0].mDryParams.Hrtf.Target.Gain = DryGain.Base;
- const auto coeffs = CalcAngleCoeffs(src_az, src_ev, Spread);
+ const auto coeffs = CalcDirectionCoeffs(std::array{xpos, ypos, zpos}, Spread);
for(uint i{0};i < NumSends;i++)
{
if(const EffectSlot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
+ ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base,
voice->mChans[0].mWetParams[i].Gains.Target);
}
}
@@ -1077,28 +1140,32 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
* the source position, at full spread (pi*2), each channel is
* left unchanged.
*/
- const float ev{lerpf(src_ev, chans[c].elevation, inv_pi_v<float>/2.0f * Spread)};
-
- float az{chans[c].angle - src_az};
- if(az < -pi_v<float>) az += pi_v<float>*2.0f;
- else if(az > pi_v<float>) az -= pi_v<float>*2.0f;
-
- az *= inv_pi_v<float>/2.0f * Spread;
+ const float a{1.0f - (inv_pi_v<float>/2.0f)*Spread};
+ std::array pos{
+ lerpf(chans[c].pos[0], xpos, a),
+ lerpf(chans[c].pos[1], ypos, a),
+ lerpf(chans[c].pos[2], zpos, a)};
+ const float len{std::sqrt(pos[0]*pos[0] + pos[1]*pos[1] + pos[2]*pos[2])};
+ if(len < 1.0f)
+ {
+ pos[0] /= len;
+ pos[1] /= len;
+ pos[2] /= len;
+ }
- az += src_az;
- if(az < -pi_v<float>) az += pi_v<float>*2.0f;
- else if(az > pi_v<float>) az -= pi_v<float>*2.0f;
+ const float ev{std::asin(clampf(pos[1], -1.0f, 1.0f))};
+ const float az{std::atan2(pos[0], -pos[2])};
Device->mHrtf->getCoeffs(ev, az, Distance*NfcScale, 0.0f,
voice->mChans[c].mDryParams.Hrtf.Target.Coeffs,
voice->mChans[c].mDryParams.Hrtf.Target.Delay);
voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base;
- const auto coeffs = CalcAngleCoeffs(az, ev, 0.0f);
+ const auto coeffs = CalcDirectionCoeffs(pos, 0.0f);
for(uint i{0};i < NumSends;i++)
{
if(const EffectSlot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
+ ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base,
voice->mChans[c].mWetParams[i].Gains.Target);
}
}
@@ -1124,19 +1191,21 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
/* Get the HRIR coefficients and delays for this channel
* position.
*/
- Device->mHrtf->getCoeffs(chans[c].elevation, chans[c].angle,
- std::numeric_limits<float>::infinity(), spread,
+ const float ev{std::asin(chans[c].pos[1])};
+ const float az{std::atan2(chans[c].pos[0], -chans[c].pos[2])};
+
+ Device->mHrtf->getCoeffs(ev, az, std::numeric_limits<float>::infinity(), spread,
voice->mChans[c].mDryParams.Hrtf.Target.Coeffs,
voice->mChans[c].mDryParams.Hrtf.Target.Delay);
voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base;
/* Normal panning for auxiliary sends. */
- const auto coeffs = CalcAngleCoeffs(chans[c].angle, chans[c].elevation, spread);
+ const auto coeffs = CalcDirectionCoeffs(chans[c].pos, spread);
for(uint i{0};i < NumSends;i++)
{
if(const EffectSlot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
+ ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base,
voice->mChans[c].mWetParams[i].Gains.Target);
}
}
@@ -1171,19 +1240,18 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
auto calc_coeffs = [xpos,ypos,zpos,Spread](RenderMode mode)
{
if(mode != RenderMode::Pairwise)
- return CalcDirectionCoeffs({xpos, ypos, zpos}, Spread);
- const float ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
- const float az{std::atan2(xpos, -zpos)};
- return CalcAngleCoeffs(ScaleAzimuthFront(az, 1.5f), ev, Spread);
+ return CalcDirectionCoeffs(std::array{xpos, ypos, zpos}, Spread);
+ const auto pos = ScaleAzimuthFront3_2(std::array{xpos, ypos, zpos});
+ return CalcDirectionCoeffs(pos, Spread);
};
const auto coeffs = calc_coeffs(Device->mRenderMode);
- ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base,
+ ComputePanGains(&Device->Dry, coeffs, DryGain.Base,
voice->mChans[0].mDryParams.Gains.Target);
for(uint i{0};i < NumSends;i++)
{
if(const EffectSlot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
+ ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base,
voice->mChans[0].mWetParams[i].Gains.Target);
}
}
@@ -1191,9 +1259,6 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
{
using namespace al::numbers;
- const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))};
- const float src_az{std::atan2(xpos, -zpos)};
-
for(size_t c{0};c < num_channels;c++)
{
/* Special-case LFE */
@@ -1213,29 +1278,29 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
* at the source position, at full spread (pi*2), each
* channel position is left unchanged.
*/
- const float ev{lerpf(src_ev, chans[c].elevation,
- inv_pi_v<float>/2.0f * Spread)};
-
- float az{chans[c].angle - src_az};
- if(az < -pi_v<float>) az += pi_v<float>*2.0f;
- else if(az > pi_v<float>) az -= pi_v<float>*2.0f;
-
- az *= inv_pi_v<float>/2.0f * Spread;
-
- az += src_az;
- if(az < -pi_v<float>) az += pi_v<float>*2.0f;
- else if(az > pi_v<float>) az -= pi_v<float>*2.0f;
+ const float a{1.0f - (inv_pi_v<float>/2.0f)*Spread};
+ std::array pos{
+ lerpf(chans[c].pos[0], xpos, a),
+ lerpf(chans[c].pos[1], ypos, a),
+ lerpf(chans[c].pos[2], zpos, a)};
+ const float len{std::sqrt(pos[0]*pos[0] + pos[1]*pos[1] + pos[2]*pos[2])};
+ if(len < 1.0f)
+ {
+ pos[0] /= len;
+ pos[1] /= len;
+ pos[2] /= len;
+ }
if(Device->mRenderMode == RenderMode::Pairwise)
- az = ScaleAzimuthFront(az, 3.0f);
- const auto coeffs = CalcAngleCoeffs(az, ev, 0.0f);
+ pos = ScaleAzimuthFront3(pos);
+ const auto coeffs = CalcDirectionCoeffs(pos, 0.0f);
- ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base,
+ ComputePanGains(&Device->Dry, coeffs, DryGain.Base,
voice->mChans[c].mDryParams.Gains.Target);
for(uint i{0};i < NumSends;i++)
{
if(const EffectSlot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
+ ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base,
voice->mChans[c].mWetParams[i].Gains.Target);
}
}
@@ -1274,16 +1339,15 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
continue;
}
- const auto coeffs = CalcAngleCoeffs((Device->mRenderMode == RenderMode::Pairwise)
- ? ScaleAzimuthFront(chans[c].angle, 3.0f) : chans[c].angle,
- chans[c].elevation, spread);
+ const auto coeffs = CalcDirectionCoeffs((Device->mRenderMode==RenderMode::Pairwise)
+ ? ScaleAzimuthFront3(chans[c].pos) : chans[c].pos, spread);
- ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base,
+ ComputePanGains(&Device->Dry, coeffs, DryGain.Base,
voice->mChans[c].mDryParams.Gains.Target);
for(uint i{0};i < NumSends;i++)
{
if(const EffectSlot *Slot{SendSlots[i]})
- ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base,
+ ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base,
voice->mChans[c].mWetParams[i].Gains.Target);
}
}
@@ -1700,22 +1764,21 @@ void SendSourceStateEvent(ContextBase *context, uint id, VChangeState state)
auto evt_vec = ring->getWriteVector();
if(evt_vec.first.len < 1) return;
- AsyncEvent *evt{al::construct_at(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf),
- AsyncEvent::SourceStateChange)};
- evt->u.srcstate.id = id;
+ auto &evt = InitAsyncEvent<AsyncSourceStateEvent>(evt_vec.first.buf);
+ evt.mId = id;
switch(state)
{
case VChangeState::Reset:
- evt->u.srcstate.state = AsyncEvent::SrcState::Reset;
+ evt.mState = AsyncSrcState::Reset;
break;
case VChangeState::Stop:
- evt->u.srcstate.state = AsyncEvent::SrcState::Stop;
+ evt.mState = AsyncSrcState::Stop;
break;
case VChangeState::Play:
- evt->u.srcstate.state = AsyncEvent::SrcState::Play;
+ evt.mState = AsyncSrcState::Play;
break;
case VChangeState::Pause:
- evt->u.srcstate.state = AsyncEvent::SrcState::Pause;
+ evt.mState = AsyncSrcState::Pause;
break;
/* Shouldn't happen. */
case VChangeState::Restart:
@@ -1812,7 +1875,7 @@ void ProcessVoiceChanges(ContextBase *ctx)
}
oldvoice->mPendingChange.store(false, std::memory_order_release);
}
- if(sendevt && enabledevt.test(AsyncEvent::SourceStateChange))
+ if(sendevt && enabledevt.test(al::to_underlying(AsyncEnableBits::SourceState)))
SendSourceStateEvent(ctx, cur->mSourceID, cur->mState);
next = cur->mNext.load(std::memory_order_acquire);
@@ -1855,7 +1918,7 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo)
const EffectSlotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire);
const al::span<Voice*> voices{ctx->getVoicesSpanAcquired()};
- /* Process pending propery updates for objects on the context. */
+ /* Process pending property updates for objects on the context. */
ProcessParamUpdates(ctx, auxslots, voices);
/* Clear auxiliary effect slot mixing buffers. */
@@ -2165,28 +2228,26 @@ void DeviceBase::handleDisconnect(const char *msg, ...)
IncrementRef(MixCount);
if(Connected.exchange(false, std::memory_order_acq_rel))
{
- AsyncEvent evt{AsyncEvent::Disconnected};
+ AsyncEvent evt{std::in_place_type<AsyncDisconnectEvent>};
+ auto &disconnect = std::get<AsyncDisconnectEvent>(evt);
va_list args;
va_start(args, msg);
- int msglen{vsnprintf(evt.u.disconnect.msg, sizeof(evt.u.disconnect.msg), msg, args)};
+ int msglen{vsnprintf(disconnect.msg, sizeof(disconnect.msg), msg, args)};
va_end(args);
- if(msglen < 0 || static_cast<size_t>(msglen) >= sizeof(evt.u.disconnect.msg))
- evt.u.disconnect.msg[sizeof(evt.u.disconnect.msg)-1] = 0;
+ if(msglen < 0 || static_cast<size_t>(msglen) >= sizeof(disconnect.msg))
+ disconnect.msg[sizeof(disconnect.msg)-1] = 0;
for(ContextBase *ctx : *mContexts.load())
{
- if(ctx->mEnabledEvts.load(std::memory_order_acquire).test(AsyncEvent::Disconnected))
+ RingBuffer *ring{ctx->mAsyncEvents.get()};
+ auto evt_data = ring->getWriteVector().first;
+ if(evt_data.len > 0)
{
- RingBuffer *ring{ctx->mAsyncEvents.get()};
- auto evt_data = ring->getWriteVector().first;
- if(evt_data.len > 0)
- {
- al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), evt);
- ring->writeAdvance(1);
- ctx->mEventSem.post();
- }
+ al::construct_at(reinterpret_cast<AsyncEvent*>(evt_data.buf), evt);
+ ring->writeAdvance(1);
+ ctx->mEventSem.post();
}
if(!ctx->mStopVoicesOnDisconnect)
diff --git a/alc/alu.h b/alc/alu.h
index 67fd09e5..33453487 100644
--- a/alc/alu.h
+++ b/alc/alu.h
@@ -2,14 +2,14 @@
#define ALU_H
#include <bitset>
-
-#include "aloptional.h"
+#include <optional>
+#include <stdint.h>
struct ALCcontext;
struct ALCdevice;
struct EffectSlot;
-enum class StereoEncoding : unsigned char;
+enum class StereoEncoding : uint8_t;
constexpr float GainMixMax{1000.0f}; /* +60dB */
@@ -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);
diff --git a/alc/backends/alsa.cpp b/alc/backends/alsa.cpp
index d620a83c..0d9ff30d 100644
--- a/alc/backends/alsa.cpp
+++ b/alc/backends/alsa.cpp
@@ -31,22 +31,22 @@
#include <exception>
#include <functional>
#include <memory>
+#include <mutex>
#include <string>
#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 "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>
@@ -248,8 +248,8 @@ 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)
@@ -258,9 +258,9 @@ const char *prefix_name(snd_pcm_stream_t stream)
return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix";
}
-al::vector<DevMap> probe_devices(snd_pcm_stream_t stream)
+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);
@@ -427,7 +427,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,7 +439,7 @@ 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;
@@ -585,7 +585,7 @@ int AlsaPlayback::mixerNoMMapProc()
continue;
}
- al::byte *WritePtr{mBuffer.data()};
+ std::byte *WritePtr{mBuffer.data()};
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);
@@ -626,10 +626,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,7 +638,7 @@ 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", static_cast<int>(name.length()), name.data()};
driver = iter->device_name;
}
else
@@ -871,16 +871,16 @@ 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};
@@ -898,10 +898,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,7 +910,7 @@ 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", static_cast<int>(name.length()), name.data()};
driver = iter->device_name;
}
else
@@ -1024,7 +1024,7 @@ 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);
@@ -1035,7 +1035,7 @@ void AlsaCapture::stop()
mDoCapture = false;
}
-void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
+void AlsaCapture::captureSamples(std::byte *buffer, uint samples)
{
if(mRing)
{
@@ -1093,7 +1093,7 @@ void AlsaCapture::captureSamples(al::byte *buffer, uint samples)
}
if(samples > 0)
std::fill_n(buffer, snd_pcm_frames_to_bytes(mPcmHandle, samples),
- al::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0));
+ std::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0));
}
uint AlsaCapture::availableSamples()
@@ -1205,7 +1205,7 @@ bool AlsaBackendFactory::init()
error = false;
#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \
+ p##f = al::bit_cast<decltype(p##f)>(GetSymbol(alsa_handle, #f)); \
if(p##f == nullptr) { \
error = true; \
missing_funcs += "\n" #f; \
diff --git a/alc/backends/base.cpp b/alc/backends/base.cpp
index e5ad8494..ab3ad028 100644
--- a/alc/backends/base.cpp
+++ b/alc/backends/base.cpp
@@ -14,7 +14,6 @@
#include "albit.h"
#include "core/logging.h"
-#include "aloptional.h"
#endif
#include "atomic.h"
@@ -38,7 +37,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()
diff --git a/alc/backends/base.h b/alc/backends/base.h
index b6b3d922..eea0d238 100644
--- a/alc/backends/base.h
+++ b/alc/backends/base.h
@@ -3,13 +3,15 @@
#include <chrono>
#include <cstdarg>
+#include <cstddef>
#include <memory>
#include <ratio>
#include <string>
+#include <string_view>
-#include "albyte.h"
#include "core/device.h"
#include "core/except.h"
+#include "alc/events.h"
using uint = unsigned int;
@@ -20,13 +22,13 @@ 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();
@@ -78,6 +80,9 @@ struct BackendFactory {
virtual bool querySupport(BackendType type) = 0;
+ virtual alc::EventSupport queryEventSupport(alc::EventType, BackendType)
+ { return alc::EventSupport::NoSupport; }
+
virtual std::string probe(BackendType type) = 0;
virtual BackendPtr createBackend(DeviceBase *device, BackendType type) = 0;
diff --git a/alc/backends/coreaudio.cpp b/alc/backends/coreaudio.cpp
index 8b0e75fd..16b0781e 100644
--- a/alc/backends/coreaudio.cpp
+++ b/alc/backends/coreaudio.cpp
@@ -22,16 +22,17 @@
#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 "core/converter.h"
@@ -42,18 +43,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 +170,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 +182,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 +207,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 +272,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 +327,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;
@@ -301,11 +361,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 +378,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", static_cast<int>(name.length()), 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",
+ static_cast<int>(name.length()), name.data()};
#endif
/* open the default output unit */
@@ -351,7 +411,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 +422,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 +435,7 @@ void CoreAudioPlayback::open(const char *name)
mAudioUnit = audioUnit;
#if CAN_ENUMERATE
- if(name)
+ if(!name.empty())
mDevice->DeviceName = name;
else
{
@@ -388,6 +448,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 +472,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 +481,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 +549,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 +559,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 +576,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 +588,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 +606,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,7 +620,7 @@ struct CoreAudioCapture final : public BackendBase {
SampleConverterPtr mConverter;
- al::vector<char> mCaptureData;
+ std::vector<char> mCaptureData;
RingBufferPtr mRing{nullptr};
@@ -568,7 +640,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
AudioBufferList*) noexcept
{
union {
- al::byte _[maxz(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))];
+ std::byte _[maxz(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))];
AudioBufferList list;
} audiobuf{};
@@ -581,7 +653,7 @@ 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;
}
@@ -590,11 +662,11 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
}
-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 +679,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", static_cast<int>(name.length()), 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",
+ static_cast<int>(name.length()), name.data()};
#endif
AudioComponentDescription desc{};
@@ -640,7 +712,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 +720,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 +729,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 +740,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 +756,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 +772,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{};
@@ -777,7 +853,7 @@ 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.
@@ -796,7 +872,7 @@ 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);
@@ -810,7 +886,7 @@ void CoreAudioCapture::open(const char *name)
mDevice->Frequency, Resampler::FastBSinc24);
#if CAN_ENUMERATE
- if(name)
+ if(!name.empty())
mDevice->DeviceName = name;
else
{
@@ -834,17 +910,17 @@ 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)
{
@@ -882,7 +958,13 @@ 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; }
@@ -930,3 +1012,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;
+}
diff --git a/alc/backends/coreaudio.h b/alc/backends/coreaudio.h
index 1252edde..6ea4307c 100644
--- a/alc/backends/coreaudio.h
+++ b/alc/backends/coreaudio.h
@@ -9,6 +9,8 @@ public:
bool querySupport(BackendType type) override;
+ alc::EventSupport queryEventSupport(alc::EventType eventType, BackendType type) override;
+
std::string probe(BackendType type) override;
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
diff --git a/alc/backends/dsound.cpp b/alc/backends/dsound.cpp
index f549c0fe..58aa69b2 100644
--- a/alc/backends/dsound.cpp
+++ b/alc/backends/dsound.cpp
@@ -25,10 +25,6 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <memory.h>
-
#include <cguid.h>
#include <mmreg.h>
#ifndef _WAVEFORMATEXTENSIBLE_
@@ -36,15 +32,22 @@
#include <ksmedia.h>
#endif
+#include <algorithm>
#include <atomic>
#include <cassert>
-#include <thread>
+#include <functional>
+#include <memory.h>
+#include <mutex>
+#include <stdlib.h>
+#include <stdio.h>
#include <string>
+#include <thread>
#include <vector>
-#include <algorithm>
-#include <functional>
+#include "albit.h"
#include "alnumeric.h"
+#include "alspan.h"
+#include "althrd_setname.h"
#include "comptr.h"
#include "core/device.h"
#include "core/helpers.h"
@@ -52,7 +55,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 +131,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 +146,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 +178,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;
@@ -299,24 +301,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 +332,8 @@ 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", static_cast<int>(name.length()),
+ name.data()};
}
guid = &iter->guid;
}
@@ -347,7 +348,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))
@@ -460,7 +461,7 @@ retry_open:
DSBUFFERDESC DSBDescription{};
DSBDescription.dwSize = sizeof(DSBDescription);
DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
- hr = mDS->CreateSoundBuffer(&DSBDescription, mPrimaryBuffer.getPtr(), nullptr);
+ hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mPrimaryBuffer), nullptr);
}
if(SUCCEEDED(hr))
hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
@@ -480,7 +481,7 @@ retry_open:
DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
DSBDescription.lpwfxFormat = &OutputType.Format;
- hr = mDS->CreateSoundBuffer(&DSBDescription, mBuffer.getPtr(), nullptr);
+ hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mBuffer), nullptr);
if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
{
mDevice->FmtType = DevFmtShort;
@@ -490,12 +491,9 @@ retry_open:
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);
@@ -550,10 +548,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;
@@ -577,24 +575,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 +606,8 @@ 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", static_cast<int>(name.length()),
+ name.data()};
}
guid = &iter->guid;
}
@@ -679,9 +676,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,7 +716,7 @@ void DSoundCapture::stop()
}
}
-void DSoundCapture::captureSamples(al::byte *buffer, uint samples)
+void DSoundCapture::captureSamples(std::byte *buffer, uint samples)
{ mRing->read(buffer, samples); }
uint DSoundCapture::availableSamples()
@@ -781,7 +778,7 @@ bool DSoundBackendFactory::init()
}
#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \
+ p##f = al::bit_cast<decltype(p##f)>(GetSymbol(ds_handle, #f)); \
if(!p##f) \
{ \
CloseLib(ds_handle); \
@@ -814,8 +811,8 @@ std::string DSoundBackendFactory::probe(BackendType type)
};
/* Initialize COM to prevent name truncation */
+ ComWrapper com{};
HRESULT hr;
- HRESULT hrcom{CoInitialize(nullptr)};
switch(type)
{
case BackendType::Playback:
@@ -834,8 +831,6 @@ std::string DSoundBackendFactory::probe(BackendType type)
std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device);
break;
}
- if(SUCCEEDED(hrcom))
- CoUninitialize();
return outnames;
}
diff --git a/alc/backends/jack.cpp b/alc/backends/jack.cpp
index 791002ca..a0a5c440 100644
--- a/alc/backends/jack.cpp
+++ b/alc/backends/jack.cpp
@@ -22,23 +22,26 @@
#include "jack.h"
+#include <array>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <memory.h>
-
-#include <array>
+#include <mutex>
#include <thread>
#include <functional>
+#include <vector>
+#include "albit.h"
#include "alc/alconfig.h"
#include "alnumeric.h"
+#include "alsem.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>
@@ -126,7 +129,7 @@ bool jack_load()
error = false;
#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \
+ p##f = al::bit_cast<decltype(p##f)>(GetSymbol(jack_handle, #f)); \
if(p##f == nullptr) { \
error = true; \
missing_funcs += "\n" #f; \
@@ -135,7 +138,7 @@ bool jack_load()
JACK_FUNCS(LOAD_FUNC);
#undef LOAD_FUNC
/* Optional symbols. These don't exist in all versions of JACK. */
-#define LOAD_SYM(f) p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(jack_handle, #f))
+#define LOAD_SYM(f) p##f = al::bit_cast<decltype(p##f)>(GetSymbol(jack_handle, #f))
LOAD_SYM(jack_error_callback);
#undef LOAD_SYM
@@ -167,10 +170,10 @@ struct DeviceEntry {
{ }
};
-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);
@@ -295,7 +298,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;
@@ -457,7 +460,7 @@ int JackPlayback::mixerProc()
}
-void JackPlayback::open(const char *name)
+void JackPlayback::open(std::string_view name)
{
if(!mClient)
{
@@ -481,9 +484,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 +496,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", static_cast<int>(name.length()), 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 +510,10 @@ bool JackPlayback::reset()
std::for_each(mPort.begin(), mPort.end(), unregister_port);
mPort.fill(nullptr);
+ mRTMixing = GetConfigValueBool(mDevice->DeviceName.c_str(), "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.
*/
@@ -586,7 +589,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])
{
diff --git a/alc/backends/loopback.cpp b/alc/backends/loopback.cpp
index bf4ab246..2972fc01 100644
--- a/alc/backends/loopback.cpp
+++ b/alc/backends/loopback.cpp
@@ -30,7 +30,7 @@ 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;
@@ -39,7 +39,7 @@ struct LoopbackBackend final : public BackendBase {
};
-void LoopbackBackend::open(const char *name)
+void LoopbackBackend::open(std::string_view name)
{
mDevice->DeviceName = name;
}
diff --git a/alc/backends/null.cpp b/alc/backends/null.cpp
index 5a8fc255..3c68e4ce 100644
--- a/alc/backends/null.cpp
+++ b/alc/backends/null.cpp
@@ -30,10 +30,10 @@
#include <functional>
#include <thread>
-#include "core/device.h"
+#include "althrd_setname.h"
#include "almalloc.h"
+#include "core/device.h"
#include "core/helpers.h"
-#include "threads.h"
namespace {
@@ -50,7 +50,7 @@ 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;
@@ -105,13 +105,13 @@ int NullBackend::mixerProc()
}
-void NullBackend::open(const char *name)
+void NullBackend::open(std::string_view name)
{
- if(!name)
+ if(name.empty())
name = nullDevice;
- else if(strcmp(name, nullDevice) != 0)
- throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
- name};
+ else if(name != nullDevice)
+ throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+ static_cast<int>(name.length()), name.data()};
mDevice->DeviceName = name;
}
diff --git a/alc/backends/oboe.cpp b/alc/backends/oboe.cpp
index 461f5a6a..b7bab19a 100644
--- a/alc/backends/oboe.cpp
+++ b/alc/backends/oboe.cpp
@@ -28,7 +28,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 +48,21 @@ oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStrea
return oboe::DataCallbackResult::Continue;
}
+void OboePlayback::onErrorAfterClose(oboe::AudioStream* audioStream, oboe::Result error)
+{
+ if (error == oboe::Result::ErrorDisconnected) {
+ mDevice->handleDisconnect("Oboe AudioStream was disconnected: %s", oboe::convertToText(error));
+ }
+ TRACE("Error was %s", oboe::convertToText(error));
+}
-void OboePlayback::open(const char *name)
+void OboePlayback::open(std::string_view name)
{
- if(!name)
+ if(name.empty())
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};
+ else if(name != device_name)
+ throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+ static_cast<int>(name.length()), name.data()};
/* Open a basic output stream, just to ensure it can work. */
oboe::ManagedStream stream;
@@ -197,8 +206,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,10 +220,10 @@ 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;
};
@@ -227,13 +235,13 @@ oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *aud
}
-void OboeCapture::open(const char *name)
+void OboeCapture::open(std::string_view name)
{
- if(!name)
+ if(name.empty())
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};
+ else if(name != device_name)
+ throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+ static_cast<int>(name.length()), name.data()};
oboe::AudioStreamBuilder builder;
builder.setDirection(oboe::Direction::Input)
@@ -315,14 +323,13 @@ 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)
+void OboeCapture::captureSamples(std::byte *buffer, uint samples)
{ mRing->read(buffer, samples); }
} // namespace
diff --git a/alc/backends/opensl.cpp b/alc/backends/opensl.cpp
index f5b98fb8..61e3c9a7 100644
--- a/alc/backends/opensl.cpp
+++ b/alc/backends/opensl.cpp
@@ -26,20 +26,22 @@
#include <stdlib.h>
#include <jni.h>
-#include <new>
#include <array>
#include <cstring>
+#include <mutex>
+#include <new>
#include <thread>
#include <functional>
#include "albit.h"
#include "alnumeric.h"
+#include "alsem.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>
@@ -159,12 +161,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;
@@ -312,13 +312,13 @@ int OpenSLPlayback::mixerProc()
}
-void OpenSLPlayback::open(const char *name)
+void OpenSLPlayback::open(std::string_view name)
{
- if(!name)
+ if(name.empty())
name = opensl_device;
- else if(strcmp(name, opensl_device) != 0)
- throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
- name};
+ else if(name != opensl_device)
+ throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+ static_cast<int>(name.length()), name.data()};
/* There's only one device, so if it's already open, there's nothing to do. */
if(mEngineObj) return;
@@ -564,7 +564,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)
@@ -642,13 +644,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 */
@@ -686,13 +686,13 @@ void OpenSLCapture::process(SLAndroidSimpleBufferQueueItf) noexcept
}
-void OpenSLCapture::open(const char* name)
+void OpenSLCapture::open(std::string_view name)
{
- if(!name)
+ if(name.empty())
name = opensl_device;
- else if(strcmp(name, opensl_device) != 0)
- throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
- name};
+ else if(name != opensl_device)
+ throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+ static_cast<int>(name.length()), name.data()};
SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
PrintErr(result, "slCreateEngine");
@@ -813,13 +813,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 +885,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};
@@ -932,7 +934,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
diff --git a/alc/backends/oss.cpp b/alc/backends/oss.cpp
index 6d4fa261..87d3ba35 100644
--- a/alc/backends/oss.cpp
+++ b/alc/backends/oss.cpp
@@ -31,27 +31,23 @@
#include <algorithm>
#include <atomic>
#include <cerrno>
-#include <cstdio>
#include <cstring>
#include <exception>
#include <functional>
#include <memory>
-#include <new>
#include <string>
#include <thread>
#include <utility>
+#include <vector>
-#include "albyte.h"
#include "alc/alconfig.h"
#include "almalloc.h"
#include "alnumeric.h"
-#include "aloptional.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>
@@ -92,22 +88,22 @@ struct DevMap {
std::string device_name;
};
-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});
}
#else
-void ALCossListAppend(al::vector<DevMap> &list, al::span<const char> handle, al::span<const char> path)
+void ALCossListAppend(std::vector<DevMap> &list, al::span<const char> handle, al::span<const char> path)
{
#ifdef ALC_OSS_DEVNODE_TRUC
for(size_t i{0};i < path.size();++i)
@@ -152,7 +148,7 @@ void ALCossListAppend(al::vector<DevMap> &list, al::span<const char> handle, al:
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)
@@ -231,14 +227,14 @@ 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;
@@ -284,7 +280,7 @@ int OSSPlayback::mixerProc()
continue;
}
- al::byte *write_ptr{mMixData.data()};
+ std::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))
@@ -308,10 +304,10 @@ int OSSPlayback::mixerProc()
}
-void OSSPlayback::open(const char *name)
+void OSSPlayback::open(std::string_view name)
{
const char *devname{DefaultPlayback.c_str()};
- if(!name)
+ if(name.empty())
name = DefaultName;
else
{
@@ -324,7 +320,7 @@ 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", static_cast<int>(name.length()), name.data()};
devname = iter->device_name.c_str();
}
@@ -447,10 +443,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};
@@ -516,10 +512,10 @@ int OSScapture::recordProc()
}
-void OSScapture::open(const char *name)
+void OSScapture::open(std::string_view name)
{
const char *devname{DefaultCapture.c_str()};
- if(!name)
+ if(name.empty())
name = DefaultName;
else
{
@@ -532,7 +528,7 @@ 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", static_cast<int>(name.length()), name.data()};
devname = iter->device_name.c_str();
}
@@ -620,7 +616,7 @@ void OSScapture::stop()
ERR("Error resetting device: %s\n", strerror(errno));
}
-void OSScapture::captureSamples(al::byte *buffer, uint samples)
+void OSScapture::captureSamples(std::byte *buffer, uint samples)
{ mRing->read(buffer, samples); }
uint OSScapture::availableSamples()
diff --git a/alc/backends/pipewire.cpp b/alc/backends/pipewire.cpp
index c6569a74..6a001d7a 100644
--- a/alc/backends/pipewire.cpp
+++ b/alc/backends/pipewire.cpp
@@ -24,6 +24,7 @@
#include <algorithm>
#include <atomic>
+#include <cstddef>
#include <cstring>
#include <cerrno>
#include <chrono>
@@ -31,16 +32,16 @@
#include <list>
#include <memory>
#include <mutex>
+#include <optional>
#include <stdint.h>
#include <thread>
#include <type_traits>
#include <utility>
-#include "albyte.h"
+#include "albit.h"
#include "alc/alconfig.h"
#include "almalloc.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "alspan.h"
#include "alstring.h"
#include "core/devformat.h"
@@ -107,12 +108,12 @@ template<typename T, size_t N>
constexpr auto get_pod_body(const spa_pod *pod) noexcept
{ return al::span<T,N>{static_cast<T*>(SPA_POD_BODY(pod)), N}; }
-constexpr auto make_pod_builder(void *data, uint32_t size) noexcept
-{ return SPA_POD_BUILDER_INIT(data, size); }
-
constexpr auto get_array_value_type(const spa_pod *pod) noexcept
{ return SPA_POD_ARRAY_VALUE_TYPE(pod); }
+constexpr auto make_pod_builder(void *data, uint32_t size) noexcept
+{ return SPA_POD_BUILDER_INIT(data, size); }
+
constexpr auto PwIdAny = PW_ID_ANY;
} // namespace
@@ -120,6 +121,44 @@ _Pragma("GCC diagnostic pop")
namespace {
+struct PodDynamicBuilder {
+private:
+ std::vector<std::byte> mStorage;
+ spa_pod_builder mPod{};
+
+ int overflow(uint32_t size) noexcept
+ {
+ try {
+ mStorage.resize(size);
+ }
+ catch(...) {
+ ERR("Failed to resize POD storage\n");
+ return -ENOMEM;
+ }
+ mPod.data = mStorage.data();
+ mPod.size = size;
+ return 0;
+ }
+
+public:
+ PodDynamicBuilder(uint32_t initSize=0) : mStorage(initSize)
+ , mPod{make_pod_builder(mStorage.data(), initSize)}
+ {
+ static constexpr auto callbacks{[]
+ {
+ spa_pod_builder_callbacks cb{};
+ cb.version = SPA_VERSION_POD_BUILDER_CALLBACKS;
+ cb.overflow = [](void *data, uint32_t size) noexcept
+ { return static_cast<PodDynamicBuilder*>(data)->overflow(size); };
+ return cb;
+ }()};
+
+ spa_pod_builder_set_callbacks(&mPod, &callbacks, this);
+ }
+
+ spa_pod_builder *get() noexcept { return &mPod; }
+};
+
/* Added in 0.3.33, but we currently only require 0.3.23. */
#ifndef PW_KEY_NODE_RATE
#define PW_KEY_NODE_RATE "node.rate"
@@ -210,7 +249,7 @@ bool pwire_load()
}
#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pwire_handle, #f)); \
+ p##f = al::bit_cast<decltype(p##f)>(GetSymbol(pwire_handle, #f)); \
if(p##f == nullptr) missing_funcs += "\n" #f; \
} while(0);
PWIRE_FUNCS(LOAD_FUNC)
@@ -304,12 +343,12 @@ al::span<const Pod_t<T>> get_array_span(const spa_pod *pod)
}
template<uint32_t T>
-al::optional<Pod_t<T>> get_value(const spa_pod *value)
+std::optional<Pod_t<T>> get_value(const spa_pod *value)
{
Pod_t<T> val{};
if(PodInfo<T>::get_value(value, &val) == 0)
return val;
- return al::nullopt;
+ return std::nullopt;
}
/* Internally, PipeWire types "inherit" from each other, but this is hidden
@@ -328,11 +367,11 @@ To as(From) noexcept = delete;
* - pw_metadata
*/
template<>
-pw_proxy* as(pw_registry *reg) noexcept { return reinterpret_cast<pw_proxy*>(reg); }
+pw_proxy* as(pw_registry *reg) noexcept { return al::bit_cast<pw_proxy*>(reg); }
template<>
-pw_proxy* as(pw_node *node) noexcept { return reinterpret_cast<pw_proxy*>(node); }
+pw_proxy* as(pw_node *node) noexcept { return al::bit_cast<pw_proxy*>(node); }
template<>
-pw_proxy* as(pw_metadata *mdata) noexcept { return reinterpret_cast<pw_proxy*>(mdata); }
+pw_proxy* as(pw_metadata *mdata) noexcept { return al::bit_cast<pw_proxy*>(mdata); }
struct PwContextDeleter {
@@ -433,8 +472,75 @@ using MainloopLockGuard = std::lock_guard<ThreadMainloop>;
* devices provided by the server.
*/
-struct NodeProxy;
-struct MetadataProxy;
+/* A generic PipeWire node proxy object used to track changes to sink and
+ * source nodes.
+ */
+struct NodeProxy {
+ static constexpr pw_node_events CreateNodeEvents()
+ {
+ pw_node_events ret{};
+ ret.version = PW_VERSION_NODE_EVENTS;
+ ret.info = [](void *object, const pw_node_info *info) noexcept
+ { static_cast<NodeProxy*>(object)->infoCallback(info); };
+ ret.param = [](void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param) noexcept
+ { static_cast<NodeProxy*>(object)->paramCallback(seq, id, index, next, param); };
+ return ret;
+ }
+
+ uint32_t mId{};
+
+ PwNodePtr mNode{};
+ spa_hook mListener{};
+
+ NodeProxy(uint32_t id, PwNodePtr node)
+ : mId{id}, mNode{std::move(node)}
+ {
+ static constexpr pw_node_events nodeEvents{CreateNodeEvents()};
+ ppw_node_add_listener(mNode.get(), &mListener, &nodeEvents, this);
+
+ /* Track changes to the enumerable and current formats (indicates the
+ * default and active format, which is what we're interested in).
+ */
+ uint32_t fmtids[]{SPA_PARAM_EnumFormat, SPA_PARAM_Format};
+ ppw_node_subscribe_params(mNode.get(), std::data(fmtids), std::size(fmtids));
+ }
+ ~NodeProxy()
+ { spa_hook_remove(&mListener); }
+
+
+ void infoCallback(const pw_node_info *info) noexcept;
+
+ void paramCallback(int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param) noexcept;
+};
+
+/* A metadata proxy object used to query the default sink and source. */
+struct MetadataProxy {
+ static constexpr pw_metadata_events CreateMetadataEvents()
+ {
+ pw_metadata_events ret{};
+ ret.version = PW_VERSION_METADATA_EVENTS;
+ ret.property = [](void *object, uint32_t id, const char *key, const char *type, const char *value) noexcept
+ { return static_cast<MetadataProxy*>(object)->propertyCallback(id, key, type, value); };
+ return ret;
+ }
+
+ uint32_t mId{};
+
+ PwMetadataPtr mMetadata{};
+ spa_hook mListener{};
+
+ MetadataProxy(uint32_t id, PwMetadataPtr mdata)
+ : mId{id}, mMetadata{std::move(mdata)}
+ {
+ static constexpr pw_metadata_events metadataEvents{CreateMetadataEvents()};
+ ppw_metadata_add_listener(mMetadata.get(), &mListener, &metadataEvents, this);
+ }
+ ~MetadataProxy()
+ { spa_hook_remove(&mListener); }
+
+ int propertyCallback(uint32_t id, const char *key, const char *type, const char *value) noexcept;
+};
+
/* The global thread watching for global events. This particular class responds
* to objects being added to or removed from the registry.
@@ -450,8 +556,8 @@ struct EventManager {
/* A list of proxy objects watching for events about changes to objects in
* the registry.
*/
- std::vector<NodeProxy*> mNodeList;
- MetadataProxy *mDefaultMetadata{nullptr};
+ std::vector<std::unique_ptr<NodeProxy>> mNodeList;
+ std::optional<MetadataProxy> mDefaultMetadata;
/* Initialization handling. When init() is called, mInitSeq is set to a
* SequenceID that marks the end of populating the registry. As objects of
@@ -463,24 +569,28 @@ struct EventManager {
std::atomic<bool> mHasAudio{false};
int mInitSeq{};
+ ~EventManager() { if(mLoop) mLoop.stop(); }
+
bool init();
- ~EventManager();
void kill();
auto lock() const { return mLoop.lock(); }
auto unlock() const { return mLoop.unlock(); }
+ inline bool initIsDone(std::memory_order m=std::memory_order_seq_cst) noexcept
+ { return mInitDone.load(m); }
+
/**
* Waits for initialization to finish. The event manager must *NOT* be
* locked when calling this.
*/
void waitForInit()
{
- if(!mInitDone.load(std::memory_order_acquire)) UNLIKELY
+ if(!initIsDone(std::memory_order_acquire)) UNLIKELY
{
MainloopUniqueLock plock{mLoop};
- plock.wait([this](){ return mInitDone.load(std::memory_order_acquire); });
+ plock.wait([this](){ return initIsDone(std::memory_order_acquire); });
}
}
@@ -496,7 +606,7 @@ struct EventManager {
plock.wait([this,&has_audio]()
{
has_audio = mHasAudio.load(std::memory_order_acquire);
- return has_audio || mInitDone.load(std::memory_order_acquire);
+ return has_audio || initIsDone(std::memory_order_acquire);
});
return has_audio;
}
@@ -506,38 +616,34 @@ struct EventManager {
/* If initialization isn't done, update the sequence ID so it won't
* complete until after currently scheduled events.
*/
- if(!mInitDone.load(std::memory_order_relaxed))
+ if(!initIsDone(std::memory_order_relaxed))
mInitSeq = ppw_core_sync(mCore.get(), PW_ID_CORE, mInitSeq);
}
void addCallback(uint32_t id, uint32_t permissions, const char *type, uint32_t version,
- const spa_dict *props);
- static void addCallbackC(void *object, uint32_t id, uint32_t permissions, const char *type,
- uint32_t version, const spa_dict *props)
- { static_cast<EventManager*>(object)->addCallback(id, permissions, type, version, props); }
+ const spa_dict *props) noexcept;
- void removeCallback(uint32_t id);
- static void removeCallbackC(void *object, uint32_t id)
- { static_cast<EventManager*>(object)->removeCallback(id); }
+ void removeCallback(uint32_t id) noexcept;
static constexpr pw_registry_events CreateRegistryEvents()
{
pw_registry_events ret{};
ret.version = PW_VERSION_REGISTRY_EVENTS;
- ret.global = &EventManager::addCallbackC;
- ret.global_remove = &EventManager::removeCallbackC;
+ ret.global = [](void *object, uint32_t id, uint32_t permissions, const char *type, uint32_t version, const spa_dict *props) noexcept
+ { static_cast<EventManager*>(object)->addCallback(id, permissions, type, version, props); };
+ ret.global_remove = [](void *object, uint32_t id) noexcept
+ { static_cast<EventManager*>(object)->removeCallback(id); };
return ret;
}
- void coreCallback(uint32_t id, int seq);
- static void coreCallbackC(void *object, uint32_t id, int seq)
- { static_cast<EventManager*>(object)->coreCallback(id, seq); }
+ void coreCallback(uint32_t id, int seq) noexcept;
static constexpr pw_core_events CreateCoreEvents()
{
pw_core_events ret{};
ret.version = PW_VERSION_CORE_EVENTS;
- ret.done = &EventManager::coreCallbackC;
+ ret.done = [](void *object, uint32_t id, int seq) noexcept
+ { static_cast<EventManager*>(object)->coreCallback(id, seq); };
return ret;
}
};
@@ -570,12 +676,23 @@ struct DeviceNode {
static std::vector<DeviceNode> sList;
static DeviceNode &Add(uint32_t id);
static DeviceNode *Find(uint32_t id);
+ static DeviceNode *FindByDevName(std::string_view devname);
static void Remove(uint32_t id);
- static std::vector<DeviceNode> &GetList() noexcept { return sList; }
+ static auto GetList() noexcept { return al::span{sList}; }
+
+ void parseSampleRate(const spa_pod *value, bool force_update) noexcept;
+ void parsePositions(const spa_pod *value, bool force_update) noexcept;
+ void parseChannelCount(const spa_pod *value, bool force_update) noexcept;
- void parseSampleRate(const spa_pod *value) noexcept;
- void parsePositions(const spa_pod *value) noexcept;
- void parseChannelCount(const spa_pod *value) noexcept;
+ void callEvent(alc::EventType type, std::string_view message)
+ {
+ /* Source nodes aren't recognized for playback, only Sink and Duplex
+ * nodes are. All node types are recognized for capture.
+ */
+ if(mType != NodeType::Source)
+ alc::Event(type, alc::DeviceType::Playback, message);
+ alc::Event(type, alc::DeviceType::Capture, message);
+ }
};
std::vector<DeviceNode> DeviceNode::sList;
std::string DefaultSinkDevice;
@@ -601,8 +718,7 @@ DeviceNode &DeviceNode::Add(uint32_t id)
auto match = std::find_if(sList.begin(), sList.end(), match_id);
if(match != sList.end()) return *match;
- sList.emplace_back();
- auto &n = sList.back();
+ auto &n = sList.emplace_back();
n.mId = id;
return n;
}
@@ -618,6 +734,17 @@ DeviceNode *DeviceNode::Find(uint32_t id)
return nullptr;
}
+DeviceNode *DeviceNode::FindByDevName(std::string_view devname)
+{
+ auto match_id = [devname](DeviceNode &n) noexcept -> bool
+ { return n.mDevName == devname; };
+
+ auto match = std::find_if(sList.begin(), sList.end(), match_id);
+ if(match != sList.end()) return al::to_address(match);
+
+ return nullptr;
+}
+
void DeviceNode::Remove(uint32_t id)
{
auto match_id = [id](DeviceNode &n) noexcept -> bool
@@ -625,6 +752,11 @@ void DeviceNode::Remove(uint32_t id)
if(n.mId != id)
return false;
TRACE("Removing device \"%s\"\n", n.mDevName.c_str());
+ if(gEventHandler.initIsDone(std::memory_order_relaxed))
+ {
+ const std::string msg{"Device removed: "+n.mName};
+ n.callEvent(alc::EventType::DeviceRemoved, msg);
+ }
return true;
};
@@ -674,7 +806,7 @@ bool MatchChannelMap(const al::span<const uint32_t> map0, const spa_audio_channe
return true;
}
-void DeviceNode::parseSampleRate(const spa_pod *value) noexcept
+void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexcept
{
/* TODO: Can this be anything else? Long, Float, Double? */
uint32_t nvals{}, choiceType{};
@@ -683,7 +815,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept
const uint podType{get_pod_type(value)};
if(podType != SPA_TYPE_Int)
{
- WARN("Unhandled sample rate POD type: %u\n", podType);
+ WARN(" Unhandled sample rate POD type: %u\n", podType);
return;
}
@@ -691,15 +823,15 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept
{
if(nvals != 3)
{
- WARN("Unexpected SPA_CHOICE_Range count: %u\n", nvals);
+ WARN(" Unexpected SPA_CHOICE_Range count: %u\n", nvals);
return;
}
auto srates = get_pod_body<int32_t,3>(value);
/* [0] is the default, [1] is the min, and [2] is the max. */
- TRACE("Device ID %" PRIu64 " sample rate: %d (range: %d -> %d)\n", mSerial, srates[0],
- srates[1], srates[2]);
- mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE));
+ TRACE(" sample rate: %d (range: %d -> %d)\n", srates[0], srates[1], srates[2]);
+ if(!mSampleRate || force_update)
+ mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE));
return;
}
@@ -707,7 +839,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept
{
if(nvals == 0)
{
- WARN("Unexpected SPA_CHOICE_Enum count: %u\n", nvals);
+ WARN(" Unexpected SPA_CHOICE_Enum count: %u\n", nvals);
return;
}
auto srates = get_pod_body<int32_t>(value, nvals);
@@ -719,7 +851,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept
others += ", ";
others += std::to_string(srates[i]);
}
- TRACE("Device ID %" PRIu64 " sample rate: %d (%s)\n", mSerial, srates[0], others.c_str());
+ TRACE(" sample rate: %d (%s)\n", srates[0], others.c_str());
/* Pick the first rate listed that's within the allowed range (default
* rate if possible).
*/
@@ -727,7 +859,8 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept
{
if(rate >= MIN_OUTPUT_RATE && rate <= MAX_OUTPUT_RATE)
{
- mSampleRate = static_cast<uint>(rate);
+ if(!mSampleRate || force_update)
+ mSampleRate = static_cast<uint>(rate);
break;
}
}
@@ -738,119 +871,100 @@ void DeviceNode::parseSampleRate(const spa_pod *value) noexcept
{
if(nvals != 1)
{
- WARN("Unexpected SPA_CHOICE_None count: %u\n", nvals);
+ WARN(" Unexpected SPA_CHOICE_None count: %u\n", nvals);
return;
}
auto srates = get_pod_body<int32_t,1>(value);
- TRACE("Device ID %" PRIu64 " sample rate: %d\n", mSerial, srates[0]);
- mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE));
+ TRACE(" sample rate: %d\n", srates[0]);
+ if(!mSampleRate || force_update)
+ mSampleRate = static_cast<uint>(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE));
return;
}
- WARN("Unhandled sample rate choice type: %u\n", choiceType);
+ WARN(" Unhandled sample rate choice type: %u\n", choiceType);
}
-void DeviceNode::parsePositions(const spa_pod *value) noexcept
+void DeviceNode::parsePositions(const spa_pod *value, bool force_update) noexcept
{
+ uint32_t choiceCount{}, choiceType{};
+ value = spa_pod_get_values(value, &choiceCount, &choiceType);
+
+ if(choiceType != SPA_CHOICE_None || choiceCount != 1)
+ {
+ ERR(" Unexpected positions choice: type=%u, count=%u\n", choiceType, choiceCount);
+ return;
+ }
+
const auto chanmap = get_array_span<SPA_TYPE_Id>(value);
if(chanmap.empty()) return;
- mIs51Rear = false;
-
- if(MatchChannelMap(chanmap, X714Map))
- mChannels = DevFmtX714;
- else if(MatchChannelMap(chanmap, X71Map))
- mChannels = DevFmtX71;
- else if(MatchChannelMap(chanmap, X61Map))
- mChannels = DevFmtX61;
- else if(MatchChannelMap(chanmap, X51Map))
- mChannels = DevFmtX51;
- else if(MatchChannelMap(chanmap, X51RearMap))
- {
- mChannels = DevFmtX51;
- mIs51Rear = true;
- }
- else if(MatchChannelMap(chanmap, QuadMap))
- mChannels = DevFmtQuad;
- else if(MatchChannelMap(chanmap, StereoMap))
- mChannels = DevFmtStereo;
- else
- mChannels = DevFmtMono;
- TRACE("Device ID %" PRIu64 " got %zu position%s for %s%s\n", mSerial, chanmap.size(),
- (chanmap.size()==1)?"":"s", DevFmtChannelsString(mChannels), mIs51Rear?"(rear)":"");
+ if(mChannels == InvalidChannelConfig || force_update)
+ {
+ mIs51Rear = false;
+
+ if(MatchChannelMap(chanmap, X714Map))
+ mChannels = DevFmtX714;
+ else if(MatchChannelMap(chanmap, X71Map))
+ mChannels = DevFmtX71;
+ else if(MatchChannelMap(chanmap, X61Map))
+ mChannels = DevFmtX61;
+ else if(MatchChannelMap(chanmap, X51Map))
+ mChannels = DevFmtX51;
+ else if(MatchChannelMap(chanmap, X51RearMap))
+ {
+ mChannels = DevFmtX51;
+ mIs51Rear = true;
+ }
+ else if(MatchChannelMap(chanmap, QuadMap))
+ mChannels = DevFmtQuad;
+ else if(MatchChannelMap(chanmap, StereoMap))
+ mChannels = DevFmtStereo;
+ else
+ mChannels = DevFmtMono;
+ }
+ TRACE(" %zu position%s for %s%s\n", chanmap.size(), (chanmap.size()==1)?"":"s",
+ DevFmtChannelsString(mChannels), mIs51Rear?"(rear)":"");
}
-void DeviceNode::parseChannelCount(const spa_pod *value) noexcept
+void DeviceNode::parseChannelCount(const spa_pod *value, bool force_update) noexcept
{
/* As a fallback with just a channel count, just assume mono or stereo. */
+ uint32_t choiceCount{}, choiceType{};
+ value = spa_pod_get_values(value, &choiceCount, &choiceType);
+
+ if(choiceType != SPA_CHOICE_None || choiceCount != 1)
+ {
+ ERR(" Unexpected positions choice: type=%u, count=%u\n", choiceType, choiceCount);
+ return;
+ }
+
const auto chancount = get_value<SPA_TYPE_Int>(value);
if(!chancount) return;
- mIs51Rear = false;
+ if(mChannels == InvalidChannelConfig || force_update)
+ {
+ mIs51Rear = false;
- if(*chancount >= 2)
- mChannels = DevFmtStereo;
- else if(*chancount >= 1)
- mChannels = DevFmtMono;
- TRACE("Device ID %" PRIu64 " got %d channel%s for %s\n", mSerial, *chancount,
- (*chancount==1)?"":"s", DevFmtChannelsString(mChannels));
+ if(*chancount >= 2)
+ mChannels = DevFmtStereo;
+ else if(*chancount >= 1)
+ mChannels = DevFmtMono;
+ }
+ TRACE(" %d channel%s for %s\n", *chancount, (*chancount==1)?"":"s",
+ DevFmtChannelsString(mChannels));
}
constexpr char MonitorPrefix[]{"Monitor of "};
-constexpr auto MonitorPrefixLen = al::size(MonitorPrefix) - 1;
+constexpr auto MonitorPrefixLen = std::size(MonitorPrefix) - 1;
constexpr char AudioSinkClass[]{"Audio/Sink"};
constexpr char AudioSourceClass[]{"Audio/Source"};
constexpr char AudioSourceVirtualClass[]{"Audio/Source/Virtual"};
constexpr char AudioDuplexClass[]{"Audio/Duplex"};
constexpr char StreamClass[]{"Stream/"};
-/* A generic PipeWire node proxy object used to track changes to sink and
- * source nodes.
- */
-struct NodeProxy {
- static constexpr pw_node_events CreateNodeEvents()
- {
- pw_node_events ret{};
- ret.version = PW_VERSION_NODE_EVENTS;
- ret.info = &NodeProxy::infoCallbackC;
- ret.param = &NodeProxy::paramCallbackC;
- return ret;
- }
-
- uint32_t mId{};
-
- PwNodePtr mNode{};
- spa_hook mListener{};
-
- NodeProxy(uint32_t id, PwNodePtr node)
- : mId{id}, mNode{std::move(node)}
- {
- static constexpr pw_node_events nodeEvents{CreateNodeEvents()};
- ppw_node_add_listener(mNode.get(), &mListener, &nodeEvents, this);
-
- /* Track changes to the enumerable formats (indicates the default
- * format, which is what we're interested in).
- */
- uint32_t fmtids[]{SPA_PARAM_EnumFormat};
- ppw_node_subscribe_params(mNode.get(), al::data(fmtids), al::size(fmtids));
- }
- ~NodeProxy()
- { spa_hook_remove(&mListener); }
-
-
- void infoCallback(const pw_node_info *info);
- static void infoCallbackC(void *object, const pw_node_info *info)
- { static_cast<NodeProxy*>(object)->infoCallback(info); }
-
- void paramCallback(int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param);
- static void paramCallbackC(void *object, int seq, uint32_t id, uint32_t index, uint32_t next,
- const spa_pod *param)
- { static_cast<NodeProxy*>(object)->paramCallback(seq, id, index, next, param); }
-};
-
-void NodeProxy::infoCallback(const pw_node_info *info)
+void NodeProxy::infoCallback(const pw_node_info *info) noexcept
{
/* We only care about property changes here (media class, name/desc).
* Format changes will automatically invoke the param callback.
@@ -888,6 +1002,7 @@ void NodeProxy::infoCallback(const pw_node_info *info)
#ifdef PW_KEY_OBJECT_SERIAL
if(const char *serial_str{spa_dict_lookup(info->props, PW_KEY_OBJECT_SERIAL)})
{
+ errno = 0;
char *serial_end{};
serial_id = std::strtoull(serial_str, &serial_end, 0);
if(*serial_end != '\0' || errno == ERANGE)
@@ -898,15 +1013,40 @@ void NodeProxy::infoCallback(const pw_node_info *info)
}
#endif
+ std::string name;
+ if(nodeName && *nodeName) name = nodeName;
+ else name = "PipeWire node #"+std::to_string(info->id);
+
const char *form_factor{spa_dict_lookup(info->props, PW_KEY_DEVICE_FORM_FACTOR)};
TRACE("Got %s device \"%s\"%s%s%s\n", AsString(ntype), devName ? devName : "(nil)",
form_factor?" (":"", form_factor?form_factor:"", form_factor?")":"");
- TRACE(" \"%s\" = ID %" PRIu64 "\n", nodeName ? nodeName : "(nil)", serial_id);
+ TRACE(" \"%s\" = ID %" PRIu64 "\n", name.c_str(), serial_id);
DeviceNode &node = DeviceNode::Add(info->id);
node.mSerial = serial_id;
- if(nodeName && *nodeName) node.mName = nodeName;
- else node.mName = "PipeWire node #"+std::to_string(info->id);
+ /* This method is called both to notify about a new sink/source node,
+ * and update properties for the node. It's unclear what properties can
+ * change for an existing node without being removed first, so err on
+ * the side of caution: send a DeviceAdded event when the name differs,
+ * and send a DeviceRemoved event if it had a name that's being
+ * replaced.
+ *
+ * This is overkill if the name or devname can't change.
+ */
+ if(node.mName != name)
+ {
+ if(gEventHandler.initIsDone(std::memory_order_relaxed))
+ {
+ if(!node.mName.empty())
+ {
+ const std::string msg{"Device removed: "+node.mName};
+ node.callEvent(alc::EventType::DeviceRemoved, msg);
+ }
+ const std::string msg{"Device added: "+name};
+ node.callEvent(alc::EventType::DeviceAdded, msg);
+ }
+ node.mName = std::move(name);
+ }
node.mDevName = devName ? devName : "";
node.mType = ntype;
node.mIsHeadphones = form_factor && (al::strcasecmp(form_factor, "headphones") == 0
@@ -914,57 +1054,30 @@ void NodeProxy::infoCallback(const pw_node_info *info)
}
}
-void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_pod *param)
+void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_pod *param) noexcept
{
- if(id == SPA_PARAM_EnumFormat)
+ if(id == SPA_PARAM_EnumFormat || id == SPA_PARAM_Format)
{
DeviceNode *node{DeviceNode::Find(mId)};
if(!node) UNLIKELY return;
+ TRACE("Device ID %" PRIu64 " %s format:\n", node->mSerial,
+ (id == SPA_PARAM_EnumFormat) ? "enumerable" : "current");
+
+ const bool force_update{id == SPA_PARAM_Format};
if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_rate)})
- node->parseSampleRate(&prop->value);
+ node->parseSampleRate(&prop->value, force_update);
if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_position)})
- node->parsePositions(&prop->value);
+ node->parsePositions(&prop->value, force_update);
else if((prop=spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_channels)) != nullptr)
- node->parseChannelCount(&prop->value);
+ node->parseChannelCount(&prop->value, force_update);
}
}
-/* A metadata proxy object used to query the default sink and source. */
-struct MetadataProxy {
- static constexpr pw_metadata_events CreateMetadataEvents()
- {
- pw_metadata_events ret{};
- ret.version = PW_VERSION_METADATA_EVENTS;
- ret.property = &MetadataProxy::propertyCallbackC;
- return ret;
- }
-
- uint32_t mId{};
-
- PwMetadataPtr mMetadata{};
- spa_hook mListener{};
-
- MetadataProxy(uint32_t id, PwMetadataPtr mdata)
- : mId{id}, mMetadata{std::move(mdata)}
- {
- static constexpr pw_metadata_events metadataEvents{CreateMetadataEvents()};
- ppw_metadata_add_listener(mMetadata.get(), &mListener, &metadataEvents, this);
- }
- ~MetadataProxy()
- { spa_hook_remove(&mListener); }
-
-
- int propertyCallback(uint32_t id, const char *key, const char *type, const char *value);
- static int propertyCallbackC(void *object, uint32_t id, const char *key, const char *type,
- const char *value)
- { return static_cast<MetadataProxy*>(object)->propertyCallback(id, key, type, value); }
-};
-
int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *type,
- const char *value)
+ const char *value) noexcept
{
if(id != PW_ID_CORE)
return 0;
@@ -997,7 +1110,7 @@ int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *ty
auto get_json_string = [](spa_json *iter)
{
- al::optional<std::string> str;
+ std::optional<std::string> str;
const char *val{};
int len{spa_json_next(iter, &val)};
@@ -1020,9 +1133,35 @@ int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *ty
TRACE("Got default %s device \"%s\"\n", isCapture ? "capture" : "playback",
propValue->c_str());
if(!isCapture)
- DefaultSinkDevice = std::move(*propValue);
+ {
+ if(DefaultSinkDevice != *propValue)
+ {
+ if(gEventHandler.mInitDone.load(std::memory_order_relaxed))
+ {
+ auto entry = DeviceNode::FindByDevName(*propValue);
+ const std::string msg{"Default playback device changed: "+
+ (entry ? entry->mName : std::string{})};
+ alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback,
+ msg);
+ }
+ DefaultSinkDevice = std::move(*propValue);
+ }
+ }
else
- DefaultSourceDevice = std::move(*propValue);
+ {
+ if(DefaultSourceDevice != *propValue)
+ {
+ if(gEventHandler.mInitDone.load(std::memory_order_relaxed))
+ {
+ auto entry = DeviceNode::FindByDevName(*propValue);
+ const std::string msg{"Default capture device changed: "+
+ (entry ? entry->mName : std::string{})};
+ alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture,
+ msg);
+ }
+ DefaultSourceDevice = std::move(*propValue);
+ }
+ }
}
else
{
@@ -1044,7 +1183,7 @@ bool EventManager::init()
return false;
}
- mContext = mLoop.newContext(pw_properties_new(PW_KEY_CONFIG_NAME, "client-rt.conf", nullptr));
+ mContext = mLoop.newContext();
if(!mContext)
{
ERR("Failed to create PipeWire event context (errno: %d)\n", errno);
@@ -1085,26 +1224,12 @@ bool EventManager::init()
return true;
}
-EventManager::~EventManager()
-{
- if(mLoop) mLoop.stop();
-
- for(NodeProxy *node : mNodeList)
- al::destroy_at(node);
- if(mDefaultMetadata)
- al::destroy_at(mDefaultMetadata);
-}
-
void EventManager::kill()
{
if(mLoop) mLoop.stop();
- for(NodeProxy *node : mNodeList)
- al::destroy_at(node);
+ mDefaultMetadata.reset();
mNodeList.clear();
- if(mDefaultMetadata)
- al::destroy_at(mDefaultMetadata);
- mDefaultMetadata = nullptr;
mRegistry = nullptr;
mCore = nullptr;
@@ -1113,7 +1238,7 @@ void EventManager::kill()
}
void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t version,
- const spa_dict *props)
+ const spa_dict *props) noexcept
{
/* We're only interested in interface nodes. */
if(std::strcmp(type, PW_TYPE_INTERFACE_Node) == 0)
@@ -1136,7 +1261,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t
/* Create the proxy object. */
auto node = PwNodePtr{static_cast<pw_node*>(pw_registry_bind(mRegistry.get(), id, type,
- version, sizeof(NodeProxy)))};
+ version, 0))};
if(!node)
{
ERR("Failed to create node proxy object (errno: %d)\n", errno);
@@ -1146,8 +1271,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t
/* Initialize the NodeProxy to hold the node object, add it to the
* active node list, and update the sync point.
*/
- auto *proxy = static_cast<NodeProxy*>(pw_proxy_get_user_data(as<pw_proxy*>(node.get())));
- mNodeList.emplace_back(al::construct_at(proxy, id, std::move(node)));
+ mNodeList.emplace_back(std::make_unique<NodeProxy>(id, std::move(node)));
syncInit();
/* Signal any waiters that we have found a source or sink for audio
@@ -1174,42 +1298,32 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t
}
auto mdata = PwMetadataPtr{static_cast<pw_metadata*>(pw_registry_bind(mRegistry.get(), id,
- type, version, sizeof(MetadataProxy)))};
+ type, version, 0))};
if(!mdata)
{
ERR("Failed to create metadata proxy object (errno: %d)\n", errno);
return;
}
- auto *proxy = static_cast<MetadataProxy*>(
- pw_proxy_get_user_data(as<pw_proxy*>(mdata.get())));
- mDefaultMetadata = al::construct_at(proxy, id, std::move(mdata));
+ mDefaultMetadata.emplace(id, std::move(mdata));
syncInit();
}
}
-void EventManager::removeCallback(uint32_t id)
+void EventManager::removeCallback(uint32_t id) noexcept
{
DeviceNode::Remove(id);
- auto clear_node = [id](NodeProxy *node) noexcept
- {
- if(node->mId != id)
- return false;
- al::destroy_at(node);
- return true;
- };
+ auto clear_node = [id](std::unique_ptr<NodeProxy> &node) noexcept
+ { return node->mId == id; };
auto node_end = std::remove_if(mNodeList.begin(), mNodeList.end(), clear_node);
mNodeList.erase(node_end, mNodeList.end());
if(mDefaultMetadata && mDefaultMetadata->mId == id)
- {
- al::destroy_at(mDefaultMetadata);
- mDefaultMetadata = nullptr;
- }
+ mDefaultMetadata.reset();
}
-void EventManager::coreCallback(uint32_t id, int seq)
+void EventManager::coreCallback(uint32_t id, int seq) noexcept
{
if(id == PW_ID_CORE && seq == mInitSeq)
{
@@ -1275,20 +1389,11 @@ spa_audio_info_raw make_spa_info(DeviceBase *device, bool is51rear, use_f32p_e u
}
class PipeWirePlayback final : public BackendBase {
- void stateChangedCallback(pw_stream_state old, pw_stream_state state, const char *error);
- static void stateChangedCallbackC(void *data, pw_stream_state old, pw_stream_state state,
- const char *error)
- { static_cast<PipeWirePlayback*>(data)->stateChangedCallback(old, state, error); }
-
- void ioChangedCallback(uint32_t id, void *area, uint32_t size);
- static void ioChangedCallbackC(void *data, uint32_t id, void *area, uint32_t size)
- { static_cast<PipeWirePlayback*>(data)->ioChangedCallback(id, area, size); }
-
- void outputCallback();
- static void outputCallbackC(void *data)
- { static_cast<PipeWirePlayback*>(data)->outputCallback(); }
+ void stateChangedCallback(pw_stream_state old, pw_stream_state state, const char *error) noexcept;
+ void ioChangedCallback(uint32_t id, void *area, uint32_t size) noexcept;
+ void outputCallback() noexcept;
- void open(const char *name) override;
+ void open(std::string_view name) override;
bool reset() override;
void start() override;
void stop() override;
@@ -1309,9 +1414,12 @@ class PipeWirePlayback final : public BackendBase {
{
pw_stream_events ret{};
ret.version = PW_VERSION_STREAM_EVENTS;
- ret.state_changed = &PipeWirePlayback::stateChangedCallbackC;
- ret.io_changed = &PipeWirePlayback::ioChangedCallbackC;
- ret.process = &PipeWirePlayback::outputCallbackC;
+ ret.state_changed = [](void *data, pw_stream_state old, pw_stream_state state, const char *error) noexcept
+ { static_cast<PipeWirePlayback*>(data)->stateChangedCallback(old, state, error); };
+ ret.io_changed = [](void *data, uint32_t id, void *area, uint32_t size) noexcept
+ { static_cast<PipeWirePlayback*>(data)->ioChangedCallback(id, area, size); };
+ ret.process = [](void *data) noexcept
+ { static_cast<PipeWirePlayback*>(data)->outputCallback(); };
return ret;
}
@@ -1327,10 +1435,10 @@ public:
};
-void PipeWirePlayback::stateChangedCallback(pw_stream_state, pw_stream_state, const char*)
+void PipeWirePlayback::stateChangedCallback(pw_stream_state, pw_stream_state, const char*) noexcept
{ mLoop.signal(false); }
-void PipeWirePlayback::ioChangedCallback(uint32_t id, void *area, uint32_t size)
+void PipeWirePlayback::ioChangedCallback(uint32_t id, void *area, uint32_t size) noexcept
{
switch(id)
{
@@ -1341,7 +1449,7 @@ void PipeWirePlayback::ioChangedCallback(uint32_t id, void *area, uint32_t size)
}
}
-void PipeWirePlayback::outputCallback()
+void PipeWirePlayback::outputCallback() noexcept
{
pw_buffer *pw_buf{pw_stream_dequeue_buffer(mStream.get())};
if(!pw_buf) UNLIKELY return;
@@ -1373,29 +1481,27 @@ void PipeWirePlayback::outputCallback()
length = minu(length, data.maxsize/sizeof(float));
*chanptr_end = static_cast<float*>(data.data);
++chanptr_end;
- }
-
- mDevice->renderSamples({mChannelPtrs.get(), chanptr_end}, length);
- for(const auto &data : datas)
- {
data.chunk->offset = 0;
data.chunk->stride = sizeof(float);
data.chunk->size = length * sizeof(float);
}
+
+ mDevice->renderSamples({mChannelPtrs.get(), chanptr_end}, length);
+
pw_buf->size = length;
pw_stream_queue_buffer(mStream.get(), pw_buf);
}
-void PipeWirePlayback::open(const char *name)
+void PipeWirePlayback::open(std::string_view name)
{
static std::atomic<uint> OpenCount{0};
uint64_t targetid{PwIdAny};
std::string devname{};
gEventHandler.waitForInit();
- if(!name)
+ if(name.empty())
{
EventWatcherLockGuard _{gEventHandler};
auto&& devlist = DeviceNode::GetList();
@@ -1430,7 +1536,7 @@ void PipeWirePlayback::open(const char *name)
auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name);
if(match == devlist.cend())
throw al::backend_exception{al::backend_error::NoDevice,
- "Device name \"%s\" not found", name};
+ "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()};
targetid = match->mSerial;
devname = match->mName;
@@ -1523,14 +1629,10 @@ bool PipeWirePlayback::reset()
*/
spa_audio_info_raw info{make_spa_info(mDevice, is51rear, ForceF32Planar)};
- /* TODO: How to tell what an appropriate size is? Examples just use this
- * magic value.
- */
- constexpr uint32_t pod_buffer_size{1024};
- auto pod_buffer = std::make_unique<al::byte[]>(pod_buffer_size);
- spa_pod_builder b{make_pod_builder(pod_buffer.get(), pod_buffer_size)};
+ static constexpr uint32_t pod_buffer_size{1024};
+ PodDynamicBuilder b(pod_buffer_size);
- const spa_pod *params{spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)};
+ const spa_pod *params{spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info)};
if(!params)
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to set PipeWire audio format parameters"};
@@ -1674,7 +1776,10 @@ void PipeWirePlayback::start()
}
#endif
if(!--wait_count)
+ {
+ ERR("Timeout getting PipeWire stream buffering info\n");
break;
+ }
plock.unlock();
std::this_thread::sleep_for(milliseconds{20});
@@ -1769,7 +1874,7 @@ ClockLatency PipeWirePlayback::getClockLatency()
delay -= monoclock - nanoseconds{ptime.now};
/* Return the mixer time and delay. Clamp the delay to no less than 0,
- * incase timer drift got that severe.
+ * in case timer drift got that severe.
*/
ClockLatency ret{};
ret.ClockTime = mixtime;
@@ -1780,19 +1885,13 @@ ClockLatency PipeWirePlayback::getClockLatency()
class PipeWireCapture final : public BackendBase {
- void stateChangedCallback(pw_stream_state old, pw_stream_state state, const char *error);
- static void stateChangedCallbackC(void *data, pw_stream_state old, pw_stream_state state,
- const char *error)
- { static_cast<PipeWireCapture*>(data)->stateChangedCallback(old, state, error); }
-
- void inputCallback();
- static void inputCallbackC(void *data)
- { static_cast<PipeWireCapture*>(data)->inputCallback(); }
+ void stateChangedCallback(pw_stream_state old, pw_stream_state state, const char *error) noexcept;
+ void inputCallback() 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;
uint64_t mTargetId{PwIdAny};
@@ -1808,8 +1907,10 @@ class PipeWireCapture final : public BackendBase {
{
pw_stream_events ret{};
ret.version = PW_VERSION_STREAM_EVENTS;
- ret.state_changed = &PipeWireCapture::stateChangedCallbackC;
- ret.process = &PipeWireCapture::inputCallbackC;
+ ret.state_changed = [](void *data, pw_stream_state old, pw_stream_state state, const char *error) noexcept
+ { static_cast<PipeWireCapture*>(data)->stateChangedCallback(old, state, error); };
+ ret.process = [](void *data) noexcept
+ { static_cast<PipeWireCapture*>(data)->inputCallback(); };
return ret;
}
@@ -1821,10 +1922,10 @@ public:
};
-void PipeWireCapture::stateChangedCallback(pw_stream_state, pw_stream_state, const char*)
+void PipeWireCapture::stateChangedCallback(pw_stream_state, pw_stream_state, const char*) noexcept
{ mLoop.signal(false); }
-void PipeWireCapture::inputCallback()
+void PipeWireCapture::inputCallback() noexcept
{
pw_buffer *pw_buf{pw_stream_dequeue_buffer(mStream.get())};
if(!pw_buf) UNLIKELY return;
@@ -1839,14 +1940,14 @@ void PipeWireCapture::inputCallback()
}
-void PipeWireCapture::open(const char *name)
+void PipeWireCapture::open(std::string_view name)
{
static std::atomic<uint> OpenCount{0};
uint64_t targetid{PwIdAny};
std::string devname{};
gEventHandler.waitForInit();
- if(!name)
+ if(name.empty())
{
EventWatcherLockGuard _{gEventHandler};
auto&& devlist = DeviceNode::GetList();
@@ -1884,16 +1985,17 @@ void PipeWireCapture::open(const char *name)
auto match_name = [name](const DeviceNode &n) -> bool
{ return n.mType != NodeType::Sink && n.mName == name; };
auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name);
- if(match == devlist.cend() && std::strncmp(name, MonitorPrefix, MonitorPrefixLen) == 0)
+ if(match == devlist.cend() && name.length() >= MonitorPrefixLen
+ && std::strncmp(name.data(), MonitorPrefix, MonitorPrefixLen) == 0)
{
- const char *sinkname{name + MonitorPrefixLen};
+ const std::string_view sinkname{name.substr(MonitorPrefixLen)};
auto match_sinkname = [sinkname](const DeviceNode &n) -> bool
{ return n.mType == NodeType::Sink && n.mName == sinkname; };
match = std::find_if(devlist.cbegin(), devlist.cend(), match_sinkname);
}
if(match == devlist.cend())
throw al::backend_exception{al::backend_error::NoDevice,
- "Device name \"%s\" not found", name};
+ "Device name \"%.*s\" not found", static_cast<int>(name.length()), name.data()};
targetid = match->mSerial;
devname = name;
@@ -1952,11 +2054,10 @@ void PipeWireCapture::open(const char *name)
}
spa_audio_info_raw info{make_spa_info(mDevice, is51rear, UseDevType)};
- constexpr uint32_t pod_buffer_size{1024};
- auto pod_buffer = std::make_unique<al::byte[]>(pod_buffer_size);
- spa_pod_builder b{make_pod_builder(pod_buffer.get(), pod_buffer_size)};
+ static constexpr uint32_t pod_buffer_size{1024};
+ PodDynamicBuilder b(pod_buffer_size);
- const spa_pod *params[]{spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)};
+ const spa_pod *params[]{spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info)};
if(!params[0])
throw al::backend_exception{al::backend_error::DeviceError,
"Failed to set PipeWire audio format parameters"};
@@ -2054,7 +2155,7 @@ void PipeWireCapture::stop()
uint PipeWireCapture::availableSamples()
{ return static_cast<uint>(mRing->readSpace()); }
-void PipeWireCapture::captureSamples(al::byte *buffer, uint samples)
+void PipeWireCapture::captureSamples(std::byte *buffer, uint samples)
{ mRing->read(buffer, samples); }
} // namespace
@@ -2164,3 +2265,18 @@ BackendFactory &PipeWireBackendFactory::getFactory()
static PipeWireBackendFactory factory{};
return factory;
}
+
+alc::EventSupport PipeWireBackendFactory::queryEventSupport(alc::EventType eventType, BackendType)
+{
+ switch(eventType)
+ {
+ case alc::EventType::DefaultDeviceChanged:
+ case alc::EventType::DeviceAdded:
+ case alc::EventType::DeviceRemoved:
+ return alc::EventSupport::FullSupport;
+
+ case alc::EventType::Count:
+ break;
+ }
+ return alc::EventSupport::NoSupport;
+}
diff --git a/alc/backends/pipewire.h b/alc/backends/pipewire.h
index 5f930239..5493684f 100644
--- a/alc/backends/pipewire.h
+++ b/alc/backends/pipewire.h
@@ -13,6 +13,8 @@ public:
bool querySupport(BackendType type) override;
+ alc::EventSupport queryEventSupport(alc::EventType eventType, BackendType type) override;
+
std::string probe(BackendType type) override;
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
diff --git a/alc/backends/portaudio.cpp b/alc/backends/portaudio.cpp
index 9c94587d..979a54d6 100644
--- a/alc/backends/portaudio.cpp
+++ b/alc/backends/portaudio.cpp
@@ -26,6 +26,7 @@
#include <cstdlib>
#include <cstring>
+#include "albit.h"
#include "alc/alconfig.h"
#include "alnumeric.h"
#include "core/device.h"
@@ -85,7 +86,7 @@ struct PortPlayback final : public BackendBase {
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;
@@ -115,13 +116,13 @@ int PortPlayback::writeCallback(const void*, void *outputBuffer, unsigned long f
}
-void PortPlayback::open(const char *name)
+void PortPlayback::open(std::string_view name)
{
- if(!name)
+ if(name.empty())
name = pa_device;
- else if(strcmp(name, pa_device) != 0)
- throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
- name};
+ else if(name != pa_device)
+ throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+ static_cast<int>(name.length()), name.data()};
PaStreamParameters params{};
auto devidopt = ConfigValueInt(nullptr, "port", "device");
@@ -244,10 +245,10 @@ struct PortCapture final : public BackendBase {
framesPerBuffer, timeInfo, statusFlags);
}
- 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};
@@ -275,13 +276,13 @@ int PortCapture::readCallback(const void *inputBuffer, void*, unsigned long fram
}
-void PortCapture::open(const char *name)
+void PortCapture::open(std::string_view name)
{
- if(!name)
+ if(name.empty())
name = pa_device;
- else if(strcmp(name, pa_device) != 0)
- throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
- name};
+ else if(name != pa_device)
+ throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+ static_cast<int>(name.length()), name.data()};
uint samples{mDevice->BufferSize};
samples = maxu(samples, 100 * mDevice->Frequency / 1000);
@@ -348,7 +349,7 @@ void PortCapture::stop()
uint PortCapture::availableSamples()
{ return static_cast<uint>(mRing->readSpace()); }
-void PortCapture::captureSamples(al::byte *buffer, uint samples)
+void PortCapture::captureSamples(std::byte *buffer, uint samples)
{ mRing->read(buffer, samples); }
} // namespace
@@ -376,7 +377,7 @@ bool PortBackendFactory::init()
return false;
#define LOAD_FUNC(f) do { \
- p##f = reinterpret_cast<decltype(p##f)>(GetSymbol(pa_handle, #f)); \
+ p##f = al::bit_cast<decltype(p##f)>(GetSymbol(pa_handle, #f)); \
if(p##f == nullptr) \
{ \
CloseLib(pa_handle); \
diff --git a/alc/backends/pulseaudio.cpp b/alc/backends/pulseaudio.cpp
index 4b0e316f..bebc182d 100644
--- a/alc/backends/pulseaudio.cpp
+++ b/alc/backends/pulseaudio.cpp
@@ -31,17 +31,18 @@
#include <cstring>
#include <limits>
#include <mutex>
+#include <optional>
#include <stdint.h>
#include <stdlib.h>
#include <string>
#include <sys/types.h>
#include <utility>
+#include <vector>
-#include "albyte.h"
+#include "albit.h"
#include "alc/alconfig.h"
#include "almalloc.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "alspan.h"
#include "core/devformat.h"
#include "core/device.h"
@@ -49,7 +50,6 @@
#include "dynload.h"
#include "opthelpers.h"
#include "strutils.h"
-#include "vector.h"
#include <pulse/pulseaudio.h>
@@ -65,6 +65,8 @@ using uint = unsigned int;
MAGIC(pa_context_get_state); \
MAGIC(pa_context_disconnect); \
MAGIC(pa_context_set_state_callback); \
+ MAGIC(pa_context_set_subscribe_callback); \
+ MAGIC(pa_context_subscribe); \
MAGIC(pa_context_errno); \
MAGIC(pa_context_connect); \
MAGIC(pa_context_get_server_info); \
@@ -136,6 +138,8 @@ PULSE_FUNCS(MAKE_FUNC)
#define pa_context_get_state ppa_context_get_state
#define pa_context_disconnect ppa_context_disconnect
#define pa_context_set_state_callback ppa_context_set_state_callback
+#define pa_context_set_subscribe_callback ppa_context_set_subscribe_callback
+#define pa_context_subscribe ppa_context_subscribe
#define pa_context_errno ppa_context_errno
#define pa_context_connect ppa_context_connect
#define pa_context_get_server_info ppa_context_get_server_info
@@ -270,6 +274,9 @@ constexpr pa_context_flags_t& operator|=(pa_context_flags_t &lhs, pa_context_fla
return lhs;
}
+constexpr pa_subscription_mask_t operator|(pa_subscription_mask_t lhs, pa_subscription_mask_t rhs)
+{ return pa_subscription_mask_t(lhs | al::to_underlying(rhs)); }
+
struct DevMap {
std::string name;
@@ -282,8 +289,8 @@ bool checkName(const al::span<const DevMap> list, const std::string &name)
return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
}
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
+std::vector<DevMap> PlaybackDevices;
+std::vector<DevMap> CaptureDevices;
/* Global flags and properties */
@@ -291,13 +298,14 @@ pa_context_flags_t pulse_ctx_flags;
class PulseMainloop {
pa_threaded_mainloop *mLoop{};
+ pa_context *mContext{};
public:
PulseMainloop() = default;
PulseMainloop(const PulseMainloop&) = delete;
PulseMainloop(PulseMainloop&& rhs) noexcept : mLoop{rhs.mLoop} { rhs.mLoop = nullptr; }
explicit PulseMainloop(pa_threaded_mainloop *loop) noexcept : mLoop{loop} { }
- ~PulseMainloop() { if(mLoop) pa_threaded_mainloop_free(mLoop); }
+ ~PulseMainloop();
PulseMainloop& operator=(const PulseMainloop&) = delete;
PulseMainloop& operator=(PulseMainloop&& rhs) noexcept
@@ -316,6 +324,7 @@ public:
auto stop() const { return pa_threaded_mainloop_stop(mLoop); }
auto getApi() const { return pa_threaded_mainloop_get_api(mLoop); }
+ auto getContext() const noexcept { return mContext; }
auto lock() const { return pa_threaded_mainloop_lock(mLoop); }
auto unlock() const { return pa_threaded_mainloop_unlock(mLoop); }
@@ -329,7 +338,7 @@ public:
static void streamSuccessCallbackC(pa_stream *stream, int success, void *pdata) noexcept
{ static_cast<PulseMainloop*>(pdata)->streamSuccessCallback(stream, success); }
- void close(pa_context *context, pa_stream *stream=nullptr);
+ void close(pa_stream *stream=nullptr);
void deviceSinkCallback(pa_context*, const pa_sink_info *info, int eol) noexcept
@@ -420,6 +429,41 @@ struct MainloopUniqueLock : public std::unique_lock<PulseMainloop> {
}
+ void setEventHandler()
+ {
+ pa_operation *op{pa_context_subscribe(mutex()->mContext,
+ PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE,
+ [](pa_context*, int, void *pdata) noexcept
+ { static_cast<PulseMainloop*>(pdata)->signal(); },
+ mutex())};
+ waitForOperation(op);
+
+ /* Watch for device added/removed events.
+ *
+ * TODO: Also track the "default" device, in as much as PulseAudio has
+ * the concept of a default device (whatever device is opened when not
+ * specifying a specific sink or source name). There doesn't seem to be
+ * an event for this.
+ */
+ auto handler = [](pa_context*, pa_subscription_event_type_t t, uint32_t, void*) noexcept
+ {
+ const auto eventFacility = (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK);
+ if(eventFacility == PA_SUBSCRIPTION_EVENT_SINK
+ || eventFacility == PA_SUBSCRIPTION_EVENT_SOURCE)
+ {
+ const auto deviceType = (eventFacility == PA_SUBSCRIPTION_EVENT_SINK)
+ ? alc::DeviceType::Playback : alc::DeviceType::Capture;
+ const auto eventType = (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK);
+ if(eventType == PA_SUBSCRIPTION_EVENT_NEW)
+ alc::Event(alc::EventType::DeviceAdded, deviceType, "Device added");
+ else if(eventType == PA_SUBSCRIPTION_EVENT_REMOVE)
+ alc::Event(alc::EventType::DeviceRemoved, deviceType, "Device removed");
+ }
+ };
+ pa_context_set_subscribe_callback(mutex()->mContext, handler, nullptr);
+ }
+
+
void contextStateCallback(pa_context *context) noexcept
{
pa_context_state_t state{pa_context_get_state(context)};
@@ -434,31 +478,46 @@ struct MainloopUniqueLock : public std::unique_lock<PulseMainloop> {
mutex()->signal();
}
- pa_context *connectContext();
- pa_stream *connectStream(const char *device_name, pa_context *context, pa_stream_flags_t flags,
+ void connectContext();
+ pa_stream *connectStream(const char *device_name, pa_stream_flags_t flags,
pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap, BackendType type);
};
using MainloopLockGuard = std::lock_guard<PulseMainloop>;
+PulseMainloop::~PulseMainloop()
+{
+ if(mContext)
+ {
+ MainloopUniqueLock _{*this};
+ pa_context_disconnect(mContext);
+ pa_context_unref(mContext);
+ }
+ if(mLoop)
+ pa_threaded_mainloop_free(mLoop);
+}
+
-pa_context *MainloopUniqueLock::connectContext()
+void MainloopUniqueLock::connectContext()
{
- pa_context *context{pa_context_new(mutex()->getApi(), nullptr)};
- if(!context) throw al::backend_exception{al::backend_error::OutOfMemory,
+ if(mutex()->mContext)
+ return;
+
+ mutex()->mContext = pa_context_new(mutex()->getApi(), nullptr);
+ if(!mutex()->mContext) throw al::backend_exception{al::backend_error::OutOfMemory,
"pa_context_new() failed"};
- pa_context_set_state_callback(context, [](pa_context *ctx, void *pdata) noexcept
+ pa_context_set_state_callback(mutex()->mContext, [](pa_context *ctx, void *pdata) noexcept
{ return static_cast<MainloopUniqueLock*>(pdata)->contextStateCallback(ctx); }, this);
int err;
- if((err=pa_context_connect(context, nullptr, pulse_ctx_flags, nullptr)) >= 0)
+ if((err=pa_context_connect(mutex()->mContext, nullptr, pulse_ctx_flags, nullptr)) >= 0)
{
pa_context_state_t state;
- while((state=pa_context_get_state(context)) != PA_CONTEXT_READY)
+ while((state=pa_context_get_state(mutex()->mContext)) != PA_CONTEXT_READY)
{
if(!PA_CONTEXT_IS_GOOD(state))
{
- err = pa_context_errno(context);
+ err = pa_context_errno(mutex()->mContext);
if(err > 0) err = -err;
break;
}
@@ -466,27 +525,25 @@ pa_context *MainloopUniqueLock::connectContext()
wait();
}
}
- pa_context_set_state_callback(context, nullptr, nullptr);
+ pa_context_set_state_callback(mutex()->mContext, nullptr, nullptr);
if(err < 0)
{
- pa_context_unref(context);
+ pa_context_unref(mutex()->mContext);
+ mutex()->mContext = nullptr;
throw al::backend_exception{al::backend_error::DeviceError, "Context did not connect (%s)",
pa_strerror(err)};
}
-
- return context;
}
-pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_context *context,
- pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap,
- BackendType type)
+pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_stream_flags_t flags,
+ pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap, BackendType type)
{
const char *stream_id{(type==BackendType::Playback) ? "Playback Stream" : "Capture Stream"};
- pa_stream *stream{pa_stream_new(context, stream_id, spec, chanmap)};
+ pa_stream *stream{pa_stream_new(mutex()->mContext, stream_id, spec, chanmap)};
if(!stream)
throw al::backend_exception{al::backend_error::OutOfMemory, "pa_stream_new() failed (%s)",
- pa_strerror(pa_context_errno(context))};
+ pa_strerror(pa_context_errno(mutex()->mContext))};
pa_stream_set_state_callback(stream, [](pa_stream *strm, void *pdata) noexcept
{ return static_cast<MainloopUniqueLock*>(pdata)->streamStateCallback(strm); }, this);
@@ -506,7 +563,7 @@ pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_context
{
if(!PA_STREAM_IS_GOOD(state))
{
- err = pa_context_errno(context);
+ err = pa_context_errno(mutex()->mContext);
pa_stream_unref(stream);
throw al::backend_exception{al::backend_error::DeviceError,
"%s did not get ready (%s)", stream_id, pa_strerror(err)};
@@ -519,75 +576,57 @@ pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_context
return stream;
}
-void PulseMainloop::close(pa_context *context, pa_stream *stream)
+void PulseMainloop::close(pa_stream *stream)
{
- MainloopUniqueLock _{*this};
- if(stream)
- {
- pa_stream_set_state_callback(stream, nullptr, nullptr);
- pa_stream_set_moved_callback(stream, nullptr, nullptr);
- pa_stream_set_write_callback(stream, nullptr, nullptr);
- pa_stream_set_buffer_attr_callback(stream, nullptr, nullptr);
- pa_stream_disconnect(stream);
- pa_stream_unref(stream);
- }
+ if(!stream)
+ return;
- pa_context_disconnect(context);
- pa_context_unref(context);
+ MainloopUniqueLock _{*this};
+ pa_stream_set_state_callback(stream, nullptr, nullptr);
+ pa_stream_set_moved_callback(stream, nullptr, nullptr);
+ pa_stream_set_write_callback(stream, nullptr, nullptr);
+ pa_stream_set_buffer_attr_callback(stream, nullptr, nullptr);
+ pa_stream_disconnect(stream);
+ pa_stream_unref(stream);
}
void PulseMainloop::probePlaybackDevices()
{
- pa_context *context{};
-
PlaybackDevices.clear();
try {
MainloopUniqueLock plock{*this};
auto sink_callback = [](pa_context *ctx, const pa_sink_info *info, int eol, void *pdata) noexcept
{ return static_cast<PulseMainloop*>(pdata)->deviceSinkCallback(ctx, info, eol); };
- context = plock.connectContext();
- pa_operation *op{pa_context_get_sink_info_by_name(context, nullptr, sink_callback, this)};
+ pa_operation *op{pa_context_get_sink_info_by_name(mContext, nullptr, sink_callback, this)};
plock.waitForOperation(op);
- op = pa_context_get_sink_info_list(context, sink_callback, this);
+ op = pa_context_get_sink_info_list(mContext, sink_callback, this);
plock.waitForOperation(op);
-
- pa_context_disconnect(context);
- pa_context_unref(context);
- context = nullptr;
}
catch(std::exception &e) {
ERR("Error enumerating devices: %s\n", e.what());
- if(context) close(context);
}
}
void PulseMainloop::probeCaptureDevices()
{
- pa_context *context{};
-
CaptureDevices.clear();
try {
MainloopUniqueLock plock{*this};
auto src_callback = [](pa_context *ctx, const pa_source_info *info, int eol, void *pdata) noexcept
{ return static_cast<PulseMainloop*>(pdata)->deviceSourceCallback(ctx, info, eol); };
- context = plock.connectContext();
- pa_operation *op{pa_context_get_source_info_by_name(context, nullptr, src_callback, this)};
+ pa_operation *op{pa_context_get_source_info_by_name(mContext, nullptr, src_callback,
+ this)};
plock.waitForOperation(op);
- op = pa_context_get_source_info_list(context, src_callback, this);
+ op = pa_context_get_source_info_list(mContext, src_callback, this);
plock.waitForOperation(op);
-
- pa_context_disconnect(context);
- pa_context_unref(context);
- context = nullptr;
}
catch(std::exception &e) {
ERR("Error enumerating devices: %s\n", e.what());
- if(context) close(context);
}
}
@@ -607,7 +646,7 @@ struct PulsePlayback final : public BackendBase {
void sinkNameCallback(pa_context *context, const pa_sink_info *info, int eol) noexcept;
void streamMovedCallback(pa_stream *stream) noexcept;
- void open(const char *name) override;
+ void open(std::string_view name) override;
bool reset() override;
void start() override;
void stop() override;
@@ -615,14 +654,13 @@ struct PulsePlayback final : public BackendBase {
PulseMainloop mMainloop;
- al::optional<std::string> mDeviceName{al::nullopt};
+ std::optional<std::string> mDeviceName{std::nullopt};
bool mIs51Rear{false};
pa_buffer_attr mAttr;
pa_sample_spec mSpec;
pa_stream *mStream{nullptr};
- pa_context *mContext{nullptr};
uint mFrameSize{0u};
@@ -630,14 +668,7 @@ struct PulsePlayback final : public BackendBase {
};
PulsePlayback::~PulsePlayback()
-{
- if(!mContext)
- return;
-
- mMainloop.close(mContext, mStream);
- mContext = nullptr;
- mStream = nullptr;
-}
+{ if(mStream) mMainloop.close(mStream); }
void PulsePlayback::bufferAttrCallback(pa_stream *stream) noexcept
@@ -750,14 +781,14 @@ void PulsePlayback::streamMovedCallback(pa_stream *stream) noexcept
}
-void PulsePlayback::open(const char *name)
+void PulsePlayback::open(std::string_view name)
{
mMainloop = PulseMainloop::Create();
mMainloop.start();
const char *pulse_name{nullptr};
const char *dev_name{nullptr};
- if(name)
+ if(!name.empty())
{
if(PlaybackDevices.empty())
mMainloop.probePlaybackDevices();
@@ -766,13 +797,13 @@ void PulsePlayback::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", static_cast<int>(name.length()), name.data()};
pulse_name = iter->device_name.c_str();
dev_name = iter->name.c_str();
}
MainloopUniqueLock plock{mMainloop};
- mContext = plock.connectContext();
+ plock.connectContext();
pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
PA_STREAM_FIX_CHANNELS};
@@ -790,7 +821,7 @@ void PulsePlayback::open(const char *name)
if(defname) pulse_name = defname->c_str();
}
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
- mStream = plock.connectStream(pulse_name, mContext, flags, nullptr, &spec, nullptr,
+ mStream = plock.connectStream(pulse_name, flags, nullptr, &spec, nullptr,
BackendType::Playback);
pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept
@@ -803,7 +834,7 @@ void PulsePlayback::open(const char *name)
{
auto name_callback = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept
{ return static_cast<PulsePlayback*>(pdata)->sinkNameCallback(context, info, eol); };
- pa_operation *op{pa_context_get_sink_info_by_name(mContext,
+ pa_operation *op{pa_context_get_sink_info_by_name(mMainloop.getContext(),
pa_stream_get_device_name(mStream), name_callback, this)};
plock.waitForOperation(op);
}
@@ -829,7 +860,8 @@ bool PulsePlayback::reset()
auto info_callback = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept
{ return static_cast<PulsePlayback*>(pdata)->sinkInfoCallback(context, info, eol); };
- pa_operation *op{pa_context_get_sink_info_by_name(mContext, deviceName, info_callback, this)};
+ pa_operation *op{pa_context_get_sink_info_by_name(mMainloop.getContext(), deviceName,
+ info_callback, this)};
plock.waitForOperation(op);
pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING |
@@ -916,7 +948,7 @@ bool PulsePlayback::reset()
mAttr.minreq = mDevice->UpdateSize * frame_size;
mAttr.fragsize = ~0u;
- mStream = plock.connectStream(deviceName, mContext, flags, &mAttr, &mSpec, &chanmap,
+ mStream = plock.connectStream(deviceName, flags, &mAttr, &mSpec, &chanmap,
BackendType::Playback);
pa_stream_set_state_callback(mStream, [](pa_stream *stream, void *pdata) noexcept
@@ -1033,42 +1065,34 @@ struct PulseCapture final : public BackendBase {
void sourceNameCallback(pa_context *context, const pa_source_info *info, int eol) noexcept;
void streamMovedCallback(pa_stream *stream) 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;
ClockLatency getClockLatency() override;
PulseMainloop mMainloop;
- al::optional<std::string> mDeviceName{al::nullopt};
+ std::optional<std::string> mDeviceName{std::nullopt};
- al::span<const al::byte> mCapBuffer;
+ al::span<const std::byte> mCapBuffer;
size_t mHoleLength{0};
size_t mPacketLength{0};
uint mLastReadable{0u};
- al::byte mSilentVal{};
+ std::byte mSilentVal{};
pa_buffer_attr mAttr{};
pa_sample_spec mSpec{};
pa_stream *mStream{nullptr};
- pa_context *mContext{nullptr};
DEF_NEWDEL(PulseCapture)
};
PulseCapture::~PulseCapture()
-{
- if(!mContext)
- return;
-
- mMainloop.close(mContext, mStream);
- mContext = nullptr;
- mStream = nullptr;
-}
+{ if(mStream) mMainloop.close(mStream); }
void PulseCapture::streamStateCallback(pa_stream *stream) noexcept
@@ -1098,7 +1122,7 @@ void PulseCapture::streamMovedCallback(pa_stream *stream) noexcept
}
-void PulseCapture::open(const char *name)
+void PulseCapture::open(std::string_view name)
{
if(!mMainloop)
{
@@ -1107,7 +1131,7 @@ void PulseCapture::open(const char *name)
}
const char *pulse_name{nullptr};
- if(name)
+ if(!name.empty())
{
if(CaptureDevices.empty())
mMainloop.probeCaptureDevices();
@@ -1116,13 +1140,13 @@ void PulseCapture::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", static_cast<int>(name.length()), name.data()};
pulse_name = iter->device_name.c_str();
mDevice->DeviceName = iter->name;
}
MainloopUniqueLock plock{mMainloop};
- mContext = plock.connectContext();
+ plock.connectContext();
pa_channel_map chanmap{};
switch(mDevice->FmtChans)
@@ -1158,7 +1182,7 @@ void PulseCapture::open(const char *name)
switch(mDevice->FmtType)
{
case DevFmtUByte:
- mSilentVal = al::byte(0x80);
+ mSilentVal = std::byte(0x80);
mSpec.format = PA_SAMPLE_U8;
break;
case DevFmtShort:
@@ -1194,7 +1218,7 @@ void PulseCapture::open(const char *name)
flags |= PA_STREAM_DONT_MOVE;
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
- mStream = plock.connectStream(pulse_name, mContext, flags, &mAttr, &mSpec, &chanmap,
+ mStream = plock.connectStream(pulse_name, flags, &mAttr, &mSpec, &chanmap,
BackendType::Capture);
pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept
@@ -1208,7 +1232,7 @@ void PulseCapture::open(const char *name)
{
auto name_callback = [](pa_context *context, const pa_source_info *info, int eol, void *pdata) noexcept
{ return static_cast<PulseCapture*>(pdata)->sourceNameCallback(context, info, eol); };
- pa_operation *op{pa_context_get_source_info_by_name(mContext,
+ pa_operation *op{pa_context_get_source_info_by_name(mMainloop.getContext(),
pa_stream_get_device_name(mStream), name_callback, this)};
plock.waitForOperation(op);
}
@@ -1230,9 +1254,9 @@ void PulseCapture::stop()
plock.waitForOperation(op);
}
-void PulseCapture::captureSamples(al::byte *buffer, uint samples)
+void PulseCapture::captureSamples(std::byte *buffer, uint samples)
{
- al::span<al::byte> dstbuf{buffer, samples * pa_frame_size(&mSpec)};
+ al::span<std::byte> dstbuf{buffer, samples * pa_frame_size(&mSpec)};
/* Capture is done in fragment-sized chunks, so we loop until we get all
* that's available.
@@ -1281,7 +1305,7 @@ void PulseCapture::captureSamples(al::byte *buffer, uint samples)
if(pa_stream_peek(mStream, &capbuf, &caplen) < 0) UNLIKELY
{
mDevice->handleDisconnect("Failed retrieving capture samples: %s",
- pa_strerror(pa_context_errno(mContext)));
+ pa_strerror(pa_context_errno(mMainloop.getContext())));
break;
}
plock.unlock();
@@ -1290,7 +1314,7 @@ void PulseCapture::captureSamples(al::byte *buffer, uint samples)
if(!capbuf) UNLIKELY
mHoleLength = caplen;
else
- mCapBuffer = {static_cast<const al::byte*>(capbuf), caplen};
+ mCapBuffer = {static_cast<const std::byte*>(capbuf), caplen};
mPacketLength = caplen;
}
if(!dstbuf.empty())
@@ -1381,7 +1405,7 @@ bool PulseBackendFactory::init()
}
#define LOAD_FUNC(x) do { \
- p##x = reinterpret_cast<decltype(p##x)>(GetSymbol(pulse_handle, #x)); \
+ p##x = al::bit_cast<decltype(p##x)>(GetSymbol(pulse_handle, #x)); \
if(!(p##x)) { \
ret = false; \
missing_funcs += "\n" #x; \
@@ -1412,9 +1436,8 @@ bool PulseBackendFactory::init()
}
MainloopUniqueLock plock{gGlobalMainloop};
- pa_context *context{plock.connectContext()};
- pa_context_disconnect(context);
- pa_context_unref(context);
+ plock.connectContext();
+ plock.setEventHandler();
return true;
}
catch(...) {
@@ -1467,3 +1490,18 @@ BackendFactory &PulseBackendFactory::getFactory()
static PulseBackendFactory factory{};
return factory;
}
+
+alc::EventSupport PulseBackendFactory::queryEventSupport(alc::EventType eventType, BackendType)
+{
+ switch(eventType)
+ {
+ case alc::EventType::DeviceAdded:
+ case alc::EventType::DeviceRemoved:
+ return alc::EventSupport::FullSupport;
+
+ case alc::EventType::DefaultDeviceChanged:
+ case alc::EventType::Count:
+ break;
+ }
+ return alc::EventSupport::NoSupport;
+}
diff --git a/alc/backends/pulseaudio.h b/alc/backends/pulseaudio.h
index 6690fe8a..4752a891 100644
--- a/alc/backends/pulseaudio.h
+++ b/alc/backends/pulseaudio.h
@@ -9,6 +9,8 @@ public:
bool querySupport(BackendType type) override;
+ alc::EventSupport queryEventSupport(alc::EventType eventType, BackendType type) override;
+
std::string probe(BackendType type) override;
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
diff --git a/alc/backends/sdl2.cpp b/alc/backends/sdl2.cpp
index a4a5a9ac..f5ed4316 100644
--- a/alc/backends/sdl2.cpp
+++ b/alc/backends/sdl2.cpp
@@ -53,10 +53,8 @@ struct Sdl2Backend final : public BackendBase {
~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;
@@ -86,7 +84,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{};
@@ -103,23 +101,37 @@ void Sdl2Backend::open(const char *name)
}
want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2;
want.samples = static_cast<Uint16>(minu(mDevice->UpdateSize, 8192));
- want.callback = &Sdl2Backend::audioCallbackC;
+ 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.
*/
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,
+ if(name.length() >= prefix_len && strncmp(name.data(), DEVNAME_PREFIX, prefix_len) == 0)
+ {
+ /* Copy the string_view to a string to ensure it's null terminated
+ * for this call.
+ */
+ const std::string devname{name.substr(prefix_len)};
+ 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 +173,7 @@ void Sdl2Backend::open(const char *name)
mFmtType = devtype;
mUpdateSize = have.samples;
- mDevice->DeviceName = name ? name : defaultDeviceName;
+ mDevice->DeviceName = name;
}
bool Sdl2Backend::reset()
diff --git a/alc/backends/sndio.cpp b/alc/backends/sndio.cpp
index 077e77f2..d54c337b 100644
--- a/alc/backends/sndio.cpp
+++ b/alc/backends/sndio.cpp
@@ -22,21 +22,21 @@
#include "sndio.h"
+#include <cinttypes>
#include <functional>
-#include <inttypes.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <thread>
+#include <vector>
#include "alnumeric.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>
@@ -57,7 +57,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,7 +65,7 @@ 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;
@@ -91,7 +91,7 @@ int SndioPlayback::mixerProc()
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 +112,13 @@ int SndioPlayback::mixerProc()
}
-void SndioPlayback::open(const char *name)
+void SndioPlayback::open(std::string_view name)
{
- if(!name)
+ if(name.empty())
name = sndio_device;
- else if(strcmp(name, sndio_device) != 0)
- throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
- name};
+ else if(name != sndio_device)
+ throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+ static_cast<int>(name.length()), name.data()};
sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)};
if(!sndHandle)
@@ -231,9 +231,9 @@ retry_params:
mBuffer.resize(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 +280,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};
@@ -349,7 +349,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())};
@@ -382,13 +382,13 @@ int SndioCapture::recordProc()
}
-void SndioCapture::open(const char *name)
+void SndioCapture::open(std::string_view name)
{
- if(!name)
+ if(name.empty())
name = sndio_device;
- else if(strcmp(name, sndio_device) != 0)
- throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
- name};
+ else if(name != sndio_device)
+ throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+ static_cast<int>(name.length()), name.data()};
mSndHandle = sio_open(nullptr, SIO_REC, true);
if(mSndHandle == nullptr)
@@ -436,7 +436,7 @@ void SndioCapture::open(const char *name)
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,
@@ -496,7 +496,7 @@ void SndioCapture::stop()
ERR("Error stopping device\n");
}
-void SndioCapture::captureSamples(al::byte *buffer, uint samples)
+void SndioCapture::captureSamples(std::byte *buffer, uint samples)
{ mRing->read(buffer, samples); }
uint SndioCapture::availableSamples()
diff --git a/alc/backends/solaris.cpp b/alc/backends/solaris.cpp
index 791609ce..38f9db19 100644
--- a/alc/backends/solaris.cpp
+++ b/alc/backends/solaris.cpp
@@ -35,17 +35,16 @@
#include <poll.h>
#include <math.h>
#include <string.h>
+#include <vector>
#include <thread>
#include <functional>
-#include "albyte.h"
#include "alc/alconfig.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>
@@ -63,7 +62,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,7 +70,7 @@ 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;
@@ -116,7 +115,7 @@ int SolarisBackend::mixerProc()
continue;
}
- al::byte *write_ptr{mBuffer.data()};
+ std::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))
@@ -140,13 +139,13 @@ int SolarisBackend::mixerProc()
}
-void SolarisBackend::open(const char *name)
+void SolarisBackend::open(std::string_view name)
{
- if(!name)
+ if(name.empty())
name = solaris_device;
- else if(strcmp(name, solaris_device) != 0)
- throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
- name};
+ else if(name != solaris_device)
+ throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+ static_cast<int>(name.length()), 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;
}
diff --git a/alc/backends/wasapi.cpp b/alc/backends/wasapi.cpp
index e834eef4..3ee98457 100644
--- a/alc/backends/wasapi.cpp
+++ b/alc/backends/wasapi.cpp
@@ -31,7 +31,9 @@
#include <wtypes.h>
#include <mmdeviceapi.h>
+#include <audiosessiontypes.h>
#include <audioclient.h>
+#include <spatialaudioclient.h>
#include <cguid.h>
#include <devpropdef.h>
#include <mmreg.h>
@@ -59,6 +61,8 @@
#include "albit.h"
#include "alc/alconfig.h"
#include "alnumeric.h"
+#include "alspan.h"
+#include "althrd_setname.h"
#include "comptr.h"
#include "core/converter.h"
#include "core/device.h"
@@ -66,8 +70,22 @@
#include "core/logging.h"
#include "ringbuffer.h"
#include "strutils.h"
-#include "threads.h"
+#if defined(ALSOFT_UWP)
+
+#include <winrt/Windows.Media.Core.h> // !!This is important!!
+#include <winrt/Windows.Foundation.Collections.h>
+#include <winrt/Windows.Devices.h>
+#include <winrt/Windows.Foundation.h>
+#include <winrt/Windows.Devices.Enumeration.h>
+#include <winrt/Windows.Media.Devices.h>
+
+using namespace winrt;
+using namespace Windows::Foundation;
+using namespace Windows::Media::Devices;
+using namespace Windows::Devices::Enumeration;
+using namespace Windows::Media::Devices;
+#endif
/* Some headers seem to define these as macros for __uuidof, which is annoying
* since some headers don't declare them at all. Hopefully the ifdef is enough
@@ -79,11 +97,11 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x
#ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
#endif
-
+#if !defined(ALSOFT_UWP)
DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
-
+#endif
namespace {
@@ -91,9 +109,9 @@ using std::chrono::nanoseconds;
using std::chrono::milliseconds;
using std::chrono::seconds;
-using ReferenceTime = std::chrono::duration<REFERENCE_TIME,std::ratio<1,10000000>>;
+using ReferenceTime = std::chrono::duration<REFERENCE_TIME,std::ratio<1,10'000'000>>;
-inline constexpr ReferenceTime operator "" _reftime(unsigned long long int n) noexcept
+constexpr ReferenceTime operator "" _reftime(unsigned long long int n) noexcept
{ return ReferenceTime{static_cast<REFERENCE_TIME>(n)}; }
@@ -124,15 +142,61 @@ constexpr DWORD X61Mask{MaskFromTopBits(X6DOT1)};
constexpr DWORD X71Mask{MaskFromTopBits(X7DOT1)};
constexpr DWORD X714Mask{MaskFromTopBits(X7DOT1DOT4)};
+
+#ifndef _MSC_VER
+constexpr AudioObjectType operator|(AudioObjectType lhs, AudioObjectType rhs) noexcept
+{ return static_cast<AudioObjectType>(lhs | al::to_underlying(rhs)); }
+#endif
+
+constexpr AudioObjectType ChannelMask_Mono{AudioObjectType_FrontCenter};
+constexpr AudioObjectType ChannelMask_Stereo{AudioObjectType_FrontLeft
+ | AudioObjectType_FrontRight};
+constexpr AudioObjectType ChannelMask_Quad{AudioObjectType_FrontLeft | AudioObjectType_FrontRight
+ | AudioObjectType_BackLeft | AudioObjectType_BackRight};
+constexpr AudioObjectType ChannelMask_X51{AudioObjectType_FrontLeft | AudioObjectType_FrontRight
+ | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency | AudioObjectType_SideLeft
+ | AudioObjectType_SideRight};
+constexpr AudioObjectType ChannelMask_X51Rear{AudioObjectType_FrontLeft
+ | AudioObjectType_FrontRight | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency
+ | AudioObjectType_BackLeft | AudioObjectType_BackRight};
+constexpr AudioObjectType ChannelMask_X61{AudioObjectType_FrontLeft | AudioObjectType_FrontRight
+ | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency | AudioObjectType_SideLeft
+ | AudioObjectType_SideRight | AudioObjectType_BackCenter};
+constexpr AudioObjectType ChannelMask_X71{AudioObjectType_FrontLeft | AudioObjectType_FrontRight
+ | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency | AudioObjectType_SideLeft
+ | AudioObjectType_SideRight | AudioObjectType_BackLeft | AudioObjectType_BackRight};
+constexpr AudioObjectType ChannelMask_X714{AudioObjectType_FrontLeft | AudioObjectType_FrontRight
+ | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency | AudioObjectType_SideLeft
+ | AudioObjectType_SideRight | AudioObjectType_BackLeft | AudioObjectType_BackRight
+ | AudioObjectType_TopFrontLeft | AudioObjectType_TopFrontRight | AudioObjectType_TopBackLeft
+ | AudioObjectType_TopBackRight};
+
+
constexpr char DevNameHead[] = "OpenAL Soft on ";
-constexpr size_t DevNameHeadLen{al::size(DevNameHead) - 1};
+constexpr size_t DevNameHeadLen{std::size(DevNameHead) - 1};
+
+
+template<typename... Ts>
+struct overloaded : Ts... { using Ts::operator()...; };
+
+template<typename... Ts>
+overloaded(Ts...) -> overloaded<Ts...>;
+
+
+template<typename T>
+constexpr auto as_unsigned(T value) noexcept
+{
+ using UT = std::make_unsigned_t<T>;
+ return static_cast<UT>(value);
+}
/* Scales the given reftime value, rounding the result. */
-inline uint RefTime2Samples(const ReferenceTime &val, uint srate)
+template<typename T>
+constexpr uint RefTime2Samples(const ReferenceTime &val, T srate) noexcept
{
const auto retval = (val*srate + ReferenceTime{seconds{1}}/2) / seconds{1};
- return static_cast<uint>(mini64(retval, std::numeric_limits<uint>::max()));
+ return static_cast<uint>(std::min<decltype(retval)>(retval, std::numeric_limits<uint>::max()));
}
@@ -142,7 +206,7 @@ class GuidPrinter {
public:
GuidPrinter(const GUID &guid)
{
- std::snprintf(mMsg, al::size(mMsg), "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+ std::snprintf(mMsg, std::size(mMsg), "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
DWORD{guid.Data1}, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
}
@@ -178,28 +242,73 @@ struct DevMap {
, endpoint_guid{std::forward<T1>(guid_)}
, devid{std::forward<T2>(devid_)}
{ }
+ /* To prevent GCC from complaining it doesn't want to inline this. */
+ ~DevMap();
};
+DevMap::~DevMap() = default;
-bool checkName(const al::vector<DevMap> &list, const std::string &name)
+bool checkName(const al::span<DevMap> list, const std::string_view name)
{
- 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();
}
-al::vector<DevMap> PlaybackDevices;
-al::vector<DevMap> CaptureDevices;
+
+struct DeviceList {
+ auto lock() noexcept(noexcept(mMutex.lock())) { return mMutex.lock(); }
+ auto unlock() noexcept(noexcept(mMutex.unlock())) { return mMutex.unlock(); }
+
+private:
+ std::mutex mMutex;
+ std::vector<DevMap> mPlayback;
+ std::vector<DevMap> mCapture;
+ std::wstring mPlaybackDefaultId;
+ std::wstring mCaptureDefaultId;
+
+ friend struct DeviceListLock;
+};
+struct DeviceListLock : public std::unique_lock<DeviceList> {
+ using std::unique_lock<DeviceList>::unique_lock;
+
+ auto& getPlaybackList() const noexcept { return mutex()->mPlayback; }
+ auto& getCaptureList() const noexcept { return mutex()->mCapture; }
+
+ void setPlaybackDefaultId(std::wstring_view devid) const { mutex()->mPlaybackDefaultId = devid; }
+ std::wstring_view getPlaybackDefaultId() const noexcept { return mutex()->mPlaybackDefaultId; }
+ void setCaptureDefaultId(std::wstring_view devid) const { mutex()->mCaptureDefaultId = devid; }
+ std::wstring_view getCaptureDefaultId() const noexcept { return mutex()->mCaptureDefaultId; }
+};
+
+DeviceList gDeviceList;
+
+
+#if defined(ALSOFT_UWP)
+enum EDataFlow {
+ eRender = 0,
+ eCapture = (eRender + 1),
+ eAll = (eCapture + 1),
+ EDataFlow_enum_count = (eAll + 1)
+};
+#endif
+
+#if defined(ALSOFT_UWP)
+using DeviceHandle = Windows::Devices::Enumeration::DeviceInformation;
+using EventRegistrationToken = winrt::event_token;
+#else
+using DeviceHandle = ComPtr<IMMDevice>;
+#endif
using NameGUIDPair = std::pair<std::string,std::string>;
-NameGUIDPair get_device_name_and_guid(IMMDevice *device)
+static NameGUIDPair GetDeviceNameAndGuid(const DeviceHandle &device)
{
static constexpr char UnknownName[]{"Unknown Device Name"};
static constexpr char UnknownGuid[]{"Unknown Device GUID"};
+#if !defined(ALSOFT_UWP)
std::string name, guid;
ComPtr<IPropertyStore> ps;
- HRESULT hr = device->OpenPropertyStore(STGM_READ, ps.getPtr());
+ HRESULT hr{device->OpenPropertyStore(STGM_READ, al::out_ptr(ps))};
if(FAILED(hr))
{
WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
@@ -207,42 +316,48 @@ NameGUIDPair get_device_name_and_guid(IMMDevice *device)
}
PropVariant pvprop;
- hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(DEVPKEY_Device_FriendlyName), pvprop.get());
+ hr = ps->GetValue(al::bit_cast<PROPERTYKEY>(DEVPKEY_Device_FriendlyName), pvprop.get());
if(FAILED(hr))
- {
WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
- name += UnknownName;
- }
else if(pvprop->vt == VT_LPWSTR)
- name += wstr_to_utf8(pvprop->pwszVal);
+ name = wstr_to_utf8(pvprop->pwszVal);
else
- {
- WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt);
- name += UnknownName;
- }
+ WARN("Unexpected Device_FriendlyName PROPVARIANT type: 0x%04x\n", pvprop->vt);
pvprop.clear();
- hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(PKEY_AudioEndpoint_GUID), pvprop.get());
+ hr = ps->GetValue(al::bit_cast<PROPERTYKEY>(PKEY_AudioEndpoint_GUID), pvprop.get());
if(FAILED(hr))
- {
WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr);
- guid = UnknownGuid;
- }
else if(pvprop->vt == VT_LPWSTR)
guid = wstr_to_utf8(pvprop->pwszVal);
else
- {
- WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvprop->vt);
- guid = UnknownGuid;
+ WARN("Unexpected AudioEndpoint_GUID PROPVARIANT type: 0x%04x\n", pvprop->vt);
+#else
+ std::string name{wstr_to_utf8(device.Name())};
+ std::string guid;
+ // device->Id is DeviceInterfacePath: \\?\SWD#MMDEVAPI#{0.0.0.00000000}.{a21c17a0-fc1d-405e-ab5a-b513422b57d1}#{e6327cad-dcec-4949-ae8a-991e976a79d2}
+ auto devIfPath = device.Id();
+ if(auto devIdStart = wcsstr(devIfPath.data(), L"}."))
+ {
+ devIdStart += 2; // L"}."
+ if(auto devIdStartEnd = wcschr(devIdStart, L'#'))
+ {
+ std::wstring wDevId{devIdStart, static_cast<size_t>(devIdStartEnd - devIdStart)};
+ guid = wstr_to_utf8(wDevId.c_str());
+ std::transform(guid.begin(), guid.end(), guid.begin(),
+ [](char ch) { return static_cast<char>(std::toupper(ch)); });
+ }
}
-
+#endif
+ if(name.empty()) name = UnknownName;
+ if(guid.empty()) guid = UnknownGuid;
return std::make_pair(std::move(name), std::move(guid));
}
-
-EndpointFormFactor get_device_formfactor(IMMDevice *device)
+#if !defined(ALSOFT_UWP)
+EndpointFormFactor GetDeviceFormfactor(IMMDevice *device)
{
ComPtr<IPropertyStore> ps;
- HRESULT hr{device->OpenPropertyStore(STGM_READ, ps.getPtr())};
+ HRESULT hr{device->OpenPropertyStore(STGM_READ, al::out_ptr(ps))};
if(FAILED(hr))
{
WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
@@ -260,90 +375,422 @@ EndpointFormFactor get_device_formfactor(IMMDevice *device)
WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt);
return formfactor;
}
+#endif
-void add_device(IMMDevice *device, const WCHAR *devid, al::vector<DevMap> &list)
+#if defined(ALSOFT_UWP)
+struct DeviceHelper final : public IActivateAudioInterfaceCompletionHandler
+#else
+struct DeviceHelper final : private IMMNotificationClient
+#endif
{
- for(auto &entry : list)
+ DeviceHelper()
{
- if(entry.devid == devid)
- return;
+#if defined(ALSOFT_UWP)
+ /* TODO: UWP also needs to watch for device added/removed events and
+ * dynamically add/remove devices from the lists.
+ */
+ mActiveClientEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
+
+ mRenderDeviceChangedToken = MediaDevice::DefaultAudioRenderDeviceChanged([this](const IInspectable& /*sender*/, const DefaultAudioRenderDeviceChangedEventArgs& args) {
+ if (args.Role() == AudioDeviceRole::Default)
+ {
+ const std::string msg{ "Default playback device changed: " +
+ wstr_to_utf8(args.Id())};
+ alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback,
+ msg);
+ }
+ });
+
+ mCaptureDeviceChangedToken = MediaDevice::DefaultAudioCaptureDeviceChanged([this](const IInspectable& /*sender*/, const DefaultAudioCaptureDeviceChangedEventArgs& args) {
+ if (args.Role() == AudioDeviceRole::Default)
+ {
+ const std::string msg{ "Default capture device changed: " +
+ wstr_to_utf8(args.Id()) };
+ alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture,
+ msg);
+ }
+ });
+#endif
+ }
+ ~DeviceHelper()
+ {
+#if defined(ALSOFT_UWP)
+ MediaDevice::DefaultAudioRenderDeviceChanged(mRenderDeviceChangedToken);
+ MediaDevice::DefaultAudioCaptureDeviceChanged(mCaptureDeviceChangedToken);
+
+ if(mActiveClientEvent != nullptr)
+ CloseHandle(mActiveClientEvent);
+ mActiveClientEvent = nullptr;
+#else
+ if(mEnumerator)
+ mEnumerator->UnregisterEndpointNotificationCallback(this);
+ mEnumerator = nullptr;
+#endif
}
- auto name_guid = get_device_name_and_guid(device);
+ /** -------------------------- IUnknown ----------------------------- */
+ std::atomic<ULONG> mRefCount{1};
+ STDMETHODIMP_(ULONG) AddRef() noexcept override { return mRefCount.fetch_add(1u) + 1u; }
+ STDMETHODIMP_(ULONG) Release() noexcept override { return mRefCount.fetch_sub(1u) - 1u; }
+
+ STDMETHODIMP QueryInterface(const IID& IId, void **UnknownPtrPtr) noexcept override
+ {
+ // Three rules of QueryInterface:
+ // https://docs.microsoft.com/en-us/windows/win32/com/rules-for-implementing-queryinterface
+ // 1. Objects must have identity.
+ // 2. The set of interfaces on an object instance must be static.
+ // 3. It must be possible to query successfully for any interface on an object from any other interface.
+
+ // If ppvObject(the address) is nullptr, then this method returns E_POINTER.
+ if(!UnknownPtrPtr)
+ return E_POINTER;
+
+ // https://docs.microsoft.com/en-us/windows/win32/com/implementing-reference-counting
+ // Whenever a client calls a method(or API function), such as QueryInterface, that returns a new interface
+ // pointer, the method being called is responsible for incrementing the reference count through the returned
+ // pointer. For example, when a client first creates an object, it receives an interface pointer to an object
+ // that, from the client's point of view, has a reference count of one. If the client then calls AddRef on the
+ // interface pointer, the reference count becomes two. The client must call Release twice on the interface
+ // pointer to drop all of its references to the object.
+#if defined(ALSOFT_UWP)
+ if(IId == __uuidof(IActivateAudioInterfaceCompletionHandler))
+ {
+ *UnknownPtrPtr = static_cast<IActivateAudioInterfaceCompletionHandler*>(this);
+ AddRef();
+ return S_OK;
+ }
+#else
+ if(IId == __uuidof(IMMNotificationClient))
+ {
+ *UnknownPtrPtr = static_cast<IMMNotificationClient*>(this);
+ AddRef();
+ return S_OK;
+ }
+#endif
+ else if(IId == __uuidof(IAgileObject) || IId == __uuidof(IUnknown))
+ {
+ *UnknownPtrPtr = static_cast<IUnknown*>(this);
+ AddRef();
+ return S_OK;
+ }
+
+ // This method returns S_OK if the interface is supported, and E_NOINTERFACE otherwise.
+ *UnknownPtrPtr = nullptr;
+ return E_NOINTERFACE;
+ }
- int count{1};
- std::string newname{name_guid.first};
- while(checkName(list, newname))
+#if defined(ALSOFT_UWP)
+ /** ----------------------- IActivateAudioInterfaceCompletionHandler ------------ */
+ HRESULT ActivateCompleted(IActivateAudioInterfaceAsyncOperation*) override
{
- newname = name_guid.first;
- newname += " #";
- newname += std::to_string(++count);
+ SetEvent(mActiveClientEvent);
+
+ // Need to return S_OK
+ return S_OK;
}
- list.emplace_back(std::move(newname), std::move(name_guid.second), devid);
- const DevMap &newentry = list.back();
+#else
+ /** ----------------------- IMMNotificationClient ------------ */
+ STDMETHODIMP OnDeviceStateChanged(LPCWSTR /*pwstrDeviceId*/, DWORD /*dwNewState*/) noexcept override { return S_OK; }
- TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", newentry.name.c_str(),
- newentry.endpoint_guid.c_str(), newentry.devid.c_str());
-}
+ STDMETHODIMP OnDeviceAdded(LPCWSTR pwstrDeviceId) noexcept override
+ {
+ ComPtr<IMMDevice> device;
+ HRESULT hr{mEnumerator->GetDevice(pwstrDeviceId, al::out_ptr(device))};
+ if(FAILED(hr))
+ {
+ ERR("Failed to get device: 0x%08lx\n", hr);
+ return S_OK;
+ }
-WCHAR *get_device_id(IMMDevice *device)
-{
- WCHAR *devid;
+ ComPtr<IMMEndpoint> endpoint;
+ hr = device->QueryInterface(__uuidof(IMMEndpoint), al::out_ptr(endpoint));
+ if(FAILED(hr))
+ {
+ ERR("Failed to get device endpoint: 0x%08lx\n", hr);
+ return S_OK;
+ }
- const HRESULT hr{device->GetId(&devid)};
- if(FAILED(hr))
+ EDataFlow flowdir{};
+ hr = endpoint->GetDataFlow(&flowdir);
+ if(FAILED(hr))
+ {
+ ERR("Failed to get endpoint data flow: 0x%08lx\n", hr);
+ return S_OK;
+ }
+
+ auto devlock = DeviceListLock{gDeviceList};
+ auto &list = (flowdir==eRender) ? devlock.getPlaybackList() : devlock.getCaptureList();
+
+ if(AddDevice(device, pwstrDeviceId, list))
+ {
+ const auto devtype = (flowdir==eRender) ? alc::DeviceType::Playback
+ : alc::DeviceType::Capture;
+ const std::string msg{"Device added: "+list.back().name};
+ alc::Event(alc::EventType::DeviceAdded, devtype, msg);
+ }
+
+ return S_OK;
+ }
+
+ STDMETHODIMP OnDeviceRemoved(LPCWSTR pwstrDeviceId) noexcept override
{
- ERR("Failed to get device id: %lx\n", hr);
- return nullptr;
+ auto devlock = DeviceListLock{gDeviceList};
+ for(auto flowdir : std::array{eRender, eCapture})
+ {
+ auto &list = (flowdir==eRender) ? devlock.getPlaybackList() : devlock.getCaptureList();
+ auto devtype = (flowdir==eRender)?alc::DeviceType::Playback : alc::DeviceType::Capture;
+
+ /* Find the ID in the list to remove. */
+ auto iter = std::find_if(list.begin(), list.end(),
+ [pwstrDeviceId](const DevMap &entry) noexcept
+ { return pwstrDeviceId == entry.devid; });
+ if(iter == list.end()) continue;
+
+ TRACE("Removing device \"%s\", \"%s\", \"%ls\"\n", iter->name.c_str(),
+ iter->endpoint_guid.c_str(), iter->devid.c_str());
+
+ std::string msg{"Device removed: "+std::move(iter->name)};
+ list.erase(iter);
+
+ alc::Event(alc::EventType::DeviceRemoved, devtype, msg);
+ }
+ return S_OK;
}
- return devid;
-}
+ STDMETHODIMP OnPropertyValueChanged(LPCWSTR /*pwstrDeviceId*/, const PROPERTYKEY /*key*/) noexcept override { return S_OK; }
-void probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, al::vector<DevMap> &list)
-{
- al::vector<DevMap>{}.swap(list);
+ STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId) noexcept override
+ {
+ if(role != eMultimedia)
+ return S_OK;
- ComPtr<IMMDeviceCollection> coll;
- HRESULT hr{devenum->EnumAudioEndpoints(flowdir, DEVICE_STATE_ACTIVE, coll.getPtr())};
- if(FAILED(hr))
+ const std::wstring_view devid{pwstrDefaultDeviceId ? pwstrDefaultDeviceId
+ : std::wstring_view{}};
+ if(flow == eRender)
+ {
+ DeviceListLock{gDeviceList}.setPlaybackDefaultId(devid);
+ const std::string msg{"Default playback device changed: " + wstr_to_utf8(devid)};
+ alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, msg);
+ }
+ else if(flow == eCapture)
+ {
+ DeviceListLock{gDeviceList}.setCaptureDefaultId(devid);
+ const std::string msg{"Default capture device changed: " + wstr_to_utf8(devid)};
+ alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, msg);
+ }
+ return S_OK;
+ }
+#endif
+
+ /** -------------------------- DeviceHelper ----------------------------- */
+ HRESULT init()
{
- ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
- return;
+#if !defined(ALSOFT_UWP)
+ HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
+ __uuidof(IMMDeviceEnumerator), al::out_ptr(mEnumerator))};
+ if(SUCCEEDED(hr))
+ mEnumerator->RegisterEndpointNotificationCallback(this);
+ else
+ WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
+ return hr;
+#else
+ return S_OK;
+#endif
}
- UINT count{0};
- hr = coll->GetCount(&count);
- if(SUCCEEDED(hr) && count > 0)
- list.reserve(count);
+ HRESULT openDevice(std::wstring_view devid, EDataFlow flow, DeviceHandle& device)
+ {
+#if !defined(ALSOFT_UWP)
+ HRESULT hr{E_FAIL};
+ if(mEnumerator)
+ {
+ if(devid.empty())
+ hr = mEnumerator->GetDefaultAudioEndpoint(flow, eMultimedia, al::out_ptr(device));
+ else
+ hr = mEnumerator->GetDevice(devid.data(), al::out_ptr(device));
+ }
+ return hr;
+#else
+ const auto deviceRole = Windows::Media::Devices::AudioDeviceRole::Default;
+ auto devIfPath =
+ devid.empty() ? (flow == eRender ? MediaDevice::GetDefaultAudioRenderId(deviceRole) : MediaDevice::GetDefaultAudioCaptureId(deviceRole))
+ : winrt::hstring(devid.data());
+ if (devIfPath.empty())
+ return E_POINTER;
+
+ auto&& deviceInfo = DeviceInformation::CreateFromIdAsync(devIfPath, nullptr, DeviceInformationKind::DeviceInterface).get();
+ if (!deviceInfo)
+ return E_NOINTERFACE;
+ device = deviceInfo;
+ return S_OK;
+#endif
+ }
- ComPtr<IMMDevice> device;
- hr = devenum->GetDefaultAudioEndpoint(flowdir, eMultimedia, device.getPtr());
- if(SUCCEEDED(hr))
+#if !defined(ALSOFT_UWP)
+ static HRESULT activateAudioClient(_In_ DeviceHandle &device, REFIID iid, void **ppv)
+ { return device->Activate(iid, CLSCTX_INPROC_SERVER, nullptr, ppv); }
+#else
+ HRESULT activateAudioClient(_In_ DeviceHandle &device, _In_ REFIID iid, void **ppv)
{
- if(WCHAR *devid{get_device_id(device.get())})
+ ComPtr<IActivateAudioInterfaceAsyncOperation> asyncOp;
+ HRESULT hr{ActivateAudioInterfaceAsync(device.Id().data(), iid, nullptr, this,
+ al::out_ptr(asyncOp))};
+ if(FAILED(hr))
+ return hr;
+
+ /* I don't like waiting for INFINITE time, but the activate operation
+ * can take an indefinite amount of time since it can require user
+ * input.
+ */
+ DWORD res{WaitForSingleObjectEx(mActiveClientEvent, INFINITE, FALSE)};
+ if(res != WAIT_OBJECT_0)
{
- add_device(device.get(), devid, list);
- CoTaskMemFree(devid);
+ ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
+ return E_FAIL;
}
- device = nullptr;
+
+ HRESULT hrActivateRes{E_FAIL};
+ ComPtr<IUnknown> punkAudioIface;
+ hr = asyncOp->GetActivateResult(&hrActivateRes, al::out_ptr(punkAudioIface));
+ if(SUCCEEDED(hr)) hr = hrActivateRes;
+ if(FAILED(hr)) return hr;
+
+ return punkAudioIface->QueryInterface(iid, ppv);
}
+#endif
- for(UINT i{0};i < count;++i)
+ std::wstring probeDevices(EDataFlow flowdir, std::vector<DevMap> &list)
{
- hr = coll->Item(i, device.getPtr());
- if(FAILED(hr)) continue;
+ std::wstring defaultId;
+ std::vector<DevMap>{}.swap(list);
+
+#if !defined(ALSOFT_UWP)
+ ComPtr<IMMDeviceCollection> coll;
+ HRESULT hr{mEnumerator->EnumAudioEndpoints(flowdir, DEVICE_STATE_ACTIVE,
+ al::out_ptr(coll))};
+ if(FAILED(hr))
+ {
+ ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
+ return defaultId;
+ }
+
+ UINT count{0};
+ hr = coll->GetCount(&count);
+ if(SUCCEEDED(hr) && count > 0)
+ list.reserve(count);
+
+ ComPtr<IMMDevice> device;
+ hr = mEnumerator->GetDefaultAudioEndpoint(flowdir, eMultimedia, al::out_ptr(device));
+ if(SUCCEEDED(hr))
+ {
+ if(WCHAR *devid{GetDeviceId(device.get())})
+ {
+ defaultId = devid;
+ CoTaskMemFree(devid);
+ }
+ device = nullptr;
+ }
+
+ for(UINT i{0};i < count;++i)
+ {
+ hr = coll->Item(i, al::out_ptr(device));
+ if(FAILED(hr))
+ continue;
- if(WCHAR *devid{get_device_id(device.get())})
+ if(WCHAR *devid{GetDeviceId(device.get())})
+ {
+ std::ignore = AddDevice(device, devid, list);
+ CoTaskMemFree(devid);
+ }
+ device = nullptr;
+ }
+#else
+ const auto deviceRole = Windows::Media::Devices::AudioDeviceRole::Default;
+ auto DefaultAudioId = flowdir == eRender ? MediaDevice::GetDefaultAudioRenderId(deviceRole)
+ : MediaDevice::GetDefaultAudioCaptureId(deviceRole);
+ if(!DefaultAudioId.empty())
{
- add_device(device.get(), devid, list);
- CoTaskMemFree(devid);
+ auto deviceInfo = DeviceInformation::CreateFromIdAsync(DefaultAudioId, nullptr,
+ DeviceInformationKind::DeviceInterface).get();
+ if(deviceInfo)
+ defaultId = deviceInfo.Id().data();
}
- device = nullptr;
+
+ // Get the string identifier of the audio renderer
+ auto AudioSelector = flowdir == eRender ? MediaDevice::GetAudioRenderSelector() : MediaDevice::GetAudioCaptureSelector();
+
+ // Setup the asynchronous callback
+ auto&& DeviceInfoCollection = DeviceInformation::FindAllAsync(AudioSelector, /*PropertyList*/nullptr, DeviceInformationKind::DeviceInterface).get();
+ if(DeviceInfoCollection)
+ {
+ try {
+ auto deviceCount = DeviceInfoCollection.Size();
+ for(unsigned int i{0};i < deviceCount;++i)
+ {
+ auto deviceInfo = DeviceInfoCollection.GetAt(i);
+ if(deviceInfo)
+ std::ignore = AddDevice(deviceInfo, deviceInfo.Id().data(), list);
+ }
+ }
+ catch (const winrt::hresult_error& /*ex*/) {
+ }
+ }
+#endif
+
+ return defaultId;
}
-}
+private:
+ static bool AddDevice(const DeviceHandle &device, const WCHAR *devid, std::vector<DevMap> &list)
+ {
+ for(auto &entry : list)
+ {
+ if(entry.devid == devid)
+ return false;
+ }
+
+ auto name_guid = GetDeviceNameAndGuid(device);
+ int count{1};
+ std::string newname{name_guid.first};
+ while(checkName(list, newname))
+ {
+ newname = name_guid.first;
+ newname += " #";
+ newname += std::to_string(++count);
+ }
+ list.emplace_back(std::move(newname), std::move(name_guid.second), devid);
+ const DevMap &newentry = list.back();
+
+ TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", newentry.name.c_str(),
+ newentry.endpoint_guid.c_str(), newentry.devid.c_str());
+ return true;
+ }
+
+#if !defined(ALSOFT_UWP)
+ static WCHAR *GetDeviceId(IMMDevice *device)
+ {
+ WCHAR *devid;
+
+ const HRESULT hr{device->GetId(&devid)};
+ if(FAILED(hr))
+ {
+ ERR("Failed to get device id: %lx\n", hr);
+ return nullptr;
+ }
+
+ return devid;
+ }
+ ComPtr<IMMDeviceEnumerator> mEnumerator{nullptr};
+
+#else
+
+ HANDLE mActiveClientEvent{nullptr};
+
+ EventRegistrationToken mRenderDeviceChangedToken;
+ EventRegistrationToken mCaptureDeviceChangedToken;
+#endif
+};
bool MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
{
@@ -430,51 +877,51 @@ enum class MsgType {
StartDevice,
StopDevice,
CloseDevice,
- EnumeratePlayback,
- EnumerateCapture,
- Count,
- QuitThread = Count
+ QuitThread
};
-constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{
- "Open Device",
- "Reset Device",
- "Start Device",
- "Stop Device",
- "Close Device",
- "Enumerate Playback",
- "Enumerate Capture"
-};
+constexpr const char *GetMessageTypeName(MsgType type) noexcept
+{
+ switch(type)
+ {
+ case MsgType::OpenDevice: return "Open Device";
+ case MsgType::ResetDevice: return "Reset Device";
+ case MsgType::StartDevice: return "Start Device";
+ case MsgType::StopDevice: return "Stop Device";
+ case MsgType::CloseDevice: return "Close Device";
+ case MsgType::QuitThread: break;
+ }
+ return "";
+}
/* Proxy interface used by the message handler. */
struct WasapiProxy {
virtual ~WasapiProxy() = default;
- virtual HRESULT openProxy(const char *name) = 0;
+ virtual HRESULT openProxy(std::string_view name) = 0;
virtual void closeProxy() = 0;
virtual HRESULT resetProxy() = 0;
virtual HRESULT startProxy() = 0;
- virtual void stopProxy() = 0;
+ virtual void stopProxy() = 0;
struct Msg {
MsgType mType;
WasapiProxy *mProxy;
- const char *mParam;
+ std::string_view mParam;
std::promise<HRESULT> mPromise;
explicit operator bool() const noexcept { return mType != MsgType::QuitThread; }
};
- static std::thread sThread;
- static std::deque<Msg> mMsgQueue;
- static std::mutex mMsgQueueLock;
- static std::condition_variable mMsgQueueCond;
- static std::mutex sThreadLock;
- static size_t sInitCount;
+ static inline std::deque<Msg> mMsgQueue;
+ static inline std::mutex mMsgQueueLock;
+ static inline std::condition_variable mMsgQueueCond;
+
+ static inline std::optional<DeviceHelper> sDeviceHelper;
- std::future<HRESULT> pushMessage(MsgType type, const char *param=nullptr)
+ std::future<HRESULT> pushMessage(MsgType type, std::string_view param={})
{
std::promise<HRESULT> promise;
std::future<HRESULT> future{promise.get_future()};
@@ -492,7 +939,7 @@ struct WasapiProxy {
std::future<HRESULT> future{promise.get_future()};
{
std::lock_guard<std::mutex> _{mMsgQueueLock};
- mMsgQueue.emplace_back(Msg{type, nullptr, nullptr, std::move(promise)});
+ mMsgQueue.emplace_back(Msg{type, nullptr, {}, std::move(promise)});
}
mMsgQueueCond.notify_one();
return future;
@@ -508,65 +955,41 @@ struct WasapiProxy {
}
static int messageHandler(std::promise<HRESULT> *promise);
-
- static HRESULT InitThread()
- {
- std::lock_guard<std::mutex> _{sThreadLock};
- HRESULT res{S_OK};
- if(!sThread.joinable())
- {
- std::promise<HRESULT> promise;
- auto future = promise.get_future();
-
- sThread = std::thread{&WasapiProxy::messageHandler, &promise};
- res = future.get();
- if(FAILED(res))
- {
- sThread.join();
- return res;
- }
- }
- ++sInitCount;
- return res;
- }
-
- static void DeinitThread()
- {
- std::lock_guard<std::mutex> _{sThreadLock};
- if(!--sInitCount && sThread.joinable())
- {
- pushMessageStatic(MsgType::QuitThread);
- sThread.join();
- }
- }
};
-std::thread WasapiProxy::sThread;
-std::deque<WasapiProxy::Msg> WasapiProxy::mMsgQueue;
-std::mutex WasapiProxy::mMsgQueueLock;
-std::condition_variable WasapiProxy::mMsgQueueCond;
-std::mutex WasapiProxy::sThreadLock;
-size_t WasapiProxy::sInitCount{0};
int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
{
TRACE("Starting message thread\n");
- HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)};
- if(FAILED(hr))
+ ComWrapper com{COINIT_MULTITHREADED};
+ if(!com)
{
- WARN("Failed to initialize COM: 0x%08lx\n", hr);
- promise->set_value(hr);
+ WARN("Failed to initialize COM: 0x%08lx\n", com.status());
+ promise->set_value(com.status());
return 0;
}
- promise->set_value(S_OK);
+
+ HRESULT hr{sDeviceHelper.emplace().init()};
+ promise->set_value(hr);
promise = nullptr;
+ if(FAILED(hr))
+ goto skip_loop;
+
+ {
+ auto devlock = DeviceListLock{gDeviceList};
+ auto defaultId = sDeviceHelper->probeDevices(eRender, devlock.getPlaybackList());
+ if(!defaultId.empty()) devlock.setPlaybackDefaultId(defaultId);
+ defaultId = sDeviceHelper->probeDevices(eCapture, devlock.getCaptureList());
+ if(!defaultId.empty()) devlock.setCaptureDefaultId(defaultId);
+ }
TRACE("Starting message loop\n");
while(Msg msg{popMessage()})
{
- TRACE("Got message \"%s\" (0x%04x, this=%p, param=%p)\n",
- MessageStr[static_cast<size_t>(msg.mType)], static_cast<uint>(msg.mType),
- static_cast<void*>(msg.mProxy), static_cast<const void*>(msg.mParam));
+ TRACE("Got message \"%s\" (0x%04x, this=%p, param=\"%.*s\")\n",
+ GetMessageTypeName(msg.mType), static_cast<uint>(msg.mType),
+ static_cast<void*>(msg.mProxy), static_cast<int>(msg.mParam.length()),
+ msg.mParam.data());
switch(msg.mType)
{
@@ -595,27 +1018,6 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
msg.mPromise.set_value(S_OK);
continue;
- case MsgType::EnumeratePlayback:
- case MsgType::EnumerateCapture:
- {
- void *ptr{};
- hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
- IID_IMMDeviceEnumerator, &ptr);
- if(FAILED(hr))
- msg.mPromise.set_value(hr);
- else
- {
- ComPtr<IMMDeviceEnumerator> devenum{static_cast<IMMDeviceEnumerator*>(ptr)};
-
- if(msg.mType == MsgType::EnumeratePlayback)
- probe_devices(devenum.get(), eRender, PlaybackDevices);
- else if(msg.mType == MsgType::EnumerateCapture)
- probe_devices(devenum.get(), eCapture, CaptureDevices);
- msg.mPromise.set_value(S_OK);
- }
- continue;
- }
-
case MsgType::QuitThread:
break;
}
@@ -623,20 +1025,22 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
msg.mPromise.set_value(E_FAIL);
}
TRACE("Message loop finished\n");
- CoUninitialize();
+
+skip_loop:
+ sDeviceHelper.reset();
return 0;
}
-
struct WasapiPlayback final : public BackendBase, WasapiProxy {
WasapiPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
~WasapiPlayback() override;
int mixerProc();
+ int mixerSpatialProc();
- void open(const char *name) override;
- HRESULT openProxy(const char *name) override;
+ void open(std::string_view name) override;
+ HRESULT openProxy(std::string_view name) override;
void closeProxy() override;
bool reset() override;
@@ -648,10 +1052,22 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy {
ClockLatency getClockLatency() override;
+ void prepareFormat(WAVEFORMATEXTENSIBLE &OutputType);
+ void finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType);
+
HRESULT mOpenStatus{E_FAIL};
- ComPtr<IMMDevice> mMMDev{nullptr};
- ComPtr<IAudioClient> mClient{nullptr};
- ComPtr<IAudioRenderClient> mRender{nullptr};
+ DeviceHandle mMMDev{nullptr};
+
+ struct PlainDevice {
+ ComPtr<IAudioClient> mClient{nullptr};
+ ComPtr<IAudioRenderClient> mRender{nullptr};
+ };
+ struct SpatialDevice {
+ ComPtr<ISpatialAudioClient> mClient{nullptr};
+ ComPtr<ISpatialAudioObjectRenderStream> mRender{nullptr};
+ AudioObjectType mStaticMask{};
+ };
+ std::variant<std::monostate,PlainDevice,SpatialDevice> mAudio;
HANDLE mNotifyEvent{nullptr};
UINT32 mOrigBufferSize{}, mOrigUpdateSize{};
@@ -673,10 +1089,7 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy {
WasapiPlayback::~WasapiPlayback()
{
if(SUCCEEDED(mOpenStatus))
- {
pushMessage(MsgType::CloseDevice).wait();
- DeinitThread();
- }
mOpenStatus = E_FAIL;
if(mNotifyEvent != nullptr)
@@ -687,24 +1100,29 @@ WasapiPlayback::~WasapiPlayback()
FORCE_ALIGN int WasapiPlayback::mixerProc()
{
- HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)};
- if(FAILED(hr))
+ ComWrapper com{COINIT_MULTITHREADED};
+ if(!com)
{
- ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
- mDevice->handleDisconnect("COM init failed: 0x%08lx", hr);
+ ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", com.status());
+ mDevice->handleDisconnect("COM init failed: 0x%08lx", com.status());
return 1;
}
+ auto &audio = std::get<PlainDevice>(mAudio);
+
SetRTPriority();
althrd_setname(MIXER_THREAD_NAME);
const uint frame_size{mFormat.Format.nChannels * mFormat.Format.wBitsPerSample / 8u};
const uint update_size{mOrigUpdateSize};
const UINT32 buffer_len{mOrigBufferSize};
+ const void *resbufferptr{};
+
+ mBufferFilled = 0;
while(!mKillNow.load(std::memory_order_relaxed))
{
UINT32 written;
- hr = mClient->GetCurrentPadding(&written);
+ HRESULT hr{audio.mClient->GetCurrentPadding(&written)};
if(FAILED(hr))
{
ERR("Failed to get padding: 0x%08lx\n", hr);
@@ -723,7 +1141,7 @@ FORCE_ALIGN int WasapiPlayback::mixerProc()
}
BYTE *buffer;
- hr = mRender->GetBuffer(len, &buffer);
+ hr = audio.mRender->GetBuffer(len, &buffer);
if(SUCCEEDED(hr))
{
if(mResampler)
@@ -735,22 +1153,15 @@ FORCE_ALIGN int WasapiPlayback::mixerProc()
{
mDevice->renderSamples(mResampleBuffer.get(), mDevice->UpdateSize,
mFormat.Format.nChannels);
+ resbufferptr = mResampleBuffer.get();
mBufferFilled = mDevice->UpdateSize;
}
- const void *src{mResampleBuffer.get()};
- uint srclen{mBufferFilled};
- uint got{mResampler->convert(&src, &srclen, buffer, len-done)};
+ uint got{mResampler->convert(&resbufferptr, &mBufferFilled, buffer, len-done)};
buffer += got*frame_size;
done += got;
mPadding.store(written + done, std::memory_order_relaxed);
- if(srclen)
- {
- const char *bsrc{static_cast<const char*>(src)};
- std::copy(bsrc, bsrc + srclen*frame_size, mResampleBuffer.get());
- }
- mBufferFilled = srclen;
}
}
else
@@ -759,7 +1170,7 @@ FORCE_ALIGN int WasapiPlayback::mixerProc()
mDevice->renderSamples(buffer, len, mFormat.Format.nChannels);
mPadding.store(written + len, std::memory_order_relaxed);
}
- hr = mRender->ReleaseBuffer(len, 0);
+ hr = audio.mRender->ReleaseBuffer(len, 0);
}
if(FAILED(hr))
{
@@ -770,12 +1181,126 @@ FORCE_ALIGN int WasapiPlayback::mixerProc()
}
mPadding.store(0u, std::memory_order_release);
- CoUninitialize();
+ return 0;
+}
+
+FORCE_ALIGN int WasapiPlayback::mixerSpatialProc()
+{
+ ComWrapper com{COINIT_MULTITHREADED};
+ if(!com)
+ {
+ ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", com.status());
+ mDevice->handleDisconnect("COM init failed: 0x%08lx", com.status());
+ return 1;
+ }
+
+ auto &audio = std::get<SpatialDevice>(mAudio);
+
+ SetRTPriority();
+ althrd_setname(MIXER_THREAD_NAME);
+
+ std::vector<ComPtr<ISpatialAudioObject>> channels;
+ std::vector<float*> buffers;
+ std::vector<float*> resbuffers;
+ std::vector<const void*> tmpbuffers;
+
+ /* TODO: Set mPadding appropriately. There doesn't seem to be a way to
+ * update it dynamically based on the stream, so a fixed size may be the
+ * best we can do.
+ */
+ mPadding.store(mOrigBufferSize-mOrigUpdateSize, std::memory_order_release);
+
+ mBufferFilled = 0;
+ while(!mKillNow.load(std::memory_order_relaxed))
+ {
+ if(DWORD res{WaitForSingleObjectEx(mNotifyEvent, 1000, FALSE)}; res != WAIT_OBJECT_0)
+ {
+ ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
+
+ HRESULT hr{audio.mRender->Reset()};
+ if(FAILED(hr))
+ {
+ ERR("ISpatialAudioObjectRenderStream::Reset failed: 0x%08lx\n", hr);
+ mDevice->handleDisconnect("Device lost: 0x%08lx", hr);
+ break;
+ }
+ }
+
+ UINT32 dynamicCount{}, framesToDo{};
+ HRESULT hr{audio.mRender->BeginUpdatingAudioObjects(&dynamicCount, &framesToDo)};
+ if(SUCCEEDED(hr))
+ {
+ if(channels.empty()) UNLIKELY
+ {
+ auto flags = as_unsigned(audio.mStaticMask);
+ channels.reserve(as_unsigned(al::popcount(flags)));
+ while(flags)
+ {
+ auto id = decltype(flags){1} << al::countr_zero(flags);
+ flags &= ~id;
+
+ channels.emplace_back();
+ audio.mRender->ActivateSpatialAudioObject(static_cast<AudioObjectType>(id),
+ al::out_ptr(channels.back()));
+ }
+ buffers.resize(channels.size());
+ if(mResampler)
+ {
+ tmpbuffers.resize(buffers.size());
+ resbuffers.resize(buffers.size());
+ for(size_t i{0};i < tmpbuffers.size();++i)
+ resbuffers[i] = reinterpret_cast<float*>(mResampleBuffer.get()) +
+ mDevice->UpdateSize*i;
+ }
+ }
+
+ /* We have to call to get each channel's buffer individually every
+ * update, unfortunately.
+ */
+ std::transform(channels.cbegin(), channels.cend(), buffers.begin(),
+ [](const ComPtr<ISpatialAudioObject> &obj) -> float*
+ {
+ BYTE *buffer{};
+ UINT32 size{};
+ obj->GetBuffer(&buffer, &size);
+ return reinterpret_cast<float*>(buffer);
+ });
+
+ if(!mResampler)
+ mDevice->renderSamples(buffers, framesToDo);
+ else
+ {
+ std::lock_guard<std::mutex> _{mMutex};
+ for(UINT32 pos{0};pos < framesToDo;)
+ {
+ if(mBufferFilled == 0)
+ {
+ mDevice->renderSamples(resbuffers, mDevice->UpdateSize);
+ std::copy(resbuffers.cbegin(), resbuffers.cend(), tmpbuffers.begin());
+ mBufferFilled = mDevice->UpdateSize;
+ }
+
+ const uint got{mResampler->convertPlanar(tmpbuffers.data(), &mBufferFilled,
+ reinterpret_cast<void*const*>(buffers.data()), framesToDo-pos)};
+ for(auto &buf : buffers)
+ buf += got;
+ pos += got;
+ }
+ }
+
+ hr = audio.mRender->EndUpdatingAudioObjects();
+ }
+
+ if(FAILED(hr))
+ ERR("Failed to update playback objects: 0x%08lx\n", hr);
+ }
+ mPadding.store(0u, std::memory_order_release);
+
return 0;
}
-void WasapiPlayback::open(const char *name)
+void WasapiPlayback::open(std::string_view name)
{
if(SUCCEEDED(mOpenStatus))
throw al::backend_exception{al::backend_error::DeviceError,
@@ -789,132 +1314,69 @@ void WasapiPlayback::open(const char *name)
"Failed to create notify events"};
}
- HRESULT hr{InitThread()};
- if(FAILED(hr))
- {
- throw al::backend_exception{al::backend_error::DeviceError,
- "Failed to init COM thread: 0x%08lx", hr};
- }
-
- if(name)
+ if(name.length() >= DevNameHeadLen
+ && std::strncmp(name.data(), DevNameHead, DevNameHeadLen) == 0)
{
- if(PlaybackDevices.empty())
- pushMessage(MsgType::EnumeratePlayback);
- if(std::strncmp(name, DevNameHead, DevNameHeadLen) == 0)
- {
- name += DevNameHeadLen;
- if(*name == '\0')
- name = nullptr;
- }
+ name = name.substr(DevNameHeadLen);
}
mOpenStatus = pushMessage(MsgType::OpenDevice, name).get();
if(FAILED(mOpenStatus))
- {
- DeinitThread();
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
mOpenStatus};
- }
}
-HRESULT WasapiPlayback::openProxy(const char *name)
+HRESULT WasapiPlayback::openProxy(std::string_view name)
{
- const wchar_t *devid{nullptr};
- if(name)
+ std::string devname;
+ std::wstring devid;
+ if(!name.empty())
{
- auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
+ auto devlock = DeviceListLock{gDeviceList};
+ auto list = al::span{devlock.getPlaybackList()};
+ auto iter = std::find_if(list.cbegin(), list.cend(),
[name](const DevMap &entry) -> bool
{ return entry.name == name || entry.endpoint_guid == name; });
- if(iter == PlaybackDevices.cend())
+ if(iter == list.cend())
{
const std::wstring wname{utf8_to_wstr(name)};
- iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
+ iter = std::find_if(list.cbegin(), list.cend(),
[&wname](const DevMap &entry) -> bool
{ return entry.devid == wname; });
}
- if(iter == PlaybackDevices.cend())
+ if(iter == list.cend())
{
- WARN("Failed to find device name matching \"%s\"\n", name);
+ WARN("Failed to find device name matching \"%.*s\"\n", static_cast<int>(name.length()),
+ name.data());
return E_FAIL;
}
- name = iter->name.c_str();
- devid = iter->devid.c_str();
+ devname = iter->name;
+ devid = iter->devid;
}
- void *ptr;
- ComPtr<IMMDevice> mmdev;
- HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
- IID_IMMDeviceEnumerator, &ptr)};
- if(SUCCEEDED(hr))
- {
- ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
- if(!devid)
- hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, mmdev.getPtr());
- else
- hr = enumerator->GetDevice(devid, mmdev.getPtr());
- }
+ HRESULT hr{sDeviceHelper->openDevice(devid, eRender, mMMDev)};
if(FAILED(hr))
{
- WARN("Failed to open device \"%s\"\n", name?name:"(default)");
+ WARN("Failed to open device \"%s\"\n", devname.empty() ? "(default)" : devname.c_str());
return hr;
}
+ if(!devname.empty())
+ mDevice->DeviceName = DevNameHead + std::move(devname);
+ else
+ mDevice->DeviceName = DevNameHead + GetDeviceNameAndGuid(mMMDev).first;
- mClient = nullptr;
- mMMDev = std::move(mmdev);
- if(name) mDevice->DeviceName = std::string{DevNameHead} + name;
- else mDevice->DeviceName = DevNameHead + get_device_name_and_guid(mMMDev.get()).first;
-
- return hr;
+ return S_OK;
}
void WasapiPlayback::closeProxy()
{
- mClient = nullptr;
+ mAudio.emplace<std::monostate>();
mMMDev = nullptr;
}
-bool WasapiPlayback::reset()
-{
- HRESULT hr{pushMessage(MsgType::ResetDevice).get()};
- if(FAILED(hr))
- throw al::backend_exception{al::backend_error::DeviceError, "0x%08lx", hr};
- return true;
-}
-
-HRESULT WasapiPlayback::resetProxy()
+void WasapiPlayback::prepareFormat(WAVEFORMATEXTENSIBLE &OutputType)
{
- mClient = nullptr;
-
- void *ptr;
- HRESULT hr{mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr)};
- if(FAILED(hr))
- {
- ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
- return hr;
- }
- mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)};
-
- WAVEFORMATEX *wfx;
- hr = mClient->GetMixFormat(&wfx);
- if(FAILED(hr))
- {
- ERR("Failed to get mix format: 0x%08lx\n", hr);
- return hr;
- }
- TraceFormat("Device mix format", wfx);
-
- WAVEFORMATEXTENSIBLE OutputType;
- if(!MakeExtensible(&OutputType, wfx))
- {
- CoTaskMemFree(wfx);
- return E_FAIL;
- }
- CoTaskMemFree(wfx);
- wfx = nullptr;
-
- const ReferenceTime per_time{ReferenceTime{seconds{mDevice->UpdateSize}} / mDevice->Frequency};
- const ReferenceTime buf_time{ReferenceTime{seconds{mDevice->BufferSize}} / mDevice->Frequency};
bool isRear51{false};
if(!mDevice->Flags.test(FrequencyRequest))
@@ -928,7 +1390,7 @@ HRESULT WasapiPlayback::resetProxy()
const uint32_t chancount{OutputType.Format.nChannels};
const DWORD chanmask{OutputType.dwChannelMask};
if(chancount >= 12 && (chanmask&X714Mask) == X7DOT1DOT4)
- mDevice->FmtChans = DevFmtX71;
+ mDevice->FmtChans = DevFmtX714;
else if(chancount >= 8 && (chanmask&X71Mask) == X7DOT1)
mDevice->FmtChans = DevFmtX71;
else if(chancount >= 7 && (chanmask&X61Mask) == X6DOT1)
@@ -1030,141 +1492,426 @@ HRESULT WasapiPlayback::resetProxy()
OutputType.Format.wBitsPerSample / 8);
OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
OutputType.Format.nBlockAlign;
+}
- TraceFormat("Requesting playback format", &OutputType.Format);
- hr = mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
- if(FAILED(hr))
+void WasapiPlayback::finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType)
+{
+ if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "allow-resampler", true))
+ mDevice->Frequency = OutputType.Format.nSamplesPerSec;
+ else
+ mDevice->Frequency = minu(mDevice->Frequency, OutputType.Format.nSamplesPerSec);
+
+ const uint32_t chancount{OutputType.Format.nChannels};
+ const DWORD chanmask{OutputType.dwChannelMask};
+ /* Don't update the channel format if the requested format fits what's
+ * supported.
+ */
+ bool chansok{false};
+ if(mDevice->Flags.test(ChannelsRequest))
+ {
+ /* When requesting a channel configuration, make sure it fits the
+ * mask's lsb (to ensure no gaps in the output channels). If there's no
+ * mask, assume the request fits with enough channels.
+ */
+ switch(mDevice->FmtChans)
+ {
+ case DevFmtMono:
+ chansok = (chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask));
+ break;
+ case DevFmtStereo:
+ chansok = (chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask));
+ break;
+ case DevFmtQuad:
+ chansok = (chancount >= 4 && ((chanmask&QuadMask) == QUAD || !chanmask));
+ break;
+ case DevFmtX51:
+ chansok = (chancount >= 6 && ((chanmask&X51Mask) == X5DOT1
+ || (chanmask&X51RearMask) == X5DOT1REAR || !chanmask));
+ break;
+ case DevFmtX61:
+ chansok = (chancount >= 7 && ((chanmask&X61Mask) == X6DOT1 || !chanmask));
+ break;
+ case DevFmtX71:
+ case DevFmtX3D71:
+ chansok = (chancount >= 8 && ((chanmask&X71Mask) == X7DOT1 || !chanmask));
+ break;
+ case DevFmtX714:
+ chansok = (chancount >= 12 && ((chanmask&X714Mask) == X7DOT1DOT4 || !chanmask));
+ case DevFmtAmbi3D:
+ break;
+ }
+ }
+ if(!chansok)
{
- WARN("Failed to check format support: 0x%08lx\n", hr);
- hr = mClient->GetMixFormat(&wfx);
+ if(chancount >= 12 && (chanmask&X714Mask) == X7DOT1DOT4)
+ mDevice->FmtChans = DevFmtX714;
+ else if(chancount >= 8 && (chanmask&X71Mask) == X7DOT1)
+ mDevice->FmtChans = DevFmtX71;
+ else if(chancount >= 7 && (chanmask&X61Mask) == X6DOT1)
+ mDevice->FmtChans = DevFmtX61;
+ else if(chancount >= 6 && ((chanmask&X51Mask) == X5DOT1
+ || (chanmask&X51RearMask) == X5DOT1REAR))
+ mDevice->FmtChans = DevFmtX51;
+ else if(chancount >= 4 && (chanmask&QuadMask) == QUAD)
+ mDevice->FmtChans = DevFmtQuad;
+ else if(chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask))
+ mDevice->FmtChans = DevFmtStereo;
+ else if(chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask))
+ mDevice->FmtChans = DevFmtMono;
+ else
+ {
+ ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels,
+ OutputType.dwChannelMask);
+ mDevice->FmtChans = DevFmtStereo;
+ OutputType.Format.nChannels = 2;
+ OutputType.dwChannelMask = STEREO;
+ }
}
- if(FAILED(hr))
+
+ if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM))
{
- ERR("Failed to find a supported format: 0x%08lx\n", hr);
- return hr;
+ if(OutputType.Format.wBitsPerSample == 8)
+ mDevice->FmtType = DevFmtUByte;
+ else if(OutputType.Format.wBitsPerSample == 16)
+ mDevice->FmtType = DevFmtShort;
+ else if(OutputType.Format.wBitsPerSample == 32)
+ mDevice->FmtType = DevFmtInt;
+ else
+ {
+ mDevice->FmtType = DevFmtShort;
+ OutputType.Format.wBitsPerSample = 16;
+ }
+ }
+ else if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+ {
+ mDevice->FmtType = DevFmtFloat;
+ OutputType.Format.wBitsPerSample = 32;
+ }
+ else
+ {
+ ERR("Unhandled format sub-type: %s\n", GuidPrinter{OutputType.SubFormat}.c_str());
+ mDevice->FmtType = DevFmtShort;
+ if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE)
+ OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
+ OutputType.Format.wBitsPerSample = 16;
+ OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
+ OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
+}
- if(wfx != nullptr)
+
+bool WasapiPlayback::reset()
+{
+ HRESULT hr{pushMessage(MsgType::ResetDevice).get()};
+ if(FAILED(hr))
+ throw al::backend_exception{al::backend_error::DeviceError, "0x%08lx", hr};
+ return true;
+}
+
+HRESULT WasapiPlayback::resetProxy()
+{
+ if(GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "spatial-api", false))
{
- TraceFormat("Got playback format", wfx);
- if(!MakeExtensible(&OutputType, wfx))
+ auto &audio = mAudio.emplace<SpatialDevice>();
+ HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(ISpatialAudioClient),
+ al::out_ptr(audio.mClient))};
+ if(FAILED(hr))
{
- CoTaskMemFree(wfx);
- return E_FAIL;
+ ERR("Failed to activate spatial audio client: 0x%08lx\n", hr);
+ goto no_spatial;
}
- CoTaskMemFree(wfx);
- wfx = nullptr;
- if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "allow-resampler", true))
- mDevice->Frequency = OutputType.Format.nSamplesPerSec;
- else
- mDevice->Frequency = minu(mDevice->Frequency, OutputType.Format.nSamplesPerSec);
+ ComPtr<IAudioFormatEnumerator> fmtenum;
+ hr = audio.mClient->GetSupportedAudioObjectFormatEnumerator(al::out_ptr(fmtenum));
+ if(FAILED(hr))
+ {
+ ERR("Failed to get format enumerator: 0x%08lx\n", hr);
+ goto no_spatial;
+ }
- const uint32_t chancount{OutputType.Format.nChannels};
- const DWORD chanmask{OutputType.dwChannelMask};
- /* Don't update the channel format if the requested format fits what's
- * supported.
- */
- bool chansok{false};
- if(mDevice->Flags.test(ChannelsRequest))
+ UINT32 fmtcount{};
+ hr = fmtenum->GetCount(&fmtcount);
+ if(FAILED(hr) || fmtcount == 0)
{
- /* When requesting a channel configuration, make sure it fits the
- * mask's lsb (to ensure no gaps in the output channels). If
- * there's no mask, assume the request fits with enough channels.
- */
- switch(mDevice->FmtChans)
+ ERR("Failed to get format count: 0x%08lx\n", hr);
+ goto no_spatial;
+ }
+
+ WAVEFORMATEX *preferredFormat{};
+ hr = fmtenum->GetFormat(0, &preferredFormat);
+ if(FAILED(hr))
+ {
+ ERR("Failed to get preferred format: 0x%08lx\n", hr);
+ goto no_spatial;
+ }
+ TraceFormat("Preferred mix format", preferredFormat);
+
+ UINT32 maxFrames{};
+ hr = audio.mClient->GetMaxFrameCount(preferredFormat, &maxFrames);
+ if(FAILED(hr))
+ ERR("Failed to get max frames: 0x%08lx\n", hr);
+ else
+ TRACE("Max sample frames: %u\n", maxFrames);
+ for(UINT32 i{1};i < fmtcount;++i)
+ {
+ WAVEFORMATEX *otherFormat{};
+ hr = fmtenum->GetFormat(i, &otherFormat);
+ if(FAILED(hr))
+ ERR("Failed to format %u: 0x%08lx\n", i+1, hr);
+ else
{
- case DevFmtMono:
- chansok = (chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask));
- break;
- case DevFmtStereo:
- chansok = (chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask));
- break;
- case DevFmtQuad:
- chansok = (chancount >= 4 && ((chanmask&QuadMask) == QUAD || !chanmask));
- break;
- case DevFmtX51:
- chansok = (chancount >= 6 && ((chanmask&X51Mask) == X5DOT1
- || (chanmask&X51RearMask) == X5DOT1REAR || !chanmask));
- break;
- case DevFmtX61:
- chansok = (chancount >= 7 && ((chanmask&X61Mask) == X6DOT1 || !chanmask));
- break;
- case DevFmtX71:
- case DevFmtX3D71:
- chansok = (chancount >= 8 && ((chanmask&X71Mask) == X7DOT1 || !chanmask));
- break;
- case DevFmtX714:
- chansok = (chancount >= 12 && ((chanmask&X714Mask) == X7DOT1DOT4 || !chanmask));
- case DevFmtAmbi3D:
- break;
+ TraceFormat("Other mix format", otherFormat);
+ UINT32 otherMaxFrames{};
+ hr = audio.mClient->GetMaxFrameCount(otherFormat, &otherMaxFrames);
+ if(FAILED(hr))
+ ERR("Failed to get max frames: 0x%08lx\n", hr);
+ else
+ TRACE("Max sample frames: %u\n", otherMaxFrames);
}
}
- if(!chansok)
+
+ WAVEFORMATEXTENSIBLE OutputType;
+ if(!MakeExtensible(&OutputType, preferredFormat))
+ goto no_spatial;
+
+ /* Force 32-bit float. This is currently required for planar output. */
+ if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE
+ && OutputType.Format.wFormatTag != WAVE_FORMAT_IEEE_FLOAT)
+ {
+ OutputType.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
+ OutputType.Format.cbSize = 0;
+ }
+ if(OutputType.Format.wBitsPerSample != 32)
{
+ OutputType.Format.nAvgBytesPerSec = OutputType.Format.nAvgBytesPerSec * 32u
+ / OutputType.Format.wBitsPerSample;
+ OutputType.Format.nBlockAlign = static_cast<WORD>(OutputType.Format.nBlockAlign * 32
+ / OutputType.Format.wBitsPerSample);
+ OutputType.Format.wBitsPerSample = 32;
+ }
+ OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
+ OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+
+ /* Match the output rate if not requesting anything specific. */
+ if(!mDevice->Flags.test(FrequencyRequest))
+ mDevice->Frequency = OutputType.Format.nSamplesPerSec;
+
+ bool isRear51{false};
+ if(!mDevice->Flags.test(ChannelsRequest))
+ {
+ const uint32_t chancount{OutputType.Format.nChannels};
+ const DWORD chanmask{OutputType.dwChannelMask};
if(chancount >= 12 && (chanmask&X714Mask) == X7DOT1DOT4)
mDevice->FmtChans = DevFmtX714;
else if(chancount >= 8 && (chanmask&X71Mask) == X7DOT1)
mDevice->FmtChans = DevFmtX71;
else if(chancount >= 7 && (chanmask&X61Mask) == X6DOT1)
mDevice->FmtChans = DevFmtX61;
- else if(chancount >= 6 && ((chanmask&X51Mask) == X5DOT1
- || (chanmask&X51RearMask) == X5DOT1REAR))
+ else if(chancount >= 6 && (chanmask&X51Mask) == X5DOT1)
mDevice->FmtChans = DevFmtX51;
+ else if(chancount >= 6 && (chanmask&X51RearMask) == X5DOT1REAR)
+ {
+ mDevice->FmtChans = DevFmtX51;
+ isRear51 = true;
+ }
else if(chancount >= 4 && (chanmask&QuadMask) == QUAD)
mDevice->FmtChans = DevFmtQuad;
else if(chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask))
mDevice->FmtChans = DevFmtStereo;
- else if(chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask))
- mDevice->FmtChans = DevFmtMono;
- else
+ /* HACK: Don't autoselect mono. Wine returns this and makes the
+ * audio terrible.
+ */
+ else if(!(chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask)))
+ ERR("Unhandled channel config: %d -- 0x%08lx\n", chancount, chanmask);
+ }
+ else
+ {
+ const uint32_t chancount{OutputType.Format.nChannels};
+ const DWORD chanmask{OutputType.dwChannelMask};
+ isRear51 = (chancount == 6 && (chanmask&X51RearMask) == X5DOT1REAR);
+ }
+
+ auto getTypeMask = [isRear51](DevFmtChannels chans) noexcept
+ {
+ switch(chans)
{
- ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels,
- OutputType.dwChannelMask);
- mDevice->FmtChans = DevFmtStereo;
- OutputType.Format.nChannels = 2;
- OutputType.dwChannelMask = STEREO;
+ case DevFmtMono: return ChannelMask_Mono;
+ case DevFmtStereo: return ChannelMask_Stereo;
+ case DevFmtQuad: return ChannelMask_Quad;
+ case DevFmtX51: return isRear51 ? ChannelMask_X51Rear : ChannelMask_X51;
+ case DevFmtX61: return ChannelMask_X61;
+ case DevFmtX3D71:
+ case DevFmtX71: return ChannelMask_X71;
+ case DevFmtX714: return ChannelMask_X714;
+ case DevFmtAmbi3D:
+ break;
}
+ return ChannelMask_Stereo;
+ };
+
+ SpatialAudioObjectRenderStreamActivationParams streamParams{};
+ streamParams.ObjectFormat = &OutputType.Format;
+ streamParams.StaticObjectTypeMask = getTypeMask(mDevice->FmtChans);
+ streamParams.Category = AudioCategory_Media;
+ streamParams.EventHandle = mNotifyEvent;
+
+ PropVariant paramProp{};
+ paramProp->vt = VT_BLOB;
+ paramProp->blob.cbSize = sizeof(streamParams);
+ paramProp->blob.pBlobData = reinterpret_cast<BYTE*>(&streamParams);
+
+ hr = audio.mClient->ActivateSpatialAudioStream(paramProp.get(),
+ __uuidof(ISpatialAudioObjectRenderStream), al::out_ptr(audio.mRender));
+ if(FAILED(hr))
+ {
+ ERR("Failed to activate spatial audio stream: 0x%08lx\n", hr);
+ goto no_spatial;
}
- if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_PCM))
+ audio.mStaticMask = streamParams.StaticObjectTypeMask;
+ mFormat = OutputType;
+
+ mDevice->FmtType = DevFmtFloat;
+ mDevice->Flags.reset(DirectEar).set(Virtualization);
+ if(streamParams.StaticObjectTypeMask == ChannelMask_Stereo)
+ mDevice->FmtChans = DevFmtStereo;
+ if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "allow-resampler", true))
+ mDevice->Frequency = OutputType.Format.nSamplesPerSec;
+ else
+ mDevice->Frequency = minu(mDevice->Frequency, OutputType.Format.nSamplesPerSec);
+
+ setDefaultWFXChannelOrder();
+
+ /* FIXME: Get the real update and buffer size. Presumably the actual
+ * device is configured once ActivateSpatialAudioStream succeeds, and
+ * an IAudioClient from the same IMMDevice accesses the same device
+ * configuration. This isn't obviously correct, but for now assume
+ * IAudioClient::GetDevicePeriod returns the current device period time
+ * that ISpatialAudioObjectRenderStream will try to wake up at.
+ *
+ * Unfortunately this won't get the buffer size of the
+ * ISpatialAudioObjectRenderStream, so we only assume there's two
+ * periods.
+ */
+ mOrigUpdateSize = mDevice->UpdateSize;
+ mOrigBufferSize = mOrigUpdateSize*2;
+ ReferenceTime per_time{ReferenceTime{seconds{mDevice->UpdateSize}} / mDevice->Frequency};
+
+ ComPtr<IAudioClient> tmpClient;
+ hr = sDeviceHelper->activateAudioClient(mMMDev, __uuidof(IAudioClient),
+ al::out_ptr(tmpClient));
+ if(FAILED(hr))
+ ERR("Failed to activate audio client: 0x%08lx\n", hr);
+ else
{
- if(OutputType.Format.wBitsPerSample == 8)
- mDevice->FmtType = DevFmtUByte;
- else if(OutputType.Format.wBitsPerSample == 16)
- mDevice->FmtType = DevFmtShort;
- else if(OutputType.Format.wBitsPerSample == 32)
- mDevice->FmtType = DevFmtInt;
+ hr = tmpClient->GetDevicePeriod(&reinterpret_cast<REFERENCE_TIME&>(per_time), nullptr);
+ if(FAILED(hr))
+ ERR("Failed to get device period: 0x%08lx\n", hr);
else
{
- mDevice->FmtType = DevFmtShort;
- OutputType.Format.wBitsPerSample = 16;
+ mOrigUpdateSize = RefTime2Samples(per_time, mFormat.Format.nSamplesPerSec);
+ mOrigBufferSize = mOrigUpdateSize*2;
}
}
- else if(IsEqualGUID(OutputType.SubFormat, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+ tmpClient = nullptr;
+
+ mDevice->UpdateSize = RefTime2Samples(per_time, mDevice->Frequency);
+ mDevice->BufferSize = mDevice->UpdateSize*2;
+
+ mResampler = nullptr;
+ mResampleBuffer = nullptr;
+ mBufferFilled = 0;
+ if(mDevice->Frequency != mFormat.Format.nSamplesPerSec)
{
- mDevice->FmtType = DevFmtFloat;
- OutputType.Format.wBitsPerSample = 32;
+ const auto flags = as_unsigned(streamParams.StaticObjectTypeMask);
+ const auto channelCount = as_unsigned(al::popcount(flags));
+ mResampler = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType,
+ channelCount, mDevice->Frequency, mFormat.Format.nSamplesPerSec,
+ Resampler::FastBSinc24);
+ mResampleBuffer = std::make_unique<char[]>(size_t{mDevice->UpdateSize} * channelCount *
+ mFormat.Format.wBitsPerSample / 8);
+
+ TRACE("Created converter for %s/%s format, dst: %luhz (%u), src: %uhz (%u)\n",
+ DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType),
+ mFormat.Format.nSamplesPerSec, mOrigUpdateSize, mDevice->Frequency,
+ mDevice->UpdateSize);
}
- else
+
+ return S_OK;
+ }
+
+no_spatial:
+ mDevice->Flags.reset(Virtualization);
+
+ auto &audio = mAudio.emplace<PlainDevice>();
+ HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(IAudioClient),
+ al::out_ptr(audio.mClient))};
+ if(FAILED(hr))
+ {
+ ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
+ return hr;
+ }
+
+ WAVEFORMATEX *wfx;
+ hr = audio.mClient->GetMixFormat(&wfx);
+ if(FAILED(hr))
+ {
+ ERR("Failed to get mix format: 0x%08lx\n", hr);
+ return hr;
+ }
+ TraceFormat("Device mix format", wfx);
+
+ WAVEFORMATEXTENSIBLE OutputType;
+ if(!MakeExtensible(&OutputType, wfx))
+ {
+ CoTaskMemFree(wfx);
+ return E_FAIL;
+ }
+ CoTaskMemFree(wfx);
+ wfx = nullptr;
+
+ const ReferenceTime per_time{ReferenceTime{seconds{mDevice->UpdateSize}} / mDevice->Frequency};
+ const ReferenceTime buf_time{ReferenceTime{seconds{mDevice->BufferSize}} / mDevice->Frequency};
+
+ prepareFormat(OutputType);
+
+ TraceFormat("Requesting playback format", &OutputType.Format);
+ hr = audio.mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
+ if(FAILED(hr))
+ {
+ WARN("Failed to check format support: 0x%08lx\n", hr);
+ hr = audio.mClient->GetMixFormat(&wfx);
+ }
+ if(FAILED(hr))
+ {
+ ERR("Failed to find a supported format: 0x%08lx\n", hr);
+ return hr;
+ }
+
+ if(wfx != nullptr)
+ {
+ TraceFormat("Got playback format", wfx);
+ if(!MakeExtensible(&OutputType, wfx))
{
- ERR("Unhandled format sub-type: %s\n", GuidPrinter{OutputType.SubFormat}.c_str());
- mDevice->FmtType = DevFmtShort;
- if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE)
- OutputType.Format.wFormatTag = WAVE_FORMAT_PCM;
- OutputType.Format.wBitsPerSample = 16;
- OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ CoTaskMemFree(wfx);
+ return E_FAIL;
}
- OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
+ CoTaskMemFree(wfx);
+ wfx = nullptr;
+
+ finalizeFormat(OutputType);
}
mFormat = OutputType;
- const EndpointFormFactor formfactor{get_device_formfactor(mMMDev.get())};
+#if !defined(ALSOFT_UWP)
+ const EndpointFormFactor formfactor{GetDeviceFormfactor(mMMDev.get())};
mDevice->Flags.set(DirectEar, (formfactor == Headphones || formfactor == Headset));
-
+#else
+ mDevice->Flags.set(DirectEar, false);
+#endif
setDefaultWFXChannelOrder();
- hr = mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
+ hr = audio.mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
buf_time.count(), 0, &OutputType.Format, nullptr);
if(FAILED(hr))
{
@@ -1174,15 +1921,22 @@ HRESULT WasapiPlayback::resetProxy()
UINT32 buffer_len{};
ReferenceTime min_per{};
- hr = mClient->GetDevicePeriod(&reinterpret_cast<REFERENCE_TIME&>(min_per), nullptr);
+ hr = audio.mClient->GetDevicePeriod(&reinterpret_cast<REFERENCE_TIME&>(min_per), nullptr);
if(SUCCEEDED(hr))
- hr = mClient->GetBufferSize(&buffer_len);
+ hr = audio.mClient->GetBufferSize(&buffer_len);
if(FAILED(hr))
{
ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
return hr;
}
+ hr = audio.mClient->SetEventHandle(mNotifyEvent);
+ if(FAILED(hr))
+ {
+ ERR("Failed to set event handle: 0x%08lx\n", hr);
+ return hr;
+ }
+
/* Find the nearest multiple of the period size to the update size */
if(min_per < per_time)
min_per *= maxi64((per_time + min_per/2) / min_per, 1);
@@ -1212,13 +1966,6 @@ HRESULT WasapiPlayback::resetProxy()
mDevice->UpdateSize);
}
- hr = mClient->SetEventHandle(mNotifyEvent);
- if(FAILED(hr))
- {
- ERR("Failed to set event handle: 0x%08lx\n", hr);
- return hr;
- }
-
return hr;
}
@@ -1235,33 +1982,61 @@ HRESULT WasapiPlayback::startProxy()
{
ResetEvent(mNotifyEvent);
- HRESULT hr{mClient->Start()};
- if(FAILED(hr))
+ auto mstate_fallback = [](std::monostate) -> HRESULT
+ { return E_FAIL; };
+ auto start_plain = [&](PlainDevice &audio) -> HRESULT
{
- ERR("Failed to start audio client: 0x%08lx\n", hr);
- return hr;
- }
+ HRESULT hr{audio.mClient->Start()};
+ if(FAILED(hr))
+ {
+ ERR("Failed to start audio client: 0x%08lx\n", hr);
+ return hr;
+ }
- void *ptr;
- hr = mClient->GetService(IID_IAudioRenderClient, &ptr);
- if(SUCCEEDED(hr))
+ hr = audio.mClient->GetService(__uuidof(IAudioRenderClient), al::out_ptr(audio.mRender));
+ if(SUCCEEDED(hr))
+ {
+ try {
+ mKillNow.store(false, std::memory_order_release);
+ mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerProc), this};
+ }
+ catch(...) {
+ audio.mRender = nullptr;
+ ERR("Failed to start thread\n");
+ hr = E_FAIL;
+ }
+ }
+ if(FAILED(hr))
+ audio.mClient->Stop();
+ return hr;
+ };
+ auto start_spatial = [&](SpatialDevice &audio) -> HRESULT
{
- mRender = ComPtr<IAudioRenderClient>{static_cast<IAudioRenderClient*>(ptr)};
+ HRESULT hr{audio.mRender->Start()};
+ if(FAILED(hr))
+ {
+ ERR("Failed to start spatial audio stream: 0x%08lx\n", hr);
+ return hr;
+ }
+
try {
mKillNow.store(false, std::memory_order_release);
- mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerProc), this};
+ mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerSpatialProc), this};
}
catch(...) {
- mRender = nullptr;
ERR("Failed to start thread\n");
hr = E_FAIL;
}
- }
- if(FAILED(hr))
- mClient->Stop();
+ if(FAILED(hr))
+ {
+ audio.mRender->Stop();
+ audio.mRender->Reset();
+ }
+ return hr;
+ };
- return hr;
+ return std::visit(overloaded{mstate_fallback, start_plain, start_spatial}, mAudio);
}
@@ -1270,14 +2045,25 @@ void WasapiPlayback::stop()
void WasapiPlayback::stopProxy()
{
- if(!mRender || !mThread.joinable())
+ if(!mThread.joinable())
return;
mKillNow.store(true, std::memory_order_release);
mThread.join();
- mRender = nullptr;
- mClient->Stop();
+ auto mstate_fallback = [](std::monostate) -> void
+ { };
+ auto stop_plain = [](PlainDevice &audio) -> void
+ {
+ audio.mRender = nullptr;
+ audio.mClient->Stop();
+ };
+ auto stop_spatial = [](SpatialDevice &audio) -> void
+ {
+ audio.mRender->Stop();
+ audio.mRender->Reset();
+ };
+ std::visit(overloaded{mstate_fallback, stop_plain, stop_spatial}, mAudio);
}
@@ -1306,8 +2092,8 @@ struct WasapiCapture final : public BackendBase, WasapiProxy {
int recordProc();
- void open(const char *name) override;
- HRESULT openProxy(const char *name) override;
+ void open(std::string_view name) override;
+ HRESULT openProxy(std::string_view name) override;
void closeProxy() override;
HRESULT resetProxy() override;
@@ -1316,11 +2102,11 @@ struct WasapiCapture final : public BackendBase, WasapiProxy {
void stop() override;
void stopProxy() override;
- void captureSamples(al::byte *buffer, uint samples) override;
+ void captureSamples(std::byte *buffer, uint samples) override;
uint availableSamples() override;
HRESULT mOpenStatus{E_FAIL};
- ComPtr<IMMDevice> mMMDev{nullptr};
+ DeviceHandle mMMDev{nullptr};
ComPtr<IAudioClient> mClient{nullptr};
ComPtr<IAudioCaptureClient> mCapture{nullptr};
HANDLE mNotifyEvent{nullptr};
@@ -1338,10 +2124,7 @@ struct WasapiCapture final : public BackendBase, WasapiProxy {
WasapiCapture::~WasapiCapture()
{
if(SUCCEEDED(mOpenStatus))
- {
pushMessage(MsgType::CloseDevice).wait();
- DeinitThread();
- }
mOpenStatus = E_FAIL;
if(mNotifyEvent != nullptr)
@@ -1352,21 +2135,21 @@ WasapiCapture::~WasapiCapture()
FORCE_ALIGN int WasapiCapture::recordProc()
{
- HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)};
- if(FAILED(hr))
+ ComWrapper com{COINIT_MULTITHREADED};
+ if(!com)
{
- ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr);
- mDevice->handleDisconnect("COM init failed: 0x%08lx", hr);
+ ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", com.status());
+ mDevice->handleDisconnect("COM init failed: 0x%08lx", com.status());
return 1;
}
althrd_setname(RECORD_THREAD_NAME);
- al::vector<float> samples;
+ std::vector<float> samples;
while(!mKillNow.load(std::memory_order_relaxed))
{
UINT32 avail;
- hr = mCapture->GetNextPacketSize(&avail);
+ HRESULT hr{mCapture->GetNextPacketSize(&avail)};
if(FAILED(hr))
ERR("Failed to get next packet size: 0x%08lx\n", hr);
else if(avail > 0)
@@ -1437,12 +2220,11 @@ FORCE_ALIGN int WasapiCapture::recordProc()
ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
}
- CoUninitialize();
return 0;
}
-void WasapiCapture::open(const char *name)
+void WasapiCapture::open(std::string_view name)
{
if(SUCCEEDED(mOpenStatus))
throw al::backend_exception{al::backend_error::DeviceError,
@@ -1456,34 +2238,18 @@ void WasapiCapture::open(const char *name)
"Failed to create notify events"};
}
- HRESULT hr{InitThread()};
- if(FAILED(hr))
- {
- throw al::backend_exception{al::backend_error::DeviceError,
- "Failed to init COM thread: 0x%08lx", hr};
- }
-
- if(name)
+ if(name.length() >= DevNameHeadLen
+ && std::strncmp(name.data(), DevNameHead, DevNameHeadLen) == 0)
{
- if(CaptureDevices.empty())
- pushMessage(MsgType::EnumerateCapture);
- if(std::strncmp(name, DevNameHead, DevNameHeadLen) == 0)
- {
- name += DevNameHeadLen;
- if(*name == '\0')
- name = nullptr;
- }
+ name = name.substr(DevNameHeadLen);
}
mOpenStatus = pushMessage(MsgType::OpenDevice, name).get();
if(FAILED(mOpenStatus))
- {
- DeinitThread();
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
mOpenStatus};
- }
- hr = pushMessage(MsgType::ResetDevice).get();
+ HRESULT hr{pushMessage(MsgType::ResetDevice).get()};
if(FAILED(hr))
{
if(hr == E_OUTOFMEMORY)
@@ -1492,52 +2258,47 @@ void WasapiCapture::open(const char *name)
}
}
-HRESULT WasapiCapture::openProxy(const char *name)
+HRESULT WasapiCapture::openProxy(std::string_view name)
{
- const wchar_t *devid{nullptr};
- if(name)
+ std::string devname;
+ std::wstring devid;
+ if(!name.empty())
{
- auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
+ auto devlock = DeviceListLock{gDeviceList};
+ auto devlist = al::span{devlock.getCaptureList()};
+ auto iter = std::find_if(devlist.cbegin(), devlist.cend(),
[name](const DevMap &entry) -> bool
{ return entry.name == name || entry.endpoint_guid == name; });
- if(iter == CaptureDevices.cend())
+ if(iter == devlist.cend())
{
const std::wstring wname{utf8_to_wstr(name)};
- iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
+ iter = std::find_if(devlist.cbegin(), devlist.cend(),
[&wname](const DevMap &entry) -> bool
{ return entry.devid == wname; });
}
- if(iter == CaptureDevices.cend())
+ if(iter == devlist.cend())
{
- WARN("Failed to find device name matching \"%s\"\n", name);
+ WARN("Failed to find device name matching \"%.*s\"\n", static_cast<int>(name.length()),
+ name.data());
return E_FAIL;
}
- name = iter->name.c_str();
- devid = iter->devid.c_str();
+ devname = iter->name;
+ devid = iter->devid;
}
- void *ptr;
- HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
- IID_IMMDeviceEnumerator, &ptr)};
- if(SUCCEEDED(hr))
- {
- ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
- if(!devid)
- hr = enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, mMMDev.getPtr());
- else
- hr = enumerator->GetDevice(devid, mMMDev.getPtr());
- }
+ HRESULT hr{sDeviceHelper->openDevice(devid, eCapture, mMMDev)};
if(FAILED(hr))
{
- WARN("Failed to open device \"%s\"\n", name?name:"(default)");
+ WARN("Failed to open device \"%s\"\n", devname.empty() ? "(default)" : devname.c_str());
return hr;
}
-
mClient = nullptr;
- if(name) mDevice->DeviceName = std::string{DevNameHead} + name;
- else mDevice->DeviceName = DevNameHead + get_device_name_and_guid(mMMDev.get()).first;
+ if(!devname.empty())
+ mDevice->DeviceName = DevNameHead + std::move(devname);
+ else
+ mDevice->DeviceName = DevNameHead + GetDeviceNameAndGuid(mMMDev).first;
- return hr;
+ return S_OK;
}
void WasapiCapture::closeProxy()
@@ -1550,14 +2311,13 @@ HRESULT WasapiCapture::resetProxy()
{
mClient = nullptr;
- void *ptr;
- HRESULT hr{mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr)};
+ HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(IAudioClient),
+ al::out_ptr(mClient))};
if(FAILED(hr))
{
ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
return hr;
}
- mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)};
WAVEFORMATEX *wfx;
hr = mClient->GetMixFormat(&wfx);
@@ -1849,11 +2609,9 @@ HRESULT WasapiCapture::startProxy()
return hr;
}
- void *ptr;
- hr = mClient->GetService(IID_IAudioCaptureClient, &ptr);
+ hr = mClient->GetService(__uuidof(IAudioCaptureClient), al::out_ptr(mCapture));
if(SUCCEEDED(hr))
{
- mCapture = ComPtr<IAudioCaptureClient>{static_cast<IAudioCaptureClient*>(ptr)};
try {
mKillNow.store(false, std::memory_order_release);
mThread = std::thread{std::mem_fn(&WasapiCapture::recordProc), this};
@@ -1892,7 +2650,7 @@ void WasapiCapture::stopProxy()
}
-void WasapiCapture::captureSamples(al::byte *buffer, uint samples)
+void WasapiCapture::captureSamples(std::byte *buffer, uint samples)
{ mRing->read(buffer, samples); }
uint WasapiCapture::availableSamples()
@@ -1904,34 +2662,13 @@ uint WasapiCapture::availableSamples()
bool WasapiBackendFactory::init()
{
static HRESULT InitResult{E_FAIL};
-
if(FAILED(InitResult)) try
{
- auto res = std::async(std::launch::async, []() -> HRESULT
- {
- HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)};
- if(FAILED(hr))
- {
- WARN("Failed to initialize COM: 0x%08lx\n", hr);
- return hr;
- }
-
- void *ptr{};
- hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
- IID_IMMDeviceEnumerator, &ptr);
- if(FAILED(hr))
- {
- WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
- CoUninitialize();
- return hr;
- }
- static_cast<IMMDeviceEnumerator*>(ptr)->Release();
- CoUninitialize();
-
- return S_OK;
- });
+ std::promise<HRESULT> promise;
+ auto future = promise.get_future();
- InitResult = res.get();
+ std::thread{&WasapiProxy::messageHandler, &promise}.detach();
+ InitResult = future.get();
}
catch(...) {
}
@@ -1944,34 +2681,45 @@ bool WasapiBackendFactory::querySupport(BackendType type)
std::string WasapiBackendFactory::probe(BackendType type)
{
- struct ProxyControl {
- HRESULT mResult{};
- ProxyControl() { mResult = WasapiProxy::InitThread(); }
- ~ProxyControl() { if(SUCCEEDED(mResult)) WasapiProxy::DeinitThread(); }
- };
- ProxyControl proxy;
-
std::string outnames;
- if(FAILED(proxy.mResult))
- return outnames;
+ auto devlock = DeviceListLock{gDeviceList};
switch(type)
{
case BackendType::Playback:
- WasapiProxy::pushMessageStatic(MsgType::EnumeratePlayback).wait();
- for(const DevMap &entry : PlaybackDevices)
{
- /* +1 to also append the null char (to ensure a null-separated list
- * and double-null terminated list).
- */
- outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1);
+ auto defaultId = devlock.getPlaybackDefaultId();
+ for(const DevMap &entry : devlock.getPlaybackList())
+ {
+ if(entry.devid != defaultId)
+ {
+ /* +1 to also append the null char (to ensure a null-
+ * separated list and double-null terminated list).
+ */
+ outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1);
+ continue;
+ }
+ /* Default device goes first. */
+ std::string name{DevNameHead + entry.name};
+ outnames.insert(0, name.c_str(), name.length()+1);
+ }
}
break;
case BackendType::Capture:
- WasapiProxy::pushMessageStatic(MsgType::EnumerateCapture).wait();
- for(const DevMap &entry : CaptureDevices)
- outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1);
+ {
+ auto defaultId = devlock.getCaptureDefaultId();
+ for(const DevMap &entry : devlock.getCaptureList())
+ {
+ if(entry.devid != defaultId)
+ {
+ outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1);
+ continue;
+ }
+ std::string name{DevNameHead + entry.name};
+ outnames.insert(0, name.c_str(), name.length()+1);
+ }
+ }
break;
}
@@ -1992,3 +2740,22 @@ BackendFactory &WasapiBackendFactory::getFactory()
static WasapiBackendFactory factory{};
return factory;
}
+
+alc::EventSupport WasapiBackendFactory::queryEventSupport(alc::EventType eventType, BackendType)
+{
+ switch(eventType)
+ {
+ case alc::EventType::DefaultDeviceChanged:
+ return alc::EventSupport::FullSupport;
+
+ case alc::EventType::DeviceAdded:
+ case alc::EventType::DeviceRemoved:
+#if !defined(ALSOFT_UWP)
+ return alc::EventSupport::FullSupport;
+#endif
+
+ case alc::EventType::Count:
+ break;
+ }
+ return alc::EventSupport::NoSupport;
+}
diff --git a/alc/backends/wasapi.h b/alc/backends/wasapi.h
index bb2671ee..12fd95ef 100644
--- a/alc/backends/wasapi.h
+++ b/alc/backends/wasapi.h
@@ -9,6 +9,8 @@ public:
bool querySupport(BackendType type) override;
+ alc::EventSupport queryEventSupport(alc::EventType eventType, BackendType type) override;
+
std::string probe(BackendType type) override;
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
diff --git a/alc/backends/wave.cpp b/alc/backends/wave.cpp
index 1b40640c..794d5cb8 100644
--- a/alc/backends/wave.cpp
+++ b/alc/backends/wave.cpp
@@ -32,19 +32,18 @@
#include <exception>
#include <functional>
#include <thread>
+#include <vector>
#include "albit.h"
-#include "albyte.h"
#include "alc/alconfig.h"
#include "almalloc.h"
#include "alnumeric.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 {
@@ -97,7 +96,7 @@ 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;
@@ -105,7 +104,7 @@ struct WaveBackend final : public BackendBase {
FILE *mFile{nullptr};
long mDataStart{-1};
- al::vector<al::byte> mBuffer;
+ std::vector<std::byte> mBuffer;
std::atomic<bool> mKillNow{true};
std::thread mThread;
@@ -155,13 +154,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]);
@@ -195,24 +194,24 @@ int WaveBackend::mixerProc()
return 0;
}
-void WaveBackend::open(const char *name)
+void WaveBackend::open(std::string_view name)
{
auto fname = ConfigValueStr(nullptr, "wave", "file");
if(!fname) throw al::backend_exception{al::backend_error::NoDevice,
"No wave output filename"};
- if(!name)
+ if(name.empty())
name = waveDevice;
- else if(strcmp(name, waveDevice) != 0)
- throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
- name};
+ else if(name != waveDevice)
+ throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found",
+ static_cast<int>(name.length()), 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())};
+ std::wstring wname{utf8_to_wstr(fname.value())};
mFile = _wfopen(wname.c_str(), L"wb");
}
#else
diff --git a/alc/backends/winmm.cpp b/alc/backends/winmm.cpp
index 38e1193f..f0fb0a1c 100644
--- a/alc/backends/winmm.cpp
+++ b/alc/backends/winmm.cpp
@@ -39,12 +39,13 @@
#include <functional>
#include "alnumeric.h"
+#include "alsem.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"
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
@@ -55,10 +56,10 @@ 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)
@@ -134,7 +135,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;
@@ -166,7 +167,7 @@ WinMMPlayback::~WinMMPlayback()
/* 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
@@ -207,18 +208,18 @@ 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",
+ static_cast<int>(name.length()), name.data()};
auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
DevFmtType fmttype{mDevice->FmtType};
@@ -369,10 +370,10 @@ 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};
@@ -405,7 +406,7 @@ WinMMCapture::~WinMMCapture()
/* 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
@@ -445,18 +446,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",
+ static_cast<int>(name.length()), name.data()};
auto DeviceID = static_cast<UINT>(std::distance(CaptureDevices.cbegin(), iter));
switch(mDevice->FmtChans)
@@ -571,7 +572,7 @@ void WinMMCapture::stop()
mIdx = 0;
}
-void WinMMCapture::captureSamples(al::byte *buffer, uint samples)
+void WinMMCapture::captureSamples(std::byte *buffer, uint samples)
{ mRing->read(buffer, samples); }
uint WinMMCapture::availableSamples()
diff --git a/alc/context.cpp b/alc/context.cpp
index e02c549b..ffc2743e 100644
--- a/alc/context.cpp
+++ b/alc/context.cpp
@@ -4,21 +4,27 @@
#include "context.h"
#include <algorithm>
+#include <array>
+#include <cstring>
#include <functional>
#include <limits>
#include <numeric>
#include <stddef.h>
#include <stdexcept>
+#include <string_view>
+#include <utility>
#include "AL/efx.h"
#include "al/auxeffectslot.h"
+#include "al/debug.h"
#include "al/source.h"
#include "al/effect.h"
#include "al/event.h"
#include "al/listener.h"
#include "albit.h"
#include "alc/alu.h"
+#include "alspan.h"
#include "core/async_event.h"
#include "core/device.h"
#include "core/effectslot.h"
@@ -42,47 +48,53 @@ using namespace std::placeholders;
using voidp = void*;
/* Default context extensions */
-constexpr ALchar alExtList[] =
- "AL_EXT_ALAW "
- "AL_EXT_BFORMAT "
- "AL_EXT_DOUBLE "
- "AL_EXT_EXPONENT_DISTANCE "
- "AL_EXT_FLOAT32 "
- "AL_EXT_IMA4 "
- "AL_EXT_LINEAR_DISTANCE "
- "AL_EXT_MCFORMATS "
- "AL_EXT_MULAW "
- "AL_EXT_MULAW_BFORMAT "
- "AL_EXT_MULAW_MCFORMATS "
- "AL_EXT_OFFSET "
- "AL_EXT_source_distance_model "
- "AL_EXT_SOURCE_RADIUS "
- "AL_EXT_STATIC_BUFFER "
- "AL_EXT_STEREO_ANGLES "
- "AL_LOKI_quadriphonic "
- "AL_SOFT_bformat_ex "
- "AL_SOFTX_bformat_hoa "
- "AL_SOFT_block_alignment "
- "AL_SOFT_buffer_length_query "
- "AL_SOFT_callback_buffer "
- "AL_SOFTX_convolution_reverb "
- "AL_SOFT_deferred_updates "
- "AL_SOFT_direct_channels "
- "AL_SOFT_direct_channels_remix "
- "AL_SOFT_effect_target "
- "AL_SOFT_events "
- "AL_SOFT_gain_clamp_ex "
- "AL_SOFTX_hold_on_disconnect "
- "AL_SOFT_loop_points "
- "AL_SOFTX_map_buffer "
- "AL_SOFT_MSADPCM "
- "AL_SOFT_source_latency "
- "AL_SOFT_source_length "
- "AL_SOFT_source_resampler "
- "AL_SOFT_source_spatialize "
- "AL_SOFT_source_start_delay "
- "AL_SOFT_UHJ "
- "AL_SOFT_UHJ_ex";
+std::vector<std::string_view> getContextExtensions() noexcept
+{
+ return std::vector<std::string_view>{
+ "AL_EXT_ALAW",
+ "AL_EXT_BFORMAT",
+ "AL_EXT_debug",
+ "AL_EXTX_direct_context",
+ "AL_EXT_DOUBLE",
+ "AL_EXT_EXPONENT_DISTANCE",
+ "AL_EXT_FLOAT32",
+ "AL_EXT_IMA4",
+ "AL_EXT_LINEAR_DISTANCE",
+ "AL_EXT_MCFORMATS",
+ "AL_EXT_MULAW",
+ "AL_EXT_MULAW_BFORMAT",
+ "AL_EXT_MULAW_MCFORMATS",
+ "AL_EXT_OFFSET",
+ "AL_EXT_source_distance_model",
+ "AL_EXT_SOURCE_RADIUS",
+ "AL_EXT_STATIC_BUFFER",
+ "AL_EXT_STEREO_ANGLES",
+ "AL_LOKI_quadriphonic",
+ "AL_SOFT_bformat_ex",
+ "AL_SOFTX_bformat_hoa",
+ "AL_SOFT_block_alignment",
+ "AL_SOFT_buffer_length_query",
+ "AL_SOFT_callback_buffer",
+ "AL_SOFTX_convolution_effect",
+ "AL_SOFT_deferred_updates",
+ "AL_SOFT_direct_channels",
+ "AL_SOFT_direct_channels_remix",
+ "AL_SOFT_effect_target",
+ "AL_SOFT_events",
+ "AL_SOFT_gain_clamp_ex",
+ "AL_SOFTX_hold_on_disconnect",
+ "AL_SOFT_loop_points",
+ "AL_SOFTX_map_buffer",
+ "AL_SOFT_MSADPCM",
+ "AL_SOFT_source_latency",
+ "AL_SOFT_source_length",
+ "AL_SOFT_source_resampler",
+ "AL_SOFT_source_spatialize",
+ "AL_SOFT_source_start_delay",
+ "AL_SOFT_UHJ",
+ "AL_SOFT_UHJ_ex",
+ };
+}
} // namespace
@@ -90,10 +102,9 @@ constexpr ALchar alExtList[] =
std::atomic<bool> ALCcontext::sGlobalContextLock{false};
std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
-thread_local ALCcontext *ALCcontext::sLocalContext{nullptr};
ALCcontext::ThreadCtx::~ThreadCtx()
{
- if(ALCcontext *ctx{ALCcontext::sLocalContext})
+ if(ALCcontext *ctx{std::exchange(ALCcontext::sLocalContext, nullptr)})
{
const bool result{ctx->releaseIfNoDelete()};
ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
@@ -105,23 +116,18 @@ thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
ALeffect ALCcontext::sDefaultEffect;
-#ifdef __MINGW32__
-ALCcontext *ALCcontext::getThreadContext() noexcept
-{ return sLocalContext; }
-void ALCcontext::setThreadContext(ALCcontext *context) noexcept
-{ sThreadContext.set(context); }
-#endif
-
-ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device)
- : ContextBase{device.get()}, mALDevice{std::move(device)}
+ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device, ContextFlagBitset flags)
+ : ContextBase{device.get()}, mALDevice{std::move(device)}, mContextFlags{flags}
{
+ mDebugGroups.emplace_back(DebugSource::Other, 0, std::string{});
+ mDebugEnabled.store(mContextFlags.test(ContextFlags::DebugBit), std::memory_order_relaxed);
}
ALCcontext::~ALCcontext()
{
TRACE("Freeing context %p\n", voidp{this});
- size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), size_t{0u},
+ size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), 0_uz,
[](size_t cur, const SourceSubList &sublist) noexcept -> size_t
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
if(count > 0)
@@ -134,7 +140,7 @@ ALCcontext::~ALCcontext()
#endif // ALSOFT_EAX
mDefaultSlot = nullptr;
- count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u},
+ count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), 0_uz,
[](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
if(count > 0)
@@ -170,26 +176,41 @@ void ALCcontext::init()
mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
}
- mExtensionList = alExtList;
+ mExtensions = getContextExtensions();
if(sBufferSubDataCompat)
{
- std::string extlist{mExtensionList};
-
- const auto pos = extlist.find("AL_EXT_SOURCE_RADIUS ");
- if(pos != std::string::npos)
- extlist.replace(pos, 20, "AL_SOFT_buffer_sub_data");
- else
- extlist += " AL_SOFT_buffer_sub_data";
-
- mExtensionListOverride = std::move(extlist);
- mExtensionList = mExtensionListOverride.c_str();
+ auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS");
+ if(iter != mExtensions.end()) mExtensions.erase(iter);
+ /* TODO: Would be nice to sort this alphabetically. Needs case-
+ * insensitive searching.
+ */
+ mExtensions.emplace_back("AL_SOFT_buffer_sub_data");
}
#ifdef ALSOFT_EAX
eax_initialize_extensions();
#endif // ALSOFT_EAX
+ if(!mExtensions.empty())
+ {
+ const size_t len{std::accumulate(mExtensions.cbegin()+1, mExtensions.cend(),
+ mExtensions.front().length(),
+ [](size_t current, std::string_view ext) noexcept
+ { return current + ext.length() + 1; })};
+
+ std::string extensions;
+ extensions.reserve(len);
+ extensions += mExtensions.front();
+ for(std::string_view ext : al::span{mExtensions}.subspan<1>())
+ {
+ extensions += ' ';
+ extensions += ext;
+ }
+
+ mExtensionsString = std::move(extensions);
+ }
+
mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f};
mParams.Matrix = alu::Matrix::Identity();
mParams.Velocity = alu::Vector{};
@@ -295,6 +316,7 @@ void ALCcontext::applyAllUpdates()
mHoldUpdates.store(false, std::memory_order_release);
}
+
#ifdef ALSOFT_EAX
namespace {
@@ -447,43 +469,15 @@ void ALCcontext::eax_initialize_extensions()
if(!eax_g_is_enabled)
return;
- const auto string_max_capacity =
- std::strlen(mExtensionList) + 1 +
- std::strlen(eax1_ext_name) + 1 +
- std::strlen(eax2_ext_name) + 1 +
- std::strlen(eax3_ext_name) + 1 +
- std::strlen(eax4_ext_name) + 1 +
- std::strlen(eax5_ext_name) + 1 +
- std::strlen(eax_x_ram_ext_name) + 1;
-
- std::string extlist;
- extlist.reserve(string_max_capacity);
-
+ mExtensions.emplace(mExtensions.begin(), eax_x_ram_ext_name);
if(eaxIsCapable())
{
- extlist += eax1_ext_name;
- extlist += ' ';
-
- extlist += eax2_ext_name;
- extlist += ' ';
-
- extlist += eax3_ext_name;
- extlist += ' ';
-
- extlist += eax4_ext_name;
- extlist += ' ';
-
- extlist += eax5_ext_name;
- extlist += ' ';
+ mExtensions.emplace(mExtensions.begin(), eax5_ext_name);
+ mExtensions.emplace(mExtensions.begin(), eax4_ext_name);
+ mExtensions.emplace(mExtensions.begin(), eax3_ext_name);
+ mExtensions.emplace(mExtensions.begin(), eax2_ext_name);
+ mExtensions.emplace(mExtensions.begin(), eax1_ext_name);
}
-
- extlist += eax_x_ram_ext_name;
- extlist += ' ';
-
- extlist += mExtensionList;
-
- mExtensionListOverride = std::move(extlist);
- mExtensionList = mExtensionListOverride.c_str();
}
void ALCcontext::eax_initialize()
@@ -1018,50 +1012,20 @@ void ALCcontext::eaxCommit()
eax_update_sources();
}
-namespace {
-class EaxSetException : public EaxException {
-public:
- explicit EaxSetException(const char* message)
- : EaxException{"EAX_SET", message}
- {}
-};
-
-[[noreturn]] void eax_fail_set(const char* message)
+FORCE_ALIGN ALenum AL_APIENTRY EAXSet(const GUID *a, ALuint b, ALuint c, ALvoid *d, ALuint e) noexcept
{
- throw EaxSetException{message};
-}
-
-class EaxGetException : public EaxException {
-public:
- explicit EaxGetException(const char* message)
- : EaxException{"EAX_GET", message}
- {}
-};
-
-[[noreturn]] void eax_fail_get(const char* message)
-{
- throw EaxGetException{message};
+ auto context = GetContextRef();
+ if(!context) UNLIKELY return AL_INVALID_OPERATION;
+ return EAXSetDirect(context.get(), a, b, c, d, e);
}
-} // namespace
-
-
-FORCE_ALIGN ALenum AL_APIENTRY EAXSet(
- const GUID* property_set_id,
- ALuint property_id,
- ALuint property_source_id,
- ALvoid* property_value,
+FORCE_ALIGN ALenum AL_APIENTRY EAXSetDirect(ALCcontext *context, const GUID *property_set_id,
+ ALuint property_id, ALuint property_source_id, ALvoid *property_value,
ALuint property_value_size) noexcept
try
{
- auto context = GetContextRef();
-
- if(!context)
- eax_fail_set("No current context.");
-
std::lock_guard<std::mutex> prop_lock{context->mPropLock};
-
return context->eax_eax_set(
property_set_id,
property_id,
@@ -1069,27 +1033,26 @@ try
property_value,
property_value_size);
}
-catch (...)
+catch(...)
{
eax_log_exception(__func__);
return AL_INVALID_OPERATION;
}
-FORCE_ALIGN ALenum AL_APIENTRY EAXGet(
- const GUID* property_set_id,
- ALuint property_id,
- ALuint property_source_id,
- ALvoid* property_value,
- ALuint property_value_size) noexcept
-try
+
+FORCE_ALIGN ALenum AL_APIENTRY EAXGet(const GUID *a, ALuint b, ALuint c, ALvoid *d, ALuint e) noexcept
{
auto context = GetContextRef();
+ if(!context) UNLIKELY return AL_INVALID_OPERATION;
+ return EAXGetDirect(context.get(), a, b, c, d, e);
+}
- if(!context)
- eax_fail_get("No current context.");
-
+FORCE_ALIGN ALenum AL_APIENTRY EAXGetDirect(ALCcontext *context, const GUID *property_set_id,
+ ALuint property_id, ALuint property_source_id, ALvoid *property_value,
+ ALuint property_value_size) noexcept
+try
+{
std::lock_guard<std::mutex> prop_lock{context->mPropLock};
-
return context->eax_eax_get(
property_set_id,
property_id,
@@ -1097,7 +1060,7 @@ try
property_value,
property_value_size);
}
-catch (...)
+catch(...)
{
eax_log_exception(__func__);
return AL_INVALID_OPERATION;
diff --git a/alc/context.h b/alc/context.h
index e8efdbf1..201c8873 100644
--- a/alc/context.h
+++ b/alc/context.h
@@ -2,10 +2,15 @@
#define ALC_CONTEXT_H
#include <atomic>
+#include <deque>
#include <memory>
#include <mutex>
#include <stdint.h>
+#include <string>
+#include <string_view>
+#include <unordered_map>
#include <utility>
+#include <vector>
#include "AL/al.h"
#include "AL/alc.h"
@@ -16,8 +21,8 @@
#include "alnumeric.h"
#include "atomic.h"
#include "core/context.h"
+#include "inprogext.h"
#include "intrusive_ptr.h"
-#include "vector.h"
#ifdef ALSOFT_EAX
#include "al/eax/call.h"
@@ -30,10 +35,39 @@
struct ALeffect;
struct ALeffectslot;
struct ALsource;
+struct DebugGroup;
+
+enum class DebugSource : uint8_t;
+enum class DebugType : uint8_t;
+enum class DebugSeverity : uint8_t;
using uint = unsigned int;
+enum ContextFlags {
+ DebugBit = 0, /* ALC_CONTEXT_DEBUG_BIT_EXT */
+};
+using ContextFlagBitset = std::bitset<sizeof(ALuint)*8>;
+
+
+struct DebugLogEntry {
+ const DebugSource mSource;
+ const DebugType mType;
+ const DebugSeverity mSeverity;
+ const uint mId;
+
+ std::string mMessage;
+
+ template<typename T>
+ DebugLogEntry(DebugSource source, DebugType type, uint id, DebugSeverity severity, T&& message)
+ : mSource{source}, mType{type}, mSeverity{severity}, mId{id}
+ , mMessage{std::forward<T>(message)}
+ { }
+ DebugLogEntry(const DebugLogEntry&) = default;
+ DebugLogEntry(DebugLogEntry&&) = default;
+};
+
+
struct SourceSubList {
uint64_t FreeMask{~0_u64};
ALsource *Sources{nullptr}; /* 64 */
@@ -76,6 +110,9 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
std::atomic<ALenum> mLastError{AL_NO_ERROR};
+ const ContextFlagBitset mContextFlags;
+ std::atomic<bool> mDebugEnabled{false};
+
DistanceModel mDistanceModel{DistanceModel::Default};
bool mSourceDistanceModel{false};
@@ -88,25 +125,32 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
ALEVENTPROCSOFT mEventCb{};
void *mEventParam{nullptr};
+ std::mutex mDebugCbLock;
+ ALDEBUGPROCEXT mDebugCb{};
+ void *mDebugParam{nullptr};
+ std::vector<DebugGroup> mDebugGroups;
+ std::deque<DebugLogEntry> mDebugLog;
+
ALlistener mListener{};
- al::vector<SourceSubList> mSourceList;
+ std::vector<SourceSubList> mSourceList;
ALuint mNumSources{0};
std::mutex mSourceLock;
- al::vector<EffectSlotSubList> mEffectSlotList;
+ std::vector<EffectSlotSubList> mEffectSlotList;
ALuint mNumEffectSlots{0u};
std::mutex mEffectSlotLock;
/* Default effect slot */
std::unique_ptr<ALeffectslot> mDefaultSlot;
- const char *mExtensionList{nullptr};
-
- std::string mExtensionListOverride{};
+ std::vector<std::string_view> mExtensions;
+ std::string mExtensionsString{};
+ std::unordered_map<ALuint,std::string> mSourceNames;
+ std::unordered_map<ALuint,std::string> mEffectSlotNames;
- ALCcontext(al::intrusive_ptr<ALCdevice> device);
+ ALCcontext(al::intrusive_ptr<ALCdevice> device, ContextFlagBitset flags);
ALCcontext(const ALCcontext&) = delete;
ALCcontext& operator=(const ALCcontext&) = delete;
~ALCcontext();
@@ -149,13 +193,25 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
#endif
void setError(ALenum errorCode, const char *msg, ...);
+ void sendDebugMessage(std::unique_lock<std::mutex> &debuglock, DebugSource source,
+ DebugType type, ALuint id, DebugSeverity severity, std::string_view message);
+
+ void debugMessage(DebugSource source, DebugType type, ALuint id, DebugSeverity severity,
+ std::string_view message)
+ {
+ if(!mDebugEnabled.load(std::memory_order_relaxed)) LIKELY
+ return;
+ std::unique_lock<std::mutex> debuglock{mDebugCbLock};
+ sendDebugMessage(debuglock, source, type, id, severity, message);
+ }
+
/* Process-wide current context */
static std::atomic<bool> sGlobalContextLock;
static std::atomic<ALCcontext*> sGlobalContext;
private:
/* Thread-local current context. */
- static thread_local ALCcontext *sLocalContext;
+ static inline thread_local ALCcontext *sLocalContext{};
/* Thread-local context handling. This handles attempting to release the
* context which may have been left current when the thread is destroyed.
@@ -168,17 +224,8 @@ private:
static thread_local ThreadCtx sThreadContext;
public:
- /* HACK: MinGW generates bad code when accessing an extern thread_local
- * object. Add a wrapper function for it that only accesses it where it's
- * defined.
- */
-#ifdef __MINGW32__
- static ALCcontext *getThreadContext() noexcept;
- static void setThreadContext(ALCcontext *context) noexcept;
-#else
static ALCcontext *getThreadContext() noexcept { return sLocalContext; }
static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); }
-#endif
/* Default effect that applies to sources that don't have an effect on send 0. */
static ALeffect sDefaultEffect;
diff --git a/alc/device.cpp b/alc/device.cpp
index 66b13c5e..27aa6f36 100644
--- a/alc/device.cpp
+++ b/alc/device.cpp
@@ -34,19 +34,19 @@ ALCdevice::~ALCdevice()
Backend = nullptr;
- size_t count{std::accumulate(BufferList.cbegin(), BufferList.cend(), size_t{0u},
+ size_t count{std::accumulate(BufferList.cbegin(), BufferList.cend(), 0_uz,
[](size_t cur, const BufferSubList &sublist) noexcept -> size_t
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
if(count > 0)
WARN("%zu Buffer%s not deleted\n", count, (count==1)?"":"s");
- count = std::accumulate(EffectList.cbegin(), EffectList.cend(), size_t{0u},
+ count = std::accumulate(EffectList.cbegin(), EffectList.cend(), 0_uz,
[](size_t cur, const EffectSubList &sublist) noexcept -> size_t
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
if(count > 0)
WARN("%zu Effect%s not deleted\n", count, (count==1)?"":"s");
- count = std::accumulate(FilterList.cbegin(), FilterList.cend(), size_t{0u},
+ count = std::accumulate(FilterList.cbegin(), FilterList.cend(), 0_uz,
[](size_t cur, const FilterSubList &sublist) noexcept -> size_t
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
if(count > 0)
diff --git a/alc/device.h b/alc/device.h
index ef50f53e..66f37a7e 100644
--- a/alc/device.h
+++ b/alc/device.h
@@ -4,9 +4,12 @@
#include <atomic>
#include <memory>
#include <mutex>
+#include <optional>
#include <stdint.h>
#include <string>
+#include <unordered_map>
#include <utility>
+#include <vector>
#include "AL/alc.h"
#include "AL/alext.h"
@@ -17,7 +20,6 @@
#include "core/device.h"
#include "inprogext.h"
#include "intrusive_ptr.h"
-#include "vector.h"
#ifdef ALSOFT_EAX
#include "al/eax/x_ram.h"
@@ -94,7 +96,7 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase {
uint AuxiliaryEffectSlotMax{};
std::string mHrtfName;
- al::vector<std::string> mHrtfList;
+ std::vector<std::string> mHrtfList;
ALCenum mHrtfStatus{ALC_FALSE};
enum class OutputMode1 : ALCenum {
@@ -117,21 +119,25 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase {
// Map of Buffers for this device
std::mutex BufferLock;
- al::vector<BufferSubList> BufferList;
+ std::vector<BufferSubList> BufferList;
// Map of Effects for this device
std::mutex EffectLock;
- al::vector<EffectSubList> EffectList;
+ std::vector<EffectSubList> EffectList;
// Map of Filters for this device
std::mutex FilterLock;
- al::vector<FilterSubList> FilterList;
+ std::vector<FilterSubList> FilterList;
#ifdef ALSOFT_EAX
ALuint eax_x_ram_free_size{eax_x_ram_max_size};
#endif // ALSOFT_EAX
+ std::unordered_map<ALuint,std::string> mBufferNames;
+ std::unordered_map<ALuint,std::string> mEffectNames;
+ std::unordered_map<ALuint,std::string> mFilterNames;
+
ALCdevice(DeviceType type);
~ALCdevice();
@@ -141,25 +147,28 @@ struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase {
{ return GetConfigValueBool(DeviceName.c_str(), block, key, def); }
template<typename T>
- inline al::optional<T> configValue(const char *block, const char *key) = delete;
+ inline std::optional<T> configValue(const char *block, const char *key) = delete;
DEF_NEWDEL(ALCdevice)
};
template<>
-inline al::optional<std::string> ALCdevice::configValue(const char *block, const char *key)
+inline std::optional<std::string> ALCdevice::configValue(const char *block, const char *key)
{ return ConfigValueStr(DeviceName.c_str(), block, key); }
template<>
-inline al::optional<int> ALCdevice::configValue(const char *block, const char *key)
+inline std::optional<int> ALCdevice::configValue(const char *block, const char *key)
{ return ConfigValueInt(DeviceName.c_str(), block, key); }
template<>
-inline al::optional<uint> ALCdevice::configValue(const char *block, const char *key)
+inline std::optional<uint> ALCdevice::configValue(const char *block, const char *key)
{ return ConfigValueUInt(DeviceName.c_str(), block, key); }
template<>
-inline al::optional<float> ALCdevice::configValue(const char *block, const char *key)
+inline std::optional<float> ALCdevice::configValue(const char *block, const char *key)
{ return ConfigValueFloat(DeviceName.c_str(), block, key); }
template<>
-inline al::optional<bool> ALCdevice::configValue(const char *block, const char *key)
+inline std::optional<bool> ALCdevice::configValue(const char *block, const char *key)
{ return ConfigValueBool(DeviceName.c_str(), block, key); }
+/** Stores the latest ALC device error. */
+void alcSetError(ALCdevice *device, ALCenum errorCode);
+
#endif
diff --git a/alc/effects/chorus.cpp b/alc/effects/chorus.cpp
index 10ccf9f6..9cbc922f 100644
--- a/alc/effects/chorus.cpp
+++ b/alc/effects/chorus.cpp
@@ -25,6 +25,7 @@
#include <climits>
#include <cstdlib>
#include <iterator>
+#include <vector>
#include "alc/effects/base.h"
#include "almalloc.h"
@@ -41,7 +42,6 @@
#include "core/resampler_limits.h"
#include "intrusive_ptr.h"
#include "opthelpers.h"
-#include "vector.h"
namespace {
@@ -49,7 +49,7 @@ namespace {
using uint = unsigned int;
struct ChorusState final : public EffectState {
- al::vector<float,16> mDelayBuffer;
+ std::vector<float> mDelayBuffer;
uint mOffset{0};
uint mLfoOffset{0};
@@ -125,16 +125,16 @@ void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot,
/* Gains for left and right sides */
static constexpr auto inv_sqrt2 = static_cast<float>(1.0 / al::numbers::sqrt2);
- static constexpr auto lcoeffs_pw = CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f});
- static constexpr auto rcoeffs_pw = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f});
- static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs({-inv_sqrt2, 0.0f, inv_sqrt2});
- static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs({ inv_sqrt2, 0.0f, inv_sqrt2});
+ static constexpr auto lcoeffs_pw = CalcDirectionCoeffs(std::array{-1.0f, 0.0f, 0.0f});
+ static constexpr auto rcoeffs_pw = CalcDirectionCoeffs(std::array{ 1.0f, 0.0f, 0.0f});
+ static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs(std::array{-inv_sqrt2, 0.0f, inv_sqrt2});
+ static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs(std::array{ inv_sqrt2, 0.0f, inv_sqrt2});
auto &lcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? lcoeffs_nrml : lcoeffs_pw;
auto &rcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? rcoeffs_nrml : rcoeffs_pw;
mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, lcoeffs.data(), Slot->Gain, mGains[0].Target);
- ComputePanGains(target.Main, rcoeffs.data(), Slot->Gain, mGains[1].Target);
+ ComputePanGains(target.Main, lcoeffs, Slot->Gain, mGains[0].Target);
+ ComputePanGains(target.Main, rcoeffs, Slot->Gain, mGains[1].Target);
float rate{props->Chorus.Rate};
if(!(rate > 0.0f))
diff --git a/alc/effects/convolution.cpp b/alc/effects/convolution.cpp
index 7f36c415..517e6b08 100644
--- a/alc/effects/convolution.cpp
+++ b/alc/effects/convolution.cpp
@@ -17,7 +17,6 @@
#include <arm_neon.h>
#endif
-#include "albyte.h"
#include "alcomplex.h"
#include "almalloc.h"
#include "alnumbers.h"
@@ -35,44 +34,49 @@
#include "core/fmt_traits.h"
#include "core/mixer.h"
#include "intrusive_ptr.h"
+#include "pffft.h"
#include "polyphase_resampler.h"
#include "vector.h"
namespace {
-/* Convolution reverb is implemented using a segmented overlap-add method. The
- * impulse response is broken up into multiple segments of 128 samples, and
- * each segment has an FFT applied with a 256-sample buffer (the latter half
- * left silent) to get its frequency-domain response. The resulting response
- * has its positive/non-mirrored frequencies saved (129 bins) in each segment.
+/* Convolution is implemented using a segmented overlap-add method. The impulse
+ * response is split into multiple segments of 128 samples, and each segment
+ * has an FFT applied with a 256-sample buffer (the latter half left silent) to
+ * get its frequency-domain response. The resulting response has its positive/
+ * non-mirrored frequencies saved (129 bins) in each segment. Note that since
+ * the 0- and half-frequency bins are real for a real signal, their imaginary
+ * components are always 0 and can be dropped, allowing their real components
+ * to be combined so only 128 complex values are stored for the 129 bins.
*
- * Input samples are similarly broken up into 128-sample segments, with an FFT
- * applied to each new incoming segment to get its 129 bins. A history of FFT'd
- * input segments is maintained, equal to the length of the impulse response.
+ * Input samples are similarly broken up into 128-sample segments, with a 256-
+ * sample FFT applied to each new incoming segment to get its 129 bins. A
+ * history of FFT'd input segments is maintained, equal to the number of
+ * impulse response segments.
*
- * To apply the reverberation, each impulse response segment is convolved with
+ * To apply the convolution, each impulse response segment is convolved with
* its paired input segment (using complex multiplies, far cheaper than FIRs),
- * accumulating into a 256-bin FFT buffer. The input history is then shifted to
- * align with later impulse response segments for next time.
+ * accumulating into a 129-bin FFT buffer. The input history is then shifted to
+ * align with later impulse response segments for the next input segment.
*
* An inverse FFT is then applied to the accumulated FFT buffer to get a 256-
* sample time-domain response for output, which is split in two halves. The
* first half is the 128-sample output, and the second half is a 128-sample
* (really, 127) delayed extension, which gets added to the output next time.
- * Convolving two time-domain responses of lengths N and M results in a time-
- * domain signal of length N+M-1, and this holds true regardless of the
- * convolution being applied in the frequency domain, so these "overflow"
- * samples need to be accounted for.
+ * Convolving two time-domain responses of length N results in a time-domain
+ * signal of length N*2 - 1, and this holds true regardless of the convolution
+ * being applied in the frequency domain, so these "overflow" samples need to
+ * be accounted for.
*
- * To avoid a delay with gathering enough input samples to apply an FFT with,
- * the first segment is applied directly in the time-domain as the samples come
- * in. Once enough have been retrieved, the FFT is applied on the input and
- * it's paired with the remaining (FFT'd) filter segments for processing.
+ * To avoid a delay with gathering enough input samples for the FFT, the first
+ * segment is applied directly in the time-domain as the samples come in. Once
+ * enough have been retrieved, the FFT is applied on the input and it's paired
+ * with the remaining (FFT'd) filter segments for processing.
*/
-void LoadSamples(float *RESTRICT dst, const al::byte *src, const size_t srcstep, FmtType srctype,
+void LoadSamples(float *RESTRICT dst, const std::byte *src, const size_t srcstep, FmtType srctype,
const size_t samples) noexcept
{
#define HANDLE_FMT(T) case T: al::LoadSampleArray<T>(dst, src, srcstep, samples); break
@@ -80,6 +84,7 @@ void LoadSamples(float *RESTRICT dst, const al::byte *src, const size_t srcstep,
{
HANDLE_FMT(FmtUByte);
HANDLE_FMT(FmtShort);
+ HANDLE_FMT(FmtInt);
HANDLE_FMT(FmtFloat);
HANDLE_FMT(FmtDouble);
HANDLE_FMT(FmtMulaw);
@@ -94,40 +99,43 @@ void LoadSamples(float *RESTRICT dst, const al::byte *src, const size_t srcstep,
}
-inline auto& GetAmbiScales(AmbiScaling scaletype) noexcept
+constexpr auto GetAmbiScales(AmbiScaling scaletype) noexcept
{
switch(scaletype)
{
- case AmbiScaling::FuMa: return AmbiScale::FromFuMa();
- case AmbiScaling::SN3D: return AmbiScale::FromSN3D();
- case AmbiScaling::UHJ: return AmbiScale::FromUHJ();
+ case AmbiScaling::FuMa: return al::span{AmbiScale::FromFuMa};
+ case AmbiScaling::SN3D: return al::span{AmbiScale::FromSN3D};
+ case AmbiScaling::UHJ: return al::span{AmbiScale::FromUHJ};
case AmbiScaling::N3D: break;
}
- return AmbiScale::FromN3D();
+ return al::span{AmbiScale::FromN3D};
}
-inline auto& GetAmbiLayout(AmbiLayout layouttype) noexcept
+constexpr auto GetAmbiLayout(AmbiLayout layouttype) noexcept
{
- if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa();
- return AmbiIndex::FromACN();
+ if(layouttype == AmbiLayout::FuMa) return al::span{AmbiIndex::FromFuMa};
+ return al::span{AmbiIndex::FromACN};
}
-inline auto& GetAmbi2DLayout(AmbiLayout layouttype) noexcept
+constexpr auto GetAmbi2DLayout(AmbiLayout layouttype) noexcept
{
- if(layouttype == AmbiLayout::FuMa) return AmbiIndex::FromFuMa2D();
- return AmbiIndex::FromACN2D();
+ if(layouttype == AmbiLayout::FuMa) return al::span{AmbiIndex::FromFuMa2D};
+ return al::span{AmbiIndex::FromACN2D};
}
-struct ChanMap {
+constexpr float sin30{0.5f};
+constexpr float cos30{0.866025403785f};
+constexpr float sin45{al::numbers::sqrt2_v<float>*0.5f};
+constexpr float cos45{al::numbers::sqrt2_v<float>*0.5f};
+constexpr float sin110{ 0.939692620786f};
+constexpr float cos110{-0.342020143326f};
+
+struct ChanPosMap {
Channel channel;
- float angle;
- float elevation;
+ std::array<float,3> pos;
};
-constexpr float Deg2Rad(float x) noexcept
-{ return static_cast<float>(al::numbers::pi / 180.0 * x); }
-
using complex_f = std::complex<float>;
@@ -181,6 +189,13 @@ void apply_fir(al::span<float> dst, const float *RESTRICT src, const float *REST
#endif
}
+
+struct PFFFTSetupDeleter {
+ void operator()(PFFFT_Setup *ptr) { pffft_destroy_setup(ptr); }
+};
+using PFFFTSetupPtr = std::unique_ptr<PFFFT_Setup,PFFFTSetupDeleter>;
+
+
struct ConvolutionState final : public EffectState {
FmtChannels mChannels{};
AmbiLayout mAmbiLayout{};
@@ -188,11 +203,13 @@ struct ConvolutionState final : public EffectState {
uint mAmbiOrder{};
size_t mFifoPos{0};
- std::array<float,ConvolveUpdateSamples*2> mInput{};
+ alignas(16) std::array<float,ConvolveUpdateSamples*2> mInput{};
al::vector<std::array<float,ConvolveUpdateSamples>,16> mFilter;
al::vector<std::array<float,ConvolveUpdateSamples*2>,16> mOutput;
- alignas(16) std::array<complex_f,ConvolveUpdateSize> mFftBuffer{};
+ PFFFTSetupPtr mFft{};
+ alignas(16) std::array<float,ConvolveUpdateSize> mFftBuffer{};
+ alignas(16) std::array<float,ConvolveUpdateSize> mFftWorkBuffer{};
size_t mCurrentSegment{0};
size_t mNumConvolveSegs{0};
@@ -204,9 +221,8 @@ struct ConvolutionState final : public EffectState {
float Current[MAX_OUTPUT_CHANNELS]{};
float Target[MAX_OUTPUT_CHANNELS]{};
};
- using ChannelDataArray = al::FlexArray<ChannelData>;
- std::unique_ptr<ChannelDataArray> mChans;
- std::unique_ptr<complex_f[]> mComplexData;
+ std::vector<ChannelData> mChans;
+ al::vector<float,16> mComplexData;
ConvolutionState() = default;
@@ -229,7 +245,7 @@ struct ConvolutionState final : public EffectState {
void ConvolutionState::NormalMix(const al::span<FloatBufferLine> samplesOut,
const size_t samplesToDo)
{
- for(auto &chan : *mChans)
+ for(auto &chan : mChans)
MixSamples({chan.mBuffer.data(), samplesToDo}, samplesOut, chan.Current, chan.Target,
samplesToDo, 0);
}
@@ -237,7 +253,7 @@ void ConvolutionState::NormalMix(const al::span<FloatBufferLine> samplesOut,
void ConvolutionState::UpsampleMix(const al::span<FloatBufferLine> samplesOut,
const size_t samplesToDo)
{
- for(auto &chan : *mChans)
+ for(auto &chan : mChans)
{
const al::span<float> src{chan.mBuffer.data(), samplesToDo};
chan.mFilter.processScale(src, chan.mHfScale, chan.mLfScale);
@@ -251,19 +267,23 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag
using UhjDecoderType = UhjDecoder<512>;
static constexpr auto DecoderPadding = UhjDecoderType::sInputPadding;
- constexpr uint MaxConvolveAmbiOrder{1u};
+ static constexpr uint MaxConvolveAmbiOrder{1u};
+
+ if(!mFft)
+ mFft = PFFFTSetupPtr{pffft_new_setup(ConvolveUpdateSize, PFFFT_REAL)};
mFifoPos = 0;
mInput.fill(0.0f);
decltype(mFilter){}.swap(mFilter);
decltype(mOutput){}.swap(mOutput);
- mFftBuffer.fill(complex_f{});
+ mFftBuffer.fill(0.0f);
+ mFftWorkBuffer.fill(0.0f);
mCurrentSegment = 0;
mNumConvolveSegs = 0;
- mChans = nullptr;
- mComplexData = nullptr;
+ decltype(mChans){}.swap(mChans);
+ decltype(mComplexData){}.swap(mComplexData);
/* An empty buffer doesn't need a convolution filter. */
if(!buffer || buffer->mSampleLen < 1) return;
@@ -273,12 +293,11 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag
mAmbiScaling = IsUHJ(mChannels) ? AmbiScaling::UHJ : buffer->mAmbiScaling;
mAmbiOrder = minu(buffer->mAmbiOrder, MaxConvolveAmbiOrder);
- constexpr size_t m{ConvolveUpdateSize/2 + 1};
const auto bytesPerSample = BytesFromFmt(buffer->mType);
const auto realChannels = buffer->channelsFromFmt();
const auto numChannels = (mChannels == FmtUHJ2) ? 3u : ChannelsFromFmt(mChannels, mAmbiOrder);
- mChans = ChannelDataArray::Create(numChannels);
+ mChans.resize(numChannels);
/* The impulse response needs to have the same sample rate as the input and
* output. The bsinc24 resampler is decent, but there is high-frequency
@@ -293,7 +312,7 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag
buffer->mSampleRate);
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)};
- for(auto &e : *mChans)
+ for(auto &e : mChans)
e.mFilter = splitter;
mFilter.resize(numChannels, {});
@@ -307,9 +326,8 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag
mNumConvolveSegs = (resampledCount+(ConvolveUpdateSamples-1)) / ConvolveUpdateSamples;
mNumConvolveSegs = maxz(mNumConvolveSegs, 2) - 1;
- const size_t complex_length{mNumConvolveSegs * m * (numChannels+1)};
- mComplexData = std::make_unique<complex_f[]>(complex_length);
- std::fill_n(mComplexData.get(), complex_length, complex_f{});
+ const size_t complex_length{mNumConvolveSegs * ConvolveUpdateSize * (numChannels+1)};
+ mComplexData.resize(complex_length, 0.0f);
/* Load the samples from the buffer. */
const size_t srclinelength{RoundUp(buffer->mSampleLen+DecoderPadding, 16)};
@@ -330,7 +348,10 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag
auto ressamples = std::make_unique<double[]>(buffer->mSampleLen +
(resampler ? resampledCount : 0));
- complex_f *filteriter = mComplexData.get() + mNumConvolveSegs*m;
+ auto ffttmp = al::vector<float,16>(ConvolveUpdateSize);
+ auto fftbuffer = std::vector<std::complex<double>>(ConvolveUpdateSize);
+
+ float *filteriter = mComplexData.data() + mNumConvolveSegs*ConvolveUpdateSize;
for(size_t c{0};c < numChannels;++c)
{
/* Resample to match the device. */
@@ -351,71 +372,85 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag
std::transform(ressamples.get(), ressamples.get()+first_size, mFilter[c].rbegin(),
[](const double d) noexcept -> float { return static_cast<float>(d); });
- auto fftbuffer = std::vector<std::complex<double>>(ConvolveUpdateSize);
size_t done{first_size};
for(size_t s{0};s < mNumConvolveSegs;++s)
{
const size_t todo{minz(resampledCount-done, ConvolveUpdateSamples)};
+ /* Apply a double-precision forward FFT for more precise frequency
+ * measurements.
+ */
auto iter = std::copy_n(&ressamples[done], todo, fftbuffer.begin());
done += todo;
std::fill(iter, fftbuffer.end(), std::complex<double>{});
+ forward_fft(al::span{fftbuffer});
- forward_fft(al::as_span(fftbuffer));
- filteriter = std::copy_n(fftbuffer.cbegin(), m, filteriter);
+ /* Convert to, and pack in, a float buffer for PFFFT. Note that the
+ * first bin stores the real component of the half-frequency bin in
+ * the imaginary component. Also scale the FFT by its length so the
+ * iFFT'd output will be normalized.
+ */
+ static constexpr float fftscale{1.0f / float{ConvolveUpdateSize}};
+ for(size_t i{0};i < ConvolveUpdateSamples;++i)
+ {
+ ffttmp[i*2 ] = static_cast<float>(fftbuffer[i].real()) * fftscale;
+ ffttmp[i*2 + 1] = static_cast<float>((i == 0) ?
+ fftbuffer[ConvolveUpdateSamples].real() : fftbuffer[i].imag()) * fftscale;
+ }
+ /* Reorder backward to make it suitable for pffft_zconvolve and the
+ * subsequent pffft_transform(..., PFFFT_BACKWARD).
+ */
+ pffft_zreorder(mFft.get(), ffttmp.data(), al::to_address(filteriter), PFFFT_BACKWARD);
+ filteriter += ConvolveUpdateSize;
}
}
}
void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot,
- const EffectProps* /*props*/, const EffectTarget target)
+ const EffectProps *props, const EffectTarget target)
{
- /* NOTE: Stereo and Rear are slightly different from normal mixing (as
- * defined in alu.cpp). These are 45 degrees from center, rather than the
- * 30 degrees used there.
- *
- * TODO: LFE is not mixed to output. This will require each buffer channel
+ /* TODO: LFE is not mixed to output. This will require each buffer channel
* to have its own output target since the main mixing buffer won't have an
* LFE channel (due to being B-Format).
*/
- static constexpr ChanMap MonoMap[1]{
- { FrontCenter, 0.0f, 0.0f }
+ static constexpr ChanPosMap MonoMap[1]{
+ { FrontCenter, std::array{0.0f, 0.0f, -1.0f} }
}, StereoMap[2]{
- { FrontLeft, Deg2Rad(-45.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 45.0f), Deg2Rad(0.0f) }
+ { FrontLeft, std::array{-sin30, 0.0f, -cos30} },
+ { FrontRight, std::array{ sin30, 0.0f, -cos30} },
}, RearMap[2]{
- { BackLeft, Deg2Rad(-135.0f), Deg2Rad(0.0f) },
- { BackRight, Deg2Rad( 135.0f), Deg2Rad(0.0f) }
+ { BackLeft, std::array{-sin30, 0.0f, cos30} },
+ { BackRight, std::array{ sin30, 0.0f, cos30} },
}, QuadMap[4]{
- { FrontLeft, Deg2Rad( -45.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 45.0f), Deg2Rad(0.0f) },
- { BackLeft, Deg2Rad(-135.0f), Deg2Rad(0.0f) },
- { BackRight, Deg2Rad( 135.0f), Deg2Rad(0.0f) }
+ { FrontLeft, std::array{-sin45, 0.0f, -cos45} },
+ { FrontRight, std::array{ sin45, 0.0f, -cos45} },
+ { BackLeft, std::array{-sin45, 0.0f, cos45} },
+ { BackRight, std::array{ sin45, 0.0f, cos45} },
}, X51Map[6]{
- { FrontLeft, Deg2Rad( -30.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) },
- { FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) },
- { LFE, 0.0f, 0.0f },
- { SideLeft, Deg2Rad(-110.0f), Deg2Rad(0.0f) },
- { SideRight, Deg2Rad( 110.0f), Deg2Rad(0.0f) }
+ { FrontLeft, std::array{-sin30, 0.0f, -cos30} },
+ { FrontRight, std::array{ sin30, 0.0f, -cos30} },
+ { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} },
+ { LFE, {} },
+ { SideLeft, std::array{-sin110, 0.0f, -cos110} },
+ { SideRight, std::array{ sin110, 0.0f, -cos110} },
}, X61Map[7]{
- { FrontLeft, Deg2Rad(-30.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) },
- { FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) },
- { LFE, 0.0f, 0.0f },
- { BackCenter, Deg2Rad(180.0f), Deg2Rad(0.0f) },
- { SideLeft, Deg2Rad(-90.0f), Deg2Rad(0.0f) },
- { SideRight, Deg2Rad( 90.0f), Deg2Rad(0.0f) }
+ { FrontLeft, std::array{-sin30, 0.0f, -cos30} },
+ { FrontRight, std::array{ sin30, 0.0f, -cos30} },
+ { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} },
+ { LFE, {} },
+ { BackCenter, std::array{ 0.0f, 0.0f, 1.0f} },
+ { SideLeft, std::array{-1.0f, 0.0f, 0.0f} },
+ { SideRight, std::array{ 1.0f, 0.0f, 0.0f} },
}, X71Map[8]{
- { FrontLeft, Deg2Rad( -30.0f), Deg2Rad(0.0f) },
- { FrontRight, Deg2Rad( 30.0f), Deg2Rad(0.0f) },
- { FrontCenter, Deg2Rad( 0.0f), Deg2Rad(0.0f) },
- { LFE, 0.0f, 0.0f },
- { BackLeft, Deg2Rad(-150.0f), Deg2Rad(0.0f) },
- { BackRight, Deg2Rad( 150.0f), Deg2Rad(0.0f) },
- { SideLeft, Deg2Rad( -90.0f), Deg2Rad(0.0f) },
- { SideRight, Deg2Rad( 90.0f), Deg2Rad(0.0f) }
+ { FrontLeft, std::array{-sin30, 0.0f, -cos30} },
+ { FrontRight, std::array{ sin30, 0.0f, -cos30} },
+ { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} },
+ { LFE, {} },
+ { BackLeft, std::array{-sin30, 0.0f, cos30} },
+ { BackRight, std::array{ sin30, 0.0f, cos30} },
+ { SideLeft, std::array{ -1.0f, 0.0f, 0.0f} },
+ { SideRight, std::array{ 1.0f, 0.0f, 0.0f} },
};
if(mNumConvolveSegs < 1) UNLIKELY
@@ -423,7 +458,7 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot
mMix = &ConvolutionState::NormalMix;
- for(auto &chan : *mChans)
+ for(auto &chan : mChans)
std::fill(std::begin(chan.Target), std::end(chan.Target), 0.0f);
const float gain{slot->Gain};
if(IsAmbisonic(mChannels))
@@ -432,46 +467,66 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot
if(mChannels == FmtUHJ2 && !device->mUhjEncoder)
{
mMix = &ConvolutionState::UpsampleMix;
- (*mChans)[0].mHfScale = 1.0f;
- (*mChans)[0].mLfScale = DecoderBase::sWLFScale;
- (*mChans)[1].mHfScale = 1.0f;
- (*mChans)[1].mLfScale = DecoderBase::sXYLFScale;
- (*mChans)[2].mHfScale = 1.0f;
- (*mChans)[2].mLfScale = DecoderBase::sXYLFScale;
+ mChans[0].mHfScale = 1.0f;
+ mChans[0].mLfScale = DecoderBase::sWLFScale;
+ mChans[1].mHfScale = 1.0f;
+ mChans[1].mLfScale = DecoderBase::sXYLFScale;
+ mChans[2].mHfScale = 1.0f;
+ mChans[2].mLfScale = DecoderBase::sXYLFScale;
}
else if(device->mAmbiOrder > mAmbiOrder)
{
mMix = &ConvolutionState::UpsampleMix;
const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder,
device->m2DMixing);
- (*mChans)[0].mHfScale = scales[0];
- (*mChans)[0].mLfScale = 1.0f;
- for(size_t i{1};i < mChans->size();++i)
+ mChans[0].mHfScale = scales[0];
+ mChans[0].mLfScale = 1.0f;
+ for(size_t i{1};i < mChans.size();++i)
{
- (*mChans)[i].mHfScale = scales[1];
- (*mChans)[i].mLfScale = 1.0f;
+ mChans[i].mHfScale = scales[1];
+ mChans[i].mLfScale = 1.0f;
}
}
mOutTarget = target.Main->Buffer;
- auto&& scales = GetAmbiScales(mAmbiScaling);
+ alu::Vector N{props->Convolution.OrientAt[0], props->Convolution.OrientAt[1],
+ props->Convolution.OrientAt[2], 0.0f};
+ N.normalize();
+ alu::Vector V{props->Convolution.OrientUp[0], props->Convolution.OrientUp[1],
+ props->Convolution.OrientUp[2], 0.0f};
+ V.normalize();
+ /* Build and normalize right-vector */
+ alu::Vector U{N.cross_product(V)};
+ U.normalize();
+
+ const float mixmatrix[4][4]{
+ {1.0f, 0.0f, 0.0f, 0.0f},
+ {0.0f, U[0], -U[1], U[2]},
+ {0.0f, -V[0], V[1], -V[2]},
+ {0.0f, -N[0], N[1], -N[2]},
+ };
+
+ const auto scales = GetAmbiScales(mAmbiScaling);
const uint8_t *index_map{Is2DAmbisonic(mChannels) ?
GetAmbi2DLayout(mAmbiLayout).data() :
GetAmbiLayout(mAmbiLayout).data()};
std::array<float,MaxAmbiChannels> coeffs{};
- for(size_t c{0u};c < mChans->size();++c)
+ for(size_t c{0u};c < mChans.size();++c)
{
const size_t acn{index_map[c]};
- coeffs[acn] = scales[acn];
- ComputePanGains(target.Main, coeffs.data(), gain, (*mChans)[c].Target);
- coeffs[acn] = 0.0f;
+ const float scale{scales[acn]};
+
+ for(size_t x{0};x < 4;++x)
+ coeffs[x] = mixmatrix[acn][x] * scale;
+
+ ComputePanGains(target.Main, coeffs, gain, mChans[c].Target);
}
}
else
{
DeviceBase *device{context->mDevice};
- al::span<const ChanMap> chanmap{};
+ al::span<const ChanPosMap> chanmap{};
switch(mChannels)
{
case FmtMono: chanmap = MonoMap; break;
@@ -493,28 +548,55 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot
mOutTarget = target.Main->Buffer;
if(device->mRenderMode == RenderMode::Pairwise)
{
- auto ScaleAzimuthFront = [](float azimuth, float scale) -> float
+ /* Scales the azimuth of the given vector by 3 if it's in front.
+ * Effectively scales +/-30 degrees to +/-90 degrees, leaving > +90
+ * and < -90 alone.
+ */
+ auto ScaleAzimuthFront = [](std::array<float,3> pos) -> std::array<float,3>
{
- constexpr float half_pi{al::numbers::pi_v<float>*0.5f};
- const float abs_azi{std::fabs(azimuth)};
- if(!(abs_azi >= half_pi))
- return std::copysign(minf(abs_azi*scale, half_pi), azimuth);
- return azimuth;
+ if(pos[2] < 0.0f)
+ {
+ /* Normalize the length of the x,z components for a 2D
+ * vector of the azimuth angle. Negate Z since {0,0,-1} is
+ * angle 0.
+ */
+ const float len2d{std::sqrt(pos[0]*pos[0] + pos[2]*pos[2])};
+ float x{pos[0] / len2d};
+ float z{-pos[2] / len2d};
+
+ /* Z > cos(pi/6) = -30 < azimuth < 30 degrees. */
+ if(z > cos30)
+ {
+ /* Triple the angle represented by x,z. */
+ x = x*3.0f - x*x*x*4.0f;
+ z = z*z*z*4.0f - z*3.0f;
+
+ /* Scale the vector back to fit in 3D. */
+ pos[0] = x * len2d;
+ pos[2] = -z * len2d;
+ }
+ else
+ {
+ /* If azimuth >= 30 degrees, clamp to 90 degrees. */
+ pos[0] = std::copysign(len2d, pos[0]);
+ pos[2] = 0.0f;
+ }
+ }
+ return pos;
};
for(size_t i{0};i < chanmap.size();++i)
{
if(chanmap[i].channel == LFE) continue;
- const auto coeffs = CalcAngleCoeffs(ScaleAzimuthFront(chanmap[i].angle, 2.0f),
- chanmap[i].elevation, 0.0f);
- ComputePanGains(target.Main, coeffs.data(), gain, (*mChans)[i].Target);
+ const auto coeffs = CalcDirectionCoeffs(ScaleAzimuthFront(chanmap[i].pos), 0.0f);
+ ComputePanGains(target.Main, coeffs, gain, mChans[i].Target);
}
}
else for(size_t i{0};i < chanmap.size();++i)
{
if(chanmap[i].channel == LFE) continue;
- const auto coeffs = CalcAngleCoeffs(chanmap[i].angle, chanmap[i].elevation, 0.0f);
- ComputePanGains(target.Main, coeffs.data(), gain, (*mChans)[i].Target);
+ const auto coeffs = CalcDirectionCoeffs(chanmap[i].pos, 0.0f);
+ ComputePanGains(target.Main, coeffs, gain, mChans[i].Target);
}
}
}
@@ -525,9 +607,7 @@ void ConvolutionState::process(const size_t samplesToDo,
if(mNumConvolveSegs < 1) UNLIKELY
return;
- constexpr size_t m{ConvolveUpdateSize/2 + 1};
size_t curseg{mCurrentSegment};
- auto &chans = *mChans;
for(size_t base{0u};base < samplesToDo;)
{
@@ -539,9 +619,9 @@ void ConvolutionState::process(const size_t samplesToDo,
/* Apply the FIR for the newly retrieved input samples, and combine it
* with the inverse FFT'd output samples.
*/
- for(size_t c{0};c < chans.size();++c)
+ for(size_t c{0};c < mChans.size();++c)
{
- auto buf_iter = chans[c].mBuffer.begin() + base;
+ auto buf_iter = mChans[c].mBuffer.begin() + base;
apply_fir({buf_iter, todo}, mInput.data()+1 + mFifoPos, mFilter[c].data());
auto fifo_iter = mOutput[c].begin() + mFifoPos;
@@ -557,59 +637,49 @@ void ConvolutionState::process(const size_t samplesToDo,
/* Move the newest input to the front for the next iteration's history. */
std::copy(mInput.cbegin()+ConvolveUpdateSamples, mInput.cend(), mInput.begin());
+ std::fill(mInput.begin()+ConvolveUpdateSamples, mInput.end(), 0.0f);
- /* Calculate the frequency domain response and add the relevant
+ /* Calculate the frequency-domain response and add the relevant
* frequency bins to the FFT history.
*/
- auto fftiter = std::copy_n(mInput.cbegin(), ConvolveUpdateSamples, mFftBuffer.begin());
- std::fill(fftiter, mFftBuffer.end(), complex_f{});
- forward_fft(al::as_span(mFftBuffer));
+ pffft_transform(mFft.get(), mInput.data(), mComplexData.data() + curseg*ConvolveUpdateSize,
+ mFftWorkBuffer.data(), PFFFT_FORWARD);
- std::copy_n(mFftBuffer.cbegin(), m, &mComplexData[curseg*m]);
-
- const complex_f *RESTRICT filter{mComplexData.get() + mNumConvolveSegs*m};
- for(size_t c{0};c < chans.size();++c)
+ const float *filter{mComplexData.data() + mNumConvolveSegs*ConvolveUpdateSize};
+ for(size_t c{0};c < mChans.size();++c)
{
- std::fill_n(mFftBuffer.begin(), m, complex_f{});
-
/* Convolve each input segment with its IR filter counterpart
* (aligned in time).
*/
- const complex_f *RESTRICT input{&mComplexData[curseg*m]};
+ mFftBuffer.fill(0.0f);
+ const float *input{&mComplexData[curseg*ConvolveUpdateSize]};
for(size_t s{curseg};s < mNumConvolveSegs;++s)
{
- for(size_t i{0};i < m;++i,++input,++filter)
- mFftBuffer[i] += *input * *filter;
+ pffft_zconvolve_accumulate(mFft.get(), input, filter, mFftBuffer.data());
+ input += ConvolveUpdateSize;
+ filter += ConvolveUpdateSize;
}
- input = mComplexData.get();
+ input = mComplexData.data();
for(size_t s{0};s < curseg;++s)
{
- for(size_t i{0};i < m;++i,++input,++filter)
- mFftBuffer[i] += *input * *filter;
+ pffft_zconvolve_accumulate(mFft.get(), input, filter, mFftBuffer.data());
+ input += ConvolveUpdateSize;
+ filter += ConvolveUpdateSize;
}
- /* Reconstruct the mirrored/negative frequencies to do a proper
- * inverse FFT.
- */
- for(size_t i{m};i < ConvolveUpdateSize;++i)
- mFftBuffer[i] = std::conj(mFftBuffer[ConvolveUpdateSize-i]);
-
/* Apply iFFT to get the 256 (really 255) samples for output. The
* 128 output samples are combined with the last output's 127
* second-half samples (and this output's second half is
* subsequently saved for next time).
*/
- inverse_fft(al::as_span(mFftBuffer));
+ pffft_transform(mFft.get(), mFftBuffer.data(), mFftBuffer.data(),
+ mFftWorkBuffer.data(), PFFFT_BACKWARD);
- /* The iFFT'd response is scaled up by the number of bins, so apply
- * the inverse to normalize the output.
- */
+ /* The filter was attenuated, so the response is already scaled. */
for(size_t i{0};i < ConvolveUpdateSamples;++i)
- mOutput[c][i] =
- (mFftBuffer[i].real()+mOutput[c][ConvolveUpdateSamples+i]) *
- (1.0f/float{ConvolveUpdateSize});
+ mOutput[c][i] = mFftBuffer[i] + mOutput[c][ConvolveUpdateSamples+i];
for(size_t i{0};i < ConvolveUpdateSamples;++i)
- mOutput[c][ConvolveUpdateSamples+i] = mFftBuffer[ConvolveUpdateSamples+i].real();
+ mOutput[c][ConvolveUpdateSamples+i] = mFftBuffer[ConvolveUpdateSamples+i];
}
/* Shift the input history. */
diff --git a/alc/effects/dedicated.cpp b/alc/effects/dedicated.cpp
index 047e6761..a9131bfa 100644
--- a/alc/effects/dedicated.cpp
+++ b/alc/effects/dedicated.cpp
@@ -74,7 +74,7 @@ void DedicatedState::update(const ContextBase*, const EffectSlot *slot,
if(slot->EffectType == EffectSlotType::DedicatedLFE)
{
- const uint idx{target.RealOut ? target.RealOut->ChannelIndex[LFE] : InvalidChannelIndex};
+ const size_t idx{target.RealOut ? target.RealOut->ChannelIndex[LFE] : InvalidChannelIndex};
if(idx != InvalidChannelIndex)
{
mOutTarget = target.RealOut->Buffer;
@@ -85,7 +85,7 @@ void DedicatedState::update(const ContextBase*, const EffectSlot *slot,
{
/* Dialog goes to the front-center speaker if it exists, otherwise it
* plays from the front-center location. */
- const uint idx{target.RealOut ? target.RealOut->ChannelIndex[FrontCenter]
+ const size_t idx{target.RealOut ? target.RealOut->ChannelIndex[FrontCenter]
: InvalidChannelIndex};
if(idx != InvalidChannelIndex)
{
@@ -94,10 +94,10 @@ void DedicatedState::update(const ContextBase*, const EffectSlot *slot,
}
else
{
- static constexpr auto coeffs = CalcDirectionCoeffs({0.0f, 0.0f, -1.0f});
+ static constexpr auto coeffs = CalcDirectionCoeffs(std::array{0.0f, 0.0f, -1.0f});
mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs.data(), Gain, mTargetGains);
+ ComputePanGains(target.Main, coeffs, Gain, mTargetGains);
}
}
}
diff --git a/alc/effects/distortion.cpp b/alc/effects/distortion.cpp
index b4e2167e..3d77ff35 100644
--- a/alc/effects/distortion.cpp
+++ b/alc/effects/distortion.cpp
@@ -95,10 +95,10 @@ void DistortionState::update(const ContextBase *context, const EffectSlot *slot,
bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f);
mBandpass.setParamsFromBandwidth(BiquadType::BandPass, cutoff/frequency/4.0f, 1.0f, bandwidth);
- static constexpr auto coeffs = CalcDirectionCoeffs({0.0f, 0.0f, -1.0f});
+ static constexpr auto coeffs = CalcDirectionCoeffs(std::array{0.0f, 0.0f, -1.0f});
mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs.data(), slot->Gain*props->Distortion.Gain, mGain);
+ ComputePanGains(target.Main, coeffs, slot->Gain*props->Distortion.Gain, mGain);
}
void DistortionState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
diff --git a/alc/effects/echo.cpp b/alc/effects/echo.cpp
index a69529dc..714649c9 100644
--- a/alc/effects/echo.cpp
+++ b/alc/effects/echo.cpp
@@ -25,6 +25,7 @@
#include <cstdlib>
#include <iterator>
#include <tuple>
+#include <vector>
#include "alc/effects/base.h"
#include "almalloc.h"
@@ -39,7 +40,6 @@
#include "core/mixer.h"
#include "intrusive_ptr.h"
#include "opthelpers.h"
-#include "vector.h"
namespace {
@@ -49,7 +49,7 @@ using uint = unsigned int;
constexpr float LowpassFreqRef{5000.0f};
struct EchoState final : public EffectState {
- al::vector<float,16> mSampleBuffer;
+ std::vector<float> mSampleBuffer;
// The echo is two tap. The delay is the number of samples from before the
// current offset
@@ -87,7 +87,7 @@ void EchoState::deviceUpdate(const DeviceBase *Device, const BufferStorage*)
const uint maxlen{NextPowerOf2(float2uint(EchoMaxDelay*frequency + 0.5f) +
float2uint(EchoMaxLRDelay*frequency + 0.5f))};
if(maxlen != mSampleBuffer.size())
- al::vector<float,16>(maxlen).swap(mSampleBuffer);
+ decltype(mSampleBuffer)(maxlen).swap(mSampleBuffer);
std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f);
for(auto &e : mGains)
@@ -118,8 +118,8 @@ void EchoState::update(const ContextBase *context, const EffectSlot *slot,
const auto coeffs1 = CalcAngleCoeffs( angle, 0.0f, 0.0f);
mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs0.data(), slot->Gain, mGains[0].Target);
- ComputePanGains(target.Main, coeffs1.data(), slot->Gain, mGains[1].Target);
+ ComputePanGains(target.Main, coeffs0, slot->Gain, mGains[0].Target);
+ ComputePanGains(target.Main, coeffs1, slot->Gain, mGains[1].Target);
}
void EchoState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
diff --git a/alc/effects/fshifter.cpp b/alc/effects/fshifter.cpp
index 3e6a7385..d3989e84 100644
--- a/alc/effects/fshifter.cpp
+++ b/alc/effects/fshifter.cpp
@@ -164,16 +164,16 @@ void FshifterState::update(const ContextBase *context, const EffectSlot *slot,
}
static constexpr auto inv_sqrt2 = static_cast<float>(1.0 / al::numbers::sqrt2);
- static constexpr auto lcoeffs_pw = CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f});
- static constexpr auto rcoeffs_pw = CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f});
- static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs({-inv_sqrt2, 0.0f, inv_sqrt2});
- static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs({ inv_sqrt2, 0.0f, inv_sqrt2});
+ static constexpr auto lcoeffs_pw = CalcDirectionCoeffs(std::array{-1.0f, 0.0f, 0.0f});
+ static constexpr auto rcoeffs_pw = CalcDirectionCoeffs(std::array{ 1.0f, 0.0f, 0.0f});
+ static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs(std::array{-inv_sqrt2, 0.0f, inv_sqrt2});
+ static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs(std::array{ inv_sqrt2, 0.0f, inv_sqrt2});
auto &lcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? lcoeffs_nrml : lcoeffs_pw;
auto &rcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? rcoeffs_nrml : rcoeffs_pw;
mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, lcoeffs.data(), slot->Gain, mGains[0].Target);
- ComputePanGains(target.Main, rcoeffs.data(), slot->Gain, mGains[1].Target);
+ ComputePanGains(target.Main, lcoeffs, slot->Gain, mGains[0].Target);
+ ComputePanGains(target.Main, rcoeffs, slot->Gain, mGains[1].Target);
}
void FshifterState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
diff --git a/alc/effects/modulator.cpp b/alc/effects/modulator.cpp
index 14ee5004..f99ba19c 100644
--- a/alc/effects/modulator.cpp
+++ b/alc/effects/modulator.cpp
@@ -45,43 +45,49 @@ namespace {
using uint = unsigned int;
-#define MAX_UPDATE_SAMPLES 128
+inline float Sin(uint index, float scale)
+{ return std::sin(static_cast<float>(index) * scale); }
-#define WAVEFORM_FRACBITS 24
-#define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
-#define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
+inline float Saw(uint index, float scale)
+{ return static_cast<float>(index)*scale - 1.0f; }
-inline float Sin(uint index)
-{
- constexpr float scale{al::numbers::pi_v<float>*2.0f / WAVEFORM_FRACONE};
- return std::sin(static_cast<float>(index) * scale);
-}
+inline float Square(uint index, float scale)
+{ return (static_cast<float>(index)*scale < 0.5f)*2.0f - 1.0f; }
-inline float Saw(uint index)
-{ return static_cast<float>(index)*(2.0f/WAVEFORM_FRACONE) - 1.0f; }
+inline float One(uint, float)
+{ return 1.0f; }
-inline float Square(uint index)
-{ return static_cast<float>(static_cast<int>((index>>(WAVEFORM_FRACBITS-2))&2) - 1); }
+struct ModulatorState final : public EffectState {
+ template<float (&func)(uint,float)>
+ void Modulate(size_t todo)
+ {
+ const uint range{mRange};
+ const float scale{mIndexScale};
+ uint index{mIndex};
-inline float One(uint) { return 1.0f; }
+ ASSUME(range > 1);
+ ASSUME(todo > 0);
-template<float (&func)(uint)>
-void Modulate(float *RESTRICT dst, uint index, const uint step, size_t todo)
-{
- for(size_t i{0u};i < todo;i++)
- {
- index += step;
- index &= WAVEFORM_FRACMASK;
- dst[i] = func(index);
+ for(size_t i{0};i < todo;)
+ {
+ size_t rem{minz(todo-i, range-index)};
+ do {
+ mModSamples[i++] = func(index++, scale);
+ } while(--rem);
+ if(index == range)
+ index = 0;
+ }
+ mIndex = index;
}
-}
-
-struct ModulatorState final : public EffectState {
- void (*mGetSamples)(float*RESTRICT, uint, const uint, size_t){};
+ void (ModulatorState::*mGenModSamples)(size_t){};
uint mIndex{0};
- uint mStep{1};
+ uint mRange{1};
+ float mIndexScale{0.0f};
+
+ alignas(16) FloatBufferLine mModSamples{};
+ alignas(16) FloatBufferLine mBuffer{};
struct {
uint mTargetChannel{InvalidChannelIndex};
@@ -102,6 +108,13 @@ struct ModulatorState final : public EffectState {
DEF_NEWDEL(ModulatorState)
};
+template<>
+void ModulatorState::Modulate<One>(size_t todo)
+{
+ std::fill_n(mModSamples.begin(), todo, 1.0f);
+ mIndex = 0;
+}
+
void ModulatorState::deviceUpdate(const DeviceBase*, const BufferStorage*)
{
for(auto &e : mChans)
@@ -117,17 +130,46 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot,
{
const DeviceBase *device{context->mDevice};
- const float step{props->Modulator.Frequency / static_cast<float>(device->Frequency)};
- mStep = fastf2u(clampf(step*WAVEFORM_FRACONE, 0.0f, float{WAVEFORM_FRACONE-1}));
-
- if(mStep == 0)
- mGetSamples = Modulate<One>;
+ /* The effective frequency will be adjusted to have a whole number of
+ * samples per cycle (at 48khz, that allows 8000, 6857.14, 6000, 5333.33,
+ * 4800, etc). We could do better by using fixed-point stepping over a sin
+ * function, with additive synthesis for the square and sawtooth waveforms,
+ * but that may need a more efficient sin function since it needs to do
+ * many iterations per sample.
+ */
+ const float samplesPerCycle{props->Modulator.Frequency > 0.0f
+ ? static_cast<float>(device->Frequency)/props->Modulator.Frequency + 0.5f
+ : 1.0f};
+ const uint range{static_cast<uint>(clampf(samplesPerCycle, 1.0f,
+ static_cast<float>(device->Frequency)))};
+ mIndex = static_cast<uint>(uint64_t{mIndex} * range / mRange);
+ mRange = range;
+
+ if(mRange == 1)
+ {
+ mIndexScale = 0.0f;
+ mGenModSamples = &ModulatorState::Modulate<One>;
+ }
else if(props->Modulator.Waveform == ModulatorWaveform::Sinusoid)
- mGetSamples = Modulate<Sin>;
+ {
+ mIndexScale = al::numbers::pi_v<float>*2.0f / static_cast<float>(mRange);
+ mGenModSamples = &ModulatorState::Modulate<Sin>;
+ }
else if(props->Modulator.Waveform == ModulatorWaveform::Sawtooth)
- mGetSamples = Modulate<Saw>;
+ {
+ mIndexScale = 2.0f / static_cast<float>(mRange-1);
+ mGenModSamples = &ModulatorState::Modulate<Saw>;
+ }
else /*if(props->Modulator.Waveform == ModulatorWaveform::Square)*/
- mGetSamples = Modulate<Square>;
+ {
+ /* For square wave, the range should be even (there should be an equal
+ * number of high and low samples). An odd number of samples per cycle
+ * would need a more complex value generator.
+ */
+ mRange = (mRange+1) & ~1u;
+ mIndexScale = 1.0f / static_cast<float>(mRange-1);
+ mGenModSamples = &ModulatorState::Modulate<Square>;
+ }
float f0norm{props->Modulator.HighPassCutoff / static_cast<float>(device->Frequency)};
f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f);
@@ -147,34 +189,22 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot,
void ModulatorState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
{
- for(size_t base{0u};base < samplesToDo;)
- {
- alignas(16) float modsamples[MAX_UPDATE_SAMPLES];
- const size_t td{minz(MAX_UPDATE_SAMPLES, samplesToDo-base)};
-
- mGetSamples(modsamples, mIndex, mStep, td);
- mIndex += static_cast<uint>(mStep * td);
- mIndex &= WAVEFORM_FRACMASK;
+ (this->*mGenModSamples)(samplesToDo);
- auto chandata = std::begin(mChans);
- for(const auto &input : samplesIn)
+ auto chandata = std::begin(mChans);
+ for(const auto &input : samplesIn)
+ {
+ const size_t outidx{chandata->mTargetChannel};
+ if(outidx != InvalidChannelIndex)
{
- const size_t outidx{chandata->mTargetChannel};
- if(outidx != InvalidChannelIndex)
- {
- alignas(16) float temps[MAX_UPDATE_SAMPLES];
-
- chandata->mFilter.process({&input[base], td}, temps);
- for(size_t i{0u};i < td;i++)
- temps[i] *= modsamples[i];
-
- MixSamples({temps, td}, samplesOut[outidx].data()+base, chandata->mCurrentGain,
- chandata->mTargetGain, samplesToDo-base);
- }
- ++chandata;
- }
+ chandata->mFilter.process({input.data(), samplesToDo}, mBuffer.data());
+ for(size_t i{0u};i < samplesToDo;++i)
+ mBuffer[i] *= mModSamples[i];
- base += td;
+ MixSamples({mBuffer.data(), samplesToDo}, samplesOut[outidx].data(),
+ chandata->mCurrentGain, chandata->mTargetGain, minz(samplesToDo, 64));
+ }
+ ++chandata;
}
}
diff --git a/alc/effects/pshifter.cpp b/alc/effects/pshifter.cpp
index 426a2264..0c27be30 100644
--- a/alc/effects/pshifter.cpp
+++ b/alc/effects/pshifter.cpp
@@ -28,7 +28,6 @@
#include <iterator>
#include "alc/effects/base.h"
-#include "alcomplex.h"
#include "almalloc.h"
#include "alnumbers.h"
#include "alnumeric.h"
@@ -40,6 +39,7 @@
#include "core/mixer.h"
#include "core/mixer/defs.h"
#include "intrusive_ptr.h"
+#include "pffft.h"
struct ContextBase;
@@ -74,6 +74,12 @@ struct Windower {
const Windower gWindow{};
+struct PFFFTSetupDeleter {
+ void operator()(PFFFT_Setup *ptr) { pffft_destroy_setup(ptr); }
+};
+using PFFFTSetupPtr = std::unique_ptr<PFFFT_Setup,PFFFTSetupDeleter>;
+
+
struct FrequencyBin {
float Magnitude;
float FreqBin;
@@ -93,7 +99,9 @@ struct PshifterState final : public EffectState {
std::array<float,StftHalfSize+1> mSumPhase;
std::array<float,StftSize> mOutputAccum;
- std::array<complex_f,StftSize> mFftBuffer;
+ PFFFTSetupPtr mFft;
+ alignas(16) std::array<float,StftSize> mFftBuffer;
+ alignas(16) std::array<float,StftSize> mFftWorkBuffer;
std::array<FrequencyBin,StftHalfSize+1> mAnalysisBuffer;
std::array<FrequencyBin,StftHalfSize+1> mSynthesisBuffer;
@@ -126,12 +134,15 @@ void PshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*)
mLastPhase.fill(0.0f);
mSumPhase.fill(0.0f);
mOutputAccum.fill(0.0f);
- mFftBuffer.fill(complex_f{});
+ mFftBuffer.fill(0.0f);
mAnalysisBuffer.fill(FrequencyBin{});
mSynthesisBuffer.fill(FrequencyBin{});
std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f);
std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
+
+ if(!mFft)
+ mFft = PFFFTSetupPtr{pffft_new_setup(StftSize, PFFFT_REAL)};
}
void PshifterState::update(const ContextBase*, const EffectSlot *slot,
@@ -142,10 +153,10 @@ void PshifterState::update(const ContextBase*, const EffectSlot *slot,
mPitchShiftI = clampu(fastf2u(pitch*MixerFracOne), MixerFracHalf, MixerFracOne*2);
mPitchShift = static_cast<float>(mPitchShiftI) * float{1.0f/MixerFracOne};
- static constexpr auto coeffs = CalcDirectionCoeffs({0.0f, 0.0f, -1.0f});
+ static constexpr auto coeffs = CalcDirectionCoeffs(std::array{0.0f, 0.0f, -1.0f});
mOutTarget = target.Main->Buffer;
- ComputePanGains(target.Main, coeffs.data(), slot->Gain, mTargetGains);
+ ComputePanGains(target.Main, coeffs, slot->Gain, mTargetGains);
}
void PshifterState::process(const size_t samplesToDo,
@@ -186,15 +197,19 @@ void PshifterState::process(const size_t samplesToDo,
mFftBuffer[k] = mFIFO[src] * gWindow.mData[k];
for(size_t src{0u}, k{StftSize-mPos};src < mPos;++src,++k)
mFftBuffer[k] = mFIFO[src] * gWindow.mData[k];
- forward_fft(al::as_span(mFftBuffer));
+ pffft_transform_ordered(mFft.get(), mFftBuffer.data(), mFftBuffer.data(),
+ mFftWorkBuffer.data(), PFFFT_FORWARD);
/* Analyze the obtained data. Since the real FFT is symmetric, only
* StftHalfSize+1 samples are needed.
*/
- for(size_t k{0u};k < StftHalfSize+1;k++)
+ for(size_t k{0u};k < StftHalfSize+1;++k)
{
- const float magnitude{std::abs(mFftBuffer[k])};
- const float phase{std::arg(mFftBuffer[k])};
+ const auto cplx = (k == 0) ? complex_f{mFftBuffer[0]} :
+ (k == StftHalfSize) ? complex_f{mFftBuffer[1]} :
+ complex_f{mFftBuffer[k*2], mFftBuffer[k*2 + 1]};
+ const float magnitude{std::abs(cplx)};
+ const float phase{std::arg(cplx)};
/* Compute the phase difference from the last update and subtract
* the expected phase difference for this bin.
@@ -266,21 +281,29 @@ void PshifterState::process(const size_t samplesToDo,
tmp -= static_cast<float>(qpd + (qpd%2));
mSumPhase[k] = tmp * al::numbers::pi_v<float>;
- mFftBuffer[k] = std::polar(mSynthesisBuffer[k].Magnitude, mSumPhase[k]);
+ const complex_f cplx{std::polar(mSynthesisBuffer[k].Magnitude, mSumPhase[k])};
+ if(k == 0)
+ mFftBuffer[0] = cplx.real();
+ else if(k == StftHalfSize)
+ mFftBuffer[1] = cplx.real();
+ else
+ {
+ mFftBuffer[k*2 + 0] = cplx.real();
+ mFftBuffer[k*2 + 1] = cplx.imag();
+ }
}
- for(size_t k{StftHalfSize+1};k < StftSize;++k)
- mFftBuffer[k] = std::conj(mFftBuffer[StftSize-k]);
/* Apply an inverse FFT to get the time-domain signal, and accumulate
* for the output with windowing.
*/
- inverse_fft(al::as_span(mFftBuffer));
+ pffft_transform_ordered(mFft.get(), mFftBuffer.data(), mFftBuffer.data(),
+ mFftWorkBuffer.data(), PFFFT_BACKWARD);
static constexpr float scale{3.0f / OversampleFactor / StftSize};
for(size_t dst{mPos}, k{0u};dst < StftSize;++dst,++k)
- mOutputAccum[dst] += gWindow.mData[k]*mFftBuffer[k].real() * scale;
+ mOutputAccum[dst] += gWindow.mData[k]*mFftBuffer[k] * scale;
for(size_t dst{0u}, k{StftSize-mPos};dst < mPos;++dst,++k)
- mOutputAccum[dst] += gWindow.mData[k]*mFftBuffer[k].real() * scale;
+ mOutputAccum[dst] += gWindow.mData[k]*mFftBuffer[k] * scale;
/* Copy out the accumulated result, then clear for the next iteration. */
std::copy_n(mOutputAccum.begin() + mPos, StftStep, mFIFO.begin() + mPos);
diff --git a/alc/effects/reverb.cpp b/alc/effects/reverb.cpp
index 3875bedb..0f1fcca1 100644
--- a/alc/effects/reverb.cpp
+++ b/alc/effects/reverb.cpp
@@ -98,8 +98,6 @@ struct CubicFilter {
constexpr CubicFilter gCubicTable;
-using namespace std::placeholders;
-
/* Max samples per process iteration. Used to limit the size needed for
* temporary buffers. Must be a multiple of 4 for SIMD alignment.
*/
@@ -122,12 +120,9 @@ constexpr size_t NUM_LINES{4u};
constexpr float MODULATION_DEPTH_COEFF{0.05f};
-/* The B-Format to A-Format conversion matrix. The arrangement of rows is
- * deliberately chosen to align the resulting lines to their spatial opposites
- * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below
- * back left). It's not quite opposite, since the A-Format results in a
- * tetrahedron, but it's close enough. Should the model be extended to 8-lines
- * in the future, true opposites can be used.
+/* The B-Format to (W-normalized) A-Format conversion matrix. This produces a
+ * tetrahedral array of discrete signals (boosted by a factor of sqrt(3), to
+ * reduce the error introduced in the conversion).
*/
alignas(16) constexpr float B2A[NUM_LINES][NUM_LINES]{
{ 0.5f, 0.5f, 0.5f, 0.5f },
@@ -136,7 +131,9 @@ alignas(16) constexpr float B2A[NUM_LINES][NUM_LINES]{
{ 0.5f, -0.5f, 0.5f, -0.5f }
};
-/* Converts A-Format to B-Format for early reflections. */
+/* Converts (W-normalized) A-Format to B-Format for early reflections (scaled
+ * by 1/sqrt(3) to compensate for the boost in the B2A matrix).
+ */
alignas(16) constexpr std::array<std::array<float,NUM_LINES>,NUM_LINES> EarlyA2B{{
{{ 0.5f, 0.5f, 0.5f, 0.5f }},
{{ 0.5f, -0.5f, 0.5f, -0.5f }},
@@ -144,7 +141,11 @@ alignas(16) constexpr std::array<std::array<float,NUM_LINES>,NUM_LINES> EarlyA2B
{{ 0.5f, 0.5f, -0.5f, -0.5f }}
}};
-/* Converts A-Format to B-Format for late reverb. */
+/* Converts (W-normalized) A-Format to B-Format for late reverb (scaled
+ * by 1/sqrt(3) to compensate for the boost in the B2A matrix). The response
+ * is rotated around Z (ambisonic X) so that the front lines are placed
+ * horizontally in front, and the rear lines are placed vertically in back.
+ */
constexpr auto InvSqrt2 = static_cast<float>(1.0/al::numbers::sqrt2);
alignas(16) constexpr std::array<std::array<float,NUM_LINES>,NUM_LINES> LateA2B{{
{{ 0.5f, 0.5f, 0.5f, 0.5f }},
@@ -330,6 +331,39 @@ struct DelayLineI {
} while(--td);
}
}
+
+ /* Writes the given input lines to the delay buffer, applying a geometric
+ * reflection. This effectively applies the matrix
+ *
+ * [ -1/2 +1/2 +1/2 +1/2 ]
+ * [ +1/2 -1/2 +1/2 +1/2 ]
+ * [ +1/2 +1/2 -1/2 +1/2 ]
+ * [ +1/2 +1/2 +1/2 -1/2 ]
+ *
+ * to the four input lines when writing to the delay buffer. The effect on
+ * the B-Format signal is negating X,Y,Z, moving each response to its
+ * spatially opposite location.
+ */
+ void writeReflected(size_t offset, const al::span<const ReverbUpdateLine,NUM_LINES> in,
+ const size_t count) const noexcept
+ {
+ ASSUME(count > 0);
+ for(size_t i{0u};i < count;)
+ {
+ offset &= Mask;
+ size_t td{minz(Mask+1 - offset, count - i)};
+ do {
+ const std::array src{in[0][i], in[1][i], in[2][i], in[3][i]};
+ ++i;
+
+ Line[offset][0] = ( src[1] + src[2] + src[3] - src[0]) * 0.5f;
+ Line[offset][1] = (src[0] + src[2] + src[3] - src[1]) * 0.5f;
+ Line[offset][2] = (src[0] + src[1] + src[3] - src[2]) * 0.5f;
+ Line[offset][3] = (src[0] + src[1] + src[2] - src[3]) * 0.5f;
+ ++offset;
+ } while(--td);
+ }
+ }
};
struct VecAllpass {
@@ -461,8 +495,9 @@ struct ReverbPipeline {
void updateDelayLine(const float earlyDelay, const float lateDelay, const float density_mult,
const float decayTime, const float frequency);
- void update3DPanning(const float *ReflectionsPan, const float *LateReverbPan,
- const float earlyGain, const float lateGain, const bool doUpmix, const MixParams *mainMix);
+ void update3DPanning(const al::span<const float,3> ReflectionsPan,
+ const al::span<const float,3> LateReverbPan, const float earlyGain, const float lateGain,
+ const bool doUpmix, const MixParams *mainMix);
void processEarly(size_t offset, const size_t samplesToDo,
const al::span<ReverbUpdateLine,NUM_LINES> tempSamples,
@@ -643,8 +678,8 @@ inline float CalcDelayLengthMult(float density)
*/
void ReverbState::allocLines(const float frequency)
{
- /* All delay line lengths are calculated to accomodate the full range of
- * lengths given their respective paramters.
+ /* All delay line lengths are calculated to accommodate the full range of
+ * lengths given their respective parameters.
*/
size_t totalSamples{0u};
@@ -1017,8 +1052,12 @@ void ReverbPipeline::updateDelayLine(const float earlyDelay, const float lateDel
mEarlyDelayTap[i][1] = float2uint((earlyDelay+length) * frequency);
mEarlyDelayCoeff[i] = CalcDecayCoeff(length, decayTime);
+ /* Reduce the late delay tap by the shortest early delay line length to
+ * compensate for the late line input being fed by the delayed early
+ * output.
+ */
length = (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS.front())/float{NUM_LINES}*density_mult +
- lateDelay;
+ std::max(lateDelay - EARLY_LINE_LENGTHS[0]*density_mult, 0.0f);
mLateDelayTap[i][1] = float2uint(length * frequency);
}
}
@@ -1028,7 +1067,7 @@ void ReverbPipeline::updateDelayLine(const float earlyDelay, const float lateDel
* focal strength. This function results in a B-Format transformation matrix
* that spatially focuses the signal in the desired direction.
*/
-std::array<std::array<float,4>,4> GetTransformFromVector(const float *vec)
+std::array<std::array<float,4>,4> GetTransformFromVector(const al::span<const float,3> vec)
{
/* Normalize the panning vector according to the N3D scale, which has an
* extra sqrt(3) term on the directional components. Converting from OpenAL
@@ -1041,9 +1080,10 @@ std::array<std::array<float,4>,4> GetTransformFromVector(const float *vec)
float mag{std::sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2])};
if(mag > 1.0f)
{
- norm[0] = vec[0] / mag * -al::numbers::sqrt3_v<float>;
- norm[1] = vec[1] / mag * al::numbers::sqrt3_v<float>;
- norm[2] = vec[2] / mag * al::numbers::sqrt3_v<float>;
+ const float scale{al::numbers::sqrt3_v<float> / mag};
+ norm[0] = vec[0] * -scale;
+ norm[1] = vec[1] * scale;
+ norm[2] = vec[2] * scale;
mag = 1.0f;
}
else
@@ -1066,8 +1106,9 @@ std::array<std::array<float,4>,4> GetTransformFromVector(const float *vec)
}
/* Update the early and late 3D panning gains. */
-void ReverbPipeline::update3DPanning(const float *ReflectionsPan, const float *LateReverbPan,
- const float earlyGain, const float lateGain, const bool doUpmix, const MixParams *mainMix)
+void ReverbPipeline::update3DPanning(const al::span<const float,3> ReflectionsPan,
+ const al::span<const float,3> LateReverbPan, const float earlyGain, const float lateGain,
+ const bool doUpmix, const MixParams *mainMix)
{
/* Create matrices that transform a B-Format signal according to the
* panning vectors.
@@ -1105,9 +1146,9 @@ void ReverbPipeline::update3DPanning(const float *ReflectionsPan, const float *L
auto latecoeffs = mult_matrix(latemat);
for(size_t i{0u};i < NUM_LINES;i++)
- ComputePanGains(mainMix, earlycoeffs[i].data(), earlyGain, mEarly.TargetGains[i]);
+ ComputePanGains(mainMix, earlycoeffs[i], earlyGain, mEarly.TargetGains[i]);
for(size_t i{0u};i < NUM_LINES;i++)
- ComputePanGains(mainMix, latecoeffs[i].data(), lateGain, mLate.TargetGains[i]);
+ ComputePanGains(mainMix, latecoeffs[i], lateGain, mLate.TargetGains[i]);
}
else
{
@@ -1137,9 +1178,9 @@ void ReverbPipeline::update3DPanning(const float *ReflectionsPan, const float *L
auto latecoeffs = mult_matrix(LateA2B, latemat);
for(size_t i{0u};i < NUM_LINES;i++)
- ComputePanGains(mainMix, earlycoeffs[i].data(), earlyGain, mEarly.TargetGains[i]);
+ ComputePanGains(mainMix, earlycoeffs[i], earlyGain, mEarly.TargetGains[i]);
for(size_t i{0u};i < NUM_LINES;i++)
- ComputePanGains(mainMix, latecoeffs[i].data(), lateGain, mLate.TargetGains[i]);
+ ComputePanGains(mainMix, latecoeffs[i], lateGain, mLate.TargetGains[i]);
}
}
@@ -1244,9 +1285,36 @@ void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot,
props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm, frequency);
}
- const float decaySamples{(props->Reverb.ReflectionsDelay + props->Reverb.LateReverbDelay
- + props->Reverb.DecayTime) * frequency};
- pipeline.mFadeSampleCount = static_cast<size_t>(minf(decaySamples, 1'000'000.0f));
+ /* Calculate the gain at the start of the late reverb stage, and the gain
+ * difference from the decay target (0.001, or -60dB).
+ */
+ const float decayBase{props->Reverb.ReflectionsGain * props->Reverb.LateReverbGain};
+ const float decayDiff{ReverbDecayGain / decayBase};
+
+ if(decayDiff < 1.0f)
+ {
+ /* Given the DecayTime (the amount of time for the late reverb to decay
+ * by -60dB), calculate the time to decay to -60dB from the start of
+ * the late reverb.
+ */
+ const float diffTime{std::log10(decayDiff)*(20.0f / -60.0f) * props->Reverb.DecayTime};
+
+ const float decaySamples{(props->Reverb.ReflectionsDelay + props->Reverb.LateReverbDelay
+ + diffTime) * frequency};
+ /* Limit to 100,000 samples (a touch over 2 seconds at 48khz) to
+ * avoid excessive double-processing.
+ */
+ pipeline.mFadeSampleCount = static_cast<size_t>(minf(decaySamples, 100'000.0f));
+ }
+ else
+ {
+ /* Otherwise, if the late reverb already starts at -60dB or less, only
+ * include the time to get to the late reverb.
+ */
+ const float decaySamples{(props->Reverb.ReflectionsDelay + props->Reverb.LateReverbDelay)
+ * frequency};
+ pipeline.mFadeSampleCount = static_cast<size_t>(minf(decaySamples, 100'000.0f));
+ }
}
@@ -1303,7 +1371,9 @@ inline auto VectorPartialScatter(const std::array<float,NUM_LINES> &RESTRICT in,
}};
}
-/* Utilizes the above, but reverses the input channels. */
+/* Utilizes the above, but also applies a geometric reflection on the input
+ * channels.
+ */
void VectorScatterRevDelayIn(const DelayLineI delay, size_t offset, const float xCoeff,
const float yCoeff, const al::span<const ReverbUpdateLine,NUM_LINES> in, const size_t count)
{
@@ -1314,9 +1384,13 @@ void VectorScatterRevDelayIn(const DelayLineI delay, size_t offset, const float
offset &= delay.Mask;
size_t td{minz(delay.Mask+1 - offset, count-i)};
do {
- std::array<float,NUM_LINES> f;
- for(size_t j{0u};j < NUM_LINES;j++)
- f[NUM_LINES-1-j] = in[j][i];
+ std::array src{in[0][i], in[1][i], in[2][i], in[3][i]};
+ std::array f{
+ ( src[1] + src[2] + src[3] - src[0]) * 0.5f,
+ (src[0] + src[2] + src[3] - src[1]) * 0.5f,
+ (src[0] + src[1] + src[3] - src[2]) * 0.5f,
+ (src[0] + src[1] + src[2] - src[3]) * 0.5f
+ };
++i;
delay.Line[offset++] = VectorPartialScatter(f, xCoeff, yCoeff);
@@ -1330,9 +1404,6 @@ void VectorScatterRevDelayIn(const DelayLineI delay, size_t offset, const float
* It works by vectorizing a regular all-pass filter and replacing the delay
* element with a scattering matrix (like the one above) and a diagonal
* matrix of delay elements.
- *
- * Two static specializations are used for transitional (cross-faded) delay
- * line processing and non-transitional processing.
*/
void VecAllpass::process(const al::span<ReverbUpdateLine,NUM_LINES> samples, size_t offset,
const float xCoeff, const float yCoeff, const size_t todo)
@@ -1379,14 +1450,13 @@ void VecAllpass::process(const al::span<ReverbUpdateLine,NUM_LINES> samples, siz
* same direction as the source) from the main delay line. These are
* attenuated and all-pass filtered (based on the diffusion parameter).
*
- * The early lines are then fed in reverse (according to the approximately
- * opposite spatial location of the A-Format lines) to create the secondary
+ * The early lines are then reflected about the origin to create the secondary
* reflections (those arriving from the opposite direction as the source).
*
* The early response is then completed by combining the primary reflections
* with the delayed and attenuated output from the early lines.
*
- * Finally, the early response is reversed, scattered (based on diffusion),
+ * Finally, the early response is reflected, scattered (based on diffusion),
* and fed into the late reverb section of the main delay line.
*/
void ReverbPipeline::processEarly(size_t offset, const size_t samplesToDo,
@@ -1442,8 +1512,7 @@ void ReverbPipeline::processEarly(size_t offset, const size_t samplesToDo,
/* Apply a delay and bounce to generate secondary reflections, combine
* with the primary reflections and write out the result for mixing.
*/
- for(size_t j{0u};j < NUM_LINES;j++)
- early_delay.write(offset, NUM_LINES-1-j, tempSamples[j].data(), todo);
+ early_delay.writeReflected(offset, tempSamples, todo);
for(size_t j{0u};j < NUM_LINES;j++)
{
size_t feedb_tap{offset - mEarly.Offset[j]};
@@ -1455,8 +1524,9 @@ void ReverbPipeline::processEarly(size_t offset, const size_t samplesToDo,
feedb_tap &= early_delay.Mask;
size_t td{minz(early_delay.Mask+1 - feedb_tap, todo - i)};
do {
- tempSamples[j][i] += early_delay.Line[feedb_tap++][j]*feedb_coeff;
- out[i] = tempSamples[j][i];
+ float sample{early_delay.Line[feedb_tap++][j]};
+ out[i] = tempSamples[j][i] + sample*feedb_coeff;
+ tempSamples[j][i] = sample;
++i;
} while(--td);
}
@@ -1475,14 +1545,19 @@ void ReverbPipeline::processEarly(size_t offset, const size_t samplesToDo,
void Modulation::calcDelays(size_t todo)
{
- constexpr float mod_scale{al::numbers::pi_v<float> * 2.0f / MOD_FRACONE};
uint idx{Index};
const uint step{Step};
const float depth{Depth};
for(size_t i{0};i < todo;++i)
{
idx += step;
- const float lfo{std::sin(static_cast<float>(idx&MOD_FRACMASK) * mod_scale)};
+ const float x{static_cast<float>(idx&MOD_FRACMASK) * (1.0f/MOD_FRACONE)};
+ /* Approximate sin(x*2pi). As long as it roughly fits a sinusoid shape
+ * and stays within [-1...+1], it needn't be perfect.
+ */
+ const float lfo{!(idx&(MOD_FRACONE>>1))
+ ? ((-16.0f * x * x) + (8.0f * x))
+ : ((16.0f * x * x) + (-8.0f * x) + (-16.0f * x) + 8.0f)};
ModDelays[i] = (lfo+1.0f) * depth;
}
Index = idx;
diff --git a/alc/events.cpp b/alc/events.cpp
new file mode 100644
index 00000000..1010a338
--- /dev/null
+++ b/alc/events.cpp
@@ -0,0 +1,95 @@
+
+#include "config.h"
+
+#include "events.h"
+
+#include "alspan.h"
+#include "core/logging.h"
+#include "device.h"
+
+
+namespace {
+
+ALCenum EnumFromEventType(const alc::EventType type)
+{
+ switch(type)
+ {
+ case alc::EventType::DefaultDeviceChanged: return ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT;
+ case alc::EventType::DeviceAdded: return ALC_EVENT_TYPE_DEVICE_ADDED_SOFT;
+ case alc::EventType::DeviceRemoved: return ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT;
+ case alc::EventType::Count: break;
+ }
+ throw std::runtime_error{"Invalid EventType: "+std::to_string(al::to_underlying(type))};
+}
+
+} // namespace
+
+namespace alc {
+
+std::optional<alc::EventType> GetEventType(ALCenum type)
+{
+ switch(type)
+ {
+ case ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT: return alc::EventType::DefaultDeviceChanged;
+ case ALC_EVENT_TYPE_DEVICE_ADDED_SOFT: return alc::EventType::DeviceAdded;
+ case ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT: return alc::EventType::DeviceRemoved;
+ }
+ return std::nullopt;
+}
+
+void Event(EventType eventType, DeviceType deviceType, ALCdevice *device, std::string_view message) noexcept
+{
+ auto eventlock = std::unique_lock{EventMutex};
+ if(EventCallback && EventsEnabled.test(al::to_underlying(eventType)))
+ EventCallback(EnumFromEventType(eventType), al::to_underlying(deviceType), device,
+ static_cast<ALCsizei>(message.length()), message.data(), EventUserPtr);
+}
+
+} // namespace alc
+
+FORCE_ALIGN ALCboolean ALC_APIENTRY alcEventControlSOFT(ALCsizei count, const ALCenum *events,
+ ALCboolean enable) noexcept
+{
+ if(enable != ALC_FALSE && enable != ALC_TRUE)
+ {
+ alcSetError(nullptr, ALC_INVALID_ENUM);
+ return ALC_FALSE;
+ }
+ if(count < 0)
+ {
+ alcSetError(nullptr, ALC_INVALID_VALUE);
+ return ALC_FALSE;
+ }
+ if(count == 0)
+ return ALC_TRUE;
+ if(!events)
+ {
+ alcSetError(nullptr, ALC_INVALID_VALUE);
+ return ALC_FALSE;
+ }
+
+ alc::EventBitSet eventSet{0};
+ for(ALCenum type : al::span{events, static_cast<ALCuint>(count)})
+ {
+ auto etype = alc::GetEventType(type);
+ if(!etype)
+ {
+ WARN("Invalid event type: 0x%04x\n", type);
+ alcSetError(nullptr, ALC_INVALID_ENUM);
+ return ALC_FALSE;
+ }
+ eventSet.set(al::to_underlying(*etype));
+ }
+
+ auto eventlock = std::unique_lock{alc::EventMutex};
+ if(enable) alc::EventsEnabled |= eventSet;
+ else alc::EventsEnabled &= ~eventSet;
+ return ALC_TRUE;
+}
+
+FORCE_ALIGN void ALC_APIENTRY alcEventCallbackSOFT(ALCEVENTPROCTYPESOFT callback, void *userParam) noexcept
+{
+ auto eventlock = std::unique_lock{alc::EventMutex};
+ alc::EventCallback = callback;
+ alc::EventUserPtr = userParam;
+}
diff --git a/alc/events.h b/alc/events.h
new file mode 100644
index 00000000..3f53ec76
--- /dev/null
+++ b/alc/events.h
@@ -0,0 +1,50 @@
+#ifndef ALC_EVENTS_H
+#define ALC_EVENTS_H
+
+#include "inprogext.h"
+#include "opthelpers.h"
+
+#include <bitset>
+#include <mutex>
+#include <optional>
+#include <string_view>
+
+
+namespace alc {
+
+enum class EventType : uint8_t {
+ DefaultDeviceChanged,
+ DeviceAdded,
+ DeviceRemoved,
+
+ Count
+};
+
+std::optional<alc::EventType> GetEventType(ALCenum type);
+
+enum class EventSupport : ALCenum {
+ FullSupport = ALC_EVENT_SUPPORTED_SOFT,
+ NoSupport = ALC_EVENT_NOT_SUPPORTED_SOFT,
+};
+
+enum class DeviceType : ALCenum {
+ Playback = ALC_PLAYBACK_DEVICE_SOFT,
+ Capture = ALC_CAPTURE_DEVICE_SOFT,
+};
+
+using EventBitSet = std::bitset<al::to_underlying(EventType::Count)>;
+inline EventBitSet EventsEnabled{0};
+
+inline std::mutex EventMutex;
+
+inline ALCEVENTPROCTYPESOFT EventCallback{};
+inline void *EventUserPtr{};
+
+void Event(EventType eventType, DeviceType deviceType, ALCdevice *device, std::string_view message) noexcept;
+
+inline void Event(EventType eventType, DeviceType deviceType, std::string_view message) noexcept
+{ Event(eventType, deviceType, nullptr, message); }
+
+} // namespace alc
+
+#endif /* ALC_EVENTS_H */
diff --git a/alc/export_list.h b/alc/export_list.h
new file mode 100644
index 00000000..c5af1ab0
--- /dev/null
+++ b/alc/export_list.h
@@ -0,0 +1,915 @@
+#ifndef ALC_EXPORT_LIST_H
+#define ALC_EXPORT_LIST_H
+
+#include "AL/alc.h"
+#include "AL/al.h"
+#include "AL/alext.h"
+
+#include "inprogext.h"
+#ifdef ALSOFT_EAX
+#include "context.h"
+#include "al/eax/x_ram.h"
+#endif
+
+
+struct FuncExport {
+ const char *funcName;
+ void *address;
+};
+#define DECL(x) { #x, reinterpret_cast<void*>(x) }
+inline const FuncExport alcFunctions[]{
+ DECL(alcCreateContext),
+ DECL(alcMakeContextCurrent),
+ DECL(alcProcessContext),
+ DECL(alcSuspendContext),
+ DECL(alcDestroyContext),
+ DECL(alcGetCurrentContext),
+ DECL(alcGetContextsDevice),
+ DECL(alcOpenDevice),
+ DECL(alcCloseDevice),
+ DECL(alcGetError),
+ DECL(alcIsExtensionPresent),
+ DECL(alcGetProcAddress),
+ DECL(alcGetEnumValue),
+ DECL(alcGetString),
+ DECL(alcGetIntegerv),
+ DECL(alcCaptureOpenDevice),
+ DECL(alcCaptureCloseDevice),
+ DECL(alcCaptureStart),
+ DECL(alcCaptureStop),
+ DECL(alcCaptureSamples),
+
+ DECL(alcSetThreadContext),
+ DECL(alcGetThreadContext),
+
+ DECL(alcLoopbackOpenDeviceSOFT),
+ DECL(alcIsRenderFormatSupportedSOFT),
+ DECL(alcRenderSamplesSOFT),
+
+ DECL(alcDevicePauseSOFT),
+ DECL(alcDeviceResumeSOFT),
+
+ DECL(alcGetStringiSOFT),
+ DECL(alcResetDeviceSOFT),
+
+ DECL(alcGetInteger64vSOFT),
+
+ DECL(alcReopenDeviceSOFT),
+
+ DECL(alcEventIsSupportedSOFT),
+ DECL(alcEventControlSOFT),
+ DECL(alcEventCallbackSOFT),
+
+ DECL(alEnable),
+ DECL(alDisable),
+ DECL(alIsEnabled),
+ DECL(alGetString),
+ DECL(alGetBooleanv),
+ DECL(alGetIntegerv),
+ DECL(alGetFloatv),
+ DECL(alGetDoublev),
+ DECL(alGetBoolean),
+ DECL(alGetInteger),
+ DECL(alGetFloat),
+ DECL(alGetDouble),
+ DECL(alGetError),
+ DECL(alIsExtensionPresent),
+ DECL(alGetProcAddress),
+ DECL(alGetEnumValue),
+ DECL(alListenerf),
+ DECL(alListener3f),
+ DECL(alListenerfv),
+ DECL(alListeneri),
+ DECL(alListener3i),
+ DECL(alListeneriv),
+ DECL(alGetListenerf),
+ DECL(alGetListener3f),
+ DECL(alGetListenerfv),
+ DECL(alGetListeneri),
+ DECL(alGetListener3i),
+ DECL(alGetListeneriv),
+ DECL(alGenSources),
+ DECL(alDeleteSources),
+ DECL(alIsSource),
+ DECL(alSourcef),
+ DECL(alSource3f),
+ DECL(alSourcefv),
+ DECL(alSourcei),
+ DECL(alSource3i),
+ DECL(alSourceiv),
+ DECL(alGetSourcef),
+ DECL(alGetSource3f),
+ DECL(alGetSourcefv),
+ DECL(alGetSourcei),
+ DECL(alGetSource3i),
+ DECL(alGetSourceiv),
+ DECL(alSourcePlayv),
+ DECL(alSourceStopv),
+ DECL(alSourceRewindv),
+ DECL(alSourcePausev),
+ DECL(alSourcePlay),
+ DECL(alSourceStop),
+ DECL(alSourceRewind),
+ DECL(alSourcePause),
+ DECL(alSourceQueueBuffers),
+ DECL(alSourceUnqueueBuffers),
+ DECL(alGenBuffers),
+ DECL(alDeleteBuffers),
+ DECL(alIsBuffer),
+ DECL(alBufferData),
+ DECL(alBufferf),
+ DECL(alBuffer3f),
+ DECL(alBufferfv),
+ DECL(alBufferi),
+ DECL(alBuffer3i),
+ DECL(alBufferiv),
+ DECL(alGetBufferf),
+ DECL(alGetBuffer3f),
+ DECL(alGetBufferfv),
+ DECL(alGetBufferi),
+ DECL(alGetBuffer3i),
+ DECL(alGetBufferiv),
+ DECL(alDopplerFactor),
+ DECL(alDopplerVelocity),
+ DECL(alSpeedOfSound),
+ DECL(alDistanceModel),
+
+ DECL(alGenFilters),
+ DECL(alDeleteFilters),
+ DECL(alIsFilter),
+ DECL(alFilteri),
+ DECL(alFilteriv),
+ DECL(alFilterf),
+ DECL(alFilterfv),
+ DECL(alGetFilteri),
+ DECL(alGetFilteriv),
+ DECL(alGetFilterf),
+ DECL(alGetFilterfv),
+ DECL(alGenEffects),
+ DECL(alDeleteEffects),
+ DECL(alIsEffect),
+ DECL(alEffecti),
+ DECL(alEffectiv),
+ DECL(alEffectf),
+ DECL(alEffectfv),
+ DECL(alGetEffecti),
+ DECL(alGetEffectiv),
+ DECL(alGetEffectf),
+ DECL(alGetEffectfv),
+ DECL(alGenAuxiliaryEffectSlots),
+ DECL(alDeleteAuxiliaryEffectSlots),
+ DECL(alIsAuxiliaryEffectSlot),
+ DECL(alAuxiliaryEffectSloti),
+ DECL(alAuxiliaryEffectSlotiv),
+ DECL(alAuxiliaryEffectSlotf),
+ DECL(alAuxiliaryEffectSlotfv),
+ DECL(alGetAuxiliaryEffectSloti),
+ DECL(alGetAuxiliaryEffectSlotiv),
+ DECL(alGetAuxiliaryEffectSlotf),
+ DECL(alGetAuxiliaryEffectSlotfv),
+
+ DECL(alDeferUpdatesSOFT),
+ DECL(alProcessUpdatesSOFT),
+
+ DECL(alSourcedSOFT),
+ DECL(alSource3dSOFT),
+ DECL(alSourcedvSOFT),
+ DECL(alGetSourcedSOFT),
+ DECL(alGetSource3dSOFT),
+ DECL(alGetSourcedvSOFT),
+ DECL(alSourcei64SOFT),
+ DECL(alSource3i64SOFT),
+ DECL(alSourcei64vSOFT),
+ DECL(alGetSourcei64SOFT),
+ DECL(alGetSource3i64SOFT),
+ DECL(alGetSourcei64vSOFT),
+
+ DECL(alGetStringiSOFT),
+
+ DECL(alBufferStorageSOFT),
+ DECL(alMapBufferSOFT),
+ DECL(alUnmapBufferSOFT),
+ DECL(alFlushMappedBufferSOFT),
+
+ DECL(alEventControlSOFT),
+ DECL(alEventCallbackSOFT),
+ DECL(alGetPointerSOFT),
+ DECL(alGetPointervSOFT),
+
+ DECL(alBufferCallbackSOFT),
+ DECL(alGetBufferPtrSOFT),
+ DECL(alGetBuffer3PtrSOFT),
+ DECL(alGetBufferPtrvSOFT),
+
+ DECL(alAuxiliaryEffectSlotPlaySOFT),
+ DECL(alAuxiliaryEffectSlotPlayvSOFT),
+ DECL(alAuxiliaryEffectSlotStopSOFT),
+ DECL(alAuxiliaryEffectSlotStopvSOFT),
+
+ DECL(alSourcePlayAtTimeSOFT),
+ DECL(alSourcePlayAtTimevSOFT),
+
+ DECL(alBufferSubDataSOFT),
+
+ DECL(alBufferDataStatic),
+
+ DECL(alDebugMessageCallbackEXT),
+ DECL(alDebugMessageInsertEXT),
+ DECL(alDebugMessageControlEXT),
+ DECL(alPushDebugGroupEXT),
+ DECL(alPopDebugGroupEXT),
+ DECL(alGetDebugMessageLogEXT),
+
+ /* Direct Context functions */
+ DECL(alcGetProcAddress2),
+ DECL(alEnableDirect),
+ DECL(alDisableDirect),
+ DECL(alIsEnabledDirect),
+ DECL(alDopplerFactorDirect),
+ DECL(alSpeedOfSoundDirect),
+ DECL(alDistanceModelDirect),
+ DECL(alGetStringDirect),
+ DECL(alGetBooleanvDirect),
+ DECL(alGetIntegervDirect),
+ DECL(alGetFloatvDirect),
+ DECL(alGetDoublevDirect),
+ DECL(alGetBooleanDirect),
+ DECL(alGetIntegerDirect),
+ DECL(alGetFloatDirect),
+ DECL(alGetDoubleDirect),
+
+ DECL(alGetErrorDirect),
+ DECL(alIsExtensionPresentDirect),
+ DECL(alGetProcAddress),
+ DECL(alGetEnumValueDirect),
+
+ DECL(alListeneriDirect),
+ DECL(alListener3iDirect),
+ DECL(alListenerivDirect),
+ DECL(alListenerfDirect),
+ DECL(alListener3fDirect),
+ DECL(alListenerfvDirect),
+ DECL(alGetListeneriDirect),
+ DECL(alGetListener3iDirect),
+ DECL(alGetListenerivDirect),
+ DECL(alGetListenerfDirect),
+ DECL(alGetListener3fDirect),
+ DECL(alGetListenerfvDirect),
+
+ DECL(alGenBuffersDirect),
+ DECL(alDeleteBuffersDirect),
+ DECL(alIsBufferDirect),
+ DECL(alBufferDataDirect),
+ DECL(alBufferiDirect),
+ DECL(alBuffer3iDirect),
+ DECL(alBufferivDirect),
+ DECL(alBufferfDirect),
+ DECL(alBuffer3fDirect),
+ DECL(alBufferfvDirect),
+ DECL(alGetBufferiDirect),
+ DECL(alGetBuffer3iDirect),
+ DECL(alGetBufferivDirect),
+ DECL(alGetBufferfDirect),
+ DECL(alGetBuffer3fDirect),
+ DECL(alGetBufferfvDirect),
+
+ DECL(alGenSourcesDirect),
+ DECL(alDeleteSourcesDirect),
+ DECL(alIsSourceDirect),
+ DECL(alSourcePlayDirect),
+ DECL(alSourceStopDirect),
+ DECL(alSourcePauseDirect),
+ DECL(alSourceRewindDirect),
+ DECL(alSourcePlayvDirect),
+ DECL(alSourceStopvDirect),
+ DECL(alSourcePausevDirect),
+ DECL(alSourceRewindvDirect),
+ DECL(alSourceiDirect),
+ DECL(alSource3iDirect),
+ DECL(alSourceivDirect),
+ DECL(alSourcefDirect),
+ DECL(alSource3fDirect),
+ DECL(alSourcefvDirect),
+ DECL(alGetSourceiDirect),
+ DECL(alGetSource3iDirect),
+ DECL(alGetSourceivDirect),
+ DECL(alGetSourcefDirect),
+ DECL(alGetSource3fDirect),
+ DECL(alGetSourcefvDirect),
+ DECL(alSourceQueueBuffersDirect),
+ DECL(alSourceUnqueueBuffersDirect),
+
+ DECL(alGenFiltersDirect),
+ DECL(alDeleteFiltersDirect),
+ DECL(alIsFilterDirect),
+ DECL(alFilteriDirect),
+ DECL(alFilterivDirect),
+ DECL(alFilterfDirect),
+ DECL(alFilterfvDirect),
+ DECL(alGetFilteriDirect),
+ DECL(alGetFilterivDirect),
+ DECL(alGetFilterfDirect),
+ DECL(alGetFilterfvDirect),
+ DECL(alGenEffectsDirect),
+ DECL(alDeleteEffectsDirect),
+ DECL(alIsEffectDirect),
+ DECL(alEffectiDirect),
+ DECL(alEffectivDirect),
+ DECL(alEffectfDirect),
+ DECL(alEffectfvDirect),
+ DECL(alGetEffectiDirect),
+ DECL(alGetEffectivDirect),
+ DECL(alGetEffectfDirect),
+ DECL(alGetEffectfvDirect),
+ DECL(alGenAuxiliaryEffectSlotsDirect),
+ DECL(alDeleteAuxiliaryEffectSlotsDirect),
+ DECL(alIsAuxiliaryEffectSlotDirect),
+ DECL(alAuxiliaryEffectSlotiDirect),
+ DECL(alAuxiliaryEffectSlotivDirect),
+ DECL(alAuxiliaryEffectSlotfDirect),
+ DECL(alAuxiliaryEffectSlotfvDirect),
+ DECL(alGetAuxiliaryEffectSlotiDirect),
+ DECL(alGetAuxiliaryEffectSlotivDirect),
+ DECL(alGetAuxiliaryEffectSlotfDirect),
+ DECL(alGetAuxiliaryEffectSlotfvDirect),
+
+ DECL(alDeferUpdatesDirectSOFT),
+ DECL(alProcessUpdatesDirectSOFT),
+ DECL(alGetStringiDirectSOFT),
+
+ DECL(alBufferDataStaticDirect),
+ DECL(alBufferCallbackDirectSOFT),
+ DECL(alBufferSubDataDirectSOFT),
+ DECL(alBufferStorageDirectSOFT),
+ DECL(alMapBufferDirectSOFT),
+ DECL(alUnmapBufferDirectSOFT),
+ DECL(alFlushMappedBufferDirectSOFT),
+
+ DECL(alSourcei64DirectSOFT),
+ DECL(alSource3i64DirectSOFT),
+ DECL(alSourcei64vDirectSOFT),
+ DECL(alSourcedDirectSOFT),
+ DECL(alSource3dDirectSOFT),
+ DECL(alSourcedvDirectSOFT),
+ DECL(alGetSourcei64DirectSOFT),
+ DECL(alGetSource3i64DirectSOFT),
+ DECL(alGetSourcei64vDirectSOFT),
+ DECL(alGetSourcedDirectSOFT),
+ DECL(alGetSource3dDirectSOFT),
+ DECL(alGetSourcedvDirectSOFT),
+ DECL(alSourcePlayAtTimeDirectSOFT),
+ DECL(alSourcePlayAtTimevDirectSOFT),
+
+ DECL(alEventControlDirectSOFT),
+ DECL(alEventCallbackDirectSOFT),
+
+ DECL(alDebugMessageCallbackDirectEXT),
+ DECL(alDebugMessageInsertDirectEXT),
+ DECL(alDebugMessageControlDirectEXT),
+ DECL(alPushDebugGroupDirectEXT),
+ DECL(alPopDebugGroupDirectEXT),
+ DECL(alGetDebugMessageLogDirectEXT),
+ DECL(alObjectLabelEXT),
+ DECL(alObjectLabelDirectEXT),
+ DECL(alGetObjectLabelEXT),
+ DECL(alGetObjectLabelDirectEXT),
+
+ /* Extra functions */
+ DECL(alsoft_set_log_callback),
+#ifdef ALSOFT_EAX
+}, eaxFunctions[]{
+ DECL(EAXGet),
+ DECL(EAXSet),
+ DECL(EAXGetBufferMode),
+ DECL(EAXSetBufferMode),
+
+ DECL(EAXGetDirect),
+ DECL(EAXSetDirect),
+ DECL(EAXGetBufferModeDirect),
+ DECL(EAXSetBufferModeDirect),
+#endif
+};
+#undef DECL
+
+struct EnumExport {
+ const char *enumName;
+ int value;
+};
+#define DECL(x) { #x, (x) }
+inline const EnumExport alcEnumerations[]{
+ DECL(ALC_INVALID),
+ DECL(ALC_FALSE),
+ DECL(ALC_TRUE),
+
+ DECL(ALC_MAJOR_VERSION),
+ DECL(ALC_MINOR_VERSION),
+ DECL(ALC_ATTRIBUTES_SIZE),
+ DECL(ALC_ALL_ATTRIBUTES),
+ DECL(ALC_DEFAULT_DEVICE_SPECIFIER),
+ DECL(ALC_DEVICE_SPECIFIER),
+ DECL(ALC_ALL_DEVICES_SPECIFIER),
+ DECL(ALC_DEFAULT_ALL_DEVICES_SPECIFIER),
+ DECL(ALC_EXTENSIONS),
+ DECL(ALC_FREQUENCY),
+ DECL(ALC_REFRESH),
+ DECL(ALC_SYNC),
+ DECL(ALC_MONO_SOURCES),
+ DECL(ALC_STEREO_SOURCES),
+ DECL(ALC_CAPTURE_DEVICE_SPECIFIER),
+ DECL(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER),
+ DECL(ALC_CAPTURE_SAMPLES),
+ DECL(ALC_CONNECTED),
+
+ DECL(ALC_EFX_MAJOR_VERSION),
+ DECL(ALC_EFX_MINOR_VERSION),
+ DECL(ALC_MAX_AUXILIARY_SENDS),
+
+ DECL(ALC_FORMAT_CHANNELS_SOFT),
+ DECL(ALC_FORMAT_TYPE_SOFT),
+
+ DECL(ALC_MONO_SOFT),
+ DECL(ALC_STEREO_SOFT),
+ DECL(ALC_QUAD_SOFT),
+ DECL(ALC_5POINT1_SOFT),
+ DECL(ALC_6POINT1_SOFT),
+ DECL(ALC_7POINT1_SOFT),
+ DECL(ALC_BFORMAT3D_SOFT),
+
+ DECL(ALC_BYTE_SOFT),
+ DECL(ALC_UNSIGNED_BYTE_SOFT),
+ DECL(ALC_SHORT_SOFT),
+ DECL(ALC_UNSIGNED_SHORT_SOFT),
+ DECL(ALC_INT_SOFT),
+ DECL(ALC_UNSIGNED_INT_SOFT),
+ DECL(ALC_FLOAT_SOFT),
+
+ DECL(ALC_HRTF_SOFT),
+ DECL(ALC_DONT_CARE_SOFT),
+ DECL(ALC_HRTF_STATUS_SOFT),
+ DECL(ALC_HRTF_DISABLED_SOFT),
+ DECL(ALC_HRTF_ENABLED_SOFT),
+ DECL(ALC_HRTF_DENIED_SOFT),
+ DECL(ALC_HRTF_REQUIRED_SOFT),
+ DECL(ALC_HRTF_HEADPHONES_DETECTED_SOFT),
+ DECL(ALC_HRTF_UNSUPPORTED_FORMAT_SOFT),
+ DECL(ALC_NUM_HRTF_SPECIFIERS_SOFT),
+ DECL(ALC_HRTF_SPECIFIER_SOFT),
+ DECL(ALC_HRTF_ID_SOFT),
+
+ DECL(ALC_AMBISONIC_LAYOUT_SOFT),
+ DECL(ALC_AMBISONIC_SCALING_SOFT),
+ DECL(ALC_AMBISONIC_ORDER_SOFT),
+ DECL(ALC_ACN_SOFT),
+ DECL(ALC_FUMA_SOFT),
+ DECL(ALC_N3D_SOFT),
+ DECL(ALC_SN3D_SOFT),
+
+ DECL(ALC_OUTPUT_LIMITER_SOFT),
+
+ DECL(ALC_DEVICE_CLOCK_SOFT),
+ DECL(ALC_DEVICE_LATENCY_SOFT),
+ DECL(ALC_DEVICE_CLOCK_LATENCY_SOFT),
+ DECL(AL_SAMPLE_OFFSET_CLOCK_SOFT),
+ DECL(AL_SEC_OFFSET_CLOCK_SOFT),
+
+ DECL(ALC_OUTPUT_MODE_SOFT),
+ DECL(ALC_ANY_SOFT),
+ DECL(ALC_STEREO_BASIC_SOFT),
+ DECL(ALC_STEREO_UHJ_SOFT),
+ DECL(ALC_STEREO_HRTF_SOFT),
+ DECL(ALC_SURROUND_5_1_SOFT),
+ DECL(ALC_SURROUND_6_1_SOFT),
+ DECL(ALC_SURROUND_7_1_SOFT),
+
+ DECL(ALC_NO_ERROR),
+ DECL(ALC_INVALID_DEVICE),
+ DECL(ALC_INVALID_CONTEXT),
+ DECL(ALC_INVALID_ENUM),
+ DECL(ALC_INVALID_VALUE),
+ DECL(ALC_OUT_OF_MEMORY),
+
+ DECL(ALC_CONTEXT_FLAGS_EXT),
+ DECL(ALC_CONTEXT_DEBUG_BIT_EXT),
+
+ DECL(ALC_PLAYBACK_DEVICE_SOFT),
+ DECL(ALC_CAPTURE_DEVICE_SOFT),
+ DECL(ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT),
+ DECL(ALC_EVENT_TYPE_DEVICE_ADDED_SOFT),
+ DECL(ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT),
+
+
+ DECL(AL_INVALID),
+ DECL(AL_NONE),
+ DECL(AL_FALSE),
+ DECL(AL_TRUE),
+
+ DECL(AL_SOURCE_RELATIVE),
+ DECL(AL_CONE_INNER_ANGLE),
+ DECL(AL_CONE_OUTER_ANGLE),
+ DECL(AL_PITCH),
+ DECL(AL_POSITION),
+ DECL(AL_DIRECTION),
+ DECL(AL_VELOCITY),
+ DECL(AL_LOOPING),
+ DECL(AL_BUFFER),
+ DECL(AL_GAIN),
+ DECL(AL_MIN_GAIN),
+ DECL(AL_MAX_GAIN),
+ DECL(AL_ORIENTATION),
+ DECL(AL_REFERENCE_DISTANCE),
+ DECL(AL_ROLLOFF_FACTOR),
+ DECL(AL_CONE_OUTER_GAIN),
+ DECL(AL_MAX_DISTANCE),
+ DECL(AL_SEC_OFFSET),
+ DECL(AL_SAMPLE_OFFSET),
+ DECL(AL_BYTE_OFFSET),
+ DECL(AL_SOURCE_TYPE),
+ DECL(AL_STATIC),
+ DECL(AL_STREAMING),
+ DECL(AL_UNDETERMINED),
+ DECL(AL_METERS_PER_UNIT),
+ DECL(AL_LOOP_POINTS_SOFT),
+ DECL(AL_DIRECT_CHANNELS_SOFT),
+
+ DECL(AL_DIRECT_FILTER),
+ DECL(AL_AUXILIARY_SEND_FILTER),
+ DECL(AL_AIR_ABSORPTION_FACTOR),
+ DECL(AL_ROOM_ROLLOFF_FACTOR),
+ DECL(AL_CONE_OUTER_GAINHF),
+ DECL(AL_DIRECT_FILTER_GAINHF_AUTO),
+ DECL(AL_AUXILIARY_SEND_FILTER_GAIN_AUTO),
+ DECL(AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO),
+
+ DECL(AL_SOURCE_STATE),
+ DECL(AL_INITIAL),
+ DECL(AL_PLAYING),
+ DECL(AL_PAUSED),
+ DECL(AL_STOPPED),
+
+ DECL(AL_BUFFERS_QUEUED),
+ DECL(AL_BUFFERS_PROCESSED),
+
+ DECL(AL_FORMAT_MONO8),
+ DECL(AL_FORMAT_MONO16),
+ DECL(AL_FORMAT_MONO_FLOAT32),
+ DECL(AL_FORMAT_MONO_DOUBLE_EXT),
+ DECL(AL_FORMAT_STEREO8),
+ DECL(AL_FORMAT_STEREO16),
+ DECL(AL_FORMAT_STEREO_FLOAT32),
+ DECL(AL_FORMAT_STEREO_DOUBLE_EXT),
+ DECL(AL_FORMAT_MONO_IMA4),
+ DECL(AL_FORMAT_STEREO_IMA4),
+ DECL(AL_FORMAT_MONO_MSADPCM_SOFT),
+ DECL(AL_FORMAT_STEREO_MSADPCM_SOFT),
+ DECL(AL_FORMAT_QUAD8_LOKI),
+ DECL(AL_FORMAT_QUAD16_LOKI),
+ DECL(AL_FORMAT_QUAD8),
+ DECL(AL_FORMAT_QUAD16),
+ DECL(AL_FORMAT_QUAD32),
+ DECL(AL_FORMAT_51CHN8),
+ DECL(AL_FORMAT_51CHN16),
+ DECL(AL_FORMAT_51CHN32),
+ DECL(AL_FORMAT_61CHN8),
+ DECL(AL_FORMAT_61CHN16),
+ DECL(AL_FORMAT_61CHN32),
+ DECL(AL_FORMAT_71CHN8),
+ DECL(AL_FORMAT_71CHN16),
+ DECL(AL_FORMAT_71CHN32),
+ DECL(AL_FORMAT_REAR8),
+ DECL(AL_FORMAT_REAR16),
+ DECL(AL_FORMAT_REAR32),
+ DECL(AL_FORMAT_MONO_MULAW),
+ DECL(AL_FORMAT_MONO_MULAW_EXT),
+ DECL(AL_FORMAT_STEREO_MULAW),
+ DECL(AL_FORMAT_STEREO_MULAW_EXT),
+ DECL(AL_FORMAT_QUAD_MULAW),
+ DECL(AL_FORMAT_51CHN_MULAW),
+ DECL(AL_FORMAT_61CHN_MULAW),
+ DECL(AL_FORMAT_71CHN_MULAW),
+ DECL(AL_FORMAT_REAR_MULAW),
+ DECL(AL_FORMAT_MONO_ALAW_EXT),
+ DECL(AL_FORMAT_STEREO_ALAW_EXT),
+
+ DECL(AL_FORMAT_BFORMAT2D_8),
+ DECL(AL_FORMAT_BFORMAT2D_16),
+ DECL(AL_FORMAT_BFORMAT2D_FLOAT32),
+ DECL(AL_FORMAT_BFORMAT2D_MULAW),
+ DECL(AL_FORMAT_BFORMAT3D_8),
+ DECL(AL_FORMAT_BFORMAT3D_16),
+ DECL(AL_FORMAT_BFORMAT3D_FLOAT32),
+ DECL(AL_FORMAT_BFORMAT3D_MULAW),
+
+ DECL(AL_FORMAT_UHJ2CHN8_SOFT),
+ DECL(AL_FORMAT_UHJ2CHN16_SOFT),
+ DECL(AL_FORMAT_UHJ2CHN_FLOAT32_SOFT),
+ DECL(AL_FORMAT_UHJ3CHN8_SOFT),
+ DECL(AL_FORMAT_UHJ3CHN16_SOFT),
+ DECL(AL_FORMAT_UHJ3CHN_FLOAT32_SOFT),
+ DECL(AL_FORMAT_UHJ4CHN8_SOFT),
+ DECL(AL_FORMAT_UHJ4CHN16_SOFT),
+ DECL(AL_FORMAT_UHJ4CHN_FLOAT32_SOFT),
+ DECL(AL_STEREO_MODE_SOFT),
+ DECL(AL_NORMAL_SOFT),
+ DECL(AL_SUPER_STEREO_SOFT),
+ DECL(AL_SUPER_STEREO_WIDTH_SOFT),
+
+ DECL(AL_FORMAT_UHJ2CHN_MULAW_SOFT),
+ DECL(AL_FORMAT_UHJ2CHN_ALAW_SOFT),
+ DECL(AL_FORMAT_UHJ2CHN_IMA4_SOFT),
+ DECL(AL_FORMAT_UHJ2CHN_MSADPCM_SOFT),
+ DECL(AL_FORMAT_UHJ3CHN_MULAW_SOFT),
+ DECL(AL_FORMAT_UHJ3CHN_ALAW_SOFT),
+ DECL(AL_FORMAT_UHJ4CHN_MULAW_SOFT),
+ DECL(AL_FORMAT_UHJ4CHN_ALAW_SOFT),
+
+ DECL(AL_FORMAT_MONO_I32),
+ DECL(AL_FORMAT_STEREO_I32),
+ DECL(AL_FORMAT_REAR_I32),
+ DECL(AL_FORMAT_QUAD_I32),
+ DECL(AL_FORMAT_51CHN_I32),
+ DECL(AL_FORMAT_61CHN_I32),
+ DECL(AL_FORMAT_71CHN_I32),
+ DECL(AL_FORMAT_UHJ2CHN_I32),
+ DECL(AL_FORMAT_UHJ3CHN_I32),
+ DECL(AL_FORMAT_UHJ4CHN_I32),
+
+ DECL(AL_FORMAT_REAR_FLOAT32),
+ DECL(AL_FORMAT_QUAD_FLOAT32),
+ DECL(AL_FORMAT_51CHN_FLOAT32),
+ DECL(AL_FORMAT_61CHN_FLOAT32),
+ DECL(AL_FORMAT_71CHN_FLOAT32),
+
+ DECL(AL_FREQUENCY),
+ DECL(AL_BITS),
+ DECL(AL_CHANNELS),
+ DECL(AL_SIZE),
+ DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT),
+ DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT),
+
+ DECL(AL_SOURCE_RADIUS),
+
+ DECL(AL_SAMPLE_OFFSET_LATENCY_SOFT),
+ DECL(AL_SEC_OFFSET_LATENCY_SOFT),
+
+ DECL(AL_STEREO_ANGLES),
+
+ DECL(AL_UNUSED),
+ DECL(AL_PENDING),
+ DECL(AL_PROCESSED),
+
+ DECL(AL_NO_ERROR),
+ DECL(AL_INVALID_NAME),
+ DECL(AL_INVALID_ENUM),
+ DECL(AL_INVALID_VALUE),
+ DECL(AL_INVALID_OPERATION),
+ DECL(AL_OUT_OF_MEMORY),
+
+ DECL(AL_VENDOR),
+ DECL(AL_VERSION),
+ DECL(AL_RENDERER),
+ DECL(AL_EXTENSIONS),
+
+ DECL(AL_DOPPLER_FACTOR),
+ DECL(AL_DOPPLER_VELOCITY),
+ DECL(AL_DISTANCE_MODEL),
+ DECL(AL_SPEED_OF_SOUND),
+ DECL(AL_SOURCE_DISTANCE_MODEL),
+ DECL(AL_DEFERRED_UPDATES_SOFT),
+ DECL(AL_GAIN_LIMIT_SOFT),
+
+ DECL(AL_INVERSE_DISTANCE),
+ DECL(AL_INVERSE_DISTANCE_CLAMPED),
+ DECL(AL_LINEAR_DISTANCE),
+ DECL(AL_LINEAR_DISTANCE_CLAMPED),
+ DECL(AL_EXPONENT_DISTANCE),
+ DECL(AL_EXPONENT_DISTANCE_CLAMPED),
+
+ DECL(AL_FILTER_TYPE),
+ DECL(AL_FILTER_NULL),
+ DECL(AL_FILTER_LOWPASS),
+ DECL(AL_FILTER_HIGHPASS),
+ DECL(AL_FILTER_BANDPASS),
+
+ DECL(AL_LOWPASS_GAIN),
+ DECL(AL_LOWPASS_GAINHF),
+
+ DECL(AL_HIGHPASS_GAIN),
+ DECL(AL_HIGHPASS_GAINLF),
+
+ DECL(AL_BANDPASS_GAIN),
+ DECL(AL_BANDPASS_GAINHF),
+ DECL(AL_BANDPASS_GAINLF),
+
+ DECL(AL_EFFECT_TYPE),
+ DECL(AL_EFFECT_NULL),
+ DECL(AL_EFFECT_REVERB),
+ DECL(AL_EFFECT_EAXREVERB),
+ DECL(AL_EFFECT_CHORUS),
+ DECL(AL_EFFECT_DISTORTION),
+ DECL(AL_EFFECT_ECHO),
+ DECL(AL_EFFECT_FLANGER),
+ DECL(AL_EFFECT_PITCH_SHIFTER),
+ DECL(AL_EFFECT_FREQUENCY_SHIFTER),
+ DECL(AL_EFFECT_VOCAL_MORPHER),
+ DECL(AL_EFFECT_RING_MODULATOR),
+ DECL(AL_EFFECT_AUTOWAH),
+ DECL(AL_EFFECT_COMPRESSOR),
+ DECL(AL_EFFECT_EQUALIZER),
+ DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT),
+ DECL(AL_EFFECT_DEDICATED_DIALOGUE),
+
+ DECL(AL_EFFECTSLOT_EFFECT),
+ DECL(AL_EFFECTSLOT_GAIN),
+ DECL(AL_EFFECTSLOT_AUXILIARY_SEND_AUTO),
+ DECL(AL_EFFECTSLOT_NULL),
+
+ DECL(AL_EAXREVERB_DENSITY),
+ DECL(AL_EAXREVERB_DIFFUSION),
+ DECL(AL_EAXREVERB_GAIN),
+ DECL(AL_EAXREVERB_GAINHF),
+ DECL(AL_EAXREVERB_GAINLF),
+ DECL(AL_EAXREVERB_DECAY_TIME),
+ DECL(AL_EAXREVERB_DECAY_HFRATIO),
+ DECL(AL_EAXREVERB_DECAY_LFRATIO),
+ DECL(AL_EAXREVERB_REFLECTIONS_GAIN),
+ DECL(AL_EAXREVERB_REFLECTIONS_DELAY),
+ DECL(AL_EAXREVERB_REFLECTIONS_PAN),
+ DECL(AL_EAXREVERB_LATE_REVERB_GAIN),
+ DECL(AL_EAXREVERB_LATE_REVERB_DELAY),
+ DECL(AL_EAXREVERB_LATE_REVERB_PAN),
+ DECL(AL_EAXREVERB_ECHO_TIME),
+ DECL(AL_EAXREVERB_ECHO_DEPTH),
+ DECL(AL_EAXREVERB_MODULATION_TIME),
+ DECL(AL_EAXREVERB_MODULATION_DEPTH),
+ DECL(AL_EAXREVERB_AIR_ABSORPTION_GAINHF),
+ DECL(AL_EAXREVERB_HFREFERENCE),
+ DECL(AL_EAXREVERB_LFREFERENCE),
+ DECL(AL_EAXREVERB_ROOM_ROLLOFF_FACTOR),
+ DECL(AL_EAXREVERB_DECAY_HFLIMIT),
+
+ DECL(AL_REVERB_DENSITY),
+ DECL(AL_REVERB_DIFFUSION),
+ DECL(AL_REVERB_GAIN),
+ DECL(AL_REVERB_GAINHF),
+ DECL(AL_REVERB_DECAY_TIME),
+ DECL(AL_REVERB_DECAY_HFRATIO),
+ DECL(AL_REVERB_REFLECTIONS_GAIN),
+ DECL(AL_REVERB_REFLECTIONS_DELAY),
+ DECL(AL_REVERB_LATE_REVERB_GAIN),
+ DECL(AL_REVERB_LATE_REVERB_DELAY),
+ DECL(AL_REVERB_AIR_ABSORPTION_GAINHF),
+ DECL(AL_REVERB_ROOM_ROLLOFF_FACTOR),
+ DECL(AL_REVERB_DECAY_HFLIMIT),
+
+ DECL(AL_CHORUS_WAVEFORM),
+ DECL(AL_CHORUS_PHASE),
+ DECL(AL_CHORUS_RATE),
+ DECL(AL_CHORUS_DEPTH),
+ DECL(AL_CHORUS_FEEDBACK),
+ DECL(AL_CHORUS_DELAY),
+
+ DECL(AL_DISTORTION_EDGE),
+ DECL(AL_DISTORTION_GAIN),
+ DECL(AL_DISTORTION_LOWPASS_CUTOFF),
+ DECL(AL_DISTORTION_EQCENTER),
+ DECL(AL_DISTORTION_EQBANDWIDTH),
+
+ DECL(AL_ECHO_DELAY),
+ DECL(AL_ECHO_LRDELAY),
+ DECL(AL_ECHO_DAMPING),
+ DECL(AL_ECHO_FEEDBACK),
+ DECL(AL_ECHO_SPREAD),
+
+ DECL(AL_FLANGER_WAVEFORM),
+ DECL(AL_FLANGER_PHASE),
+ DECL(AL_FLANGER_RATE),
+ DECL(AL_FLANGER_DEPTH),
+ DECL(AL_FLANGER_FEEDBACK),
+ DECL(AL_FLANGER_DELAY),
+
+ DECL(AL_FREQUENCY_SHIFTER_FREQUENCY),
+ DECL(AL_FREQUENCY_SHIFTER_LEFT_DIRECTION),
+ DECL(AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION),
+
+ DECL(AL_RING_MODULATOR_FREQUENCY),
+ DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF),
+ DECL(AL_RING_MODULATOR_WAVEFORM),
+
+ DECL(AL_PITCH_SHIFTER_COARSE_TUNE),
+ DECL(AL_PITCH_SHIFTER_FINE_TUNE),
+
+ DECL(AL_COMPRESSOR_ONOFF),
+
+ DECL(AL_EQUALIZER_LOW_GAIN),
+ DECL(AL_EQUALIZER_LOW_CUTOFF),
+ DECL(AL_EQUALIZER_MID1_GAIN),
+ DECL(AL_EQUALIZER_MID1_CENTER),
+ DECL(AL_EQUALIZER_MID1_WIDTH),
+ DECL(AL_EQUALIZER_MID2_GAIN),
+ DECL(AL_EQUALIZER_MID2_CENTER),
+ DECL(AL_EQUALIZER_MID2_WIDTH),
+ DECL(AL_EQUALIZER_HIGH_GAIN),
+ DECL(AL_EQUALIZER_HIGH_CUTOFF),
+
+ DECL(AL_DEDICATED_GAIN),
+
+ DECL(AL_AUTOWAH_ATTACK_TIME),
+ DECL(AL_AUTOWAH_RELEASE_TIME),
+ DECL(AL_AUTOWAH_RESONANCE),
+ DECL(AL_AUTOWAH_PEAK_GAIN),
+
+ DECL(AL_VOCAL_MORPHER_PHONEMEA),
+ DECL(AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING),
+ DECL(AL_VOCAL_MORPHER_PHONEMEB),
+ DECL(AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING),
+ DECL(AL_VOCAL_MORPHER_WAVEFORM),
+ DECL(AL_VOCAL_MORPHER_RATE),
+
+ DECL(AL_EFFECTSLOT_TARGET_SOFT),
+
+ DECL(AL_NUM_RESAMPLERS_SOFT),
+ DECL(AL_DEFAULT_RESAMPLER_SOFT),
+ DECL(AL_SOURCE_RESAMPLER_SOFT),
+ DECL(AL_RESAMPLER_NAME_SOFT),
+
+ DECL(AL_SOURCE_SPATIALIZE_SOFT),
+ DECL(AL_AUTO_SOFT),
+
+ DECL(AL_MAP_READ_BIT_SOFT),
+ DECL(AL_MAP_WRITE_BIT_SOFT),
+ DECL(AL_MAP_PERSISTENT_BIT_SOFT),
+ DECL(AL_PRESERVE_DATA_BIT_SOFT),
+
+ DECL(AL_EVENT_CALLBACK_FUNCTION_SOFT),
+ DECL(AL_EVENT_CALLBACK_USER_PARAM_SOFT),
+ DECL(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT),
+ DECL(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT),
+ DECL(AL_EVENT_TYPE_DISCONNECTED_SOFT),
+
+ DECL(AL_DROP_UNMATCHED_SOFT),
+ DECL(AL_REMIX_UNMATCHED_SOFT),
+
+ DECL(AL_AMBISONIC_LAYOUT_SOFT),
+ DECL(AL_AMBISONIC_SCALING_SOFT),
+ DECL(AL_FUMA_SOFT),
+ DECL(AL_ACN_SOFT),
+ DECL(AL_SN3D_SOFT),
+ DECL(AL_N3D_SOFT),
+
+ DECL(AL_BUFFER_CALLBACK_FUNCTION_SOFT),
+ DECL(AL_BUFFER_CALLBACK_USER_PARAM_SOFT),
+
+ DECL(AL_UNPACK_AMBISONIC_ORDER_SOFT),
+
+ DECL(AL_EFFECT_CONVOLUTION_SOFT),
+ DECL(AL_EFFECTSLOT_STATE_SOFT),
+
+ DECL(AL_DONT_CARE_EXT),
+ DECL(AL_DEBUG_OUTPUT_EXT),
+ DECL(AL_DEBUG_CALLBACK_FUNCTION_EXT),
+ DECL(AL_DEBUG_CALLBACK_USER_PARAM_EXT),
+ DECL(AL_DEBUG_SOURCE_API_EXT),
+ DECL(AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT),
+ DECL(AL_DEBUG_SOURCE_THIRD_PARTY_EXT),
+ DECL(AL_DEBUG_SOURCE_APPLICATION_EXT),
+ DECL(AL_DEBUG_SOURCE_OTHER_EXT),
+ DECL(AL_DEBUG_TYPE_ERROR_EXT),
+ DECL(AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT),
+ DECL(AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT),
+ DECL(AL_DEBUG_TYPE_PORTABILITY_EXT),
+ DECL(AL_DEBUG_TYPE_PERFORMANCE_EXT),
+ DECL(AL_DEBUG_TYPE_MARKER_EXT),
+ DECL(AL_DEBUG_TYPE_PUSH_GROUP_EXT),
+ DECL(AL_DEBUG_TYPE_POP_GROUP_EXT),
+ DECL(AL_DEBUG_TYPE_OTHER_EXT),
+ DECL(AL_DEBUG_SEVERITY_HIGH_EXT),
+ DECL(AL_DEBUG_SEVERITY_MEDIUM_EXT),
+ DECL(AL_DEBUG_SEVERITY_LOW_EXT),
+ DECL(AL_DEBUG_SEVERITY_NOTIFICATION_EXT),
+ DECL(AL_DEBUG_LOGGED_MESSAGES_EXT),
+ DECL(AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT),
+ DECL(AL_MAX_DEBUG_MESSAGE_LENGTH_EXT),
+ DECL(AL_MAX_DEBUG_LOGGED_MESSAGES_EXT),
+ DECL(AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT),
+ DECL(AL_MAX_LABEL_LENGTH_EXT),
+ DECL(AL_STACK_OVERFLOW_EXT),
+ DECL(AL_STACK_UNDERFLOW_EXT),
+ DECL(AL_BUFFER_EXT),
+ DECL(AL_SOURCE_EXT),
+ DECL(AL_FILTER_EXT),
+ DECL(AL_EFFECT_EXT),
+ DECL(AL_AUXILIARY_EFFECT_SLOT_EXT),
+
+ DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT),
+#ifdef ALSOFT_EAX
+}, eaxEnumerations[]{
+ DECL(AL_EAX_RAM_SIZE),
+ DECL(AL_EAX_RAM_FREE),
+ DECL(AL_STORAGE_AUTOMATIC),
+ DECL(AL_STORAGE_HARDWARE),
+ DECL(AL_STORAGE_ACCESSIBLE),
+#endif // ALSOFT_EAX
+};
+#undef DECL
+
+#endif /* ALC_EXPORT_LIST_H */
diff --git a/alc/inprogext.h b/alc/inprogext.h
index ccb9a4be..c5b09f13 100644
--- a/alc/inprogext.h
+++ b/alc/inprogext.h
@@ -16,15 +16,23 @@ typedef unsigned int ALbitfieldSOFT;
#define AL_MAP_WRITE_BIT_SOFT 0x00000002
#define AL_MAP_PERSISTENT_BIT_SOFT 0x00000004
#define AL_PRESERVE_DATA_BIT_SOFT 0x00000008
-typedef void (AL_APIENTRY*LPALBUFFERSTORAGESOFT)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags);
-typedef void* (AL_APIENTRY*LPALMAPBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access);
-typedef void (AL_APIENTRY*LPALUNMAPBUFFERSOFT)(ALuint buffer);
-typedef void (AL_APIENTRY*LPALFLUSHMAPPEDBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length);
+typedef void (AL_APIENTRY*LPALBUFFERSTORAGESOFT)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) AL_API_NOEXCEPT17;
+typedef void* (AL_APIENTRY*LPALMAPBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALUNMAPBUFFERSOFT)(ALuint buffer) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALFLUSHMAPPEDBUFFERSOFT)(ALuint buffer, ALsizei offset, ALsizei length) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALBUFFERSTORAGEDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) AL_API_NOEXCEPT17;
+typedef void* (AL_APIENTRY*LPALMAPBUFFERDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALUNMAPBUFFERDIRECTSOFT)(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALFLUSHMAPPEDBUFFERDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALsizei offset, ALsizei length) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags);
-AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access);
-AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer);
-AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length);
+AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) AL_API_NOEXCEPT;
+AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length) AL_API_NOEXCEPT;
+void AL_APIENTRY alBufferStorageDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) AL_API_NOEXCEPT;
+void* AL_APIENTRY alMapBufferDirectSOFT(ALCcontext *context, ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) AL_API_NOEXCEPT;
+void AL_APIENTRY alUnmapBufferDirectSOFT(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT;
+void AL_APIENTRY alFlushMappedBufferDirectSOFT(ALCcontext *context, ALuint buffer, ALsizei offset, ALsizei length) AL_API_NOEXCEPT;
#endif
#endif
@@ -33,19 +41,20 @@ AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, A
#define AL_UNPACK_AMBISONIC_ORDER_SOFT 0x199D
#endif
-#ifndef AL_SOFT_convolution_reverb
-#define AL_SOFT_convolution_reverb
-#define AL_EFFECT_CONVOLUTION_REVERB_SOFT 0xA000
-#define AL_EFFECTSLOT_STATE_SOFT 0x199D
-typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTPLAYSOFT)(ALuint slotid);
-typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTPLAYVSOFT)(ALsizei n, const ALuint *slotids);
-typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTSTOPSOFT)(ALuint slotid);
-typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTSTOPVSOFT)(ALsizei n, const ALuint *slotids);
+#ifndef AL_SOFT_convolution_effect
+#define AL_SOFT_convolution_effect
+#define AL_EFFECT_CONVOLUTION_SOFT 0xA000
+#define AL_CONVOLUTION_ORIENTATION_SOFT 0x100F /* same as AL_ORIENTATION */
+#define AL_EFFECTSLOT_STATE_SOFT 0x199E
+typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTPLAYSOFT)(ALuint slotid) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTPLAYVSOFT)(ALsizei n, const ALuint *slotids) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTSTOPSOFT)(ALuint slotid) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTSTOPVSOFT)(ALsizei n, const ALuint *slotids) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid);
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids);
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid);
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids);
+AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) AL_API_NOEXCEPT;
#endif
#endif
@@ -55,16 +64,374 @@ AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *
#endif
-/* Non-standard export. Not part of any extension. */
-AL_API const ALchar* AL_APIENTRY alsoft_get_version(void);
+#ifndef AL_EXT_direct_context
+#define AL_EXT_direct_context
+typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS2)(ALCdevice *device, const ALCchar *funcname) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALENABLEDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDISABLEDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISENABLEDDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDOPPLERFACTORDIRECT)(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSPEEDOFSOUNDDIRECT)(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDISTANCEMODELDIRECT)(ALCcontext *context, ALenum distanceModel) AL_API_NOEXCEPT17;
+typedef const ALchar* (AL_APIENTRY *LPALGETSTRINGDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBOOLEANVDIRECT)(ALCcontext *context, ALenum param, ALboolean *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETINTEGERVDIRECT)(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETFLOATVDIRECT)(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETDOUBLEVDIRECT)(ALCcontext *context, ALenum param, ALdouble *values) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALGETBOOLEANDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17;
+typedef ALint (AL_APIENTRY *LPALGETINTEGERDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17;
+typedef ALfloat (AL_APIENTRY *LPALGETFLOATDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17;
+typedef ALdouble (AL_APIENTRY *LPALGETDOUBLEDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17;
+typedef ALenum (AL_APIENTRY *LPALGETERRORDIRECT)(ALCcontext *context) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENTDIRECT)(ALCcontext *context, const ALchar *extname) AL_API_NOEXCEPT17;
+typedef void* (AL_APIENTRY *LPALGETPROCADDRESSDIRECT)(ALCcontext *context, const ALchar *fname) AL_API_NOEXCEPT17;
+typedef ALenum (AL_APIENTRY *LPALGETENUMVALUEDIRECT)(ALCcontext *context, const ALchar *ename) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALLISTENERFDIRECT)(ALCcontext *context, ALenum param, ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALLISTENER3FDIRECT)(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALLISTENERFVDIRECT)(ALCcontext *context, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALLISTENERIDIRECT)(ALCcontext *context, ALenum param, ALint value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALLISTENER3IDIRECT)(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALLISTENERIVDIRECT)(ALCcontext *context, ALenum param, const ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETLISTENERFDIRECT)(ALCcontext *context, ALenum param, ALfloat *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETLISTENER3FDIRECT)(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETLISTENERFVDIRECT)(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETLISTENERIDIRECT)(ALCcontext *context, ALenum param, ALint *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETLISTENER3IDIRECT)(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETLISTENERIVDIRECT)(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGENSOURCESDIRECT)(ALCcontext *context, ALsizei n, ALuint *sources) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDELETESOURCESDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISSOURCEDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEFDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCE3FDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEFVDIRECT)(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEIDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCE3IDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEIVDIRECT)(ALCcontext *context, ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCEFDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCE3FDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCEFVDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCEIDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCE3IDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCEIVDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEPLAYVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCESTOPVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEREWINDVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEPAUSEVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEPLAYDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCESTOPDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEREWINDDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEPAUSEDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERSDIRECT)(ALCcontext *context, ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERSDIRECT)(ALCcontext *context, ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGENBUFFERSDIRECT)(ALCcontext *context, ALsizei n, ALuint *buffers) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDELETEBUFFERSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISBUFFERDIRECT)(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFERDATADIRECT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFERFDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFER3FDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFERFVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFERIDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFER3IDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFERIVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFERFDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFER3FDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFERFVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFERIDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFER3IDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFERIVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT17;
+/* ALC_EXT_EFX */
+typedef void (AL_APIENTRY *LPALGENEFFECTSDIRECT)(ALCcontext *context, ALsizei n, ALuint *effects) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDELETEEFFECTSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *effects) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISEFFECTDIRECT)(ALCcontext *context, ALuint effect) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALEFFECTIDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALEFFECTIVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, const ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALEFFECTFDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALEFFECTFVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETEFFECTIDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETEFFECTIVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETEFFECTFDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETEFFECTFVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGENFILTERSDIRECT)(ALCcontext *context, ALsizei n, ALuint *filters) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDELETEFILTERSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *filters) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISFILTERDIRECT)(ALCcontext *context, ALuint filter) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALFILTERIDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALFILTERIVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, const ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALFILTERFDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALFILTERFVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETFILTERIDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETFILTERIVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETFILTERFDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETFILTERFVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTSDIRECT)(ALCcontext *context, ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOTDIRECT)(ALCcontext *context, ALuint effectslot) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *values) AL_API_NOEXCEPT17;
+/* AL_EXT_BUFFER_DATA_STATIC */
+typedef void (AL_APIENTRY *LPALBUFFERDATASTATICDIRECT)(ALCcontext *context, ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT17;
+/* AL_EXT_debug */
+typedef void (AL_APIENTRY*LPALDEBUGMESSAGECALLBACKDIRECTEXT)(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALDEBUGMESSAGEINSERTDIRECTEXT)(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALDEBUGMESSAGECONTROLDIRECTEXT)(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALPUSHDEBUGGROUPDIRECTEXT)(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALPOPDEBUGGROUPDIRECTEXT)(ALCcontext *context) AL_API_NOEXCEPT17;
+typedef ALuint (AL_APIENTRY*LPALGETDEBUGMESSAGELOGDIRECTEXT)(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALOBJECTLABELDIRECTEXT)(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETOBJECTLABELDIRECTEXT)(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT17;
+/* AL_EXT_FOLDBACK */
+typedef void (AL_APIENTRY *LPALREQUESTFOLDBACKSTARTDIRECT)(ALCcontext *context, ALenum mode, ALsizei count, ALsizei length, ALfloat *mem, LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALREQUESTFOLDBACKSTOPDIRECT)(ALCcontext *context) AL_API_NOEXCEPT17;
+/* AL_SOFT_buffer_sub_data */
+typedef void (AL_APIENTRY *LPALBUFFERSUBDATADIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) AL_API_NOEXCEPT17;
+/* AL_SOFT_source_latency */
+typedef void (AL_APIENTRY *LPALSOURCEDDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCE3DDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble,ALdouble,ALdouble) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEDVDIRECTSOFT)(ALCcontext*,ALuint,ALenum,const ALdouble*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCEDDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCE3DDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCEDVDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEI64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCE3I64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEI64VDIRECTSOFT)(ALCcontext*,ALuint,ALenum,const ALint64SOFT*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCEI64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCE3I64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCEI64VDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17;
+/* AL_SOFT_deferred_updates */
+typedef void (AL_APIENTRY *LPALDEFERUPDATESDIRECTSOFT)(ALCcontext *context) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALPROCESSUPDATESDIRECTSOFT)(ALCcontext *context) AL_API_NOEXCEPT17;
+/* AL_SOFT_source_resampler */
+typedef const ALchar* (AL_APIENTRY *LPALGETSTRINGIDIRECTSOFT)(ALCcontext *context, ALenum pname, ALsizei index) AL_API_NOEXCEPT17;
+/* AL_SOFT_events */
+typedef void (AL_APIENTRY *LPALEVENTCONTROLDIRECTSOFT)(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALEVENTCALLBACKDIRECTSOFT)(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT17;
+typedef void* (AL_APIENTRY *LPALGETPOINTERDIRECTSOFT)(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETPOINTERVDIRECTSOFT)(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT17;
+/* AL_SOFT_callback_buffer */
+typedef void (AL_APIENTRY *LPALBUFFERCALLBACKDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFERPTRDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFER3PTRDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFERPTRVDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **values) AL_API_NOEXCEPT17;
+/* AL_SOFT_source_start_delay */
+typedef void (AL_APIENTRY *LPALSOURCEPLAYATTIMEDIRECTSOFT)(ALCcontext *context, ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEPLAYATTIMEVDIRECTSOFT)(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT17;
+/* EAX */
+typedef ALenum (AL_APIENTRY *LPEAXSETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint property_source_id, ALvoid *property_buffer, ALuint property_size) AL_API_NOEXCEPT17;
+typedef ALenum (AL_APIENTRY *LPEAXGETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint property_source_id, ALvoid *property_value, ALuint property_value_size) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPEAXSETBUFFERMODEDIRECT)(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) AL_API_NOEXCEPT17;
+typedef ALenum (AL_APIENTRY *LPEAXGETBUFFERMODEDIRECT)(ALCcontext *context, ALuint buffer, ALint *pReserved) AL_API_NOEXCEPT17;
+#ifdef AL_ALEXT_PROTOTYPES
+ALCvoid* AL_APIENTRY alcGetProcAddress2(ALCdevice *device, const ALchar *funcName) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT;
+void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT;
+ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT;
+void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT;
+void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum distanceModel) AL_API_NOEXCEPT;
+
+const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum param, ALboolean *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum param, ALdouble *values) AL_API_NOEXCEPT;
+ALboolean AL_APIENTRY alGetBooleanDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT;
+ALint AL_APIENTRY alGetIntegerDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT;
+ALfloat AL_APIENTRY alGetFloatDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT;
+ALdouble AL_APIENTRY alGetDoubleDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT;
+
+ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) AL_API_NOEXCEPT;
+ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extname) AL_API_NOEXCEPT;
+void* AL_APIENTRY alGetProcAddressDirect(ALCcontext *context, const ALchar *fname) AL_API_NOEXCEPT;
+ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext *context, const ALchar *ename) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) AL_API_NOEXCEPT;
+void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param, const ALfloat *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint value) AL_API_NOEXCEPT;
+void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param, const ALint *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param, ALfloat *value) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alGenSourcesDirect(ALCcontext *context, ALsizei n, ALuint *sources) AL_API_NOEXCEPT;
+void AL_APIENTRY alDeleteSourcesDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT;
+ALboolean AL_APIENTRY alIsSourceDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT;
+void AL_APIENTRY alSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT;
+void AL_APIENTRY alSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourceivDirect(ALCcontext *context, ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetSourceivDirect(ALCcontext *context, ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourcePlayDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourceStopDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourceRewindDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourcePauseDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourcePlayvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourceStopvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourceRewindvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourcePausevDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourceUnqueueBuffersDirect(ALCcontext *context, ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alGenBuffersDirect(ALCcontext *context, ALsizei n, ALuint *buffers) AL_API_NOEXCEPT;
+void AL_APIENTRY alDeleteBuffersDirect(ALCcontext *context, ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT;
+ALboolean AL_APIENTRY alIsBufferDirect(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT;
+void AL_APIENTRY alBufferDataDirect(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT;
+void AL_APIENTRY alBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT;
+void AL_APIENTRY alBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT;
+void AL_APIENTRY alBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) AL_API_NOEXCEPT;
+void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n, const ALuint *effects) AL_API_NOEXCEPT;
+ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) AL_API_NOEXCEPT;
+void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint iValue) AL_API_NOEXCEPT;
+void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, const ALint *piValues) AL_API_NOEXCEPT;
+void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat flValue) AL_API_NOEXCEPT;
+void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *piValue) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *piValues) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) AL_API_NOEXCEPT;
+void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n, const ALuint *filters) AL_API_NOEXCEPT;
+ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) AL_API_NOEXCEPT;
+void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint iValue) AL_API_NOEXCEPT;
+void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, const ALint *piValues) AL_API_NOEXCEPT;
+void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat flValue) AL_API_NOEXCEPT;
+void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *piValue) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *piValues) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT;
+void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT;
+ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context, ALuint effectslot) AL_API_NOEXCEPT;
+void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint iValue) AL_API_NOEXCEPT;
+void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *piValues) AL_API_NOEXCEPT;
+void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat flValue) AL_API_NOEXCEPT;
+void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *piValue) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *piValues) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alBufferDataStaticDirect(ALCcontext *context, ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT;
+void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT;
+void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT;
+void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT;
+void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) AL_API_NOEXCEPT;
+ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT;
+void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alRequestFoldbackStartDirect(ALCcontext *context, ALenum mode, ALsizei count, ALsizei length, ALfloat *mem, LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT;
+void AL_APIENTRY alRequestFoldbackStopDirect(ALCcontext *context) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value) AL_API_NOEXCEPT;
+void AL_APIENTRY alSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALdouble *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value) AL_API_NOEXCEPT;
+void AL_APIENTRY alSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALint64SOFT *values) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *values) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alDeferUpdatesDirectSOFT(ALCcontext *context) AL_API_NOEXCEPT;
+void AL_APIENTRY alProcessUpdatesDirectSOFT(ALCcontext *context) AL_API_NOEXCEPT;
+
+const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context, ALenum pname, ALsizei index) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT;
+void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT;
+void* AL_APIENTRY alGetPointerDirectSOFT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alBufferCallbackDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetBufferPtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetBuffer3PtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT;
+
+void AL_APIENTRY alSourcePlayAtTimeDirectSOFT(ALCcontext *context, ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourcePlayAtTimevDirectSOFT(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT;
+
+ALenum AL_APIENTRY EAXSetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint property_source_id, ALvoid *property_value, ALuint property_value_size) AL_API_NOEXCEPT;
+ALenum AL_APIENTRY EAXGetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint property_source_id, ALvoid *property_value, ALuint property_value_size) AL_API_NOEXCEPT;
+ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) AL_API_NOEXCEPT;
+ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer, ALint *pReserved) AL_API_NOEXCEPT;
+#endif
+#endif
+
+#ifndef AL_EXT_int32
+#define AL_EXT_int32
+#define AL_FORMAT_MONO_I32 0x1202 /* Same as AL_FORMAT_MONO32 */
+#define AL_FORMAT_STEREO_I32 0x1203 /* Same as AL_FORMAT_STEREO32 */
+#define AL_FORMAT_REAR_I32 0x19DB
+#define AL_FORMAT_QUAD_I32 0x19DC
+#define AL_FORMAT_51CHN_I32 0x19DD
+#define AL_FORMAT_61CHN_I32 0x19DE
+#define AL_FORMAT_71CHN_I32 0x19DF
+#define AL_FORMAT_UHJ2CHN_I32 0x19E0
+#define AL_FORMAT_UHJ3CHN_I32 0x19E1
+#define AL_FORMAT_UHJ4CHN_I32 0x19E2
+
+#define AL_FORMAT_REAR_FLOAT32 0x19E3
+#define AL_FORMAT_QUAD_FLOAT32 0x19E4
+#define AL_FORMAT_51CHN_FLOAT32 0x19E5
+#define AL_FORMAT_61CHN_FLOAT32 0x19E6
+#define AL_FORMAT_71CHN_FLOAT32 0x19E7
+#endif
+
+/* Non-standard exports. Not part of any extension. */
+AL_API const ALchar* AL_APIENTRY alsoft_get_version(void) noexcept;
+
+typedef void (ALC_APIENTRY*LPALSOFTLOGCALLBACK)(void *userptr, char level, const char *message, int length) noexcept;
+void ALC_APIENTRY alsoft_set_log_callback(LPALSOFTLOGCALLBACK callback, void *userptr) noexcept;
/* Functions from abandoned extensions. Only here for binary compatibility. */
AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb,
- const ALuint *buffers);
+ const ALuint *buffers) noexcept;
-AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname);
-AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values);
+AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values) AL_API_NOEXCEPT;
+ALint64SOFT AL_APIENTRY alGetInteger64DirectSOFT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetInteger64vDirectSOFT(ALCcontext *context, ALenum pname, ALint64SOFT *values) AL_API_NOEXCEPT;
#ifdef __cplusplus
} /* extern "C" */
diff --git a/alc/panning.cpp b/alc/panning.cpp
index d118f99c..b512a42a 100644
--- a/alc/panning.cpp
+++ b/alc/panning.cpp
@@ -32,7 +32,9 @@
#include <memory>
#include <new>
#include <numeric>
+#include <optional>
#include <string>
+#include <vector>
#include "AL/al.h"
#include "AL/alc.h"
@@ -45,7 +47,6 @@
#include "almalloc.h"
#include "alnumbers.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "alspan.h"
#include "alstring.h"
#include "alu.h"
@@ -313,17 +314,17 @@ void InitDistanceComp(ALCdevice *device, const al::span<const Channel> channels,
}
-inline auto& GetAmbiScales(DevAmbiScaling scaletype) noexcept
+constexpr auto GetAmbiScales(DevAmbiScaling scaletype) noexcept
{
- if(scaletype == DevAmbiScaling::FuMa) return AmbiScale::FromFuMa();
- if(scaletype == DevAmbiScaling::SN3D) return AmbiScale::FromSN3D();
- return AmbiScale::FromN3D();
+ if(scaletype == DevAmbiScaling::FuMa) return al::span{AmbiScale::FromFuMa};
+ if(scaletype == DevAmbiScaling::SN3D) return al::span{AmbiScale::FromSN3D};
+ return al::span{AmbiScale::FromN3D};
}
-inline auto& GetAmbiLayout(DevAmbiLayout layouttype) noexcept
+constexpr auto GetAmbiLayout(DevAmbiLayout layouttype) noexcept
{
- if(layouttype == DevAmbiLayout::FuMa) return AmbiIndex::FromFuMa();
- return AmbiIndex::FromACN();
+ if(layouttype == DevAmbiLayout::FuMa) return al::span{AmbiIndex::FromFuMa};
+ return al::span{AmbiIndex::FromACN};
}
@@ -346,16 +347,16 @@ DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf,
}
std::copy_n(std::begin(conf->HFOrderGain),
- std::min(al::size(conf->HFOrderGain), al::size(decoder.mOrderGain)),
+ std::min(std::size(conf->HFOrderGain), std::size(decoder.mOrderGain)),
std::begin(decoder.mOrderGain));
std::copy_n(std::begin(conf->LFOrderGain),
- std::min(al::size(conf->LFOrderGain), al::size(decoder.mOrderGainLF)),
+ std::min(std::size(conf->LFOrderGain), std::size(decoder.mOrderGainLF)),
std::begin(decoder.mOrderGainLF));
const auto num_coeffs = decoder.mIs3D ? AmbiChannelsFromOrder(decoder.mOrder)
: Ambi2DChannelsFromOrder(decoder.mOrder);
- const auto idx_map = decoder.mIs3D ? AmbiIndex::FromACN().data()
- : AmbiIndex::FromACN2D().data();
+ const auto idx_map = decoder.mIs3D ? AmbiIndex::FromACN.data()
+ : AmbiIndex::FromACN2D.data();
const auto hfmatrix = conf->HFMatrix;
const auto lfmatrix = conf->LFMatrix;
@@ -600,13 +601,13 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
case DevFmtX714: decoder = X714Config; break;
case DevFmtX3D71: decoder = X3D71Config; break;
case DevFmtAmbi3D:
- auto&& acnmap = GetAmbiLayout(device->mAmbiLayout);
- auto&& n3dscale = GetAmbiScales(device->mAmbiScale);
+ const auto acnmap = GetAmbiLayout(device->mAmbiLayout);
+ const auto n3dscale = GetAmbiScales(device->mAmbiScale);
/* For DevFmtAmbi3D, the ambisonic order is already set. */
const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)};
std::transform(acnmap.begin(), acnmap.begin()+count, std::begin(device->Dry.AmbiMap),
- [&n3dscale](const uint8_t &acn) noexcept -> BFChannelConfig
+ [n3dscale](const uint8_t &acn) noexcept -> BFChannelConfig
{ return BFChannelConfig{1.0f/n3dscale[acn], acn}; });
AllocChannels(device, count, 0);
device->m2DMixing = false;
@@ -628,7 +629,7 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
const size_t ambicount{decoder.mIs3D ? AmbiChannelsFromOrder(decoder.mOrder) :
Ambi2DChannelsFromOrder(decoder.mOrder)};
const bool dual_band{hqdec && !decoder.mCoeffsLF.empty()};
- al::vector<ChannelDec> chancoeffs, chancoeffslf;
+ std::vector<ChannelDec> chancoeffs, chancoeffslf;
for(size_t i{0u};i < decoder.mChannels.size();++i)
{
const uint idx{device->channelIdxByName(decoder.mChannels[i])};
@@ -639,8 +640,8 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
continue;
}
- auto ordermap = decoder.mIs3D ? AmbiIndex::OrderFromChannel().data()
- : AmbiIndex::OrderFrom2DChannel().data();
+ auto ordermap = decoder.mIs3D ? AmbiIndex::OrderFromChannel.data()
+ : AmbiIndex::OrderFrom2DChannel.data();
chancoeffs.resize(maxz(chancoeffs.size(), idx+1u), ChannelDec{});
al::span<const float,MaxAmbiChannels> src{decoder.mCoeffs[i]};
@@ -662,11 +663,11 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
device->mAmbiOrder = decoder.mOrder;
device->m2DMixing = !decoder.mIs3D;
- const al::span<const uint8_t> acnmap{decoder.mIs3D ? AmbiIndex::FromACN().data() :
- AmbiIndex::FromACN2D().data(), ambicount};
- auto&& coeffscale = GetAmbiScales(decoder.mScaling);
+ const al::span<const uint8_t> acnmap{decoder.mIs3D ? AmbiIndex::FromACN.data() :
+ AmbiIndex::FromACN2D.data(), ambicount};
+ const auto coeffscale = GetAmbiScales(decoder.mScaling);
std::transform(acnmap.begin(), acnmap.end(), std::begin(device->Dry.AmbiMap),
- [&coeffscale](const uint8_t &acn) noexcept
+ [coeffscale](const uint8_t &acn) noexcept
{ return BFChannelConfig{1.0f/coeffscale[acn], acn}; });
AllocChannels(device, ambicount, device->channelsFromFmt());
@@ -676,7 +677,7 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
/* Only enable the stablizer if the decoder does not output to the
* front-center channel.
*/
- const auto cidx = device->RealOut.ChannelIndex[FrontCenter];
+ const size_t cidx{device->RealOut.ChannelIndex[FrontCenter]};
bool hasfc{false};
if(cidx < chancoeffs.size())
{
@@ -818,9 +819,9 @@ void InitHrtfPanning(ALCdevice *device)
/*RMS 8.340921354e-01f, 7.182670250e-01f, 5.107426573e-01f, 2.541870634e-01f*/
};
- static_assert(al::size(AmbiPoints1O) == al::size(AmbiMatrix1O), "First-Order Ambisonic HRTF mismatch");
- static_assert(al::size(AmbiPoints2O) == al::size(AmbiMatrix2O), "Second-Order Ambisonic HRTF mismatch");
- static_assert(al::size(AmbiPoints3O) == al::size(AmbiMatrix3O), "Third-Order Ambisonic HRTF mismatch");
+ static_assert(std::size(AmbiPoints1O) == std::size(AmbiMatrix1O), "First-Order Ambisonic HRTF mismatch");
+ static_assert(std::size(AmbiPoints2O) == std::size(AmbiMatrix2O), "Second-Order Ambisonic HRTF mismatch");
+ static_assert(std::size(AmbiPoints3O) == std::size(AmbiMatrix3O), "Third-Order Ambisonic HRTF mismatch");
/* A 700hz crossover frequency provides tighter sound imaging at the sweet
* spot with ambisonic decoding, as the distance between the ears is closer
@@ -901,7 +902,7 @@ void InitHrtfPanning(ALCdevice *device)
device->m2DMixing = false;
const size_t count{AmbiChannelsFromOrder(ambi_order)};
- std::transform(AmbiIndex::FromACN().begin(), AmbiIndex::FromACN().begin()+count,
+ std::transform(AmbiIndex::FromACN.begin(), AmbiIndex::FromACN.begin()+count,
std::begin(device->Dry.AmbiMap),
[](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; }
);
@@ -924,16 +925,16 @@ void InitUhjPanning(ALCdevice *device)
device->mAmbiOrder = 1;
device->m2DMixing = true;
- auto acnmap_begin = AmbiIndex::FromFuMa2D().begin();
+ auto acnmap_begin = AmbiIndex::FromFuMa2D.begin();
std::transform(acnmap_begin, acnmap_begin + count, std::begin(device->Dry.AmbiMap),
[](const uint8_t &acn) noexcept -> BFChannelConfig
- { return BFChannelConfig{1.0f/AmbiScale::FromUHJ()[acn], acn}; });
+ { return BFChannelConfig{1.0f/AmbiScale::FromUHJ[acn], acn}; });
AllocChannels(device, count, device->channelsFromFmt());
}
} // namespace
-void aluInitRenderer(ALCdevice *device, int hrtf_id, al::optional<StereoEncoding> stereomode)
+void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional<StereoEncoding> stereomode)
{
/* Hold the HRTF the device last used, in case it's used again. */
HrtfStorePtr old_hrtf{std::move(device->mHrtf)};
@@ -978,28 +979,39 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, al::optional<StereoEncoding
{
ERR("Failed to load layout file %s\n", config);
ERR(" %s\n", err->c_str());
+ return false;
}
else if(conf.NumSpeakers > MAX_OUTPUT_CHANNELS)
+ {
ERR("Unsupported decoder speaker count %zu (max %d)\n", conf.NumSpeakers,
MAX_OUTPUT_CHANNELS);
+ return false;
+ }
else if(conf.ChanMask > Ambi3OrderMask)
+ {
ERR("Unsupported decoder channel mask 0x%04x (max 0x%x)\n", conf.ChanMask,
Ambi3OrderMask);
- else
- {
- device->mXOverFreq = clampf(conf.XOverFreq, 100.0f, 1000.0f);
-
- decoder_store = std::make_unique<DecoderConfig<DualBand,MAX_OUTPUT_CHANNELS>>();
- decoder = MakeDecoderView(device, &conf, *decoder_store);
- for(size_t i{0};i < decoder.mChannels.size();++i)
- speakerdists[i] = conf.Speakers[i].Distance;
+ return false;
}
+
+ TRACE("Using %s decoder: \"%s\"\n", DevFmtChannelsString(device->FmtChans),
+ conf.Description.c_str());
+ device->mXOverFreq = clampf(conf.XOverFreq, 100.0f, 1000.0f);
+
+ decoder_store = std::make_unique<DecoderConfig<DualBand,MAX_OUTPUT_CHANNELS>>();
+ decoder = MakeDecoderView(device, &conf, *decoder_store);
+ for(size_t i{0};i < decoder.mChannels.size();++i)
+ speakerdists[i] = conf.Speakers[i].Distance;
+ return true;
};
+ bool usingCustom{false};
if(layout)
{
if(auto decopt = device->configValue<std::string>("decoder", layout))
- load_config(decopt->c_str());
+ usingCustom = load_config(decopt->c_str());
}
+ if(!usingCustom && device->FmtChans != DevFmtAmbi3D)
+ TRACE("Using built-in %s decoder\n", DevFmtChannelsString(device->FmtChans));
/* Enable the stablizer only for formats that have front-left, front-
* right, and front-center outputs.
@@ -1143,7 +1155,7 @@ void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context)
slot->mWetBuffer.resize(count);
- auto acnmap_begin = AmbiIndex::FromACN().begin();
+ auto acnmap_begin = AmbiIndex::FromACN.begin();
auto iter = std::transform(acnmap_begin, acnmap_begin + count, slot->Wet.AmbiMap.begin(),
[](const uint8_t &acn) noexcept -> BFChannelConfig
{ return BFChannelConfig{1.0f, acn}; });
diff --git a/alsoftrc.sample b/alsoftrc.sample
index 2906cca4..42802184 100644
--- a/alsoftrc.sample
+++ b/alsoftrc.sample
@@ -56,10 +56,10 @@
# Sets the default output channel configuration. If left unspecified, one will
# try to be detected from the system, with a fallback to stereo. The available
# values are: mono, stereo, quad, surround51, surround61, surround71,
-# surround3d71, ambi1, ambi2, ambi3. Note that the ambi* configurations output
-# ambisonic channels of the given order (using ACN ordering and SN3D
-# normalization by default), which need to be decoded to play correctly on
-# speakers.
+# surround714, surround3d71, ambi1, ambi2, ambi3. Note that the ambi*
+# configurations output ambisonic channels of the given order (using ACN
+# ordering and SN3D normalization by default), which need to be decoded to
+# play correctly on speakers.
#channels =
## sample-type:
@@ -343,6 +343,11 @@
# docs/ambdec.txt for a description of the file format.
#surround71 =
+## surround714:
+# Decoder configuration file for 7.1.4 Surround channel output. See
+# docs/ambdec.txt for a description of the file format.
+#surround714 =
+
## surround3d71:
# Decoder configuration file for 3D7.1 Surround channel output. See
# docs/ambdec.txt for a description of the file format. See also
@@ -574,6 +579,12 @@
##
[wasapi]
+## spatial-api:
+# Specifies whether to use a Spatial Audio stream for playback. This may
+# provide expanded capabilities for surround sound and with-height speaker
+# configurations. Very experimental.
+#spatial-api = false
+
## allow-resampler:
# Specifies whether to allow an extra resampler pass on the output. Enabling
# this will allow the playback device to be set to a different sample rate
@@ -638,6 +649,15 @@
##
[game_compat]
+## default-error: (global)
+# An error value returned by alGetError when there's no current context. The
+# default value is AL_INVALID_OPERATION, which lets the caller know the
+# operation could not be executed. Some applications may erroneously call
+# alGetError without a current context and expect 0 (AL_NO_ERROR), however
+# that may cause other applications to think earlier AL calls succeeded when
+# they actually failed.
+#default-error = 0xA004
+
## nfc-scale: (global)
# A meters-per-unit distance scale applied to NFC filters. If a game doesn't
# use real-world meters for in-game units, the filters may create a too-near
diff --git a/appveyor.yml b/appveyor.yml
index aa155af4..e86f7a30 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,8 +1,8 @@
version: 1.23.1.{build}
environment:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
- GEN: "Visual Studio 15 2017"
+ APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+ GEN: "Visual Studio 16 2019"
matrix:
- ARCH: Win32
CFG: Release
diff --git a/cmake/FindOpenSL.cmake b/cmake/FindOpenSL.cmake
index 00428749..3df54d44 100644
--- a/cmake/FindOpenSL.cmake
+++ b/cmake/FindOpenSL.cmake
@@ -2,8 +2,9 @@
# Find the OpenSL libraries
#
# This module defines the following variables and targets:
-# OPENSL_FOUND - True if OPENSL was found
-# OpenSL::OpenSLES - The OpenSLES target
+# OPENSL_FOUND - True if OPENSL was found
+# OPENSL_INCLUDE_DIRS - The OpenSL include paths
+# OPENSL_LIBRARIES - The OpenSL libraries to link
#
#=============================================================================
@@ -53,11 +54,8 @@ find_package_handle_standard_args(OpenSL REQUIRED_VARS OPENSL_LIBRARY OPENSL_INC
OPENSL_ANDROID_INCLUDE_DIR)
if(OPENSL_FOUND)
- add_library(OpenSL::OpenSLES UNKNOWN IMPORTED)
- set_target_properties(OpenSL::OpenSLES PROPERTIES
- IMPORTED_LOCATION ${OPENSL_LIBRARY}
- INTERFACE_INCLUDE_DIRECTORIES ${OPENSL_INCLUDE_DIR}
- INTERFACE_INCLUDE_DIRECTORIES ${OPENSL_ANDROID_INCLUDE_DIR})
+ set(OPENSL_LIBRARIES ${OPENSL_LIBRARY})
+ set(OPENSL_INCLUDE_DIRS ${OPENSL_INCLUDE_DIR} ${OPENSL_ANDROID_INCLUDE_DIR})
endif()
mark_as_advanced(OPENSL_INCLUDE_DIR OPENSL_ANDROID_INCLUDE_DIR OPENSL_LIBRARY)
diff --git a/common/albit.h b/common/albit.h
index ad596208..82a4a00d 100644
--- a/common/albit.h
+++ b/common/albit.h
@@ -2,7 +2,9 @@
#define AL_BIT_H
#include <cstdint>
+#include <cstring>
#include <limits>
+#include <new>
#include <type_traits>
#if !defined(__GNUC__) && (defined(_WIN32) || defined(_WIN64))
#include <intrin.h>
@@ -10,6 +12,16 @@
namespace al {
+template<typename To, typename From>
+std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From>
+ && std::is_trivially_copyable_v<To>,
+To> bit_cast(const From &src) noexcept
+{
+ alignas(To) char dst[sizeof(To)];
+ std::memcpy(&dst[0], &src, sizeof(To));
+ return *std::launder(reinterpret_cast<To*>(&dst[0]));
+}
+
#ifdef __BYTE_ORDER__
enum class endian {
little = __ORDER_LITTLE_ENDIAN__,
diff --git a/common/albyte.h b/common/albyte.h
deleted file mode 100644
index be586869..00000000
--- a/common/albyte.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef AL_BYTE_H
-#define AL_BYTE_H
-
-#include <cstddef>
-#include <cstdint>
-#include <limits>
-#include <type_traits>
-
-using uint = unsigned int;
-
-namespace al {
-
-using byte = unsigned char;
-
-} // namespace al
-
-#endif /* AL_BYTE_H */
diff --git a/common/alcomplex.cpp b/common/alcomplex.cpp
index 4420a1bb..82a0c43c 100644
--- a/common/alcomplex.cpp
+++ b/common/alcomplex.cpp
@@ -4,6 +4,7 @@
#include "alcomplex.h"
#include <algorithm>
+#include <array>
#include <cassert>
#include <cmath>
#include <cstddef>
@@ -20,6 +21,7 @@ namespace {
using ushort = unsigned short;
using ushort2 = std::pair<ushort,ushort>;
+using complex_d = std::complex<double>;
constexpr size_t BitReverseCounter(size_t log2_size) noexcept
{
@@ -34,7 +36,7 @@ template<size_t N>
struct BitReverser {
static_assert(N <= sizeof(ushort)*8, "Too many bits for the bit-reversal table.");
- ushort2 mData[BitReverseCounter(N)]{};
+ std::array<ushort2,BitReverseCounter(N)> mData{};
constexpr BitReverser()
{
@@ -44,12 +46,13 @@ struct BitReverser {
/* Bit-reversal permutation applied to a sequence of fftsize items. */
for(size_t idx{1u};idx < fftsize-1;++idx)
{
- size_t revidx{0u}, imask{idx};
- for(size_t i{0};i < N;++i)
- {
- revidx = (revidx<<1) | (imask&1);
- imask >>= 1;
- }
+ size_t revidx{idx};
+ revidx = ((revidx&0xaaaaaaaa) >> 1) | ((revidx&0x55555555) << 1);
+ revidx = ((revidx&0xcccccccc) >> 2) | ((revidx&0x33333333) << 2);
+ revidx = ((revidx&0xf0f0f0f0) >> 4) | ((revidx&0x0f0f0f0f) << 4);
+ revidx = ((revidx&0xff00ff00) >> 8) | ((revidx&0x00ff00ff) << 8);
+ revidx = (revidx >> 16) | ((revidx&0x0000ffff) << 16);
+ revidx >>= 32-N;
if(idx < revidx)
{
@@ -58,14 +61,13 @@ struct BitReverser {
++ret_i;
}
}
- assert(ret_i == al::size(mData));
+ assert(ret_i == std::size(mData));
}
};
-/* These bit-reversal swap tables support up to 10-bit indices (1024 elements),
- * which is the largest used by OpenAL Soft's filters and effects. Larger FFT
- * requests, used by some utilities where performance is less important, will
- * use a slower table-less path.
+/* These bit-reversal swap tables support up to 11-bit indices (2048 elements),
+ * which is large enough for the filters and effects in OpenAL Soft. Larger FFT
+ * requests will use a slower table-less path.
*/
constexpr BitReverser<2> BitReverser2{};
constexpr BitReverser<3> BitReverser3{};
@@ -76,7 +78,8 @@ constexpr BitReverser<7> BitReverser7{};
constexpr BitReverser<8> BitReverser8{};
constexpr BitReverser<9> BitReverser9{};
constexpr BitReverser<10> BitReverser10{};
-constexpr std::array<al::span<const ushort2>,11> gBitReverses{{
+constexpr BitReverser<11> BitReverser11{};
+constexpr std::array<al::span<const ushort2>,12> gBitReverses{{
{}, {},
BitReverser2.mData,
BitReverser3.mData,
@@ -86,14 +89,29 @@ constexpr std::array<al::span<const ushort2>,11> gBitReverses{{
BitReverser7.mData,
BitReverser8.mData,
BitReverser9.mData,
- BitReverser10.mData
+ BitReverser10.mData,
+ BitReverser11.mData
+}};
+
+/* Lookup table for std::polar(1, pi / (1<<index)); */
+template<typename T>
+constexpr std::array<std::complex<T>,gBitReverses.size()-1> gArgAngle{{
+ {static_cast<T>(-1.00000000000000000e+00), static_cast<T>(0.00000000000000000e+00)},
+ {static_cast<T>( 0.00000000000000000e+00), static_cast<T>(1.00000000000000000e+00)},
+ {static_cast<T>( 7.07106781186547524e-01), static_cast<T>(7.07106781186547524e-01)},
+ {static_cast<T>( 9.23879532511286756e-01), static_cast<T>(3.82683432365089772e-01)},
+ {static_cast<T>( 9.80785280403230449e-01), static_cast<T>(1.95090322016128268e-01)},
+ {static_cast<T>( 9.95184726672196886e-01), static_cast<T>(9.80171403295606020e-02)},
+ {static_cast<T>( 9.98795456205172393e-01), static_cast<T>(4.90676743274180143e-02)},
+ {static_cast<T>( 9.99698818696204220e-01), static_cast<T>(2.45412285229122880e-02)},
+ {static_cast<T>( 9.99924701839144541e-01), static_cast<T>(1.22715382857199261e-02)},
+ {static_cast<T>( 9.99981175282601143e-01), static_cast<T>(6.13588464915447536e-03)},
+ {static_cast<T>( 9.99995293809576172e-01), static_cast<T>(3.06795676296597627e-03)}
}};
} // namespace
-template<typename Real>
-std::enable_if_t<std::is_floating_point<Real>::value>
-complex_fft(const al::span<std::complex<Real>> buffer, const al::type_identity_t<Real> sign)
+void complex_fft(const al::span<std::complex<double>> buffer, const double sign)
{
const size_t fftsize{buffer.size()};
/* Get the number of bits used for indexing. Simplifies bit-reversal and
@@ -101,48 +119,82 @@ complex_fft(const al::span<std::complex<Real>> buffer, const al::type_identity_t
*/
const size_t log2_size{static_cast<size_t>(al::countr_zero(fftsize))};
- if(log2_size >= gBitReverses.size()) UNLIKELY
+ if(log2_size < gBitReverses.size()) LIKELY
{
- for(size_t idx{1u};idx < fftsize-1;++idx)
+ for(auto &rev : gBitReverses[log2_size])
+ std::swap(buffer[rev.first], buffer[rev.second]);
+
+ /* Iterative form of Danielson-Lanczos lemma */
+ for(size_t i{0};i < log2_size;++i)
{
- size_t revidx{0u}, imask{idx};
- for(size_t i{0};i < log2_size;++i)
+ const size_t step2{1_uz << i};
+ const size_t step{2_uz << i};
+ /* The first iteration of the inner loop would have u=1, which we
+ * can simplify to remove a number of complex multiplies.
+ */
+ for(size_t k{0};k < fftsize;k+=step)
{
- revidx = (revidx<<1) | (imask&1);
- imask >>= 1;
+ const complex_d temp{buffer[k+step2]};
+ buffer[k+step2] = buffer[k] - temp;
+ buffer[k] += temp;
}
- if(idx < revidx)
- std::swap(buffer[idx], buffer[revidx]);
+ const complex_d w{gArgAngle<double>[i].real(), gArgAngle<double>[i].imag()*sign};
+ complex_d u{w};
+ for(size_t j{1};j < step2;j++)
+ {
+ for(size_t k{j};k < fftsize;k+=step)
+ {
+ const complex_d temp{buffer[k+step2] * u};
+ buffer[k+step2] = buffer[k] - temp;
+ buffer[k] += temp;
+ }
+ u *= w;
+ }
}
}
- else for(auto &rev : gBitReverses[log2_size])
- std::swap(buffer[rev.first], buffer[rev.second]);
-
- /* Iterative form of Danielson-Lanczos lemma */
- const Real pi{al::numbers::pi_v<Real> * sign};
- size_t step2{1u};
- for(size_t i{0};i < log2_size;++i)
+ else
{
- const Real arg{pi / static_cast<Real>(step2)};
+ for(size_t idx{1u};idx < fftsize-1;++idx)
+ {
+ size_t revidx{idx};
+ revidx = ((revidx&0xaaaaaaaa) >> 1) | ((revidx&0x55555555) << 1);
+ revidx = ((revidx&0xcccccccc) >> 2) | ((revidx&0x33333333) << 2);
+ revidx = ((revidx&0xf0f0f0f0) >> 4) | ((revidx&0x0f0f0f0f) << 4);
+ revidx = ((revidx&0xff00ff00) >> 8) | ((revidx&0x00ff00ff) << 8);
+ revidx = (revidx >> 16) | ((revidx&0x0000ffff) << 16);
+ revidx >>= 32-log2_size;
+
+ if(idx < revidx)
+ std::swap(buffer[idx], buffer[revidx]);
+ }
- /* TODO: Would std::polar(1.0, arg) be any better? */
- const std::complex<Real> w{std::cos(arg), std::sin(arg)};
- std::complex<Real> u{1.0, 0.0};
- const size_t step{step2 << 1};
- for(size_t j{0};j < step2;j++)
+ const double pi{al::numbers::pi * sign};
+ for(size_t i{0};i < log2_size;++i)
{
- for(size_t k{j};k < fftsize;k+=step)
+ const size_t step2{1_uz << i};
+ const size_t step{2_uz << i};
+ for(size_t k{0};k < fftsize;k+=step)
{
- std::complex<Real> temp{buffer[k+step2] * u};
+ const complex_d temp{buffer[k+step2]};
buffer[k+step2] = buffer[k] - temp;
buffer[k] += temp;
}
- u *= w;
+ const double arg{pi / static_cast<double>(step2)};
+ const complex_d w{std::polar(1.0, arg)};
+ complex_d u{w};
+ for(size_t j{1};j < step2;j++)
+ {
+ for(size_t k{j};k < fftsize;k+=step)
+ {
+ const complex_d temp{buffer[k+step2] * u};
+ buffer[k+step2] = buffer[k] - temp;
+ buffer[k] += temp;
+ }
+ u *= w;
+ }
}
-
- step2 <<= 1;
}
}
@@ -165,7 +217,3 @@ void complex_hilbert(const al::span<std::complex<double>> buffer)
forward_fft(buffer);
}
-
-
-template void complex_fft<>(const al::span<std::complex<float>> buffer, const float sign);
-template void complex_fft<>(const al::span<std::complex<double>> buffer, const double sign);
diff --git a/common/alcomplex.h b/common/alcomplex.h
index 794c3526..f730ba9e 100644
--- a/common/alcomplex.h
+++ b/common/alcomplex.h
@@ -11,27 +11,23 @@
* FFT and 1 is inverse FFT. Applies the Discrete Fourier Transform (DFT) to
* the data supplied in the buffer, which MUST BE power of two.
*/
-template<typename Real>
-std::enable_if_t<std::is_floating_point<Real>::value>
-complex_fft(const al::span<std::complex<Real>> buffer, const al::type_identity_t<Real> sign);
+void complex_fft(const al::span<std::complex<double>> buffer, const double sign);
/**
* Calculate the frequency-domain response of the time-domain signal in the
* provided buffer, which MUST BE power of two.
*/
-template<typename Real, size_t N>
-std::enable_if_t<std::is_floating_point<Real>::value>
-forward_fft(const al::span<std::complex<Real>,N> buffer)
-{ complex_fft(buffer.subspan(0), -1); }
+template<size_t N>
+void forward_fft(const al::span<std::complex<double>,N> buffer)
+{ complex_fft(al::span<std::complex<double>>{buffer}, -1.0); }
/**
* Calculate the time-domain signal of the frequency-domain response in the
* provided buffer, which MUST BE power of two.
*/
-template<typename Real, size_t N>
-std::enable_if_t<std::is_floating_point<Real>::value>
-inverse_fft(const al::span<std::complex<Real>,N> buffer)
-{ complex_fft(buffer.subspan(0), 1); }
+template<size_t N>
+void inverse_fft(const al::span<std::complex<double>,N> buffer)
+{ complex_fft(al::span<std::complex<double>>{buffer}, +1.0); }
/**
* Calculate the complex helical sequence (discrete-time analytical signal) of
diff --git a/common/aldeque.h b/common/aldeque.h
deleted file mode 100644
index 3f99bf00..00000000
--- a/common/aldeque.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef ALDEQUE_H
-#define ALDEQUE_H
-
-#include <deque>
-
-#include "almalloc.h"
-
-
-namespace al {
-
-template<typename T>
-using deque = std::deque<T, al::allocator<T>>;
-
-} // namespace al
-
-#endif /* ALDEQUE_H */
diff --git a/common/almalloc.h b/common/almalloc.h
index a795fc3b..873473ca 100644
--- a/common/almalloc.h
+++ b/common/almalloc.h
@@ -117,7 +117,9 @@ constexpr T *to_address(T *p) noexcept
template<typename T>
constexpr auto to_address(const T &p) noexcept
-{ return to_address(p.operator->()); }
+{
+ return ::al::to_address(p.operator->());
+}
template<typename T, typename ...Args>
@@ -125,71 +127,6 @@ constexpr T* construct_at(T *ptr, Args&& ...args)
noexcept(std::is_nothrow_constructible<T, Args...>::value)
{ return ::new(static_cast<void*>(ptr)) T{std::forward<Args>(args)...}; }
-/* At least VS 2015 complains that 'ptr' is unused when the given type's
- * destructor is trivial (a no-op). So disable that warning for this call.
- */
-DIAGNOSTIC_PUSH
-msc_pragma(warning(disable : 4100))
-template<typename T>
-constexpr std::enable_if_t<!std::is_array<T>::value>
-destroy_at(T *ptr) noexcept(std::is_nothrow_destructible<T>::value)
-{ ptr->~T(); }
-DIAGNOSTIC_POP
-template<typename T>
-constexpr std::enable_if_t<std::is_array<T>::value>
-destroy_at(T *ptr) noexcept(std::is_nothrow_destructible<std::remove_all_extents_t<T>>::value)
-{
- for(auto &elem : *ptr)
- al::destroy_at(std::addressof(elem));
-}
-
-template<typename T>
-constexpr void destroy(T first, T end) noexcept(noexcept(al::destroy_at(std::addressof(*first))))
-{
- while(first != end)
- {
- al::destroy_at(std::addressof(*first));
- ++first;
- }
-}
-
-template<typename T, typename N>
-constexpr std::enable_if_t<std::is_integral<N>::value,T>
-destroy_n(T first, N count) noexcept(noexcept(al::destroy_at(std::addressof(*first))))
-{
- if(count != 0)
- {
- do {
- al::destroy_at(std::addressof(*first));
- ++first;
- } while(--count);
- }
- return first;
-}
-
-
-template<typename T, typename N>
-inline std::enable_if_t<std::is_integral<N>::value,
-T> uninitialized_default_construct_n(T first, N count)
-{
- using ValueT = typename std::iterator_traits<T>::value_type;
- T current{first};
- if(count != 0)
- {
- try {
- do {
- ::new(static_cast<void*>(std::addressof(*current))) ValueT;
- ++current;
- } while(--count);
- }
- catch(...) {
- al::destroy(first, current);
- throw;
- }
- }
- return current;
-}
-
/* Storage for flexible array data. This is trivially destructible if type T is
* trivially destructible.
@@ -209,7 +146,7 @@ struct FlexArrayStorage {
}
FlexArrayStorage(size_t size) : mSize{size}
- { al::uninitialized_default_construct_n(mArray, mSize); }
+ { std::uninitialized_default_construct_n(mArray, mSize); }
~FlexArrayStorage() = default;
FlexArrayStorage(const FlexArrayStorage&) = delete;
@@ -231,8 +168,8 @@ struct FlexArrayStorage<T,alignment,false> {
}
FlexArrayStorage(size_t size) : mSize{size}
- { al::uninitialized_default_construct_n(mArray, mSize); }
- ~FlexArrayStorage() { al::destroy_n(mArray, mSize); }
+ { std::uninitialized_default_construct_n(mArray, mSize); }
+ ~FlexArrayStorage() { std::destroy_n(mArray, mSize); }
FlexArrayStorage(const FlexArrayStorage&) = delete;
FlexArrayStorage& operator=(const FlexArrayStorage&) = delete;
diff --git a/common/alnumbers.h b/common/alnumbers.h
index 37a55410..e92d7b87 100644
--- a/common/alnumbers.h
+++ b/common/alnumbers.h
@@ -13,21 +13,21 @@ namespace detail_ {
} // detail_
template<typename T>
-static constexpr auto pi_v = detail_::as_fp<T>(3.141592653589793238462643383279502884L);
+inline constexpr auto pi_v = detail_::as_fp<T>(3.141592653589793238462643383279502884L);
template<typename T>
-static constexpr auto inv_pi_v = detail_::as_fp<T>(0.318309886183790671537767526745028724L);
+inline constexpr auto inv_pi_v = detail_::as_fp<T>(0.318309886183790671537767526745028724L);
template<typename T>
-static constexpr auto sqrt2_v = detail_::as_fp<T>(1.414213562373095048801688724209698079L);
+inline constexpr auto sqrt2_v = detail_::as_fp<T>(1.414213562373095048801688724209698079L);
template<typename T>
-static constexpr auto sqrt3_v = detail_::as_fp<T>(1.732050807568877293527446341505872367L);
+inline constexpr auto sqrt3_v = detail_::as_fp<T>(1.732050807568877293527446341505872367L);
-static constexpr auto pi = pi_v<double>;
-static constexpr auto inv_pi = inv_pi_v<double>;
-static constexpr auto sqrt2 = sqrt2_v<double>;
-static constexpr auto sqrt3 = sqrt3_v<double>;
+inline constexpr auto pi = pi_v<double>;
+inline constexpr auto inv_pi = inv_pi_v<double>;
+inline constexpr auto sqrt2 = sqrt2_v<double>;
+inline constexpr auto sqrt3 = sqrt3_v<double>;
} // namespace numbers
diff --git a/common/alnumeric.h b/common/alnumeric.h
index d6919e40..6281b012 100644
--- a/common/alnumeric.h
+++ b/common/alnumeric.h
@@ -5,6 +5,7 @@
#include <cmath>
#include <cstddef>
#include <cstdint>
+#include <type_traits>
#ifdef HAVE_INTRIN_H
#include <intrin.h>
#endif
@@ -12,12 +13,18 @@
#include <xmmintrin.h>
#endif
+#include "albit.h"
#include "altraits.h"
#include "opthelpers.h"
-inline constexpr int64_t operator "" _i64(unsigned long long int n) noexcept { return static_cast<int64_t>(n); }
-inline constexpr uint64_t operator "" _u64(unsigned long long int n) noexcept { return static_cast<uint64_t>(n); }
+constexpr auto operator "" _i64(unsigned long long n) noexcept { return static_cast<int64_t>(n); }
+constexpr auto operator "" _u64(unsigned long long n) noexcept { return static_cast<uint64_t>(n); }
+
+constexpr auto operator "" _z(unsigned long long n) noexcept
+{ return static_cast<std::make_signed_t<size_t>>(n); }
+constexpr auto operator "" _uz(unsigned long long n) noexcept { return static_cast<size_t>(n); }
+constexpr auto operator "" _zu(unsigned long long n) noexcept { return static_cast<size_t>(n); }
constexpr inline float minf(float a, float b) noexcept
@@ -82,6 +89,9 @@ constexpr inline float cubic(float val1, float val2, float val3, float val4, flo
return val1*a0 + val2*a1 + val3*a2 + val4*a3;
}
+constexpr inline double lerpd(double val1, double val2, double mu) noexcept
+{ return val1 + (val2-val1)*mu; }
+
/** Find the next power-of-2 for non-power-of-2 numbers. */
inline uint32_t NextPowerOf2(uint32_t value) noexcept
@@ -159,21 +169,16 @@ inline int float2int(float f) noexcept
#elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0) \
|| ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \
&& !defined(__SSE_MATH__))
- int sign, shift, mant;
- union {
- float f;
- int i;
- } conv;
+ const int conv_i{al::bit_cast<int>(f)};
- conv.f = f;
- sign = (conv.i>>31) | 1;
- shift = ((conv.i>>23)&0xff) - (127+23);
+ const int sign{(conv_i>>31) | 1};
+ const int shift{((conv_i>>23)&0xff) - (127+23)};
/* Over/underflow */
if(shift >= 31 || shift < -23) UNLIKELY
return 0;
- mant = (conv.i&0x7fffff) | 0x800000;
+ const int mant{(conv_i&0x7fffff) | 0x800000};
if(shift < 0) LIKELY
return (mant >> -shift) * sign;
return (mant << shift) * sign;
@@ -195,25 +200,19 @@ inline int double2int(double d) noexcept
#elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2) \
|| ((defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \
&& !defined(__SSE2_MATH__))
- int sign, shift;
- int64_t mant;
- union {
- double d;
- int64_t i64;
- } conv;
+ const int64_t conv_i64{al::bit_cast<int64_t>(d)};
- conv.d = d;
- sign = (conv.i64 >> 63) | 1;
- shift = ((conv.i64 >> 52) & 0x7ff) - (1023 + 52);
+ const int sign{static_cast<int>(conv_i64 >> 63) | 1};
+ const int shift{(static_cast<int>(conv_i64 >> 52) & 0x7ff) - (1023 + 52)};
/* Over/underflow */
if(shift >= 63 || shift < -52) UNLIKELY
return 0;
- mant = (conv.i64 & 0xfffffffffffff_i64) | 0x10000000000000_i64;
+ const int64_t mant{(conv_i64 & 0xfffffffffffff_i64) | 0x10000000000000_i64};
if(shift < 0) LIKELY
- return (int)(mant >> -shift) * sign;
- return (int)(mant << shift) * sign;
+ return static_cast<int>(mant >> -shift) * sign;
+ return static_cast<int>(mant << shift) * sign;
#else
@@ -246,19 +245,14 @@ inline float fast_roundf(float f) noexcept
/* Integral limit, where sub-integral precision is not available for
* floats.
*/
- static const float ilim[2]{
+ static constexpr float ilim[2]{
8388608.0f /* 0x1.0p+23 */,
-8388608.0f /* -0x1.0p+23 */
};
- unsigned int sign, expo;
- union {
- float f;
- unsigned int i;
- } conv;
+ const unsigned int conv_i{al::bit_cast<unsigned int>(f)};
- conv.f = f;
- sign = (conv.i>>31)&0x01;
- expo = (conv.i>>23)&0xff;
+ const unsigned int sign{(conv_i>>31)&0x01};
+ const unsigned int expo{(conv_i>>23)&0xff};
if(expo >= 150/*+23*/) UNLIKELY
{
@@ -283,12 +277,6 @@ inline float fast_roundf(float f) noexcept
}
-template<typename T>
-constexpr const T& clamp(const T& value, const T& min_value, const T& max_value) noexcept
-{
- return std::min(std::max(value, min_value), max_value);
-}
-
// Converts level (mB) to gain.
inline float level_mb_to_gain(float x)
{
diff --git a/common/aloptional.h b/common/aloptional.h
deleted file mode 100644
index 6de16799..00000000
--- a/common/aloptional.h
+++ /dev/null
@@ -1,353 +0,0 @@
-#ifndef AL_OPTIONAL_H
-#define AL_OPTIONAL_H
-
-#include <initializer_list>
-#include <type_traits>
-#include <utility>
-
-#include "almalloc.h"
-
-namespace al {
-
-struct nullopt_t { };
-struct in_place_t { };
-
-constexpr nullopt_t nullopt{};
-constexpr in_place_t in_place{};
-
-#define NOEXCEPT_AS(...) noexcept(noexcept(__VA_ARGS__))
-
-namespace detail_ {
-/* Base storage struct for an optional. Defines a trivial destructor, for types
- * that can be trivially destructed.
- */
-template<typename T, bool = std::is_trivially_destructible<T>::value>
-struct optstore_base {
- bool mHasValue{false};
- union {
- char mDummy{};
- T mValue;
- };
-
- constexpr optstore_base() noexcept { }
- template<typename ...Args>
- constexpr explicit optstore_base(in_place_t, Args&& ...args)
- noexcept(std::is_nothrow_constructible<T, Args...>::value)
- : mHasValue{true}, mValue{std::forward<Args>(args)...}
- { }
- ~optstore_base() = default;
-};
-
-/* Specialization needing a non-trivial destructor. */
-template<typename T>
-struct optstore_base<T, false> {
- bool mHasValue{false};
- union {
- char mDummy{};
- T mValue;
- };
-
- constexpr optstore_base() noexcept { }
- template<typename ...Args>
- constexpr explicit optstore_base(in_place_t, Args&& ...args)
- noexcept(std::is_nothrow_constructible<T, Args...>::value)
- : mHasValue{true}, mValue{std::forward<Args>(args)...}
- { }
- ~optstore_base() { if(mHasValue) al::destroy_at(std::addressof(mValue)); }
-};
-
-/* Next level of storage, which defines helpers to construct and destruct the
- * stored object.
- */
-template<typename T>
-struct optstore_helper : public optstore_base<T> {
- using optstore_base<T>::optstore_base;
-
- template<typename... Args>
- constexpr void construct(Args&& ...args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
- {
- al::construct_at(std::addressof(this->mValue), std::forward<Args>(args)...);
- this->mHasValue = true;
- }
-
- constexpr void reset() noexcept
- {
- if(this->mHasValue)
- al::destroy_at(std::addressof(this->mValue));
- this->mHasValue = false;
- }
-
- constexpr void assign(const optstore_helper &rhs)
- noexcept(std::is_nothrow_copy_constructible<T>::value
- && std::is_nothrow_copy_assignable<T>::value)
- {
- if(!rhs.mHasValue)
- this->reset();
- else if(this->mHasValue)
- this->mValue = rhs.mValue;
- else
- this->construct(rhs.mValue);
- }
-
- constexpr void assign(optstore_helper&& rhs)
- noexcept(std::is_nothrow_move_constructible<T>::value
- && std::is_nothrow_move_assignable<T>::value)
- {
- if(!rhs.mHasValue)
- this->reset();
- else if(this->mHasValue)
- this->mValue = std::move(rhs.mValue);
- else
- this->construct(std::move(rhs.mValue));
- }
-};
-
-/* Define copy and move constructors and assignment operators, which may or may
- * not be trivial.
- */
-template<typename T, bool trivial_copy = std::is_trivially_copy_constructible<T>::value,
- bool trivial_move = std::is_trivially_move_constructible<T>::value,
- /* Trivial assignment is dependent on trivial construction+destruction. */
- bool = trivial_copy && std::is_trivially_copy_assignable<T>::value
- && std::is_trivially_destructible<T>::value,
- bool = trivial_move && std::is_trivially_move_assignable<T>::value
- && std::is_trivially_destructible<T>::value>
-struct optional_storage;
-
-/* Some versions of GCC have issues with 'this' in the following noexcept(...)
- * statements, so this macro is a workaround.
- */
-#define _this std::declval<optional_storage*>()
-
-/* Completely trivial. */
-template<typename T>
-struct optional_storage<T, true, true, true, true> : public optstore_helper<T> {
- using optstore_helper<T>::optstore_helper;
- constexpr optional_storage() noexcept = default;
- constexpr optional_storage(const optional_storage&) = default;
- constexpr optional_storage(optional_storage&&) = default;
- constexpr optional_storage& operator=(const optional_storage&) = default;
- constexpr optional_storage& operator=(optional_storage&&) = default;
-};
-
-/* Non-trivial move assignment. */
-template<typename T>
-struct optional_storage<T, true, true, true, false> : public optstore_helper<T> {
- using optstore_helper<T>::optstore_helper;
- constexpr optional_storage() noexcept = default;
- constexpr optional_storage(const optional_storage&) = default;
- constexpr optional_storage(optional_storage&&) = default;
- constexpr optional_storage& operator=(const optional_storage&) = default;
- constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
- { this->assign(std::move(rhs)); return *this; }
-};
-
-/* Non-trivial move construction. */
-template<typename T>
-struct optional_storage<T, true, false, true, false> : public optstore_helper<T> {
- using optstore_helper<T>::optstore_helper;
- constexpr optional_storage() noexcept = default;
- constexpr optional_storage(const optional_storage&) = default;
- constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
- { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
- constexpr optional_storage& operator=(const optional_storage&) = default;
- constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
- { this->assign(std::move(rhs)); return *this; }
-};
-
-/* Non-trivial copy assignment. */
-template<typename T>
-struct optional_storage<T, true, true, false, true> : public optstore_helper<T> {
- using optstore_helper<T>::optstore_helper;
- constexpr optional_storage() noexcept = default;
- constexpr optional_storage(const optional_storage&) = default;
- constexpr optional_storage(optional_storage&&) = default;
- constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
- { this->assign(rhs); return *this; }
- constexpr optional_storage& operator=(optional_storage&&) = default;
-};
-
-/* Non-trivial copy construction. */
-template<typename T>
-struct optional_storage<T, false, true, false, true> : public optstore_helper<T> {
- using optstore_helper<T>::optstore_helper;
- constexpr optional_storage() noexcept = default;
- constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
- { if(rhs.mHasValue) this->construct(rhs.mValue); }
- constexpr optional_storage(optional_storage&&) = default;
- constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
- { this->assign(rhs); return *this; }
- constexpr optional_storage& operator=(optional_storage&&) = default;
-};
-
-/* Non-trivial assignment. */
-template<typename T>
-struct optional_storage<T, true, true, false, false> : public optstore_helper<T> {
- using optstore_helper<T>::optstore_helper;
- constexpr optional_storage() noexcept = default;
- constexpr optional_storage(const optional_storage&) = default;
- constexpr optional_storage(optional_storage&&) = default;
- constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
- { this->assign(rhs); return *this; }
- constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
- { this->assign(std::move(rhs)); return *this; }
-};
-
-/* Non-trivial assignment, non-trivial move construction. */
-template<typename T>
-struct optional_storage<T, true, false, false, false> : public optstore_helper<T> {
- using optstore_helper<T>::optstore_helper;
- constexpr optional_storage() noexcept = default;
- constexpr optional_storage(const optional_storage&) = default;
- constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
- { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
- constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
- { this->assign(rhs); return *this; }
- constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
- { this->assign(std::move(rhs)); return *this; }
-};
-
-/* Non-trivial assignment, non-trivial copy construction. */
-template<typename T>
-struct optional_storage<T, false, true, false, false> : public optstore_helper<T> {
- using optstore_helper<T>::optstore_helper;
- constexpr optional_storage() noexcept = default;
- constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
- { if(rhs.mHasValue) this->construct(rhs.mValue); }
- constexpr optional_storage(optional_storage&&) = default;
- constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
- { this->assign(rhs); return *this; }
- constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
- { this->assign(std::move(rhs)); return *this; }
-};
-
-/* Completely non-trivial. */
-template<typename T>
-struct optional_storage<T, false, false, false, false> : public optstore_helper<T> {
- using optstore_helper<T>::optstore_helper;
- constexpr optional_storage() noexcept = default;
- constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
- { if(rhs.mHasValue) this->construct(rhs.mValue); }
- constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
- { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
- constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
- { this->assign(rhs); return *this; }
- constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
- { this->assign(std::move(rhs)); return *this; }
-};
-
-#undef _this
-
-} // namespace detail_
-
-#define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true
-
-template<typename T>
-class optional {
- using storage_t = detail_::optional_storage<T>;
-
- storage_t mStore{};
-
-public:
- using value_type = T;
-
- constexpr optional() = default;
- constexpr optional(const optional&) = default;
- constexpr optional(optional&&) = default;
- constexpr optional(nullopt_t) noexcept { }
- template<typename ...Args>
- constexpr explicit optional(in_place_t, Args&& ...args)
- NOEXCEPT_AS(storage_t{al::in_place, std::forward<Args>(args)...})
- : mStore{al::in_place, std::forward<Args>(args)...}
- { }
- template<typename U, REQUIRES(std::is_constructible<T, U&&>::value
- && !std::is_same<std::decay_t<U>, al::in_place_t>::value
- && !std::is_same<std::decay_t<U>, optional<T>>::value
- && std::is_convertible<U&&, T>::value)>
- constexpr optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)})
- : mStore{al::in_place, std::forward<U>(rhs)}
- { }
- template<typename U, REQUIRES(std::is_constructible<T, U&&>::value
- && !std::is_same<std::decay_t<U>, al::in_place_t>::value
- && !std::is_same<std::decay_t<U>, optional<T>>::value
- && !std::is_convertible<U&&, T>::value)>
- constexpr explicit optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)})
- : mStore{al::in_place, std::forward<U>(rhs)}
- { }
- ~optional() = default;
-
- constexpr optional& operator=(const optional&) = default;
- constexpr optional& operator=(optional&&) = default;
- constexpr optional& operator=(nullopt_t) noexcept { mStore.reset(); return *this; }
- template<typename U=T>
- constexpr std::enable_if_t<std::is_constructible<T, U>::value
- && std::is_assignable<T&, U>::value
- && !std::is_same<std::decay_t<U>, optional<T>>::value
- && (!std::is_same<std::decay_t<U>, T>::value || !std::is_scalar<U>::value),
- optional&> operator=(U&& rhs)
- {
- if(mStore.mHasValue)
- mStore.mValue = std::forward<U>(rhs);
- else
- mStore.construct(std::forward<U>(rhs));
- return *this;
- }
-
- constexpr const T* operator->() const { return std::addressof(mStore.mValue); }
- constexpr T* operator->() { return std::addressof(mStore.mValue); }
- constexpr const T& operator*() const& { return mStore.mValue; }
- constexpr T& operator*() & { return mStore.mValue; }
- constexpr const T&& operator*() const&& { return std::move(mStore.mValue); }
- constexpr T&& operator*() && { return std::move(mStore.mValue); }
-
- constexpr explicit operator bool() const noexcept { return mStore.mHasValue; }
- constexpr bool has_value() const noexcept { return mStore.mHasValue; }
-
- constexpr T& value() & { return mStore.mValue; }
- constexpr const T& value() const& { return mStore.mValue; }
- constexpr T&& value() && { return std::move(mStore.mValue); }
- constexpr const T&& value() const&& { return std::move(mStore.mValue); }
-
- template<typename U>
- constexpr T value_or(U&& defval) const&
- { return bool(*this) ? **this : static_cast<T>(std::forward<U>(defval)); }
- template<typename U>
- constexpr T value_or(U&& defval) &&
- { return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); }
-
- template<typename ...Args>
- constexpr T& emplace(Args&& ...args)
- {
- mStore.reset();
- mStore.construct(std::forward<Args>(args)...);
- return mStore.mValue;
- }
- template<typename U, typename ...Args>
- constexpr std::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
- T&> emplace(std::initializer_list<U> il, Args&& ...args)
- {
- mStore.reset();
- mStore.construct(il, std::forward<Args>(args)...);
- return mStore.mValue;
- }
-
- constexpr void reset() noexcept { mStore.reset(); }
-};
-
-template<typename T>
-constexpr optional<std::decay_t<T>> make_optional(T&& arg)
-{ return optional<std::decay_t<T>>{in_place, std::forward<T>(arg)}; }
-
-template<typename T, typename... Args>
-constexpr optional<T> make_optional(Args&& ...args)
-{ return optional<T>{in_place, std::forward<Args>(args)...}; }
-
-template<typename T, typename U, typename... Args>
-constexpr optional<T> make_optional(std::initializer_list<U> il, Args&& ...args)
-{ return optional<T>{in_place, il, std::forward<Args>(args)...}; }
-
-#undef REQUIRES
-#undef NOEXCEPT_AS
-} // namespace al
-
-#endif /* AL_OPTIONAL_H */
diff --git a/common/threads.cpp b/common/alsem.cpp
index 19a6bbf0..6a92b35c 100644
--- a/common/threads.cpp
+++ b/common/alsem.cpp
@@ -20,11 +20,12 @@
#include "config.h"
-#include "opthelpers.h"
-#include "threads.h"
+#include "alsem.h"
#include <system_error>
+#include "opthelpers.h"
+
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
@@ -32,37 +33,6 @@
#include <limits>
-void althrd_setname(const char *name)
-{
-#if defined(_MSC_VER) && !defined(_M_ARM)
-
-#define MS_VC_EXCEPTION 0x406D1388
-#pragma pack(push,8)
- struct {
- DWORD dwType; // Must be 0x1000.
- LPCSTR szName; // Pointer to name (in user addr space).
- DWORD dwThreadID; // Thread ID (-1=caller thread).
- DWORD dwFlags; // Reserved for future use, must be zero.
- } info;
-#pragma pack(pop)
- info.dwType = 0x1000;
- info.szName = name;
- info.dwThreadID = ~DWORD{0};
- info.dwFlags = 0;
-
- __try {
- RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
- }
- __except(EXCEPTION_CONTINUE_EXECUTION) {
- }
-#undef MS_VC_EXCEPTION
-
-#else
-
- (void)name;
-#endif
-}
-
namespace al {
semaphore::semaphore(unsigned int initial)
@@ -93,49 +63,8 @@ bool semaphore::try_wait() noexcept
#else
-#include <pthread.h>
-#ifdef HAVE_PTHREAD_NP_H
-#include <pthread_np.h>
-#endif
-#include <tuple>
-
-namespace {
-
-using setname_t1 = int(*)(const char*);
-using setname_t2 = int(*)(pthread_t, const char*);
-using setname_t3 = void(*)(pthread_t, const char*);
-using setname_t4 = int(*)(pthread_t, const char*, void*);
-
-void setname_caller(setname_t1 func, const char *name)
-{ func(name); }
-
-void setname_caller(setname_t2 func, const char *name)
-{ func(pthread_self(), name); }
-
-void setname_caller(setname_t3 func, const char *name)
-{ func(pthread_self(), name); }
-
-void setname_caller(setname_t4 func, const char *name)
-{ func(pthread_self(), "%s", static_cast<void*>(const_cast<char*>(name))); }
-
-} // namespace
-
-void althrd_setname(const char *name)
-{
-#if defined(HAVE_PTHREAD_SET_NAME_NP)
- setname_caller(pthread_set_name_np, name);
-#elif defined(HAVE_PTHREAD_SETNAME_NP)
- setname_caller(pthread_setname_np, name);
-#endif
- /* Avoid unused function/parameter warnings. */
- std::ignore = name;
- std::ignore = static_cast<void(*)(setname_t1,const char*)>(&setname_caller);
- std::ignore = static_cast<void(*)(setname_t2,const char*)>(&setname_caller);
- std::ignore = static_cast<void(*)(setname_t3,const char*)>(&setname_caller);
- std::ignore = static_cast<void(*)(setname_t4,const char*)>(&setname_caller);
-}
-
-#ifdef __APPLE__
+/* Do not try using libdispatch on systems where it is absent. */
+#if defined(AL_APPLE_HAVE_DISPATCH)
namespace al {
diff --git a/common/threads.h b/common/alsem.h
index 1cdb5d8f..9f72d1c6 100644
--- a/common/threads.h
+++ b/common/alsem.h
@@ -1,30 +1,25 @@
-#ifndef AL_THREADS_H
-#define AL_THREADS_H
-
-#if defined(__GNUC__) && defined(__i386__)
-/* force_align_arg_pointer is required for proper function arguments aligning
- * when SSE code is used. Some systems (Windows, QNX) do not guarantee our
- * thread functions will be properly aligned on the stack, even though GCC may
- * generate code with the assumption that it is. */
-#define FORCE_ALIGN __attribute__((force_align_arg_pointer))
-#else
-#define FORCE_ALIGN
-#endif
+#ifndef COMMON_ALSEM_H
+#define COMMON_ALSEM_H
#if defined(__APPLE__)
+#include <AvailabilityMacros.h>
+#include <TargetConditionals.h>
+#if (((MAC_OS_X_VERSION_MIN_REQUIRED > 1050) && !defined(__ppc__)) || TARGET_OS_IOS || TARGET_OS_TV)
#include <dispatch/dispatch.h>
+#define AL_APPLE_HAVE_DISPATCH 1
+#else
+#include <semaphore.h> /* Fallback option for Apple without a working libdispatch */
+#endif
#elif !defined(_WIN32)
#include <semaphore.h>
#endif
-void althrd_setname(const char *name);
-
namespace al {
class semaphore {
#ifdef _WIN32
using native_type = void*;
-#elif defined(__APPLE__)
+#elif defined(AL_APPLE_HAVE_DISPATCH)
using native_type = dispatch_semaphore_t;
#else
using native_type = sem_t;
@@ -45,4 +40,4 @@ public:
} // namespace al
-#endif /* AL_THREADS_H */
+#endif /* COMMON_ALSEM_H */
diff --git a/common/alspan.h b/common/alspan.h
index 1d6cdfe5..341ce7c8 100644
--- a/common/alspan.h
+++ b/common/alspan.h
@@ -12,41 +12,12 @@
namespace al {
-template<typename T>
-constexpr auto size(const T &cont) noexcept(noexcept(cont.size())) -> decltype(cont.size())
-{ return cont.size(); }
-
-template<typename T, size_t N>
-constexpr size_t size(const T (&)[N]) noexcept
-{ return N; }
-
-
-template<typename T>
-constexpr auto data(T &cont) noexcept(noexcept(cont.data())) -> decltype(cont.data())
-{ return cont.data(); }
-
-template<typename T>
-constexpr auto data(const T &cont) noexcept(noexcept(cont.data())) -> decltype(cont.data())
-{ return cont.data(); }
-
-template<typename T, size_t N>
-constexpr T* data(T (&arr)[N]) noexcept
-{ return arr; }
-
-template<typename T>
-constexpr const T* data(std::initializer_list<T> list) noexcept
-{ return list.begin(); }
-
-
constexpr size_t dynamic_extent{static_cast<size_t>(-1)};
template<typename T, size_t E=dynamic_extent>
class span;
namespace detail_ {
- template<typename... Ts>
- using void_t = void;
-
template<typename T>
struct is_span_ : std::false_type { };
template<typename T, size_t E>
@@ -65,16 +36,19 @@ namespace detail_ {
constexpr bool has_size_and_data = false;
template<typename T>
constexpr bool has_size_and_data<T,
- void_t<decltype(al::size(std::declval<T>())), decltype(al::data(std::declval<T>()))>>
+ std::void_t<decltype(std::size(std::declval<T>())),decltype(std::data(std::declval<T>()))>>
= true;
+ template<typename C>
+ constexpr bool is_valid_container_type = !is_span_v<C> && !is_std_array_v<C>
+ && !std::is_array<C>::value && has_size_and_data<C>;
+
template<typename T, typename U>
constexpr bool is_array_compatible = std::is_convertible<T(*)[],U(*)[]>::value;
template<typename C, typename T>
- constexpr bool is_valid_container = !is_span_v<C> && !is_std_array_v<C>
- && !std::is_array<C>::value && has_size_and_data<C>
- && is_array_compatible<std::remove_pointer_t<decltype(al::data(std::declval<C&>()))>,T>;
+ constexpr bool is_valid_container = is_valid_container_type<C>
+ && is_array_compatible<std::remove_pointer_t<decltype(std::data(std::declval<C&>()))>,T>;
} // namespace detail_
#define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true
@@ -102,30 +76,33 @@ public:
template<bool is0=(extent == 0), REQUIRES(is0)>
constexpr span() noexcept { }
template<typename U>
- constexpr explicit span(U iter, index_type) : mData{to_address(iter)} { }
+ constexpr explicit span(U iter, index_type) : mData{::al::to_address(iter)} { }
template<typename U, typename V, REQUIRES(!std::is_convertible<V,size_t>::value)>
- constexpr explicit span(U first, V) : mData{to_address(first)} { }
+ constexpr explicit span(U first, V) : mData{::al::to_address(first)}
+ {}
constexpr span(type_identity_t<element_type> (&arr)[E]) noexcept
- : span{al::data(arr), al::size(arr)}
+ : span{std::data(arr), std::size(arr)}
+ { }
+ constexpr span(std::array<value_type,E> &arr) noexcept
+ : span{std::data(arr), std::size(arr)}
{ }
- constexpr span(std::array<value_type,E> &arr) noexcept : span{al::data(arr), al::size(arr)} { }
template<typename U=T, REQUIRES(std::is_const<U>::value)>
constexpr span(const std::array<value_type,E> &arr) noexcept
- : span{al::data(arr), al::size(arr)}
+ : span{std::data(arr), std::size(arr)}
{ }
template<typename U, REQUIRES(detail_::is_valid_container<U, element_type>)>
- constexpr explicit span(U&& cont) : span{al::data(cont), al::size(cont)} { }
+ constexpr explicit span(U&& cont) : span{std::data(cont), std::size(cont)} { }
template<typename U, index_type N, REQUIRES(!std::is_same<element_type,U>::value
&& detail_::is_array_compatible<U,element_type> && N == dynamic_extent)>
constexpr explicit span(const span<U,N> &span_) noexcept
- : span{al::data(span_), al::size(span_)}
+ : span{std::data(span_), std::size(span_)}
{ }
template<typename U, index_type N, REQUIRES(!std::is_same<element_type,U>::value
&& detail_::is_array_compatible<U,element_type> && N == extent)>
- constexpr span(const span<U,N> &span_) noexcept : span{al::data(span_), al::size(span_)} { }
+ constexpr span(const span<U,N> &span_) noexcept : span{std::data(span_), std::size(span_)} { }
constexpr span(const span&) noexcept = default;
constexpr span& operator=(const span &rhs) noexcept = default;
@@ -215,30 +192,31 @@ public:
constexpr span() noexcept = default;
template<typename U>
- constexpr span(U iter, index_type count)
- : mData{to_address(iter)}, mDataEnd{to_address(iter)+count}
+ constexpr span(U iter, index_type count) : mData{::al::to_address(iter)}, mDataEnd{::al::to_address(iter) + count}
{ }
template<typename U, typename V, REQUIRES(!std::is_convertible<V,size_t>::value)>
- constexpr span(U first, V last) : span{to_address(first), static_cast<size_t>(last-first)}
+ constexpr span(U first, V last) : span{::al::to_address(first), static_cast<size_t>(last - first)}
{ }
template<size_t N>
constexpr span(type_identity_t<element_type> (&arr)[N]) noexcept
- : span{al::data(arr), al::size(arr)}
+ : span{std::data(arr), std::size(arr)}
{ }
template<size_t N>
- constexpr span(std::array<value_type,N> &arr) noexcept : span{al::data(arr), al::size(arr)} { }
+ constexpr span(std::array<value_type,N> &arr) noexcept
+ : span{std::data(arr), std::size(arr)}
+ { }
template<size_t N, typename U=T, REQUIRES(std::is_const<U>::value)>
constexpr span(const std::array<value_type,N> &arr) noexcept
- : span{al::data(arr), al::size(arr)}
+ : span{std::data(arr), std::size(arr)}
{ }
template<typename U, REQUIRES(detail_::is_valid_container<U, element_type>)>
- constexpr span(U&& cont) : span{al::data(cont), al::size(cont)} { }
+ constexpr span(U&& cont) : span{std::data(cont), std::size(cont)} { }
template<typename U, size_t N, REQUIRES((!std::is_same<element_type,U>::value || extent != N)
&& detail_::is_array_compatible<U,element_type>)>
- constexpr span(const span<U,N> &span_) noexcept : span{al::data(span_), al::size(span_)} { }
+ constexpr span(const span<U,N> &span_) noexcept : span{std::data(span_), std::size(span_)} { }
constexpr span(const span&) noexcept = default;
constexpr span& operator=(const span &rhs) noexcept = default;
@@ -322,30 +300,21 @@ constexpr inline auto span<T,E>::subspan(size_t offset, size_t count) const
span<element_type>{mData+offset, mData+offset+count};
}
-/* Helpers to deal with the lack of user-defined deduction guides (C++17). */
-template<typename T, typename U>
-constexpr auto as_span(T ptr, U count_or_end)
-{
- using value_type = typename std::pointer_traits<T>::element_type;
- return span<value_type>{ptr, count_or_end};
-}
-template<typename T, size_t N>
-constexpr auto as_span(T (&arr)[N]) noexcept { return span<T,N>{al::data(arr), al::size(arr)}; }
-template<typename T, size_t N>
-constexpr auto as_span(std::array<T,N> &arr) noexcept
-{ return span<T,N>{al::data(arr), al::size(arr)}; }
-template<typename T, size_t N>
-constexpr auto as_span(const std::array<T,N> &arr) noexcept
-{ return span<std::add_const_t<T>,N>{al::data(arr), al::size(arr)}; }
-template<typename U, REQUIRES(!detail_::is_span_v<U> && !detail_::is_std_array_v<U>
- && !std::is_array<U>::value && detail_::has_size_and_data<U>)>
-constexpr auto as_span(U&& cont)
-{
- using value_type = std::remove_pointer_t<decltype(al::data(std::declval<U&>()))>;
- return span<value_type>{al::data(cont), al::size(cont)};
-}
-template<typename T, size_t N>
-constexpr auto as_span(span<T,N> span_) noexcept { return span_; }
+
+template<typename T, typename EndOrSize>
+span(T, EndOrSize) -> span<std::remove_reference_t<decltype(*std::declval<T&>())>>;
+
+template<typename T, std::size_t N>
+span(T (&)[N]) -> span<T, N>;
+
+template<typename T, std::size_t N>
+span(std::array<T, N>&) -> span<T, N>;
+
+template<typename T, std::size_t N>
+span(const std::array<T, N>&) -> span<const T, N>;
+
+template<typename C, REQUIRES(detail_::is_valid_container_type<C>)>
+span(C&&) -> span<std::remove_pointer_t<decltype(std::data(std::declval<C&>()))>>;
#undef REQUIRES
diff --git a/common/althrd_setname.cpp b/common/althrd_setname.cpp
new file mode 100644
index 00000000..22d33092
--- /dev/null
+++ b/common/althrd_setname.cpp
@@ -0,0 +1,76 @@
+
+#include "config.h"
+
+#include "althrd_setname.h"
+
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+void althrd_setname(const char *name [[maybe_unused]])
+{
+#if defined(_MSC_VER) && !defined(_M_ARM)
+
+#define MS_VC_EXCEPTION 0x406D1388
+#pragma pack(push,8)
+ struct {
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+ } info;
+#pragma pack(pop)
+ info.dwType = 0x1000;
+ info.szName = name;
+ info.dwThreadID = ~DWORD{0};
+ info.dwFlags = 0;
+
+ /* FIXME: How to do this on MinGW? */
+ __try {
+ RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION) {
+ }
+#undef MS_VC_EXCEPTION
+#endif
+}
+
+#else
+
+#include <pthread.h>
+#ifdef HAVE_PTHREAD_NP_H
+#include <pthread_np.h>
+#endif
+
+namespace {
+
+using setname_t1 = int(*)(const char*);
+using setname_t2 = int(*)(pthread_t, const char*);
+using setname_t3 = void(*)(pthread_t, const char*);
+using setname_t4 = int(*)(pthread_t, const char*, void*);
+
+[[maybe_unused]] void setname_caller(setname_t1 func, const char *name)
+{ func(name); }
+
+[[maybe_unused]] void setname_caller(setname_t2 func, const char *name)
+{ func(pthread_self(), name); }
+
+[[maybe_unused]] void setname_caller(setname_t3 func, const char *name)
+{ func(pthread_self(), name); }
+
+[[maybe_unused]] void setname_caller(setname_t4 func, const char *name)
+{ func(pthread_self(), "%s", static_cast<void*>(const_cast<char*>(name))); }
+
+} // namespace
+
+void althrd_setname(const char *name [[maybe_unused]])
+{
+#if defined(HAVE_PTHREAD_SET_NAME_NP)
+ setname_caller(pthread_set_name_np, name);
+#elif defined(HAVE_PTHREAD_SETNAME_NP)
+ setname_caller(pthread_setname_np, name);
+#endif
+}
+
+#endif
diff --git a/common/althrd_setname.h b/common/althrd_setname.h
new file mode 100644
index 00000000..0e22c0a9
--- /dev/null
+++ b/common/althrd_setname.h
@@ -0,0 +1,6 @@
+#ifndef COMMON_ALTHRD_SETNAME_H
+#define COMMON_ALTHRD_SETNAME_H
+
+void althrd_setname(const char *name);
+
+#endif /* COMMON_ALTHRD_SETNAME_H */
diff --git a/common/comptr.h b/common/comptr.h
index cdc6dec0..9f8fd294 100644
--- a/common/comptr.h
+++ b/common/comptr.h
@@ -2,49 +2,84 @@
#define COMMON_COMPTR_H
#include <cstddef>
+#include <memory>
+#include <type_traits>
#include <utility>
+#include <variant>
-#include "opthelpers.h"
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <objbase.h>
+
+struct ComWrapper {
+ HRESULT mStatus{};
+
+ ComWrapper(void *reserved, DWORD coinit)
+ : mStatus{CoInitializeEx(reserved, coinit)}
+ { }
+ ComWrapper(DWORD coinit=COINIT_APARTMENTTHREADED)
+ : mStatus{CoInitializeEx(nullptr, coinit)}
+ { }
+ ComWrapper(ComWrapper&& rhs) { mStatus = std::exchange(rhs.mStatus, E_FAIL); }
+ ComWrapper(const ComWrapper&) = delete;
+ ~ComWrapper() { if(SUCCEEDED(mStatus)) CoUninitialize(); }
+
+ ComWrapper& operator=(ComWrapper&& rhs)
+ {
+ if(SUCCEEDED(mStatus))
+ CoUninitialize();
+ mStatus = std::exchange(rhs.mStatus, E_FAIL);
+ return *this;
+ }
+ ComWrapper& operator=(const ComWrapper&) = delete;
+
+ HRESULT status() const noexcept { return mStatus; }
+ explicit operator bool() const noexcept { return SUCCEEDED(status()); }
+
+ void uninit()
+ {
+ if(SUCCEEDED(mStatus))
+ CoUninitialize();
+ mStatus = E_FAIL;
+ }
+};
template<typename T>
-class ComPtr {
- T *mPtr{nullptr};
+struct ComPtr {
+ using element_type = T;
+
+ static constexpr bool RefIsNoexcept{noexcept(std::declval<T&>().AddRef())
+ && noexcept(std::declval<T&>().Release())};
-public:
ComPtr() noexcept = default;
- ComPtr(const ComPtr &rhs) : mPtr{rhs.mPtr} { if(mPtr) mPtr->AddRef(); }
+ ComPtr(const ComPtr &rhs) noexcept(RefIsNoexcept) : mPtr{rhs.mPtr}
+ { if(mPtr) mPtr->AddRef(); }
ComPtr(ComPtr&& rhs) noexcept : mPtr{rhs.mPtr} { rhs.mPtr = nullptr; }
ComPtr(std::nullptr_t) noexcept { }
explicit ComPtr(T *ptr) noexcept : mPtr{ptr} { }
~ComPtr() { if(mPtr) mPtr->Release(); }
- ComPtr& operator=(const ComPtr &rhs)
+ ComPtr& operator=(const ComPtr &rhs) noexcept(RefIsNoexcept)
{
- if(!rhs.mPtr)
+ if constexpr(RefIsNoexcept)
{
- if(mPtr)
- mPtr->Release();
- mPtr = nullptr;
+ if(rhs.mPtr) rhs.mPtr->AddRef();
+ if(mPtr) mPtr->Release();
+ mPtr = rhs.mPtr;
+ return *this;
}
else
{
- rhs.mPtr->AddRef();
- try {
- if(mPtr)
- mPtr->Release();
- mPtr = rhs.mPtr;
- }
- catch(...) {
- rhs.mPtr->Release();
- throw;
- }
+ ComPtr tmp{rhs};
+ if(mPtr) mPtr->Release();
+ mPtr = tmp.release();
+ return *this;
}
- return *this;
}
- ComPtr& operator=(ComPtr&& rhs)
+ ComPtr& operator=(ComPtr&& rhs) noexcept(RefIsNoexcept)
{
- if(&rhs != this) LIKELY
+ if(&rhs != this)
{
if(mPtr) mPtr->Release();
mPtr = std::exchange(rhs.mPtr, nullptr);
@@ -52,17 +87,63 @@ public:
return *this;
}
+ void reset(T *ptr=nullptr) noexcept(RefIsNoexcept)
+ {
+ if(mPtr) mPtr->Release();
+ mPtr = ptr;
+ }
+
explicit operator bool() const noexcept { return mPtr != nullptr; }
T& operator*() const noexcept { return *mPtr; }
T* operator->() const noexcept { return mPtr; }
T* get() const noexcept { return mPtr; }
- T** getPtr() noexcept { return &mPtr; }
T* release() noexcept { return std::exchange(mPtr, nullptr); }
void swap(ComPtr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
void swap(ComPtr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
+
+private:
+ T *mPtr{nullptr};
+};
+
+
+namespace al {
+
+template<typename SP, typename PT, typename ...Args>
+class out_ptr_t {
+ static_assert(!std::is_same_v<PT,void*>);
+
+ SP &mRes;
+ std::variant<PT,void*> mPtr{};
+
+public:
+ out_ptr_t(SP &res) : mRes{res} { }
+ ~out_ptr_t()
+ {
+ auto set_res = [this](auto &ptr)
+ { mRes.reset(static_cast<PT>(ptr)); };
+ std::visit(set_res, mPtr);
+ }
+ out_ptr_t(const out_ptr_t&) = delete;
+
+ out_ptr_t& operator=(const out_ptr_t&) = delete;
+
+ operator PT*() noexcept
+ { return &std::get<PT>(mPtr); }
+
+ operator void**() noexcept
+ { return &mPtr.template emplace<void*>(); }
};
+template<typename T=void, typename SP, typename ...Args>
+auto out_ptr(SP &res)
+{
+ using ptype = typename SP::element_type*;
+ return out_ptr_t<SP,ptype>{res};
+}
+
+} // namespace al
+
#endif
diff --git a/common/dynload.cpp b/common/dynload.cpp
index f1c2a7eb..86c36e00 100644
--- a/common/dynload.cpp
+++ b/common/dynload.cpp
@@ -3,6 +3,7 @@
#include "dynload.h"
+#include "albit.h"
#include "strutils.h"
#ifdef _WIN32
@@ -17,7 +18,7 @@ void *LoadLib(const char *name)
void CloseLib(void *handle)
{ FreeLibrary(static_cast<HMODULE>(handle)); }
void *GetSymbol(void *handle, const char *name)
-{ return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name)); }
+{ return al::bit_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name)); }
#elif defined(HAVE_DLFCN_H)
diff --git a/common/opthelpers.h b/common/opthelpers.h
index 596c2455..dc43ccdb 100644
--- a/common/opthelpers.h
+++ b/common/opthelpers.h
@@ -19,10 +19,13 @@
#ifdef __GNUC__
#define force_inline [[gnu::always_inline]] inline
+#define NOINLINE [[gnu::noinline]]
#elif defined(_MSC_VER)
#define force_inline __forceinline
+#define NOINLINE __declspec(noinline)
#else
#define force_inline inline
+#define NOINLINE
#endif
/* Unlike the likely attribute, ASSUME requires the condition to be true or
diff --git a/common/pffft.cpp b/common/pffft.cpp
new file mode 100644
index 00000000..71f71fa6
--- /dev/null
+++ b/common/pffft.cpp
@@ -0,0 +1,2259 @@
+//$ nobt
+
+/* Copyright (c) 2013 Julien Pommier ( [email protected] )
+ * Copyright (c) 2023 Christopher Robinson
+ *
+ * Based on original fortran 77 code from FFTPACKv4 from NETLIB
+ * (http://www.netlib.org/fftpack), authored by Dr Paul Swarztrauber
+ * of NCAR, in 1985.
+ *
+ * As confirmed by the NCAR fftpack software curators, the following
+ * FFTPACKv5 license applies to FFTPACKv4 sources. My changes are
+ * released under the same terms.
+ *
+ * FFTPACK license:
+ *
+ * http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html
+ *
+ * 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.
+ *
+ *
+ * PFFFT : a Pretty Fast FFT.
+ *
+ * This file is largerly based on the original FFTPACK implementation, modified
+ * in order to take advantage of SIMD instructions of modern CPUs.
+ */
+
+#include "pffft.h"
+
+#include <array>
+#include <assert.h>
+#include <cmath>
+#include <cstring>
+#include <stdio.h>
+#include <stdlib.h>
+#include <vector>
+
+#include "albit.h"
+#include "almalloc.h"
+#include "alnumbers.h"
+#include "alspan.h"
+#include "opthelpers.h"
+
+
+namespace {
+
+using uint = unsigned int;
+
+
+/* Vector support macros: the rest of the code is independent of
+ * SSE/Altivec/NEON -- adding support for other platforms with 4-element
+ * vectors should be limited to these macros
+ */
+
+/* Define PFFFT_SIMD_DISABLE if you want to use scalar code instead of SIMD code */
+//#define PFFFT_SIMD_DISABLE
+
+#ifndef PFFFT_SIMD_DISABLE
+/*
+ * Altivec support macros
+ */
+#if defined(__ppc__) || defined(__ppc64__) || defined(__powerpc__) || defined(__powerpc64__)
+typedef vector float v4sf;
+#define SIMD_SZ 4
+#define VZERO() ((vector float) vec_splat_u8(0))
+#define VMUL(a,b) vec_madd(a,b, VZERO())
+#define VADD vec_add
+#define VMADD vec_madd
+#define VSUB vec_sub
+#define LD_PS1 vec_splats
+force_inline v4sf vset4(float a, float b, float c, float d) noexcept
+{
+ /* There a more efficient way to do this? */
+ alignas(16) std::array<float,4> vals{{a, b, c, d}};
+ return vec_ld(0, vals.data());
+}
+#define VSET4 vset4
+#define VINSERT0(v, a) vec_insert((a), (v), 0)
+#define VEXTRACT0(v) vec_extract((v), 0)
+
+force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept
+{
+ v4sf tmp{vec_mergeh(in1, in2)};
+ out2 = vec_mergel(in1, in2);
+ out1 = tmp;
+}
+force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept
+{
+ v4sf tmp{vec_perm(in1, in2, (vector unsigned char)(0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27))};
+ out2 = vec_perm(in1, in2, (vector unsigned char)(4,5,6,7,12,13,14,15,20,21,22,23,28,29,30,31));
+ out1 = tmp;
+}
+
+force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept
+{
+ v4sf y0{vec_mergeh(x0, x2)};
+ v4sf y1{vec_mergel(x0, x2)};
+ v4sf y2{vec_mergeh(x1, x3)};
+ v4sf y3{vec_mergel(x1, x3)};
+ x0 = vec_mergeh(y0, y2);
+ x1 = vec_mergel(y0, y2);
+ x2 = vec_mergeh(y1, y3);
+ x3 = vec_mergel(y1, y3);
+}
+
+#define VSWAPHL(a,b) vec_perm(a,b, (vector unsigned char)(16,17,18,19,20,21,22,23,8,9,10,11,12,13,14,15))
+
+/*
+ * SSE1 support macros
+ */
+#elif defined(__x86_64__) || defined(__SSE__) || defined(_M_X64) || \
+ (defined(_M_IX86_FP) && _M_IX86_FP >= 1)
+
+#include <xmmintrin.h>
+typedef __m128 v4sf;
+#define SIMD_SZ 4 // 4 floats by simd vector -- this is pretty much hardcoded in the preprocess/finalize functions anyway so you will have to work if you want to enable AVX with its 256-bit vectors.
+#define VZERO _mm_setzero_ps
+#define VMUL _mm_mul_ps
+#define VADD _mm_add_ps
+#define VMADD(a,b,c) _mm_add_ps(_mm_mul_ps(a,b), c)
+#define VSUB _mm_sub_ps
+#define LD_PS1 _mm_set1_ps
+#define VSET4 _mm_setr_ps
+#define VINSERT0(v, a) _mm_move_ss((v), _mm_set_ss(a))
+#define VEXTRACT0 _mm_cvtss_f32
+
+force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept
+{
+ v4sf tmp{_mm_unpacklo_ps(in1, in2)};
+ out2 = _mm_unpackhi_ps(in1, in2);
+ out1 = tmp;
+}
+force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept
+{
+ v4sf tmp{_mm_shuffle_ps(in1, in2, _MM_SHUFFLE(2,0,2,0))};
+ out2 = _mm_shuffle_ps(in1, in2, _MM_SHUFFLE(3,1,3,1));
+ out1 = tmp;
+}
+
+force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept
+{ _MM_TRANSPOSE4_PS(x0, x1, x2, x3); }
+
+#define VSWAPHL(a,b) _mm_shuffle_ps(b, a, _MM_SHUFFLE(3,2,1,0))
+
+/*
+ * ARM NEON support macros
+ */
+#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(__arm64)
+
+#include <arm_neon.h>
+typedef float32x4_t v4sf;
+#define SIMD_SZ 4
+#define VZERO() vdupq_n_f32(0)
+#define VMUL vmulq_f32
+#define VADD vaddq_f32
+#define VMADD(a,b,c) vmlaq_f32(c,a,b)
+#define VSUB vsubq_f32
+#define LD_PS1 vdupq_n_f32
+force_inline v4sf vset4(float a, float b, float c, float d) noexcept
+{
+ float32x4_t ret{vmovq_n_f32(a)};
+ ret = vsetq_lane_f32(b, ret, 1);
+ ret = vsetq_lane_f32(c, ret, 2);
+ ret = vsetq_lane_f32(d, ret, 3);
+ return ret;
+}
+#define VSET4 vset4
+#define VINSERT0(v, a) vsetq_lane_f32((a), (v), 0)
+#define VEXTRACT0(v) vgetq_lane_f32((v), 0)
+
+force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept
+{
+ float32x4x2_t tmp{vzipq_f32(in1, in2)};
+ out1 = tmp.val[0];
+ out2 = tmp.val[1];
+}
+force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept
+{
+ float32x4x2_t tmp{vuzpq_f32(in1, in2)};
+ out1 = tmp.val[0];
+ out2 = tmp.val[1];
+}
+
+force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept
+{
+ /* marginally faster version:
+ * asm("vtrn.32 %q0, %q1;\n"
+ * "vtrn.32 %q2, %q3\n
+ * "vswp %f0, %e2\n
+ * "vswp %f1, %e3"
+ * : "+w"(x0), "+w"(x1), "+w"(x2), "+w"(x3)::);
+ */
+ float32x4x2_t t0_{vzipq_f32(x0, x2)};
+ float32x4x2_t t1_{vzipq_f32(x1, x3)};
+ float32x4x2_t u0_{vzipq_f32(t0_.val[0], t1_.val[0])};
+ float32x4x2_t u1_{vzipq_f32(t0_.val[1], t1_.val[1])};
+ x0 = u0_.val[0];
+ x1 = u0_.val[1];
+ x2 = u1_.val[0];
+ x3 = u1_.val[1];
+}
+
+#define VSWAPHL(a,b) vcombine_f32(vget_low_f32(b), vget_high_f32(a))
+
+/*
+ * Generic GCC vector macros
+ */
+#elif defined(__GNUC__)
+
+using v4sf [[gnu::vector_size(16), gnu::aligned(16)]] = float;
+#define SIMD_SZ 4
+#define VZERO() v4sf{0,0,0,0}
+#define VMUL(a,b) ((a) * (b))
+#define VADD(a,b) ((a) + (b))
+#define VMADD(a,b,c) ((a)*(b) + (c))
+#define VSUB(a,b) ((a) - (b))
+
+constexpr force_inline v4sf ld_ps1(float a) noexcept { return v4sf{a, a, a, a}; }
+#define LD_PS1 ld_ps1
+#define VSET4(a, b, c, d) v4sf{(a), (b), (c), (d)}
+constexpr force_inline v4sf vinsert0(v4sf v, float a) noexcept
+{ return v4sf{a, v[1], v[2], v[3]}; }
+#define VINSERT0 vinsert0
+#define VEXTRACT0(v) ((v)[0])
+
+force_inline v4sf unpacklo(v4sf a, v4sf b) noexcept
+{ return v4sf{a[0], b[0], a[1], b[1]}; }
+force_inline v4sf unpackhi(v4sf a, v4sf b) noexcept
+{ return v4sf{a[2], b[2], a[3], b[3]}; }
+
+force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept
+{
+ v4sf tmp{unpacklo(in1, in2)};
+ out2 = unpackhi(in1, in2);
+ out1 = tmp;
+}
+force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept
+{
+ v4sf tmp{in1[0], in1[2], in2[0], in2[2]};
+ out2 = v4sf{in1[1], in1[3], in2[1], in2[3]};
+ out1 = tmp;
+}
+
+force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept
+{
+ v4sf tmp0{unpacklo(x0, x1)};
+ v4sf tmp2{unpacklo(x2, x3)};
+ v4sf tmp1{unpackhi(x0, x1)};
+ v4sf tmp3{unpackhi(x2, x3)};
+ x0 = v4sf{tmp0[0], tmp0[1], tmp2[0], tmp2[1]};
+ x1 = v4sf{tmp0[2], tmp0[3], tmp2[2], tmp2[3]};
+ x2 = v4sf{tmp1[0], tmp1[1], tmp3[0], tmp3[1]};
+ x3 = v4sf{tmp1[2], tmp1[3], tmp3[2], tmp3[3]};
+}
+
+force_inline v4sf vswaphl(v4sf a, v4sf b) noexcept
+{ return v4sf{b[0], b[1], a[2], a[3]}; }
+#define VSWAPHL vswaphl
+
+#else
+
+#warning "building with simd disabled !\n";
+#define PFFFT_SIMD_DISABLE // fallback to scalar code
+#endif
+
+#endif /* PFFFT_SIMD_DISABLE */
+
+// fallback mode for situations where SIMD is not available, use scalar mode instead
+#ifdef PFFFT_SIMD_DISABLE
+typedef float v4sf;
+#define SIMD_SZ 1
+#define VZERO() 0.f
+#define VMUL(a,b) ((a)*(b))
+#define VADD(a,b) ((a)+(b))
+#define VMADD(a,b,c) ((a)*(b)+(c))
+#define VSUB(a,b) ((a)-(b))
+#define LD_PS1(p) (p)
+#endif
+
+inline bool valigned(const float *ptr) noexcept
+{
+ static constexpr uintptr_t alignmask{SIMD_SZ*4 - 1};
+ return (reinterpret_cast<uintptr_t>(ptr) & alignmask) == 0;
+}
+
+// shortcuts for complex multiplications
+force_inline void vcplxmul(v4sf &ar, v4sf &ai, v4sf br, v4sf bi) noexcept
+{
+ v4sf tmp{VMUL(ar, bi)};
+ ar = VSUB(VMUL(ar, br), VMUL(ai, bi));
+ ai = VMADD(ai, br, tmp);
+}
+force_inline void vcplxmulconj(v4sf &ar, v4sf &ai, v4sf br, v4sf bi) noexcept
+{
+ v4sf tmp{VMUL(ar, bi)};
+ ar = VMADD(ai, bi, VMUL(ar, br));
+ ai = VSUB(VMUL(ai, br), tmp);
+}
+
+#if !defined(PFFFT_SIMD_DISABLE)
+
+#define assertv4(v,f0,f1,f2,f3) assert(v##_f[0] == (f0) && v##_f[1] == (f1) && v##_f[2] == (f2) && v##_f[3] == (f3))
+
+/* detect bugs with the vector support macros */
+[[maybe_unused]] void validate_pffft_simd()
+{
+ using float4 = std::array<float,4>;
+ static constexpr float f[16]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+
+ float4 a0_f, a1_f, a2_f, a3_f, t_f, u_f;
+ v4sf a0_v, a1_v, a2_v, a3_v, t_v, u_v;
+ std::memcpy(&a0_v, f, 4*sizeof(float));
+ std::memcpy(&a1_v, f+4, 4*sizeof(float));
+ std::memcpy(&a2_v, f+8, 4*sizeof(float));
+ std::memcpy(&a3_v, f+12, 4*sizeof(float));
+
+ t_v = VZERO(); t_f = al::bit_cast<float4>(t_v);
+ printf("VZERO=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]); assertv4(t, 0, 0, 0, 0);
+ t_v = VADD(a1_v, a2_v); t_f = al::bit_cast<float4>(t_v);
+ printf("VADD(4:7,8:11)=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]); assertv4(t, 12, 14, 16, 18);
+ t_v = VMUL(a1_v, a2_v); t_f = al::bit_cast<float4>(t_v);
+ printf("VMUL(4:7,8:11)=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]); assertv4(t, 32, 45, 60, 77);
+ t_v = VMADD(a1_v, a2_v,a0_v); t_f = al::bit_cast<float4>(t_v);
+ printf("VMADD(4:7,8:11,0:3)=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]); assertv4(t, 32, 46, 62, 80);
+
+ interleave2(a1_v,a2_v,t_v,u_v); t_f = al::bit_cast<float4>(t_v); u_f = al::bit_cast<float4>(u_v);
+ printf("INTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3], u_f[0], u_f[1], u_f[2], u_f[3]);
+ assertv4(t, 4, 8, 5, 9); assertv4(u, 6, 10, 7, 11);
+ uninterleave2(a1_v,a2_v,t_v,u_v); t_f = al::bit_cast<float4>(t_v); u_f = al::bit_cast<float4>(u_v);
+ printf("UNINTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3], u_f[0], u_f[1], u_f[2], u_f[3]);
+ assertv4(t, 4, 6, 8, 10); assertv4(u, 5, 7, 9, 11);
+
+ t_v=LD_PS1(f[15]); t_f = al::bit_cast<float4>(t_v);
+ printf("LD_PS1(15)=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]);
+ assertv4(t, 15, 15, 15, 15);
+ t_v = VSWAPHL(a1_v, a2_v); t_f = al::bit_cast<float4>(t_v);
+ printf("VSWAPHL(4:7,8:11)=[%2g %2g %2g %2g]\n", t_f[0], t_f[1], t_f[2], t_f[3]);
+ assertv4(t, 8, 9, 6, 7);
+ vtranspose4(a0_v, a1_v, a2_v, a3_v);
+ a0_f = al::bit_cast<float4>(a0_v);
+ a1_f = al::bit_cast<float4>(a1_v);
+ a2_f = al::bit_cast<float4>(a2_v);
+ a3_f = al::bit_cast<float4>(a3_v);
+ printf("VTRANSPOSE4(0:3,4:7,8:11,12:15)=[%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g]\n",
+ a0_f[0], a0_f[1], a0_f[2], a0_f[3], a1_f[0], a1_f[1], a1_f[2], a1_f[3],
+ a2_f[0], a2_f[1], a2_f[2], a2_f[3], a3_f[0], a3_f[1], a3_f[2], a3_f[3]);
+ assertv4(a0, 0, 4, 8, 12); assertv4(a1, 1, 5, 9, 13); assertv4(a2, 2, 6, 10, 14); assertv4(a3, 3, 7, 11, 15);
+}
+#endif //!PFFFT_SIMD_DISABLE
+
+/* SSE and co like 16-bytes aligned pointers */
+#define MALLOC_V4SF_ALIGNMENT 64 // with a 64-byte alignment, we are even aligned on L2 cache lines...
+
+/*
+ passf2 and passb2 has been merged here, fsign = -1 for passf2, +1 for passb2
+*/
+NOINLINE void passf2_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch,
+ const float *wa1, const float fsign)
+{
+ const size_t l1ido{l1*ido};
+ if(ido <= 2)
+ {
+ for(size_t k{0};k < l1ido;k += ido, ch += ido, cc += 2*ido)
+ {
+ ch[0] = VADD(cc[0], cc[ido+0]);
+ ch[l1ido] = VSUB(cc[0], cc[ido+0]);
+ ch[1] = VADD(cc[1], cc[ido+1]);
+ ch[l1ido + 1] = VSUB(cc[1], cc[ido+1]);
+ }
+ }
+ else
+ {
+ for(size_t k{0};k < l1ido;k += ido, ch += ido, cc += 2*ido)
+ {
+ for(size_t i{0};i < ido-1;i += 2)
+ {
+ v4sf tr2{VSUB(cc[i+0], cc[i+ido+0])};
+ v4sf ti2{VSUB(cc[i+1], cc[i+ido+1])};
+ v4sf wr{LD_PS1(wa1[i])}, wi{LD_PS1(wa1[i+1]*fsign)};
+ ch[i] = VADD(cc[i+0], cc[i+ido+0]);
+ ch[i+1] = VADD(cc[i+1], cc[i+ido+1]);
+ vcplxmul(tr2, ti2, wr, wi);
+ ch[i+l1ido] = tr2;
+ ch[i+l1ido+1] = ti2;
+ }
+ }
+ }
+}
+
+/*
+ passf3 and passb3 has been merged here, fsign = -1 for passf3, +1 for passb3
+*/
+NOINLINE void passf3_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch,
+ const float *wa1, const float *wa2, const float fsign)
+{
+ assert(ido > 2);
+
+ const v4sf taur{LD_PS1(-0.5f)};
+ const v4sf taui{LD_PS1(0.866025403784439f*fsign)};
+ const size_t l1ido{l1*ido};
+ for(size_t k{0};k < l1ido;k += ido, cc += 3*ido, ch +=ido)
+ {
+ for(size_t i{0};i < ido-1;i += 2)
+ {
+ v4sf tr2{VADD(cc[i+ido], cc[i+2*ido])};
+ v4sf cr2{VMADD(taur, tr2, cc[i])};
+ ch[i] = VADD(tr2, cc[i]);
+ v4sf ti2{VADD(cc[i+ido+1], cc[i+2*ido+1])};
+ v4sf ci2{VMADD(taur, ti2, cc[i+1])};
+ ch[i+1] = VADD(cc[i+1], ti2);
+ v4sf cr3{VMUL(taui, VSUB(cc[i+ido], cc[i+2*ido]))};
+ v4sf ci3{VMUL(taui, VSUB(cc[i+ido+1], cc[i+2*ido+1]))};
+ v4sf dr2{VSUB(cr2, ci3)};
+ v4sf dr3{VADD(cr2, ci3)};
+ v4sf di2{VADD(ci2, cr3)};
+ v4sf di3{VSUB(ci2, cr3)};
+ float wr1{wa1[i]}, wi1{fsign*wa1[i+1]}, wr2{wa2[i]}, wi2{fsign*wa2[i+1]};
+ vcplxmul(dr2, di2, LD_PS1(wr1), LD_PS1(wi1));
+ ch[i+l1ido] = dr2;
+ ch[i+l1ido + 1] = di2;
+ vcplxmul(dr3, di3, LD_PS1(wr2), LD_PS1(wi2));
+ ch[i+2*l1ido] = dr3;
+ ch[i+2*l1ido+1] = di3;
+ }
+ }
+} /* passf3 */
+
+NOINLINE void passf4_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch,
+ const float *wa1, const float *wa2, const float *wa3, const float fsign)
+{
+ /* fsign == -1 for forward transform and +1 for backward transform */
+ const v4sf vsign{LD_PS1(fsign)};
+ const size_t l1ido{l1*ido};
+ if(ido == 2)
+ {
+ for(size_t k{0};k < l1ido;k += ido, ch += ido, cc += 4*ido)
+ {
+ v4sf tr1{VSUB(cc[0], cc[2*ido + 0])};
+ v4sf tr2{VADD(cc[0], cc[2*ido + 0])};
+ v4sf ti1{VSUB(cc[1], cc[2*ido + 1])};
+ v4sf ti2{VADD(cc[1], cc[2*ido + 1])};
+ v4sf ti4{VMUL(VSUB(cc[1*ido + 0], cc[3*ido + 0]), vsign)};
+ v4sf tr4{VMUL(VSUB(cc[3*ido + 1], cc[1*ido + 1]), vsign)};
+ v4sf tr3{VADD(cc[ido + 0], cc[3*ido + 0])};
+ v4sf ti3{VADD(cc[ido + 1], cc[3*ido + 1])};
+
+ ch[0*l1ido + 0] = VADD(tr2, tr3);
+ ch[0*l1ido + 1] = VADD(ti2, ti3);
+ ch[1*l1ido + 0] = VADD(tr1, tr4);
+ ch[1*l1ido + 1] = VADD(ti1, ti4);
+ ch[2*l1ido + 0] = VSUB(tr2, tr3);
+ ch[2*l1ido + 1] = VSUB(ti2, ti3);
+ ch[3*l1ido + 0] = VSUB(tr1, tr4);
+ ch[3*l1ido + 1] = VSUB(ti1, ti4);
+ }
+ }
+ else
+ {
+ for(size_t k{0};k < l1ido;k += ido, ch+=ido, cc += 4*ido)
+ {
+ for(size_t i{0};i < ido-1;i+=2)
+ {
+ v4sf tr1{VSUB(cc[i + 0], cc[i + 2*ido + 0])};
+ v4sf tr2{VADD(cc[i + 0], cc[i + 2*ido + 0])};
+ v4sf ti1{VSUB(cc[i + 1], cc[i + 2*ido + 1])};
+ v4sf ti2{VADD(cc[i + 1], cc[i + 2*ido + 1])};
+ v4sf tr4{VMUL(VSUB(cc[i + 3*ido + 1], cc[i + 1*ido + 1]), vsign)};
+ v4sf ti4{VMUL(VSUB(cc[i + 1*ido + 0], cc[i + 3*ido + 0]), vsign)};
+ v4sf tr3{VADD(cc[i + ido + 0], cc[i + 3*ido + 0])};
+ v4sf ti3{VADD(cc[i + ido + 1], cc[i + 3*ido + 1])};
+
+ ch[i] = VADD(tr2, tr3);
+ v4sf cr3{VSUB(tr2, tr3)};
+ ch[i + 1] = VADD(ti2, ti3);
+ v4sf ci3{VSUB(ti2, ti3)};
+
+ v4sf cr2{VADD(tr1, tr4)};
+ v4sf cr4{VSUB(tr1, tr4)};
+ v4sf ci2{VADD(ti1, ti4)};
+ v4sf ci4{VSUB(ti1, ti4)};
+ float wr1{wa1[i]}, wi1{fsign*wa1[i+1]};
+ vcplxmul(cr2, ci2, LD_PS1(wr1), LD_PS1(wi1));
+ float wr2{wa2[i]}, wi2{fsign*wa2[i+1]};
+ ch[i + l1ido] = cr2;
+ ch[i + l1ido + 1] = ci2;
+
+ vcplxmul(cr3, ci3, LD_PS1(wr2), LD_PS1(wi2));
+ float wr3{wa3[i]}, wi3{fsign*wa3[i+1]};
+ ch[i + 2*l1ido] = cr3;
+ ch[i + 2*l1ido + 1] = ci3;
+
+ vcplxmul(cr4, ci4, LD_PS1(wr3), LD_PS1(wi3));
+ ch[i + 3*l1ido] = cr4;
+ ch[i + 3*l1ido + 1] = ci4;
+ }
+ }
+ }
+} /* passf4 */
+
+/*
+ * passf5 and passb5 has been merged here, fsign = -1 for passf5, +1 for passb5
+ */
+NOINLINE void passf5_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch,
+ const float *wa1, const float *wa2, const float *wa3, const float *wa4, const float fsign)
+{
+ const v4sf tr11{LD_PS1(0.309016994374947f)};
+ const v4sf tr12{LD_PS1(-0.809016994374947f)};
+ const v4sf ti11{LD_PS1(0.951056516295154f*fsign)};
+ const v4sf ti12{LD_PS1(0.587785252292473f*fsign)};
+
+#define cc_ref(a_1,a_2) cc[(a_2-1)*ido + (a_1) + 1]
+#define ch_ref(a_1,a_3) ch[(a_3-1)*l1*ido + (a_1) + 1]
+
+ assert(ido > 2);
+ for(size_t k{0};k < l1;++k, cc += 5*ido, ch += ido)
+ {
+ for(size_t i{0};i < ido-1;i += 2)
+ {
+ v4sf ti5{VSUB(cc_ref(i , 2), cc_ref(i , 5))};
+ v4sf ti2{VADD(cc_ref(i , 2), cc_ref(i , 5))};
+ v4sf ti4{VSUB(cc_ref(i , 3), cc_ref(i , 4))};
+ v4sf ti3{VADD(cc_ref(i , 3), cc_ref(i , 4))};
+ v4sf tr5{VSUB(cc_ref(i-1, 2), cc_ref(i-1, 5))};
+ v4sf tr2{VADD(cc_ref(i-1, 2), cc_ref(i-1, 5))};
+ v4sf tr4{VSUB(cc_ref(i-1, 3), cc_ref(i-1, 4))};
+ v4sf tr3{VADD(cc_ref(i-1, 3), cc_ref(i-1, 4))};
+ ch_ref(i-1, 1) = VADD(cc_ref(i-1, 1), VADD(tr2, tr3));
+ ch_ref(i , 1) = VADD(cc_ref(i , 1), VADD(ti2, ti3));
+ v4sf cr2{VADD(cc_ref(i-1, 1), VMADD(tr11, tr2, VMUL(tr12, tr3)))};
+ v4sf ci2{VADD(cc_ref(i , 1), VMADD(tr11, ti2, VMUL(tr12, ti3)))};
+ v4sf cr3{VADD(cc_ref(i-1, 1), VMADD(tr12, tr2, VMUL(tr11, tr3)))};
+ v4sf ci3{VADD(cc_ref(i , 1), VMADD(tr12, ti2, VMUL(tr11, ti3)))};
+ v4sf cr5{VMADD(ti11, tr5, VMUL(ti12, tr4))};
+ v4sf ci5{VMADD(ti11, ti5, VMUL(ti12, ti4))};
+ v4sf cr4{VSUB(VMUL(ti12, tr5), VMUL(ti11, tr4))};
+ v4sf ci4{VSUB(VMUL(ti12, ti5), VMUL(ti11, ti4))};
+ v4sf dr3{VSUB(cr3, ci4)};
+ v4sf dr4{VADD(cr3, ci4)};
+ v4sf di3{VADD(ci3, cr4)};
+ v4sf di4{VSUB(ci3, cr4)};
+ v4sf dr5{VADD(cr2, ci5)};
+ v4sf dr2{VSUB(cr2, ci5)};
+ v4sf di5{VSUB(ci2, cr5)};
+ v4sf di2{VADD(ci2, cr5)};
+ float wr1{wa1[i]}, wi1{fsign*wa1[i+1]}, wr2{wa2[i]}, wi2{fsign*wa2[i+1]};
+ float wr3{wa3[i]}, wi3{fsign*wa3[i+1]}, wr4{wa4[i]}, wi4{fsign*wa4[i+1]};
+ vcplxmul(dr2, di2, LD_PS1(wr1), LD_PS1(wi1));
+ ch_ref(i - 1, 2) = dr2;
+ ch_ref(i, 2) = di2;
+ vcplxmul(dr3, di3, LD_PS1(wr2), LD_PS1(wi2));
+ ch_ref(i - 1, 3) = dr3;
+ ch_ref(i, 3) = di3;
+ vcplxmul(dr4, di4, LD_PS1(wr3), LD_PS1(wi3));
+ ch_ref(i - 1, 4) = dr4;
+ ch_ref(i, 4) = di4;
+ vcplxmul(dr5, di5, LD_PS1(wr4), LD_PS1(wi4));
+ ch_ref(i - 1, 5) = dr5;
+ ch_ref(i, 5) = di5;
+ }
+ }
+#undef ch_ref
+#undef cc_ref
+}
+
+NOINLINE void radf2_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc,
+ v4sf *RESTRICT ch, const float *wa1)
+{
+ const size_t l1ido{l1*ido};
+ for(size_t k{0};k < l1ido;k += ido)
+ {
+ v4sf a{cc[k]}, b{cc[k + l1ido]};
+ ch[2*k] = VADD(a, b);
+ ch[2*(k+ido)-1] = VSUB(a, b);
+ }
+ if(ido < 2)
+ return;
+ if(ido != 2)
+ {
+ for(size_t k{0};k < l1ido;k += ido)
+ {
+ for(size_t i{2};i < ido;i += 2)
+ {
+ v4sf tr2{cc[i - 1 + k + l1ido]}, ti2{cc[i + k + l1ido]};
+ v4sf br{cc[i - 1 + k]}, bi{cc[i + k]};
+ vcplxmulconj(tr2, ti2, LD_PS1(wa1[i - 2]), LD_PS1(wa1[i - 1]));
+ ch[i + 2*k] = VADD(bi, ti2);
+ ch[2*(k+ido) - i] = VSUB(ti2, bi);
+ ch[i - 1 + 2*k] = VADD(br, tr2);
+ ch[2*(k+ido) - i -1] = VSUB(br, tr2);
+ }
+ }
+ if((ido&1) == 1)
+ return;
+ }
+ const v4sf minus_one{LD_PS1(-1.0f)};
+ for(size_t k{0};k < l1ido;k += ido)
+ {
+ ch[2*k + ido] = VMUL(minus_one, cc[ido-1 + k + l1ido]);
+ ch[2*k + ido-1] = cc[k + ido-1];
+ }
+} /* radf2 */
+
+
+NOINLINE void radb2_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch,
+ const float *wa1)
+{
+ const size_t l1ido{l1*ido};
+ for(size_t k{0};k < l1ido;k += ido)
+ {
+ v4sf a{cc[2*k]};
+ v4sf b{cc[2*(k+ido) - 1]};
+ ch[k] = VADD(a, b);
+ ch[k + l1ido] = VSUB(a, b);
+ }
+ if(ido < 2)
+ return;
+ if(ido != 2)
+ {
+ for(size_t k{0};k < l1ido;k += ido)
+ {
+ for(size_t i{2};i < ido;i += 2)
+ {
+ v4sf a{cc[i-1 + 2*k]};
+ v4sf b{cc[2*(k + ido) - i - 1]};
+ v4sf c{cc[i+0 + 2*k]};
+ v4sf d{cc[2*(k + ido) - i + 0]};
+ ch[i-1 + k] = VADD(a, b);
+ v4sf tr2{VSUB(a, b)};
+ ch[i+0 + k] = VSUB(c, d);
+ v4sf ti2{VADD(c, d)};
+ vcplxmul(tr2, ti2, LD_PS1(wa1[i - 2]), LD_PS1(wa1[i - 1]));
+ ch[i-1 + k + l1ido] = tr2;
+ ch[i+0 + k + l1ido] = ti2;
+ }
+ }
+ if((ido&1) == 1)
+ return;
+ }
+ const v4sf minus_two{LD_PS1(-2.0f)};
+ for(size_t k{0};k < l1ido;k += ido)
+ {
+ v4sf a{cc[2*k + ido-1]};
+ v4sf b{cc[2*k + ido]};
+ ch[k + ido-1] = VADD(a,a);
+ ch[k + ido-1 + l1ido] = VMUL(minus_two, b);
+ }
+} /* radb2 */
+
+void radf3_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch,
+ const float *wa1, const float *wa2)
+{
+ const v4sf taur{LD_PS1(-0.5f)};
+ const v4sf taui{LD_PS1(0.866025403784439f)};
+ for(size_t k{0};k < l1;++k)
+ {
+ v4sf cr2{VADD(cc[(k + l1)*ido], cc[(k + 2*l1)*ido])};
+ ch[ (3*k )*ido] = VADD(cc[k*ido], cr2);
+ ch[ (3*k + 2)*ido] = VMUL(taui, VSUB(cc[(k + l1*2)*ido], cc[(k + l1)*ido]));
+ ch[ido-1 + (3*k + 1)*ido] = VMADD(taur, cr2, cc[k*ido]);
+ }
+ if(ido == 1)
+ return;
+ for(size_t k{0};k < l1;++k)
+ {
+ for(size_t i{2};i < ido;i += 2)
+ {
+ const size_t ic{ido - i};
+ v4sf wr1{LD_PS1(wa1[i - 2])};
+ v4sf wi1{LD_PS1(wa1[i - 1])};
+ v4sf dr2{cc[i - 1 + (k + l1)*ido]};
+ v4sf di2{cc[i + (k + l1)*ido]};
+ vcplxmulconj(dr2, di2, wr1, wi1);
+
+ v4sf wr2{LD_PS1(wa2[i - 2])};
+ v4sf wi2{LD_PS1(wa2[i - 1])};
+ v4sf dr3{cc[i - 1 + (k + l1*2)*ido]};
+ v4sf di3{cc[i + (k + l1*2)*ido]};
+ vcplxmulconj(dr3, di3, wr2, wi2);
+
+ v4sf cr2{VADD(dr2, dr3)};
+ v4sf ci2{VADD(di2, di3)};
+ ch[i - 1 + 3*k*ido] = VADD(cc[i - 1 + k*ido], cr2);
+ ch[i + 3*k*ido] = VADD(cc[i + k*ido], ci2);
+ v4sf tr2{VMADD(taur, cr2, cc[i - 1 + k*ido])};
+ v4sf ti2{VMADD(taur, ci2, cc[i + k*ido])};
+ v4sf tr3{VMUL(taui, VSUB(di2, di3))};
+ v4sf ti3{VMUL(taui, VSUB(dr3, dr2))};
+ ch[i - 1 + (3*k + 2)*ido] = VADD(tr2, tr3);
+ ch[ic - 1 + (3*k + 1)*ido] = VSUB(tr2, tr3);
+ ch[i + (3*k + 2)*ido] = VADD(ti2, ti3);
+ ch[ic + (3*k + 1)*ido] = VSUB(ti3, ti2);
+ }
+ }
+} /* radf3 */
+
+
+void radb3_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch,
+ const float *wa1, const float *wa2)
+{
+ static constexpr float taur{-0.5f};
+ static constexpr float taui{0.866025403784439f};
+ static constexpr float taui_2{taui*2.0f};
+
+ const v4sf vtaur{LD_PS1(taur)};
+ const v4sf vtaui_2{LD_PS1(taui_2)};
+ for(size_t k{0};k < l1;++k)
+ {
+ v4sf tr2 = cc[ido-1 + (3*k + 1)*ido];
+ tr2 = VADD(tr2,tr2);
+ v4sf cr2 = VMADD(vtaur, tr2, cc[3*k*ido]);
+ ch[k*ido] = VADD(cc[3*k*ido], tr2);
+ v4sf ci3 = VMUL(vtaui_2, cc[(3*k + 2)*ido]);
+ ch[(k + l1)*ido] = VSUB(cr2, ci3);
+ ch[(k + 2*l1)*ido] = VADD(cr2, ci3);
+ }
+ if(ido == 1)
+ return;
+ const v4sf vtaui{LD_PS1(taui)};
+ for(size_t k{0};k < l1;++k)
+ {
+ for(size_t i{2};i < ido;i += 2)
+ {
+ const size_t ic{ido - i};
+ v4sf tr2{VADD(cc[i - 1 + (3*k + 2)*ido], cc[ic - 1 + (3*k + 1)*ido])};
+ v4sf cr2{VMADD(vtaur, tr2, cc[i - 1 + 3*k*ido])};
+ ch[i - 1 + k*ido] = VADD(cc[i - 1 + 3*k*ido], tr2);
+ v4sf ti2{VSUB(cc[i + (3*k + 2)*ido], cc[ic + (3*k + 1)*ido])};
+ v4sf ci2{VMADD(vtaur, ti2, cc[i + 3*k*ido])};
+ ch[i + k*ido] = VADD(cc[i + 3*k*ido], ti2);
+ v4sf cr3{VMUL(vtaui, VSUB(cc[i - 1 + (3*k + 2)*ido], cc[ic - 1 + (3*k + 1)*ido]))};
+ v4sf ci3{VMUL(vtaui, VADD(cc[i + (3*k + 2)*ido], cc[ic + (3*k + 1)*ido]))};
+ v4sf dr2{VSUB(cr2, ci3)};
+ v4sf dr3{VADD(cr2, ci3)};
+ v4sf di2{VADD(ci2, cr3)};
+ v4sf di3{VSUB(ci2, cr3)};
+ vcplxmul(dr2, di2, LD_PS1(wa1[i-2]), LD_PS1(wa1[i-1]));
+ ch[i - 1 + (k + l1)*ido] = dr2;
+ ch[i + (k + l1)*ido] = di2;
+ vcplxmul(dr3, di3, LD_PS1(wa2[i-2]), LD_PS1(wa2[i-1]));
+ ch[i - 1 + (k + 2*l1)*ido] = dr3;
+ ch[i + (k + 2*l1)*ido] = di3;
+ }
+ }
+} /* radb3 */
+
+NOINLINE void radf4_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc,
+ v4sf *RESTRICT ch, const float *RESTRICT wa1, const float *RESTRICT wa2,
+ const float *RESTRICT wa3)
+{
+ const size_t l1ido{l1*ido};
+ {
+ const v4sf *RESTRICT cc_{cc}, *RESTRICT cc_end{cc + l1ido};
+ v4sf *RESTRICT ch_{ch};
+ while(cc != cc_end)
+ {
+ // this loop represents between 25% and 40% of total radf4_ps cost !
+ v4sf a0{cc[0]}, a1{cc[l1ido]};
+ v4sf a2{cc[2*l1ido]}, a3{cc[3*l1ido]};
+ v4sf tr1{VADD(a1, a3)};
+ v4sf tr2{VADD(a0, a2)};
+ ch[2*ido-1] = VSUB(a0, a2);
+ ch[2*ido ] = VSUB(a3, a1);
+ ch[0 ] = VADD(tr1, tr2);
+ ch[4*ido-1] = VSUB(tr2, tr1);
+ cc += ido; ch += 4*ido;
+ }
+ cc = cc_;
+ ch = ch_;
+ }
+ if(ido < 2)
+ return;
+ if(ido != 2)
+ {
+ for(size_t k{0};k < l1ido;k += ido)
+ {
+ const v4sf *RESTRICT pc{cc + 1 + k};
+ for(size_t i{2};i < ido;i += 2, pc += 2)
+ {
+ const size_t ic{ido - i};
+
+ v4sf cr2{pc[1*l1ido+0]};
+ v4sf ci2{pc[1*l1ido+1]};
+ v4sf wr{LD_PS1(wa1[i - 2])};
+ v4sf wi{LD_PS1(wa1[i - 1])};
+ vcplxmulconj(cr2,ci2,wr,wi);
+
+ v4sf cr3{pc[2*l1ido+0]};
+ v4sf ci3{pc[2*l1ido+1]};
+ wr = LD_PS1(wa2[i-2]);
+ wi = LD_PS1(wa2[i-1]);
+ vcplxmulconj(cr3, ci3, wr, wi);
+
+ v4sf cr4{pc[3*l1ido]};
+ v4sf ci4{pc[3*l1ido+1]};
+ wr = LD_PS1(wa3[i-2]);
+ wi = LD_PS1(wa3[i-1]);
+ vcplxmulconj(cr4, ci4, wr, wi);
+
+ /* at this point, on SSE, five of "cr2 cr3 cr4 ci2 ci3 ci4" should be loaded in registers */
+
+ v4sf tr1{VADD(cr2,cr4)};
+ v4sf tr4{VSUB(cr4,cr2)};
+ v4sf tr2{VADD(pc[0],cr3)};
+ v4sf tr3{VSUB(pc[0],cr3)};
+ ch[i - 1 + 4*k ] = VADD(tr2,tr1);
+ ch[ic - 1 + 4*k + 3*ido] = VSUB(tr2,tr1); // at this point tr1 and tr2 can be disposed
+ v4sf ti1{VADD(ci2,ci4)};
+ v4sf ti4{VSUB(ci2,ci4)};
+ ch[i - 1 + 4*k + 2*ido] = VADD(tr3,ti4);
+ ch[ic - 1 + 4*k + 1*ido] = VSUB(tr3,ti4); // dispose tr3, ti4
+ v4sf ti2{VADD(pc[1],ci3)};
+ v4sf ti3{VSUB(pc[1],ci3)};
+ ch[i + 4*k ] = VADD(ti1, ti2);
+ ch[ic + 4*k + 3*ido] = VSUB(ti1, ti2);
+ ch[i + 4*k + 2*ido] = VADD(tr4, ti3);
+ ch[ic + 4*k + 1*ido] = VSUB(tr4, ti3);
+ }
+ }
+ if((ido&1) == 1)
+ return;
+ }
+ const v4sf minus_hsqt2{LD_PS1(al::numbers::sqrt2_v<float> * -0.5f)};
+ for(size_t k{0};k < l1ido;k += ido)
+ {
+ v4sf a{cc[ido-1 + k + l1ido]}, b{cc[ido-1 + k + 3*l1ido]};
+ v4sf c{cc[ido-1 + k]}, d{cc[ido-1 + k + 2*l1ido]};
+ v4sf ti1{VMUL(minus_hsqt2, VADD(b, a))};
+ v4sf tr1{VMUL(minus_hsqt2, VSUB(b, a))};
+ ch[ido-1 + 4*k ] = VADD(c, tr1);
+ ch[ido-1 + 4*k + 2*ido] = VSUB(c, tr1);
+ ch[ 4*k + 1*ido] = VSUB(ti1, d);
+ ch[ 4*k + 3*ido] = VADD(ti1, d);
+ }
+} /* radf4 */
+
+
+NOINLINE void radb4_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc,
+ v4sf *RESTRICT ch, const float *RESTRICT wa1, const float *RESTRICT wa2,
+ const float *RESTRICT wa3)
+{
+ const v4sf two{LD_PS1(2.0f)};
+ const size_t l1ido{l1*ido};
+ {
+ const v4sf *RESTRICT cc_{cc}, *RESTRICT ch_end{ch + l1ido};
+ v4sf *ch_{ch};
+ while(ch != ch_end)
+ {
+ v4sf a{cc[0]}, b{cc[4*ido-1]};
+ v4sf c{cc[2*ido]}, d{cc[2*ido-1]};
+ v4sf tr3{VMUL(two,d)};
+ v4sf tr2{VADD(a,b)};
+ v4sf tr1{VSUB(a,b)};
+ v4sf tr4{VMUL(two,c)};
+ ch[0*l1ido] = VADD(tr2, tr3);
+ ch[2*l1ido] = VSUB(tr2, tr3);
+ ch[1*l1ido] = VSUB(tr1, tr4);
+ ch[3*l1ido] = VADD(tr1, tr4);
+
+ cc += 4*ido; ch += ido;
+ }
+ cc = cc_; ch = ch_;
+ }
+ if(ido < 2)
+ return;
+ if(ido != 2)
+ {
+ for(size_t k{0};k < l1ido;k += ido)
+ {
+ const v4sf *RESTRICT pc{cc - 1 + 4*k};
+ v4sf *RESTRICT ph{ch + k + 1};
+ for(size_t i{2};i < ido;i += 2)
+ {
+ v4sf tr1{VSUB(pc[ i], pc[4*ido - i])};
+ v4sf tr2{VADD(pc[ i], pc[4*ido - i])};
+ v4sf ti4{VSUB(pc[2*ido + i], pc[2*ido - i])};
+ v4sf tr3{VADD(pc[2*ido + i], pc[2*ido - i])};
+ ph[0] = VADD(tr2, tr3);
+ v4sf cr3{VSUB(tr2, tr3)};
+
+ v4sf ti3{VSUB(pc[2*ido + i + 1], pc[2*ido - i + 1])};
+ v4sf tr4{VADD(pc[2*ido + i + 1], pc[2*ido - i + 1])};
+ v4sf cr2{VSUB(tr1, tr4)};
+ v4sf cr4{VADD(tr1, tr4)};
+
+ v4sf ti1{VADD(pc[i + 1], pc[4*ido - i + 1])};
+ v4sf ti2{VSUB(pc[i + 1], pc[4*ido - i + 1])};
+
+ ph[1] = VADD(ti2, ti3); ph += l1ido;
+ v4sf ci3{VSUB(ti2, ti3)};
+ v4sf ci2{VADD(ti1, ti4)};
+ v4sf ci4{VSUB(ti1, ti4)};
+ vcplxmul(cr2, ci2, LD_PS1(wa1[i-2]), LD_PS1(wa1[i-1]));
+ ph[0] = cr2;
+ ph[1] = ci2; ph += l1ido;
+ vcplxmul(cr3, ci3, LD_PS1(wa2[i-2]), LD_PS1(wa2[i-1]));
+ ph[0] = cr3;
+ ph[1] = ci3; ph += l1ido;
+ vcplxmul(cr4, ci4, LD_PS1(wa3[i-2]), LD_PS1(wa3[i-1]));
+ ph[0] = cr4;
+ ph[1] = ci4; ph = ph - 3*l1ido + 2;
+ }
+ }
+ if((ido&1) == 1)
+ return;
+ }
+ const v4sf minus_sqrt2{LD_PS1(-1.414213562373095f)};
+ for(size_t k{0};k < l1ido;k += ido)
+ {
+ const size_t i0{4*k + ido};
+ v4sf c{cc[i0-1]}, d{cc[i0 + 2*ido-1]};
+ v4sf a{cc[i0+0]}, b{cc[i0 + 2*ido+0]};
+ v4sf tr1{VSUB(c,d)};
+ v4sf tr2{VADD(c,d)};
+ v4sf ti1{VADD(b,a)};
+ v4sf ti2{VSUB(b,a)};
+ ch[ido-1 + k + 0*l1ido] = VADD(tr2,tr2);
+ ch[ido-1 + k + 1*l1ido] = VMUL(minus_sqrt2, VSUB(ti1, tr1));
+ ch[ido-1 + k + 2*l1ido] = VADD(ti2, ti2);
+ ch[ido-1 + k + 3*l1ido] = VMUL(minus_sqrt2, VADD(ti1, tr1));
+ }
+} /* radb4 */
+
+void radf5_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch,
+ const float *wa1, const float *wa2, const float *wa3, const float *wa4)
+{
+ const v4sf tr11{LD_PS1(0.309016994374947f)};
+ const v4sf ti11{LD_PS1(0.951056516295154f)};
+ const v4sf tr12{LD_PS1(-0.809016994374947f)};
+ const v4sf ti12{LD_PS1(0.587785252292473f)};
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*l1 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*5 + (a_2))*ido + a_1]
+
+ /* Parameter adjustments */
+ ch -= 1 + ido * 6;
+ cc -= 1 + ido * (1 + l1);
+
+ /* Function Body */
+ for(size_t k{1};k <= l1;++k)
+ {
+ v4sf cr2{VADD(cc_ref(1, k, 5), cc_ref(1, k, 2))};
+ v4sf ci5{VSUB(cc_ref(1, k, 5), cc_ref(1, k, 2))};
+ v4sf cr3{VADD(cc_ref(1, k, 4), cc_ref(1, k, 3))};
+ v4sf ci4{VSUB(cc_ref(1, k, 4), cc_ref(1, k, 3))};
+ ch_ref(1, 1, k) = VADD(cc_ref(1, k, 1), VADD(cr2, cr3));
+ ch_ref(ido, 2, k) = VADD(cc_ref(1, k, 1), VMADD(tr11, cr2, VMUL(tr12, cr3)));
+ ch_ref(1, 3, k) = VMADD(ti11, ci5, VMUL(ti12, ci4));
+ ch_ref(ido, 4, k) = VADD(cc_ref(1, k, 1), VMADD(tr12, cr2, VMUL(tr11, cr3)));
+ ch_ref(1, 5, k) = VSUB(VMUL(ti12, ci5), VMUL(ti11, ci4));
+ //printf("pffft: radf5, k=%d ch_ref=%f, ci4=%f\n", k, ch_ref(1, 5, k), ci4);
+ }
+ if(ido == 1)
+ return;
+
+ const size_t idp2{ido + 2};
+ for(size_t k{1};k <= l1;++k)
+ {
+ for(size_t i{3};i <= ido;i += 2)
+ {
+ const size_t ic{idp2 - i};
+ v4sf dr2{LD_PS1(wa1[i-3])};
+ v4sf di2{LD_PS1(wa1[i-2])};
+ v4sf dr3{LD_PS1(wa2[i-3])};
+ v4sf di3{LD_PS1(wa2[i-2])};
+ v4sf dr4{LD_PS1(wa3[i-3])};
+ v4sf di4{LD_PS1(wa3[i-2])};
+ v4sf dr5{LD_PS1(wa4[i-3])};
+ v4sf di5{LD_PS1(wa4[i-2])};
+ vcplxmulconj(dr2, di2, cc_ref(i-1, k, 2), cc_ref(i, k, 2));
+ vcplxmulconj(dr3, di3, cc_ref(i-1, k, 3), cc_ref(i, k, 3));
+ vcplxmulconj(dr4, di4, cc_ref(i-1, k, 4), cc_ref(i, k, 4));
+ vcplxmulconj(dr5, di5, cc_ref(i-1, k, 5), cc_ref(i, k, 5));
+ v4sf cr2{VADD(dr2, dr5)};
+ v4sf ci5{VSUB(dr5, dr2)};
+ v4sf cr5{VSUB(di2, di5)};
+ v4sf ci2{VADD(di2, di5)};
+ v4sf cr3{VADD(dr3, dr4)};
+ v4sf ci4{VSUB(dr4, dr3)};
+ v4sf cr4{VSUB(di3, di4)};
+ v4sf ci3{VADD(di3, di4)};
+ ch_ref(i - 1, 1, k) = VADD(cc_ref(i - 1, k, 1), VADD(cr2, cr3));
+ ch_ref(i, 1, k) = VSUB(cc_ref(i, k, 1), VADD(ci2, ci3));
+ v4sf tr2{VADD(cc_ref(i - 1, k, 1), VMADD(tr11, cr2, VMUL(tr12, cr3)))};
+ v4sf ti2{VSUB(cc_ref(i, k, 1), VMADD(tr11, ci2, VMUL(tr12, ci3)))};
+ v4sf tr3{VADD(cc_ref(i - 1, k, 1), VMADD(tr12, cr2, VMUL(tr11, cr3)))};
+ v4sf ti3{VSUB(cc_ref(i, k, 1), VMADD(tr12, ci2, VMUL(tr11, ci3)))};
+ v4sf tr5{VMADD(ti11, cr5, VMUL(ti12, cr4))};
+ v4sf ti5{VMADD(ti11, ci5, VMUL(ti12, ci4))};
+ v4sf tr4{VSUB(VMUL(ti12, cr5), VMUL(ti11, cr4))};
+ v4sf ti4{VSUB(VMUL(ti12, ci5), VMUL(ti11, ci4))};
+ ch_ref(i - 1, 3, k) = VSUB(tr2, tr5);
+ ch_ref(ic - 1, 2, k) = VADD(tr2, tr5);
+ ch_ref(i , 3, k) = VADD(ti5, ti2);
+ ch_ref(ic , 2, k) = VSUB(ti5, ti2);
+ ch_ref(i - 1, 5, k) = VSUB(tr3, tr4);
+ ch_ref(ic - 1, 4, k) = VADD(tr3, tr4);
+ ch_ref(i , 5, k) = VADD(ti4, ti3);
+ ch_ref(ic , 4, k) = VSUB(ti4, ti3);
+ }
+ }
+#undef cc_ref
+#undef ch_ref
+} /* radf5 */
+
+void radb5_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch,
+ const float *wa1, const float *wa2, const float *wa3, const float *wa4)
+{
+ const v4sf tr11{LD_PS1(0.309016994374947f)};
+ const v4sf ti11{LD_PS1(0.951056516295154f)};
+ const v4sf tr12{LD_PS1(-0.809016994374947f)};
+ const v4sf ti12{LD_PS1(0.587785252292473f)};
+
+#define cc_ref(a_1,a_2,a_3) cc[((a_3)*5 + (a_2))*ido + a_1]
+#define ch_ref(a_1,a_2,a_3) ch[((a_3)*l1 + (a_2))*ido + a_1]
+
+ /* Parameter adjustments */
+ ch -= 1 + ido*(1 + l1);
+ cc -= 1 + ido*6;
+
+ /* Function Body */
+ for(size_t k{1};k <= l1;++k)
+ {
+ v4sf ti5{VADD(cc_ref( 1, 3, k), cc_ref(1, 3, k))};
+ v4sf ti4{VADD(cc_ref( 1, 5, k), cc_ref(1, 5, k))};
+ v4sf tr2{VADD(cc_ref(ido, 2, k), cc_ref(ido, 2, k))};
+ v4sf tr3{VADD(cc_ref(ido, 4, k), cc_ref(ido, 4, k))};
+ ch_ref(1, k, 1) = VADD(cc_ref(1, 1, k), VADD(tr2, tr3));
+ v4sf cr2{VADD(cc_ref(1, 1, k), VMADD(tr11, tr2, VMUL(tr12, tr3)))};
+ v4sf cr3{VADD(cc_ref(1, 1, k), VMADD(tr12, tr2, VMUL(tr11, tr3)))};
+ v4sf ci5{VMADD(ti11, ti5, VMUL(ti12, ti4))};
+ v4sf ci4{VSUB(VMUL(ti12, ti5), VMUL(ti11, ti4))};
+ ch_ref(1, k, 2) = VSUB(cr2, ci5);
+ ch_ref(1, k, 3) = VSUB(cr3, ci4);
+ ch_ref(1, k, 4) = VADD(cr3, ci4);
+ ch_ref(1, k, 5) = VADD(cr2, ci5);
+ }
+ if(ido == 1)
+ return;
+
+ const size_t idp2{ido + 2};
+ for(size_t k{1};k <= l1;++k)
+ {
+ for(size_t i{3};i <= ido;i += 2)
+ {
+ const size_t ic{idp2 - i};
+ v4sf ti5{VADD(cc_ref(i , 3, k), cc_ref(ic , 2, k))};
+ v4sf ti2{VSUB(cc_ref(i , 3, k), cc_ref(ic , 2, k))};
+ v4sf ti4{VADD(cc_ref(i , 5, k), cc_ref(ic , 4, k))};
+ v4sf ti3{VSUB(cc_ref(i , 5, k), cc_ref(ic , 4, k))};
+ v4sf tr5{VSUB(cc_ref(i-1, 3, k), cc_ref(ic-1, 2, k))};
+ v4sf tr2{VADD(cc_ref(i-1, 3, k), cc_ref(ic-1, 2, k))};
+ v4sf tr4{VSUB(cc_ref(i-1, 5, k), cc_ref(ic-1, 4, k))};
+ v4sf tr3{VADD(cc_ref(i-1, 5, k), cc_ref(ic-1, 4, k))};
+ ch_ref(i - 1, k, 1) = VADD(cc_ref(i-1, 1, k), VADD(tr2, tr3));
+ ch_ref(i , k, 1) = VADD(cc_ref(i , 1, k), VADD(ti2, ti3));
+ v4sf cr2{VADD(cc_ref(i-1, 1, k), VMADD(tr11, tr2, VMUL(tr12, tr3)))};
+ v4sf ci2{VADD(cc_ref(i , 1, k), VMADD(tr11, ti2, VMUL(tr12, ti3)))};
+ v4sf cr3{VADD(cc_ref(i-1, 1, k), VMADD(tr12, tr2, VMUL(tr11, tr3)))};
+ v4sf ci3{VADD(cc_ref(i , 1, k), VMADD(tr12, ti2, VMUL(tr11, ti3)))};
+ v4sf cr5{VMADD(ti11, tr5, VMUL(ti12, tr4))};
+ v4sf ci5{VMADD(ti11, ti5, VMUL(ti12, ti4))};
+ v4sf cr4{VSUB(VMUL(ti12, tr5), VMUL(ti11, tr4))};
+ v4sf ci4{VSUB(VMUL(ti12, ti5), VMUL(ti11, ti4))};
+ v4sf dr3{VSUB(cr3, ci4)};
+ v4sf dr4{VADD(cr3, ci4)};
+ v4sf di3{VADD(ci3, cr4)};
+ v4sf di4{VSUB(ci3, cr4)};
+ v4sf dr5{VADD(cr2, ci5)};
+ v4sf dr2{VSUB(cr2, ci5)};
+ v4sf di5{VSUB(ci2, cr5)};
+ v4sf di2{VADD(ci2, cr5)};
+ vcplxmul(dr2, di2, LD_PS1(wa1[i-3]), LD_PS1(wa1[i-2]));
+ vcplxmul(dr3, di3, LD_PS1(wa2[i-3]), LD_PS1(wa2[i-2]));
+ vcplxmul(dr4, di4, LD_PS1(wa3[i-3]), LD_PS1(wa3[i-2]));
+ vcplxmul(dr5, di5, LD_PS1(wa4[i-3]), LD_PS1(wa4[i-2]));
+
+ ch_ref(i-1, k, 2) = dr2; ch_ref(i, k, 2) = di2;
+ ch_ref(i-1, k, 3) = dr3; ch_ref(i, k, 3) = di3;
+ ch_ref(i-1, k, 4) = dr4; ch_ref(i, k, 4) = di4;
+ ch_ref(i-1, k, 5) = dr5; ch_ref(i, k, 5) = di5;
+ }
+ }
+#undef cc_ref
+#undef ch_ref
+} /* radb5 */
+
+NOINLINE v4sf *rfftf1_ps(const size_t n, const v4sf *input_readonly, v4sf *work1, v4sf *work2,
+ const float *wa, const al::span<const uint,15> ifac)
+{
+ assert(work1 != work2);
+
+ const v4sf *in{input_readonly};
+ v4sf *out{in == work2 ? work1 : work2};
+ const size_t nf{ifac[1]};
+ size_t l2{n};
+ size_t iw{n-1};
+ for(size_t k1{1};k1 <= nf;++k1)
+ {
+ size_t kh{nf - k1};
+ size_t ip{ifac[kh + 2]};
+ size_t l1{l2 / ip};
+ size_t ido{n / l2};
+ iw -= (ip - 1)*ido;
+ switch(ip)
+ {
+ case 5:
+ {
+ size_t ix2{iw + ido};
+ size_t ix3{ix2 + ido};
+ size_t ix4{ix3 + ido};
+ radf5_ps(ido, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4]);
+ }
+ break;
+ case 4:
+ {
+ size_t ix2{iw + ido};
+ size_t ix3{ix2 + ido};
+ radf4_ps(ido, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3]);
+ }
+ break;
+ case 3:
+ {
+ size_t ix2{iw + ido};
+ radf3_ps(ido, l1, in, out, &wa[iw], &wa[ix2]);
+ }
+ break;
+ case 2:
+ radf2_ps(ido, l1, in, out, &wa[iw]);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ l2 = l1;
+ if(out == work2)
+ {
+ out = work1;
+ in = work2;
+ }
+ else
+ {
+ out = work2;
+ in = work1;
+ }
+ }
+ return const_cast<v4sf*>(in); /* this is in fact the output .. */
+} /* rfftf1 */
+
+NOINLINE v4sf *rfftb1_ps(const size_t n, const v4sf *input_readonly, v4sf *work1, v4sf *work2,
+ const float *wa, const al::span<const uint,15> ifac)
+{
+ assert(work1 != work2);
+
+ const v4sf *in{input_readonly};
+ v4sf *out{in == work2 ? work1 : work2};
+ const size_t nf{ifac[1]};
+ size_t l1{1};
+ size_t iw{0};
+ for(size_t k1{1};k1 <= nf;++k1)
+ {
+ size_t ip{ifac[k1 + 1]};
+ size_t l2{ip*l1};
+ size_t ido{n / l2};
+ switch(ip)
+ {
+ case 5:
+ {
+ size_t ix2{iw + ido};
+ size_t ix3{ix2 + ido};
+ size_t ix4{ix3 + ido};
+ radb5_ps(ido, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4]);
+ }
+ break;
+ case 4:
+ {
+ size_t ix2{iw + ido};
+ size_t ix3{ix2 + ido};
+ radb4_ps(ido, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3]);
+ }
+ break;
+ case 3:
+ {
+ size_t ix2{iw + ido};
+ radb3_ps(ido, l1, in, out, &wa[iw], &wa[ix2]);
+ }
+ break;
+ case 2:
+ radb2_ps(ido, l1, in, out, &wa[iw]);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ l1 = l2;
+ iw += (ip - 1)*ido;
+
+ if(out == work2)
+ {
+ out = work1;
+ in = work2;
+ }
+ else
+ {
+ out = work2;
+ in = work1;
+ }
+ }
+ return const_cast<v4sf*>(in); /* this is in fact the output .. */
+}
+
+v4sf *cfftf1_ps(const size_t n, const v4sf *input_readonly, v4sf *work1, v4sf *work2,
+ const float *wa, const al::span<const uint,15> ifac, const float fsign)
+{
+ assert(work1 != work2);
+
+ const v4sf *in{input_readonly};
+ v4sf *out{in == work2 ? work1 : work2};
+ const size_t nf{ifac[1]};
+ size_t l1{1}, iw{0};
+ for(size_t k1{2};k1 <= nf+1;++k1)
+ {
+ const size_t ip{ifac[k1]};
+ const size_t l2{ip*l1};
+ const size_t ido{n / l2};
+ const size_t idot{ido + ido};
+ switch(ip)
+ {
+ case 5:
+ {
+ size_t ix2{iw + idot};
+ size_t ix3{ix2 + idot};
+ size_t ix4{ix3 + idot};
+ passf5_ps(idot, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], &wa[ix4], fsign);
+ }
+ break;
+ case 4:
+ {
+ size_t ix2{iw + idot};
+ size_t ix3{ix2 + idot};
+ passf4_ps(idot, l1, in, out, &wa[iw], &wa[ix2], &wa[ix3], fsign);
+ }
+ break;
+ case 3:
+ {
+ size_t ix2{iw + idot};
+ passf3_ps(idot, l1, in, out, &wa[iw], &wa[ix2], fsign);
+ }
+ break;
+ case 2:
+ passf2_ps(idot, l1, in, out, &wa[iw], fsign);
+ break;
+ default:
+ assert(0);
+ }
+ l1 = l2;
+ iw += (ip - 1)*idot;
+ if(out == work2)
+ {
+ out = work1;
+ in = work2;
+ }
+ else
+ {
+ out = work2;
+ in = work1;
+ }
+ }
+
+ return const_cast<v4sf*>(in); /* this is in fact the output .. */
+}
+
+
+uint decompose(const uint n, const al::span<uint,15> ifac, const al::span<const uint,4> ntryh)
+{
+ uint nl{n}, nf{0};
+ for(const uint ntry : ntryh)
+ {
+ while(nl != 1)
+ {
+ const uint nq{nl / ntry};
+ const uint nr{nl % ntry};
+ if(nr != 0) break;
+
+ ifac[2+nf++] = ntry;
+ nl = nq;
+ if(ntry == 2 && nf != 1)
+ {
+ for(size_t i{2};i <= nf;++i)
+ {
+ size_t ib{nf - i + 2};
+ ifac[ib + 1] = ifac[ib];
+ }
+ ifac[2] = 2;
+ }
+ }
+ }
+ ifac[0] = n;
+ ifac[1] = nf;
+ return nf;
+}
+
+void rffti1_ps(const uint n, float *wa, const al::span<uint,15> ifac)
+{
+ static constexpr uint ntryh[]{4,2,3,5};
+
+ const uint nf{decompose(n, ifac, ntryh)};
+ const double argh{2.0*al::numbers::pi / n};
+ size_t is{0};
+ size_t nfm1{nf - 1};
+ size_t l1{1};
+ for(size_t k1{0};k1 < nfm1;++k1)
+ {
+ const size_t ip{ifac[k1+2]};
+ const size_t l2{l1*ip};
+ const size_t ido{n / l2};
+ const size_t ipm{ip - 1};
+ size_t ld{0};
+ for(size_t j{0};j < ipm;++j)
+ {
+ size_t i{is};
+ ld += l1;
+ const double argld{static_cast<double>(ld)*argh};
+ double fi{0.0};
+ for(size_t ii{2};ii < ido;ii += 2)
+ {
+ fi += 1.0;
+ wa[i++] = static_cast<float>(std::cos(fi*argld));
+ wa[i++] = static_cast<float>(std::sin(fi*argld));
+ }
+ is += ido;
+ }
+ l1 = l2;
+ }
+} /* rffti1 */
+
+void cffti1_ps(const uint n, float *wa, const al::span<uint,15> ifac)
+{
+ static constexpr uint ntryh[]{5,3,4,2};
+
+ const uint nf{decompose(n, ifac, ntryh)};
+ const double argh{2.0*al::numbers::pi / n};
+ size_t i{1};
+ size_t l1{1};
+ for(size_t k1{0};k1 < nf;++k1)
+ {
+ const size_t ip{ifac[k1+2]};
+ const size_t l2{l1*ip};
+ const size_t ido{n / l2};
+ const size_t idot{ido + ido + 2};
+ const size_t ipm{ip - 1};
+ size_t ld{0};
+ for(size_t j{0};j < ipm;++j)
+ {
+ size_t i1{i};
+ wa[i-1] = 1;
+ wa[i] = 0;
+ ld += l1;
+ const double argld{static_cast<double>(ld)*argh};
+ double fi{0.0};
+ for(size_t ii{3};ii < idot;ii += 2)
+ {
+ fi += 1.0;
+ wa[++i] = static_cast<float>(std::cos(fi*argld));
+ wa[++i] = static_cast<float>(std::sin(fi*argld));
+ }
+ if(ip > 5)
+ {
+ wa[i1-1] = wa[i-1];
+ wa[i1] = wa[i];
+ }
+ }
+ l1 = l2;
+ }
+} /* cffti1 */
+
+} // namespace
+
+void *pffft_aligned_malloc(size_t nb_bytes)
+{ return al_malloc(MALLOC_V4SF_ALIGNMENT, nb_bytes); }
+
+void pffft_aligned_free(void *p) { al_free(p); }
+
+int pffft_simd_size() { return SIMD_SZ; }
+
+struct PFFFT_Setup {
+ uint N;
+ uint Ncvec; // nb of complex simd vectors (N/4 if PFFFT_COMPLEX, N/8 if PFFFT_REAL)
+ std::array<uint,15> ifac;
+ pffft_transform_t transform;
+
+ float *twiddle; // N/4 elements
+ alignas(MALLOC_V4SF_ALIGNMENT) v4sf e[1]; // N/4*3 elements
+};
+
+PFFFT_Setup *pffft_new_setup(unsigned int N, pffft_transform_t transform)
+{
+ assert(transform == PFFFT_REAL || transform == PFFFT_COMPLEX);
+ assert(N > 0);
+ /* unfortunately, the fft size must be a multiple of 16 for complex FFTs
+ * and 32 for real FFTs -- a lot of stuff would need to be rewritten to
+ * handle other cases (or maybe just switch to a scalar fft, I don't know..)
+ */
+ if(transform == PFFFT_REAL)
+ assert((N%(2*SIMD_SZ*SIMD_SZ)) == 0);
+ else
+ assert((N%(SIMD_SZ*SIMD_SZ)) == 0);
+
+ const uint Ncvec = (transform == PFFFT_REAL ? N/2 : N)/SIMD_SZ;
+ const size_t storelen{offsetof(PFFFT_Setup, e[0]) + (2u*Ncvec * sizeof(v4sf))};
+
+ void *store{al_calloc(MALLOC_V4SF_ALIGNMENT, storelen)};
+ if(!store) return nullptr;
+
+ PFFFT_Setup *s{::new(store) PFFFT_Setup{}};
+ s->N = N;
+ s->transform = transform;
+ /* nb of complex simd vectors */
+ s->Ncvec = Ncvec;
+ s->twiddle = reinterpret_cast<float*>(&s->e[2u*Ncvec*(SIMD_SZ-1)/SIMD_SZ]);
+
+ if constexpr(SIMD_SZ > 1)
+ {
+ auto e = std::vector<float>(2u*Ncvec*(SIMD_SZ-1), 0.0f);
+ for(size_t k{0};k < s->Ncvec;++k)
+ {
+ const size_t i{k / SIMD_SZ};
+ const size_t j{k % SIMD_SZ};
+ for(size_t m{0};m < SIMD_SZ-1;++m)
+ {
+ const double A{-2.0*al::numbers::pi*static_cast<double>((m+1)*k) / N};
+ e[((i*3 + m)*2 + 0)*SIMD_SZ + j] = static_cast<float>(std::cos(A));
+ e[((i*3 + m)*2 + 1)*SIMD_SZ + j] = static_cast<float>(std::sin(A));
+ }
+ }
+ std::memcpy(s->e, e.data(), e.size()*sizeof(float));
+ }
+ if(transform == PFFFT_REAL)
+ rffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac);
+ else
+ cffti1_ps(N/SIMD_SZ, s->twiddle, s->ifac);
+
+ /* check that N is decomposable with allowed prime factors */
+ size_t m{1};
+ for(size_t k{0};k < s->ifac[1];++k)
+ m *= s->ifac[2+k];
+
+ if(m != N/SIMD_SZ)
+ {
+ pffft_destroy_setup(s);
+ s = nullptr;
+ }
+
+ return s;
+}
+
+
+void pffft_destroy_setup(PFFFT_Setup *s)
+{
+ std::destroy_at(s);
+ al_free(s);
+}
+
+#if !defined(PFFFT_SIMD_DISABLE)
+
+namespace {
+
+/* [0 0 1 2 3 4 5 6 7 8] -> [0 8 7 6 5 4 3 2 1] */
+void reversed_copy(const size_t N, const v4sf *in, const int in_stride, v4sf *RESTRICT out)
+{
+ v4sf g0, g1;
+ interleave2(in[0], in[1], g0, g1);
+ in += in_stride;
+
+ *--out = VSWAPHL(g0, g1); // [g0l, g0h], [g1l g1h] -> [g1l, g0h]
+ for(size_t k{1};k < N;++k)
+ {
+ v4sf h0, h1;
+ interleave2(in[0], in[1], h0, h1);
+ in += in_stride;
+ *--out = VSWAPHL(g1, h0);
+ *--out = VSWAPHL(h0, h1);
+ g1 = h1;
+ }
+ *--out = VSWAPHL(g1, g0);
+}
+
+void unreversed_copy(const size_t N, const v4sf *in, v4sf *RESTRICT out, const int out_stride)
+{
+ v4sf g0{in[0]}, g1{g0};
+ ++in;
+ for(size_t k{1};k < N;++k)
+ {
+ v4sf h0{*in++}; v4sf h1{*in++};
+ g1 = VSWAPHL(g1, h0);
+ h0 = VSWAPHL(h0, h1);
+ uninterleave2(h0, g1, out[0], out[1]);
+ out += out_stride;
+ g1 = h1;
+ }
+ v4sf h0{*in++}, h1{g0};
+ g1 = VSWAPHL(g1, h0);
+ h0 = VSWAPHL(h0, h1);
+ uninterleave2(h0, g1, out[0], out[1]);
+}
+
+void pffft_cplx_finalize(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out, const v4sf *e)
+{
+ assert(in != out);
+
+ const size_t dk{Ncvec/SIMD_SZ}; // number of 4x4 matrix blocks
+ for(size_t k{0};k < dk;++k)
+ {
+ v4sf r0{in[8*k+0]}, i0{in[8*k+1]};
+ v4sf r1{in[8*k+2]}, i1{in[8*k+3]};
+ v4sf r2{in[8*k+4]}, i2{in[8*k+5]};
+ v4sf r3{in[8*k+6]}, i3{in[8*k+7]};
+ vtranspose4(r0,r1,r2,r3);
+ vtranspose4(i0,i1,i2,i3);
+ vcplxmul(r1,i1,e[k*6+0],e[k*6+1]);
+ vcplxmul(r2,i2,e[k*6+2],e[k*6+3]);
+ vcplxmul(r3,i3,e[k*6+4],e[k*6+5]);
+
+ v4sf sr0{VADD(r0,r2)}, dr0{VSUB(r0, r2)};
+ v4sf sr1{VADD(r1,r3)}, dr1{VSUB(r1, r3)};
+ v4sf si0{VADD(i0,i2)}, di0{VSUB(i0, i2)};
+ v4sf si1{VADD(i1,i3)}, di1{VSUB(i1, i3)};
+
+ /* transformation for each column is:
+ *
+ * [1 1 1 1 0 0 0 0] [r0]
+ * [1 0 -1 0 0 -1 0 1] [r1]
+ * [1 -1 1 -1 0 0 0 0] [r2]
+ * [1 0 -1 0 0 1 0 -1] [r3]
+ * [0 0 0 0 1 1 1 1] * [i0]
+ * [0 1 0 -1 1 0 -1 0] [i1]
+ * [0 0 0 0 1 -1 1 -1] [i2]
+ * [0 -1 0 1 1 0 -1 0] [i3]
+ */
+
+ r0 = VADD(sr0, sr1); i0 = VADD(si0, si1);
+ r1 = VADD(dr0, di1); i1 = VSUB(di0, dr1);
+ r2 = VSUB(sr0, sr1); i2 = VSUB(si0, si1);
+ r3 = VSUB(dr0, di1); i3 = VADD(di0, dr1);
+
+ *out++ = r0; *out++ = i0; *out++ = r1; *out++ = i1;
+ *out++ = r2; *out++ = i2; *out++ = r3; *out++ = i3;
+ }
+}
+
+void pffft_cplx_preprocess(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out, const v4sf *e)
+{
+ assert(in != out);
+
+ const size_t dk{Ncvec/SIMD_SZ}; // number of 4x4 matrix blocks
+ for(size_t k{0};k < dk;++k)
+ {
+ v4sf r0{in[8*k+0]}, i0{in[8*k+1]};
+ v4sf r1{in[8*k+2]}, i1{in[8*k+3]};
+ v4sf r2{in[8*k+4]}, i2{in[8*k+5]};
+ v4sf r3{in[8*k+6]}, i3{in[8*k+7]};
+
+ v4sf sr0{VADD(r0,r2)}, dr0{VSUB(r0, r2)};
+ v4sf sr1{VADD(r1,r3)}, dr1{VSUB(r1, r3)};
+ v4sf si0{VADD(i0,i2)}, di0{VSUB(i0, i2)};
+ v4sf si1{VADD(i1,i3)}, di1{VSUB(i1, i3)};
+
+ r0 = VADD(sr0, sr1); i0 = VADD(si0, si1);
+ r1 = VSUB(dr0, di1); i1 = VADD(di0, dr1);
+ r2 = VSUB(sr0, sr1); i2 = VSUB(si0, si1);
+ r3 = VADD(dr0, di1); i3 = VSUB(di0, dr1);
+
+ vcplxmulconj(r1,i1,e[k*6+0],e[k*6+1]);
+ vcplxmulconj(r2,i2,e[k*6+2],e[k*6+3]);
+ vcplxmulconj(r3,i3,e[k*6+4],e[k*6+5]);
+
+ vtranspose4(r0,r1,r2,r3);
+ vtranspose4(i0,i1,i2,i3);
+
+ *out++ = r0; *out++ = i0; *out++ = r1; *out++ = i1;
+ *out++ = r2; *out++ = i2; *out++ = r3; *out++ = i3;
+ }
+}
+
+
+force_inline void pffft_real_finalize_4x4(const v4sf *in0, const v4sf *in1, const v4sf *in,
+ const v4sf *e, v4sf *RESTRICT out)
+{
+ v4sf r0{*in0}, i0{*in1};
+ v4sf r1{*in++}; v4sf i1{*in++};
+ v4sf r2{*in++}; v4sf i2{*in++};
+ v4sf r3{*in++}; v4sf i3{*in++};
+ vtranspose4(r0,r1,r2,r3);
+ vtranspose4(i0,i1,i2,i3);
+
+ /* transformation for each column is:
+ *
+ * [1 1 1 1 0 0 0 0] [r0]
+ * [1 0 -1 0 0 -1 0 1] [r1]
+ * [1 0 -1 0 0 1 0 -1] [r2]
+ * [1 -1 1 -1 0 0 0 0] [r3]
+ * [0 0 0 0 1 1 1 1] * [i0]
+ * [0 -1 0 1 -1 0 1 0] [i1]
+ * [0 -1 0 1 1 0 -1 0] [i2]
+ * [0 0 0 0 -1 1 -1 1] [i3]
+ */
+
+ //cerr << "matrix initial, before e , REAL:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n";
+ //cerr << "matrix initial, before e, IMAG :\n 1: " << i0 << "\n 1: " << i1 << "\n 1: " << i2 << "\n 1: " << i3 << "\n";
+
+ vcplxmul(r1,i1,e[0],e[1]);
+ vcplxmul(r2,i2,e[2],e[3]);
+ vcplxmul(r3,i3,e[4],e[5]);
+
+ //cerr << "matrix initial, real part:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n";
+ //cerr << "matrix initial, imag part:\n 1: " << i0 << "\n 1: " << i1 << "\n 1: " << i2 << "\n 1: " << i3 << "\n";
+
+ v4sf sr0{VADD(r0,r2)}, dr0{VSUB(r0,r2)};
+ v4sf sr1{VADD(r1,r3)}, dr1{VSUB(r3,r1)};
+ v4sf si0{VADD(i0,i2)}, di0{VSUB(i0,i2)};
+ v4sf si1{VADD(i1,i3)}, di1{VSUB(i3,i1)};
+
+ r0 = VADD(sr0, sr1);
+ r3 = VSUB(sr0, sr1);
+ i0 = VADD(si0, si1);
+ i3 = VSUB(si1, si0);
+ r1 = VADD(dr0, di1);
+ r2 = VSUB(dr0, di1);
+ i1 = VSUB(dr1, di0);
+ i2 = VADD(dr1, di0);
+
+ *out++ = r0;
+ *out++ = i0;
+ *out++ = r1;
+ *out++ = i1;
+ *out++ = r2;
+ *out++ = i2;
+ *out++ = r3;
+ *out++ = i3;
+}
+
+NOINLINE void pffft_real_finalize(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out,
+ const v4sf *e)
+{
+ static constexpr float s{al::numbers::sqrt2_v<float>/2.0f};
+
+ assert(in != out);
+ const size_t dk{Ncvec/SIMD_SZ}; // number of 4x4 matrix blocks
+ /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */
+
+ const v4sf zero{VZERO()};
+ const auto cr = al::bit_cast<std::array<float,SIMD_SZ>>(in[0]);
+ const auto ci = al::bit_cast<std::array<float,SIMD_SZ>>(in[Ncvec*2-1]);
+ pffft_real_finalize_4x4(&zero, &zero, in+1, e, out);
+
+ /* [cr0 cr1 cr2 cr3 ci0 ci1 ci2 ci3]
+ *
+ * [Xr(1)] ] [1 1 1 1 0 0 0 0]
+ * [Xr(N/4) ] [0 0 0 0 1 s 0 -s]
+ * [Xr(N/2) ] [1 0 -1 0 0 0 0 0]
+ * [Xr(3N/4)] [0 0 0 0 1 -s 0 s]
+ * [Xi(1) ] [1 -1 1 -1 0 0 0 0]
+ * [Xi(N/4) ] [0 0 0 0 0 -s -1 -s]
+ * [Xi(N/2) ] [0 -1 0 1 0 0 0 0]
+ * [Xi(3N/4)] [0 0 0 0 0 -s 1 -s]
+ */
+
+ const float xr0{(cr[0]+cr[2]) + (cr[1]+cr[3])}; out[0] = VINSERT0(out[0], xr0);
+ const float xi0{(cr[0]+cr[2]) - (cr[1]+cr[3])}; out[1] = VINSERT0(out[1], xi0);
+ const float xr2{(cr[0]-cr[2])}; out[4] = VINSERT0(out[4], xr2);
+ const float xi2{(cr[3]-cr[1])}; out[5] = VINSERT0(out[5], xi2);
+ const float xr1{ ci[0] + s*(ci[1]-ci[3])}; out[2] = VINSERT0(out[2], xr1);
+ const float xi1{-ci[2] - s*(ci[1]+ci[3])}; out[3] = VINSERT0(out[3], xi1);
+ const float xr3{ ci[0] - s*(ci[1]-ci[3])}; out[6] = VINSERT0(out[6], xr3);
+ const float xi3{ ci[2] - s*(ci[1]+ci[3])}; out[7] = VINSERT0(out[7], xi3);
+
+ for(size_t k{1};k < dk;++k)
+ pffft_real_finalize_4x4(&in[8*k-1], &in[8*k+0], in + 8*k+1, e + k*6, out + k*8);
+}
+
+force_inline void pffft_real_preprocess_4x4(const v4sf *in, const v4sf *e, v4sf *RESTRICT out,
+ const bool first)
+{
+ v4sf r0{in[0]}, i0{in[1]}, r1{in[2]}, i1{in[3]};
+ v4sf r2{in[4]}, i2{in[5]}, r3{in[6]}, i3{in[7]};
+
+ /* transformation for each column is:
+ *
+ * [1 1 1 1 0 0 0 0] [r0]
+ * [1 0 0 -1 0 -1 -1 0] [r1]
+ * [1 -1 -1 1 0 0 0 0] [r2]
+ * [1 0 0 -1 0 1 1 0] [r3]
+ * [0 0 0 0 1 -1 1 -1] * [i0]
+ * [0 -1 1 0 1 0 0 1] [i1]
+ * [0 0 0 0 1 1 -1 -1] [i2]
+ * [0 1 -1 0 1 0 0 1] [i3]
+ */
+
+ v4sf sr0{VADD(r0,r3)}, dr0{VSUB(r0,r3)};
+ v4sf sr1{VADD(r1,r2)}, dr1{VSUB(r1,r2)};
+ v4sf si0{VADD(i0,i3)}, di0{VSUB(i0,i3)};
+ v4sf si1{VADD(i1,i2)}, di1{VSUB(i1,i2)};
+
+ r0 = VADD(sr0, sr1);
+ r2 = VSUB(sr0, sr1);
+ r1 = VSUB(dr0, si1);
+ r3 = VADD(dr0, si1);
+ i0 = VSUB(di0, di1);
+ i2 = VADD(di0, di1);
+ i1 = VSUB(si0, dr1);
+ i3 = VADD(si0, dr1);
+
+ vcplxmulconj(r1,i1,e[0],e[1]);
+ vcplxmulconj(r2,i2,e[2],e[3]);
+ vcplxmulconj(r3,i3,e[4],e[5]);
+
+ vtranspose4(r0,r1,r2,r3);
+ vtranspose4(i0,i1,i2,i3);
+
+ if(!first)
+ {
+ *out++ = r0;
+ *out++ = i0;
+ }
+ *out++ = r1;
+ *out++ = i1;
+ *out++ = r2;
+ *out++ = i2;
+ *out++ = r3;
+ *out++ = i3;
+}
+
+NOINLINE void pffft_real_preprocess(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out,
+ const v4sf *e)
+{
+ static constexpr float sqrt2{al::numbers::sqrt2_v<float>};
+
+ assert(in != out);
+ const size_t dk{Ncvec/SIMD_SZ}; // number of 4x4 matrix blocks
+ /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */
+
+ std::array<float,SIMD_SZ> Xr, Xi;
+ for(size_t k{0};k < SIMD_SZ;++k)
+ {
+ Xr[k] = VEXTRACT0(in[2*k]);
+ Xi[k] = VEXTRACT0(in[2*k + 1]);
+ }
+
+ pffft_real_preprocess_4x4(in, e, out+1, true); // will write only 6 values
+
+ /* [Xr0 Xr1 Xr2 Xr3 Xi0 Xi1 Xi2 Xi3]
+ *
+ * [cr0] [1 0 2 0 1 0 0 0]
+ * [cr1] [1 0 0 0 -1 0 -2 0]
+ * [cr2] [1 0 -2 0 1 0 0 0]
+ * [cr3] [1 0 0 0 -1 0 2 0]
+ * [ci0] [0 2 0 2 0 0 0 0]
+ * [ci1] [0 s 0 -s 0 -s 0 -s]
+ * [ci2] [0 0 0 0 0 -2 0 2]
+ * [ci3] [0 -s 0 s 0 -s 0 -s]
+ */
+ for(size_t k{1};k < dk;++k)
+ pffft_real_preprocess_4x4(in+8*k, e + k*6, out-1+k*8, false);
+
+ const float cr0{(Xr[0]+Xi[0]) + 2*Xr[2]};
+ const float cr1{(Xr[0]-Xi[0]) - 2*Xi[2]};
+ const float cr2{(Xr[0]+Xi[0]) - 2*Xr[2]};
+ const float cr3{(Xr[0]-Xi[0]) + 2*Xi[2]};
+ out[0] = VSET4(cr0, cr1, cr2, cr3);
+ const float ci0{ 2*(Xr[1]+Xr[3])};
+ const float ci1{ sqrt2*(Xr[1]-Xr[3]) - sqrt2*(Xi[1]+Xi[3])};
+ const float ci2{ 2*(Xi[3]-Xi[1])};
+ const float ci3{-sqrt2*(Xr[1]-Xr[3]) - sqrt2*(Xi[1]+Xi[3])};
+ out[2*Ncvec-1] = VSET4(ci0, ci1, ci2, ci3);
+}
+
+
+void pffft_transform_internal(const PFFFT_Setup *setup, const v4sf *vinput, v4sf *voutput,
+ v4sf *scratch, const pffft_direction_t direction, const bool ordered)
+{
+ assert(scratch != nullptr);
+ assert(voutput != scratch);
+
+ const size_t Ncvec{setup->Ncvec};
+ const bool nf_odd{(setup->ifac[1]&1) != 0};
+
+ v4sf *buff[2]{voutput, scratch};
+ bool ib{nf_odd != ordered};
+ if(direction == PFFFT_FORWARD)
+ {
+ /* Swap the initial work buffer for forward FFTs, which helps avoid an
+ * extra copy for output.
+ */
+ ib = !ib;
+ if(setup->transform == PFFFT_REAL)
+ {
+ ib = (rfftf1_ps(Ncvec*2, vinput, buff[ib], buff[!ib], setup->twiddle, setup->ifac) == buff[1]);
+ pffft_real_finalize(Ncvec, buff[ib], buff[!ib], setup->e);
+ }
+ else
+ {
+ v4sf *tmp{buff[ib]};
+ for(size_t k=0; k < Ncvec; ++k)
+ uninterleave2(vinput[k*2], vinput[k*2+1], tmp[k*2], tmp[k*2+1]);
+
+ ib = (cfftf1_ps(Ncvec, buff[ib], buff[!ib], buff[ib], setup->twiddle, setup->ifac, -1.0f) == buff[1]);
+ pffft_cplx_finalize(Ncvec, buff[ib], buff[!ib], setup->e);
+ }
+ if(ordered)
+ pffft_zreorder(setup, reinterpret_cast<float*>(buff[!ib]),
+ reinterpret_cast<float*>(buff[ib]), PFFFT_FORWARD);
+ else
+ ib = !ib;
+ }
+ else
+ {
+ if(vinput == buff[ib])
+ ib = !ib; // may happen when finput == foutput
+
+ if(ordered)
+ {
+ pffft_zreorder(setup, reinterpret_cast<const float*>(vinput),
+ reinterpret_cast<float*>(buff[ib]), PFFFT_BACKWARD);
+ vinput = buff[ib];
+ ib = !ib;
+ }
+ if(setup->transform == PFFFT_REAL)
+ {
+ pffft_real_preprocess(Ncvec, vinput, buff[ib], setup->e);
+ ib = (rfftb1_ps(Ncvec*2, buff[ib], buff[0], buff[1], setup->twiddle, setup->ifac) == buff[1]);
+ }
+ else
+ {
+ pffft_cplx_preprocess(Ncvec, vinput, buff[ib], setup->e);
+ ib = (cfftf1_ps(Ncvec, buff[ib], buff[0], buff[1], setup->twiddle, setup->ifac, +1.0f) == buff[1]);
+ for(size_t k{0};k < Ncvec;++k)
+ interleave2(buff[ib][k*2], buff[ib][k*2+1], buff[ib][k*2], buff[ib][k*2+1]);
+ }
+ }
+
+ if(buff[ib] != voutput)
+ {
+ /* extra copy required -- this situation should only happen when finput == foutput */
+ assert(vinput==voutput);
+ for(size_t k{0};k < Ncvec;++k)
+ {
+ v4sf a{buff[ib][2*k]}, b{buff[ib][2*k+1]};
+ voutput[2*k] = a; voutput[2*k+1] = b;
+ }
+ }
+}
+
+} // namespace
+
+void pffft_zreorder(const PFFFT_Setup *setup, const float *in, float *out,
+ pffft_direction_t direction)
+{
+ assert(in != out);
+
+ const size_t N{setup->N}, Ncvec{setup->Ncvec};
+ const v4sf *vin{reinterpret_cast<const v4sf*>(in)};
+ v4sf *RESTRICT vout{reinterpret_cast<v4sf*>(out)};
+ if(setup->transform == PFFFT_REAL)
+ {
+ const size_t dk{N/32};
+ if(direction == PFFFT_FORWARD)
+ {
+ for(size_t k{0};k < dk;++k)
+ {
+ interleave2(vin[k*8 + 0], vin[k*8 + 1], vout[2*(0*dk + k) + 0], vout[2*(0*dk + k) + 1]);
+ interleave2(vin[k*8 + 4], vin[k*8 + 5], vout[2*(2*dk + k) + 0], vout[2*(2*dk + k) + 1]);
+ }
+ reversed_copy(dk, vin+2, 8, vout + N/SIMD_SZ/2);
+ reversed_copy(dk, vin+6, 8, vout + N/SIMD_SZ);
+ }
+ else
+ {
+ for(size_t k{0};k < dk;++k)
+ {
+ uninterleave2(vin[2*(0*dk + k) + 0], vin[2*(0*dk + k) + 1], vout[k*8 + 0], vout[k*8 + 1]);
+ uninterleave2(vin[2*(2*dk + k) + 0], vin[2*(2*dk + k) + 1], vout[k*8 + 4], vout[k*8 + 5]);
+ }
+ unreversed_copy(dk, vin + N/SIMD_SZ/4, vout + N/SIMD_SZ - 6, -8);
+ unreversed_copy(dk, vin + 3*N/SIMD_SZ/4, vout + N/SIMD_SZ - 2, -8);
+ }
+ }
+ else
+ {
+ if(direction == PFFFT_FORWARD)
+ {
+ for(size_t k{0};k < Ncvec;++k)
+ {
+ size_t kk{(k/4) + (k%4)*(Ncvec/4)};
+ interleave2(vin[k*2], vin[k*2+1], vout[kk*2], vout[kk*2+1]);
+ }
+ }
+ else
+ {
+ for(size_t k{0};k < Ncvec;++k)
+ {
+ size_t kk{(k/4) + (k%4)*(Ncvec/4)};
+ uninterleave2(vin[kk*2], vin[kk*2+1], vout[k*2], vout[k*2+1]);
+ }
+ }
+ }
+}
+
+void pffft_zconvolve_scale_accumulate(const PFFFT_Setup *s, const float *a, const float *b,
+ float *ab, float scaling)
+{
+ const size_t Ncvec{s->Ncvec};
+ const v4sf *RESTRICT va{reinterpret_cast<const v4sf*>(a)};
+ const v4sf *RESTRICT vb{reinterpret_cast<const v4sf*>(b)};
+ v4sf *RESTRICT vab{reinterpret_cast<v4sf*>(ab)};
+
+#ifdef __arm__
+ __builtin_prefetch(va);
+ __builtin_prefetch(vb);
+ __builtin_prefetch(vab);
+ __builtin_prefetch(va+2);
+ __builtin_prefetch(vb+2);
+ __builtin_prefetch(vab+2);
+ __builtin_prefetch(va+4);
+ __builtin_prefetch(vb+4);
+ __builtin_prefetch(vab+4);
+ __builtin_prefetch(va+6);
+ __builtin_prefetch(vb+6);
+ __builtin_prefetch(vab+6);
+#ifndef __clang__
+#define ZCONVOLVE_USING_INLINE_NEON_ASM
+#endif
+#endif
+
+ const float ar1{VEXTRACT0(va[0])};
+ const float ai1{VEXTRACT0(va[1])};
+ const float br1{VEXTRACT0(vb[0])};
+ const float bi1{VEXTRACT0(vb[1])};
+ const float abr1{VEXTRACT0(vab[0])};
+ const float abi1{VEXTRACT0(vab[1])};
+
+#ifdef ZCONVOLVE_USING_INLINE_ASM
+ /* Inline asm version, unfortunately miscompiled by clang 3.2, at least on
+ * Ubuntu. So this will be restricted to GCC.
+ *
+ * Does it still miscompile with Clang? Is it even needed with today's
+ * optimizers?
+ */
+ const float *a_{a}, *b_{b}; float *ab_{ab};
+ size_t N{Ncvec};
+ asm volatile("mov r8, %2 \n"
+ "vdup.f32 q15, %4 \n"
+ "1: \n"
+ "pld [%0,#64] \n"
+ "pld [%1,#64] \n"
+ "pld [%2,#64] \n"
+ "pld [%0,#96] \n"
+ "pld [%1,#96] \n"
+ "pld [%2,#96] \n"
+ "vld1.f32 {q0,q1}, [%0,:128]! \n"
+ "vld1.f32 {q4,q5}, [%1,:128]! \n"
+ "vld1.f32 {q2,q3}, [%0,:128]! \n"
+ "vld1.f32 {q6,q7}, [%1,:128]! \n"
+ "vld1.f32 {q8,q9}, [r8,:128]! \n"
+
+ "vmul.f32 q10, q0, q4 \n"
+ "vmul.f32 q11, q0, q5 \n"
+ "vmul.f32 q12, q2, q6 \n"
+ "vmul.f32 q13, q2, q7 \n"
+ "vmls.f32 q10, q1, q5 \n"
+ "vmla.f32 q11, q1, q4 \n"
+ "vld1.f32 {q0,q1}, [r8,:128]! \n"
+ "vmls.f32 q12, q3, q7 \n"
+ "vmla.f32 q13, q3, q6 \n"
+ "vmla.f32 q8, q10, q15 \n"
+ "vmla.f32 q9, q11, q15 \n"
+ "vmla.f32 q0, q12, q15 \n"
+ "vmla.f32 q1, q13, q15 \n"
+ "vst1.f32 {q8,q9},[%2,:128]! \n"
+ "vst1.f32 {q0,q1},[%2,:128]! \n"
+ "subs %3, #2 \n"
+ "bne 1b \n"
+ : "+r"(a_), "+r"(b_), "+r"(ab_), "+r"(N) : "r"(scaling) : "r8", "q0","q1","q2","q3","q4","q5","q6","q7","q8","q9", "q10","q11","q12","q13","q15","memory");
+
+#else
+
+ /* Default routine, works fine for non-arm cpus with current compilers. */
+ const v4sf vscal{LD_PS1(scaling)};
+ for(size_t i{0};i < Ncvec;i += 2)
+ {
+ v4sf ar4{va[2*i+0]}, ai4{va[2*i+1]};
+ v4sf br4{vb[2*i+0]}, bi4{vb[2*i+1]};
+ vcplxmul(ar4, ai4, br4, bi4);
+ vab[2*i+0] = VMADD(ar4, vscal, vab[2*i+0]);
+ vab[2*i+1] = VMADD(ai4, vscal, vab[2*i+1]);
+ ar4 = va[2*i+2]; ai4 = va[2*i+3];
+ br4 = vb[2*i+2]; bi4 = vb[2*i+3];
+ vcplxmul(ar4, ai4, br4, bi4);
+ vab[2*i+2] = VMADD(ar4, vscal, vab[2*i+2]);
+ vab[2*i+3] = VMADD(ai4, vscal, vab[2*i+3]);
+ }
+#endif
+
+ if(s->transform == PFFFT_REAL)
+ {
+ vab[0] = VINSERT0(vab[0], abr1 + ar1*br1*scaling);
+ vab[1] = VINSERT0(vab[1], abi1 + ai1*bi1*scaling);
+ }
+}
+
+void pffft_zconvolve_accumulate(const PFFFT_Setup *s, const float *a, const float *b, float *ab)
+{
+ const size_t Ncvec{s->Ncvec};
+ const v4sf *RESTRICT va{reinterpret_cast<const v4sf*>(a)};
+ const v4sf *RESTRICT vb{reinterpret_cast<const v4sf*>(b)};
+ v4sf *RESTRICT vab{reinterpret_cast<v4sf*>(ab)};
+
+#ifdef __arm__
+ __builtin_prefetch(va);
+ __builtin_prefetch(vb);
+ __builtin_prefetch(vab);
+ __builtin_prefetch(va+2);
+ __builtin_prefetch(vb+2);
+ __builtin_prefetch(vab+2);
+ __builtin_prefetch(va+4);
+ __builtin_prefetch(vb+4);
+ __builtin_prefetch(vab+4);
+ __builtin_prefetch(va+6);
+ __builtin_prefetch(vb+6);
+ __builtin_prefetch(vab+6);
+#endif
+
+ const float ar1{VEXTRACT0(va[0])};
+ const float ai1{VEXTRACT0(va[1])};
+ const float br1{VEXTRACT0(vb[0])};
+ const float bi1{VEXTRACT0(vb[1])};
+ const float abr1{VEXTRACT0(vab[0])};
+ const float abi1{VEXTRACT0(vab[1])};
+
+ /* No inline assembly for this version. I'm not familiar enough with NEON
+ * assembly, and I don't know that it's needed with today's optimizers.
+ */
+ for(size_t i{0};i < Ncvec;i += 2)
+ {
+ v4sf ar4{va[2*i+0]}, ai4{va[2*i+1]};
+ v4sf br4{vb[2*i+0]}, bi4{vb[2*i+1]};
+ vcplxmul(ar4, ai4, br4, bi4);
+ vab[2*i+0] = VADD(ar4, vab[2*i+0]);
+ vab[2*i+1] = VADD(ai4, vab[2*i+1]);
+ ar4 = va[2*i+2]; ai4 = va[2*i+3];
+ br4 = vb[2*i+2]; bi4 = vb[2*i+3];
+ vcplxmul(ar4, ai4, br4, bi4);
+ vab[2*i+2] = VADD(ar4, vab[2*i+2]);
+ vab[2*i+3] = VADD(ai4, vab[2*i+3]);
+ }
+
+ if(s->transform == PFFFT_REAL)
+ {
+ vab[0] = VINSERT0(vab[0], abr1 + ar1*br1);
+ vab[1] = VINSERT0(vab[1], abi1 + ai1*bi1);
+ }
+}
+
+
+void pffft_transform(const PFFFT_Setup *setup, const float *input, float *output, float *work,
+ pffft_direction_t direction)
+{
+ assert(valigned(input) && valigned(output) && valigned(work));
+ pffft_transform_internal(setup, reinterpret_cast<const v4sf*>(al::assume_aligned<16>(input)),
+ reinterpret_cast<v4sf*>(al::assume_aligned<16>(output)),
+ reinterpret_cast<v4sf*>(al::assume_aligned<16>(work)), direction, false);
+}
+
+void pffft_transform_ordered(const PFFFT_Setup *setup, const float *input, float *output,
+ float *work, pffft_direction_t direction)
+{
+ assert(valigned(input) && valigned(output) && valigned(work));
+ pffft_transform_internal(setup, reinterpret_cast<const v4sf*>(al::assume_aligned<16>(input)),
+ reinterpret_cast<v4sf*>(al::assume_aligned<16>(output)),
+ reinterpret_cast<v4sf*>(al::assume_aligned<16>(work)), direction, true);
+}
+
+#else // defined(PFFFT_SIMD_DISABLE)
+
+// standard routine using scalar floats, without SIMD stuff.
+
+namespace {
+
+#define pffft_transform_internal_nosimd pffft_transform_internal
+void pffft_transform_internal_nosimd(const PFFFT_Setup *setup, const float *input, float *output,
+ float *scratch, const pffft_direction_t direction, bool ordered)
+{
+ const size_t Ncvec{setup->Ncvec};
+ const bool nf_odd{(setup->ifac[1]&1) != 0};
+
+ assert(scratch != nullptr);
+
+ /* z-domain data for complex transforms is already ordered without SIMD. */
+ if(setup->transform == PFFFT_COMPLEX)
+ ordered = false;
+
+ float *buff[2]{output, scratch};
+ bool ib{nf_odd != ordered};
+ if(direction == PFFFT_FORWARD)
+ {
+ if(setup->transform == PFFFT_REAL)
+ ib = (rfftf1_ps(Ncvec*2, input, buff[ib], buff[!ib], setup->twiddle, setup->ifac) == buff[1]);
+ else
+ ib = (cfftf1_ps(Ncvec, input, buff[ib], buff[!ib], setup->twiddle, setup->ifac, -1.0f) == buff[1]);
+ if(ordered)
+ {
+ pffft_zreorder(setup, buff[ib], buff[!ib], PFFFT_FORWARD);
+ ib = !ib;
+ }
+ }
+ else
+ {
+ if (input == buff[ib])
+ ib = !ib; // may happen when finput == foutput
+
+ if(ordered)
+ {
+ pffft_zreorder(setup, input, buff[ib], PFFFT_BACKWARD);
+ input = buff[ib];
+ ib = !ib;
+ }
+ if(setup->transform == PFFFT_REAL)
+ ib = (rfftb1_ps(Ncvec*2, input, buff[ib], buff[!ib], setup->twiddle, setup->ifac) == buff[1]);
+ else
+ ib = (cfftf1_ps(Ncvec, input, buff[ib], buff[!ib], setup->twiddle, setup->ifac, +1.0f) == buff[1]);
+ }
+ if(buff[ib] != output)
+ {
+ // extra copy required -- this situation should happens only when finput == foutput
+ assert(input==output);
+ for(size_t k{0};k < Ncvec;++k)
+ {
+ float a{buff[ib][2*k]}, b{buff[ib][2*k+1]};
+ output[2*k] = a; output[2*k+1] = b;
+ }
+ }
+}
+
+} // namespace
+
+#define pffft_zreorder_nosimd pffft_zreorder
+void pffft_zreorder_nosimd(const PFFFT_Setup *setup, const float *in, float *RESTRICT out,
+ pffft_direction_t direction)
+{
+ const size_t N{setup->N};
+ if(setup->transform == PFFFT_COMPLEX)
+ {
+ for(size_t k{0};k < 2*N;++k)
+ out[k] = in[k];
+ return;
+ }
+ else if(direction == PFFFT_FORWARD)
+ {
+ float x_N{in[N-1]};
+ for(size_t k{N-1};k > 1;--k)
+ out[k] = in[k-1];
+ out[0] = in[0];
+ out[1] = x_N;
+ }
+ else
+ {
+ float x_N{in[1]};
+ for(size_t k{1};k < N-1;++k)
+ out[k] = in[k+1];
+ out[0] = in[0];
+ out[N-1] = x_N;
+ }
+}
+
+void pffft_zconvolve_scale_accumulate(const PFFFT_Setup *s, const float *a, const float *b,
+ float *ab, float scaling)
+{
+ size_t Ncvec{s->Ncvec};
+
+ if(s->transform == PFFFT_REAL)
+ {
+ // take care of the fftpack ordering
+ ab[0] += a[0]*b[0]*scaling;
+ ab[2*Ncvec-1] += a[2*Ncvec-1]*b[2*Ncvec-1]*scaling;
+ ++ab; ++a; ++b; --Ncvec;
+ }
+ for(size_t i{0};i < Ncvec;++i)
+ {
+ float ar{a[2*i+0]}, ai{a[2*i+1]};
+ const float br{b[2*i+0]}, bi{b[2*i+1]};
+ vcplxmul(ar, ai, br, bi);
+ ab[2*i+0] += ar*scaling;
+ ab[2*i+1] += ai*scaling;
+ }
+}
+
+void pffft_zconvolve_accumulate(const PFFFT_Setup *s, const float *a, const float *b, float *ab)
+{
+ size_t Ncvec{s->Ncvec};
+
+ if(s->transform == PFFFT_REAL)
+ {
+ // take care of the fftpack ordering
+ ab[0] += a[0]*b[0];
+ ab[2*Ncvec-1] += a[2*Ncvec-1]*b[2*Ncvec-1];
+ ++ab; ++a; ++b; --Ncvec;
+ }
+ for(size_t i{0};i < Ncvec;++i)
+ {
+ float ar{a[2*i+0]}, ai{a[2*i+1]};
+ const float br{b[2*i+0]}, bi{b[2*i+1]};
+ vcplxmul(ar, ai, br, bi);
+ ab[2*i+0] += ar;
+ ab[2*i+1] += ai;
+ }
+}
+
+
+void pffft_transform(const PFFFT_Setup *setup, const float *input, float *output, float *work,
+ pffft_direction_t direction)
+{
+ pffft_transform_internal(setup, input, output, work, direction, false);
+}
+
+void pffft_transform_ordered(const PFFFT_Setup *setup, const float *input, float *output,
+ float *work, pffft_direction_t direction)
+{
+ pffft_transform_internal(setup, input, output, work, direction, true);
+}
+
+#endif // defined(PFFFT_SIMD_DISABLE)
diff --git a/common/pffft.h b/common/pffft.h
new file mode 100644
index 00000000..9cff9e54
--- /dev/null
+++ b/common/pffft.h
@@ -0,0 +1,192 @@
+/* Copyright (c) 2013 Julien Pommier ( [email protected] )
+
+ Based on original fortran 77 code from FFTPACKv4 from NETLIB,
+ authored by Dr Paul Swarztrauber of NCAR, in 1985.
+
+ As confirmed by the NCAR fftpack software curators, the following
+ FFTPACKv5 license applies to FFTPACKv4 sources. My changes are
+ released under the same terms.
+
+ FFTPACK license:
+
+ http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html
+
+ 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.
+*/
+
+/* PFFFT : a Pretty Fast FFT.
+ *
+ * This is basically an adaptation of the single precision fftpack (v4) as
+ * found on netlib taking advantage of SIMD instructions found on CPUs such as
+ * Intel x86 (SSE1), PowerPC (Altivec), and Arm (NEON).
+ *
+ * For architectures where SIMD instructions aren't available, the code falls
+ * back to a scalar version.
+ *
+ * Restrictions:
+ *
+ * - 1D transforms only, with 32-bit single precision.
+ *
+ * - supports only transforms for inputs of length N of the form
+ * N=(2^a)*(3^b)*(5^c), given a >= 5, b >=0, c >= 0 (32, 48, 64, 96, 128, 144,
+ * 160, etc are all acceptable lengths). Performance is best for 128<=N<=8192.
+ *
+ * - all (float*) pointers for the functions below are expected to have a
+ * "SIMD-compatible" alignment, that is 16 bytes.
+ *
+ * You can allocate such buffers with the pffft_aligned_malloc function, and
+ * deallocate them with pffft_aligned_free (or with stuff like posix_memalign,
+ * aligned_alloc, etc).
+ *
+ * Note that for the z-domain data of real transforms, when in the canonical
+ * order (as interleaved complex numbers) both 0-frequency and half-frequency
+ * components, which are real, are assembled in the first entry as
+ * F(0)+i*F(n/2+1). The original fftpack placed F(n/2+1) at the end of the
+ * arrays instead.
+ */
+
+#ifndef PFFFT_H
+#define PFFFT_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* opaque struct holding internal stuff (precomputed twiddle factors) this
+ * struct can be shared by many threads as it contains only read-only data.
+ */
+typedef struct PFFFT_Setup PFFFT_Setup;
+
+#ifndef PFFFT_COMMON_ENUMS
+#define PFFFT_COMMON_ENUMS
+
+/* direction of the transform */
+typedef enum { PFFFT_FORWARD, PFFFT_BACKWARD } pffft_direction_t;
+
+/* type of transform */
+typedef enum { PFFFT_REAL, PFFFT_COMPLEX } pffft_transform_t;
+
+#endif
+
+/**
+ * Prepare for performing transforms of size N -- the returned PFFFT_Setup
+ * structure is read-only so it can safely be shared by multiple concurrent
+ * threads.
+ */
+PFFFT_Setup *pffft_new_setup(unsigned int N, pffft_transform_t transform);
+void pffft_destroy_setup(PFFFT_Setup *setup);
+
+/**
+ * Perform a Fourier transform. The z-domain data is stored in the most
+ * efficient order for transforming back or using for convolution, and as
+ * such, there's no guarantee to the order of the values. If you need to have
+ * its content sorted in the usual way, that is as an array of interleaved
+ * complex numbers, either use pffft_transform_ordered, or call pffft_zreorder
+ * after the forward fft and before the backward fft.
+ *
+ * Transforms are not scaled: PFFFT_BACKWARD(PFFFT_FORWARD(x)) = N*x. Typically
+ * you will want to scale the backward transform by 1/N.
+ *
+ * The 'work' pointer must point to an area of N (2*N for complex fft) floats,
+ * properly aligned. It cannot be NULL.
+ *
+ * The input and output parameters may alias.
+ */
+void pffft_transform(const PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction);
+
+/**
+ * Similar to pffft_transform, but handles the complex values in the usual form
+ * (interleaved complex numbers). This is similar to calling
+ * pffft_transform(..., PFFFT_FORWARD) followed by
+ * pffft_zreorder(..., PFFFT_FORWARD), or
+ * pffft_zreorder(..., PFFFT_BACKWARD) followed by
+ * pffft_transform(..., PFFFT_BACKWARD), for the given direction.
+ *
+ * The input and output parameters may alias.
+ */
+void pffft_transform_ordered(const PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction);
+
+/**
+ * Reorder the z-domain data. For PFFFT_FORWARD, it reorders from the internal
+ * representation to the "canonical" order (as interleaved complex numbers).
+ * For PFFFT_BACKWARD, it reorders from the canonical order to the internal
+ * order suitable for pffft_transform(..., PFFFT_BACKWARD) or
+ * pffft_zconvolve_accumulate.
+ *
+ * The input and output parameters should not alias.
+ */
+void pffft_zreorder(const PFFFT_Setup *setup, const float *input, float *output, pffft_direction_t direction);
+
+/**
+ * Perform a multiplication of the z-domain data in dft_a and dft_b, and scale
+ * and accumulate into dft_ab. The arrays should have been obtained with
+ * pffft_transform(..., PFFFT_FORWARD) or pffft_zreorder(..., PFFFT_BACKWARD)
+ * and should *not* be in the usual order (otherwise just perform the operation
+ * yourself as the dft coeffs are stored as interleaved complex numbers).
+ *
+ * The operation performed is: dft_ab += (dft_a * dft_b)*scaling
+ *
+ * The dft_a, dft_b, and dft_ab parameters may alias.
+ */
+void pffft_zconvolve_scale_accumulate(const PFFFT_Setup *setup, const float *dft_a, const float *dft_b, float *dft_ab, float scaling);
+
+/**
+ * Perform a multiplication of the z-domain data in dft_a and dft_b, and
+ * accumulate into dft_ab.
+ *
+ * The operation performed is: dft_ab += dft_a * dft_b
+ *
+ * The dft_a, dft_b, and dft_ab parameters may alias.
+ */
+void pffft_zconvolve_accumulate(const PFFFT_Setup *setup, const float *dft_a, const float *dft_b, float *dft_ab);
+
+/**
+ * The float buffers must have the correct alignment (16-byte boundary on intel
+ * and powerpc). This function may be used to obtain such correctly aligned
+ * buffers.
+ */
+void *pffft_aligned_malloc(size_t nb_bytes);
+void pffft_aligned_free(void *ptr);
+
+/* Return 4 or 1 depending if vectorization was enable when building pffft.cpp. */
+int pffft_simd_size();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PFFFT_H
diff --git a/common/phase_shifter.h b/common/phase_shifter.h
index 0d4166bc..e1a83dab 100644
--- a/common/phase_shifter.h
+++ b/common/phase_shifter.h
@@ -9,11 +9,14 @@
#include <array>
#include <stddef.h>
+#include <type_traits>
#include "alcomplex.h"
#include "alspan.h"
+struct NoInit { };
+
/* Implements a wide-band +90 degree phase-shift. Note that this should be
* given one sample less of a delay (FilterSize/2 - 1) compared to the direct
* signal delay (FilterSize/2) to properly align.
@@ -53,14 +56,16 @@ struct PhaseShifterT {
std::fill_n(fftBuffer.get(), fft_size, complex_d{});
fftBuffer[half_size] = 1.0;
- forward_fft(al::as_span(fftBuffer.get(), fft_size));
- for(size_t i{0};i < half_size+1;++i)
+ forward_fft(al::span{fftBuffer.get(), fft_size});
+ fftBuffer[0] *= std::numeric_limits<double>::epsilon();
+ for(size_t i{1};i < half_size;++i)
fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()};
+ fftBuffer[half_size] *= std::numeric_limits<double>::epsilon();
for(size_t i{half_size+1};i < fft_size;++i)
fftBuffer[i] = std::conj(fftBuffer[fft_size - i]);
- inverse_fft(al::as_span(fftBuffer.get(), fft_size));
+ inverse_fft(al::span{fftBuffer.get(), fft_size});
- auto fftiter = fftBuffer.get() + half_size + (FilterSize/2 - 1);
+ auto fftiter = fftBuffer.get() + fft_size - 1;
for(float &coeff : mCoeffs)
{
coeff = static_cast<float>(fftiter->real() / double{fft_size});
@@ -68,29 +73,12 @@ struct PhaseShifterT {
}
}
+ PhaseShifterT(NoInit) { }
+
void process(al::span<float> dst, const float *RESTRICT src) const;
private:
#if defined(HAVE_NEON)
- /* There doesn't seem to be NEON intrinsics to do this kind of stipple
- * shuffling, so there's two custom methods for it.
- */
- static auto shuffle_2020(float32x4_t a, float32x4_t b)
- {
- float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 0))};
- ret = vsetq_lane_f32(vgetq_lane_f32(a, 2), ret, 1);
- ret = vsetq_lane_f32(vgetq_lane_f32(b, 0), ret, 2);
- ret = vsetq_lane_f32(vgetq_lane_f32(b, 2), ret, 3);
- return ret;
- }
- static auto shuffle_3131(float32x4_t a, float32x4_t b)
- {
- float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 1))};
- ret = vsetq_lane_f32(vgetq_lane_f32(a, 3), ret, 1);
- ret = vsetq_lane_f32(vgetq_lane_f32(b, 1), ret, 2);
- ret = vsetq_lane_f32(vgetq_lane_f32(b, 3), ret, 3);
- return ret;
- }
static auto unpacklo(float32x4_t a, float32x4_t b)
{
float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))};
@@ -171,9 +159,10 @@ inline void PhaseShifterT<S>::process(al::span<float> dst, const float *RESTRICT
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
const float32x4_t s0{vld1q_f32(&src[j*2])};
const float32x4_t s1{vld1q_f32(&src[j*2 + 4])};
+ const float32x4x2_t values{vuzpq_f32(s0, s1)};
- r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs);
- r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs);
+ r04 = vmlaq_f32(r04, values.val[0], coeffs);
+ r14 = vmlaq_f32(r14, values.val[1], coeffs);
}
src += 2;
diff --git a/common/polyphase_resampler.cpp b/common/polyphase_resampler.cpp
index 14f7e40d..9c569b3a 100644
--- a/common/polyphase_resampler.cpp
+++ b/common/polyphase_resampler.cpp
@@ -3,6 +3,8 @@
#include <algorithm>
#include <cmath>
+#include <numeric>
+#include <stdexcept>
#include "alnumbers.h"
#include "opthelpers.h"
@@ -14,34 +16,36 @@ constexpr double Epsilon{1e-9};
using uint = unsigned int;
-/* This is the normalized cardinal sine (sinc) function.
- *
- * sinc(x) = { 1, x = 0
- * { sin(pi x) / (pi x), otherwise.
- */
-double Sinc(const double x)
-{
- if(std::abs(x) < Epsilon) UNLIKELY
- return 1.0;
- return std::sin(al::numbers::pi*x) / (al::numbers::pi*x);
-}
+#if __cpp_lib_math_special_functions >= 201603L
+using std::cyl_bessel_i;
+
+#else
/* The zero-order modified Bessel function of the first kind, used for the
* Kaiser window.
*
* I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
* = sum_{k=0}^inf ((x / 2)^k / k!)^2
+ *
+ * This implementation only handles nu = 0, and isn't the most precise (it
+ * starts with the largest value and accumulates successively smaller values,
+ * compounding the rounding and precision error), but it's good enough.
*/
-constexpr double BesselI_0(const double x)
+template<typename T, typename U>
+U cyl_bessel_i(T nu, U x)
{
- // Start at k=1 since k=0 is trivial.
+ if(nu != T{0})
+ throw std::runtime_error{"cyl_bessel_i: nu != 0"};
+
+ /* Start at k=1 since k=0 is trivial. */
const double x2{x/2.0};
double term{1.0};
double sum{1.0};
int k{1};
- // Let the integration converge until the term of the sum is no longer
- // significant.
+ /* Let the integration converge until the term of the sum is no longer
+ * significant.
+ */
double last_sum{};
do {
const double y{x2 / k};
@@ -50,7 +54,20 @@ constexpr double BesselI_0(const double x)
term *= y * y;
sum += term;
} while(sum != last_sum);
- return sum;
+ return static_cast<U>(sum);
+}
+#endif
+
+/* This is the normalized cardinal sine (sinc) function.
+ *
+ * sinc(x) = { 1, x = 0
+ * { sin(pi x) / (pi x), otherwise.
+ */
+double Sinc(const double x)
+{
+ if(std::abs(x) < Epsilon) UNLIKELY
+ return 1.0;
+ return std::sin(al::numbers::pi*x) / (al::numbers::pi*x);
}
/* Calculate a Kaiser window from the given beta value and a normalized k
@@ -67,23 +84,11 @@ constexpr double BesselI_0(const double x)
*
* k = 2 i / M - 1, where 0 <= i <= M.
*/
-double Kaiser(const double b, const double k)
+double Kaiser(const double beta, const double k, const double besseli_0_beta)
{
if(!(k >= -1.0 && k <= 1.0))
return 0.0;
- return BesselI_0(b * std::sqrt(1.0 - k*k)) / BesselI_0(b);
-}
-
-// Calculates the greatest common divisor of a and b.
-constexpr uint Gcd(uint x, uint y)
-{
- while(y > 0)
- {
- const uint z{y};
- y = x % y;
- x = z;
- }
- return x;
+ return cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta;
}
/* Calculates the size (order) of the Kaiser window. Rejection is in dB and
@@ -124,11 +129,11 @@ constexpr double CalcKaiserBeta(const double rejection)
* p -- gain compensation factor when sampling
* f_t -- normalized center frequency (or cutoff; 0.5 is nyquist)
*/
-double SincFilter(const uint l, const double b, const double gain, const double cutoff,
- const uint i)
+double SincFilter(const uint l, const double beta, const double besseli_0_beta, const double gain,
+ const double cutoff, const uint i)
{
const double x{static_cast<double>(i) - l};
- return Kaiser(b, x / l) * 2.0 * gain * cutoff * Sinc(2.0 * cutoff * x);
+ return Kaiser(beta, x/l, besseli_0_beta) * 2.0 * gain * cutoff * Sinc(2.0 * cutoff * x);
}
} // namespace
@@ -137,7 +142,7 @@ double SincFilter(const uint l, const double b, const double gain, const double
// that's used to cut frequencies above the destination nyquist.
void PPhaseResampler::init(const uint srcRate, const uint dstRate)
{
- const uint gcd{Gcd(srcRate, dstRate)};
+ const uint gcd{std::gcd(srcRate, dstRate)};
mP = dstRate / gcd;
mQ = srcRate / gcd;
@@ -160,11 +165,12 @@ void PPhaseResampler::init(const uint srcRate, const uint dstRate)
// calculating the left offset to avoid increasing the transition width.
const uint l{(CalcKaiserOrder(180.0, width)+1) / 2};
const double beta{CalcKaiserBeta(180.0)};
+ const double besseli_0_beta{cyl_bessel_i(0, beta)};
mM = l*2 + 1;
mL = l;
mF.resize(mM);
for(uint i{0};i < mM;i++)
- mF[i] = SincFilter(l, beta, mP, cutoff, i);
+ mF[i] = SincFilter(l, beta, besseli_0_beta, mP, cutoff, i);
}
// Perform the upsample-filter-downsample resampling operation using a
diff --git a/common/ringbuffer.cpp b/common/ringbuffer.cpp
index 0aec1d49..af1f3669 100644
--- a/common/ringbuffer.cpp
+++ b/common/ringbuffer.cpp
@@ -29,9 +29,9 @@
#include "almalloc.h"
-RingBufferPtr RingBuffer::Create(size_t sz, size_t elem_sz, int limit_writes)
+RingBufferPtr RingBuffer::Create(std::size_t sz, std::size_t elem_sz, int limit_writes)
{
- size_t power_of_two{0u};
+ std::size_t power_of_two{0u};
if(sz > 0)
{
power_of_two = sz;
@@ -40,15 +40,14 @@ RingBufferPtr RingBuffer::Create(size_t sz, size_t elem_sz, int limit_writes)
power_of_two |= power_of_two>>4;
power_of_two |= power_of_two>>8;
power_of_two |= power_of_two>>16;
-#if SIZE_MAX > UINT_MAX
- power_of_two |= power_of_two>>32;
-#endif
+ if constexpr(SIZE_MAX > UINT_MAX)
+ power_of_two |= power_of_two>>32;
}
++power_of_two;
- if(power_of_two <= sz || power_of_two > std::numeric_limits<size_t>::max()/elem_sz)
+ if(power_of_two <= sz || power_of_two > std::numeric_limits<std::size_t>::max()/elem_sz)
throw std::overflow_error{"Ring buffer size overflow"};
- const size_t bufbytes{power_of_two * elem_sz};
+ const std::size_t bufbytes{power_of_two * elem_sz};
RingBufferPtr rb{new(FamCount(bufbytes)) RingBuffer{bufbytes}};
rb->mWriteSize = limit_writes ? sz : (power_of_two-1);
rb->mSizeMask = power_of_two - 1;
@@ -61,20 +60,20 @@ void RingBuffer::reset() noexcept
{
mWritePtr.store(0, std::memory_order_relaxed);
mReadPtr.store(0, std::memory_order_relaxed);
- std::fill_n(mBuffer.begin(), (mSizeMask+1)*mElemSize, al::byte{});
+ std::fill_n(mBuffer.begin(), (mSizeMask+1)*mElemSize, std::byte{});
}
-size_t RingBuffer::read(void *dest, size_t cnt) noexcept
+std::size_t RingBuffer::read(void *dest, std::size_t cnt) noexcept
{
- const size_t free_cnt{readSpace()};
+ const std::size_t free_cnt{readSpace()};
if(free_cnt == 0) return 0;
- const size_t to_read{std::min(cnt, free_cnt)};
- size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask};
+ const std::size_t to_read{std::min(cnt, free_cnt)};
+ std::size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask};
- size_t n1, n2;
- const size_t cnt2{read_ptr + to_read};
+ std::size_t n1, n2;
+ const std::size_t cnt2{read_ptr + to_read};
if(cnt2 > mSizeMask+1)
{
n1 = mSizeMask+1 - read_ptr;
@@ -87,7 +86,7 @@ size_t RingBuffer::read(void *dest, size_t cnt) noexcept
}
auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize,
- static_cast<al::byte*>(dest));
+ static_cast<std::byte*>(dest));
read_ptr += n1;
if(n2 > 0)
{
@@ -98,16 +97,16 @@ size_t RingBuffer::read(void *dest, size_t cnt) noexcept
return to_read;
}
-size_t RingBuffer::peek(void *dest, size_t cnt) const noexcept
+std::size_t RingBuffer::peek(void *dest, std::size_t cnt) const noexcept
{
- const size_t free_cnt{readSpace()};
+ const std::size_t free_cnt{readSpace()};
if(free_cnt == 0) return 0;
- const size_t to_read{std::min(cnt, free_cnt)};
- size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask};
+ const std::size_t to_read{std::min(cnt, free_cnt)};
+ std::size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask};
- size_t n1, n2;
- const size_t cnt2{read_ptr + to_read};
+ std::size_t n1, n2;
+ const std::size_t cnt2{read_ptr + to_read};
if(cnt2 > mSizeMask+1)
{
n1 = mSizeMask+1 - read_ptr;
@@ -120,22 +119,22 @@ size_t RingBuffer::peek(void *dest, size_t cnt) const noexcept
}
auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize,
- static_cast<al::byte*>(dest));
+ static_cast<std::byte*>(dest));
if(n2 > 0)
std::copy_n(mBuffer.begin(), n2*mElemSize, outiter);
return to_read;
}
-size_t RingBuffer::write(const void *src, size_t cnt) noexcept
+std::size_t RingBuffer::write(const void *src, std::size_t cnt) noexcept
{
- const size_t free_cnt{writeSpace()};
+ const std::size_t free_cnt{writeSpace()};
if(free_cnt == 0) return 0;
- const size_t to_write{std::min(cnt, free_cnt)};
- size_t write_ptr{mWritePtr.load(std::memory_order_relaxed) & mSizeMask};
+ const std::size_t to_write{std::min(cnt, free_cnt)};
+ std::size_t write_ptr{mWritePtr.load(std::memory_order_relaxed) & mSizeMask};
- size_t n1, n2;
- const size_t cnt2{write_ptr + to_write};
+ std::size_t n1, n2;
+ const std::size_t cnt2{write_ptr + to_write};
if(cnt2 > mSizeMask+1)
{
n1 = mSizeMask+1 - write_ptr;
@@ -147,7 +146,7 @@ size_t RingBuffer::write(const void *src, size_t cnt) noexcept
n2 = 0;
}
- auto srcbytes = static_cast<const al::byte*>(src);
+ auto srcbytes = static_cast<const std::byte*>(src);
std::copy_n(srcbytes, n1*mElemSize, mBuffer.begin() + write_ptr*mElemSize);
write_ptr += n1;
if(n2 > 0)
@@ -164,26 +163,26 @@ auto RingBuffer::getReadVector() const noexcept -> DataPair
{
DataPair ret;
- size_t w{mWritePtr.load(std::memory_order_acquire)};
- size_t r{mReadPtr.load(std::memory_order_acquire)};
+ std::size_t w{mWritePtr.load(std::memory_order_acquire)};
+ std::size_t r{mReadPtr.load(std::memory_order_acquire)};
w &= mSizeMask;
r &= mSizeMask;
- const size_t free_cnt{(w-r) & mSizeMask};
+ const std::size_t free_cnt{(w-r) & mSizeMask};
- const size_t cnt2{r + free_cnt};
+ const std::size_t cnt2{r + free_cnt};
if(cnt2 > mSizeMask+1)
{
/* Two part vector: the rest of the buffer after the current read ptr,
* plus some from the start of the buffer. */
- ret.first.buf = const_cast<al::byte*>(mBuffer.data() + r*mElemSize);
+ ret.first.buf = const_cast<std::byte*>(mBuffer.data() + r*mElemSize);
ret.first.len = mSizeMask+1 - r;
- ret.second.buf = const_cast<al::byte*>(mBuffer.data());
+ ret.second.buf = const_cast<std::byte*>(mBuffer.data());
ret.second.len = cnt2 & mSizeMask;
}
else
{
/* Single part vector: just the rest of the buffer */
- ret.first.buf = const_cast<al::byte*>(mBuffer.data() + r*mElemSize);
+ ret.first.buf = const_cast<std::byte*>(mBuffer.data() + r*mElemSize);
ret.first.len = free_cnt;
ret.second.buf = nullptr;
ret.second.len = 0;
@@ -196,25 +195,25 @@ auto RingBuffer::getWriteVector() const noexcept -> DataPair
{
DataPair ret;
- size_t w{mWritePtr.load(std::memory_order_acquire)};
- size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask};
+ std::size_t w{mWritePtr.load(std::memory_order_acquire)};
+ std::size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask};
w &= mSizeMask;
r &= mSizeMask;
- const size_t free_cnt{(r-w-1) & mSizeMask};
+ const std::size_t free_cnt{(r-w-1) & mSizeMask};
- const size_t cnt2{w + free_cnt};
+ const std::size_t cnt2{w + free_cnt};
if(cnt2 > mSizeMask+1)
{
/* Two part vector: the rest of the buffer after the current write ptr,
* plus some from the start of the buffer. */
- ret.first.buf = const_cast<al::byte*>(mBuffer.data() + w*mElemSize);
+ ret.first.buf = const_cast<std::byte*>(mBuffer.data() + w*mElemSize);
ret.first.len = mSizeMask+1 - w;
- ret.second.buf = const_cast<al::byte*>(mBuffer.data());
+ ret.second.buf = const_cast<std::byte*>(mBuffer.data());
ret.second.len = cnt2 & mSizeMask;
}
else
{
- ret.first.buf = const_cast<al::byte*>(mBuffer.data() + w*mElemSize);
+ ret.first.buf = const_cast<std::byte*>(mBuffer.data() + w*mElemSize);
ret.first.len = free_cnt;
ret.second.buf = nullptr;
ret.second.len = 0;
diff --git a/common/ringbuffer.h b/common/ringbuffer.h
index 2a3797b0..8c65c3af 100644
--- a/common/ringbuffer.h
+++ b/common/ringbuffer.h
@@ -2,11 +2,10 @@
#define RINGBUFFER_H
#include <atomic>
+#include <cstddef>
#include <memory>
-#include <stddef.h>
#include <utility>
-#include "albyte.h"
#include "almalloc.h"
@@ -18,23 +17,23 @@
struct RingBuffer {
private:
- std::atomic<size_t> mWritePtr{0u};
- std::atomic<size_t> mReadPtr{0u};
- size_t mWriteSize{0u};
- size_t mSizeMask{0u};
- size_t mElemSize{0u};
+ std::atomic<std::size_t> mWritePtr{0u};
+ std::atomic<std::size_t> mReadPtr{0u};
+ std::size_t mWriteSize{0u};
+ std::size_t mSizeMask{0u};
+ std::size_t mElemSize{0u};
- al::FlexArray<al::byte, 16> mBuffer;
+ al::FlexArray<std::byte, 16> mBuffer;
public:
struct Data {
- al::byte *buf;
- size_t len;
+ std::byte *buf;
+ std::size_t len;
};
using DataPair = std::pair<Data,Data>;
- RingBuffer(const size_t count) : mBuffer{count} { }
+ RingBuffer(const std::size_t count) : mBuffer{count} { }
/** Reset the read and write pointers to zero. This is not thread safe. */
void reset() noexcept;
@@ -56,7 +55,7 @@ public:
* Return the number of elements available for reading. This is the number
* of elements in front of the read pointer and behind the write pointer.
*/
- size_t readSpace() const noexcept
+ std::size_t readSpace() const noexcept
{
const size_t w{mWritePtr.load(std::memory_order_acquire)};
const size_t r{mReadPtr.load(std::memory_order_acquire)};
@@ -67,14 +66,14 @@ public:
* The copying data reader. Copy at most `cnt' elements into `dest'.
* Returns the actual number of elements copied.
*/
- size_t read(void *dest, size_t cnt) noexcept;
+ std::size_t read(void *dest, std::size_t cnt) noexcept;
/**
* The copying data reader w/o read pointer advance. Copy at most `cnt'
* elements into `dest'. Returns the actual number of elements copied.
*/
- size_t peek(void *dest, size_t cnt) const noexcept;
+ std::size_t peek(void *dest, std::size_t cnt) const noexcept;
/** Advance the read pointer `cnt' places. */
- void readAdvance(size_t cnt) noexcept
+ void readAdvance(std::size_t cnt) noexcept
{ mReadPtr.fetch_add(cnt, std::memory_order_acq_rel); }
@@ -82,7 +81,7 @@ public:
* Return the number of elements available for writing. This is the number
* of elements in front of the write pointer and behind the read pointer.
*/
- size_t writeSpace() const noexcept
+ std::size_t writeSpace() const noexcept
{
const size_t w{mWritePtr.load(std::memory_order_acquire)};
const size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask};
@@ -93,12 +92,12 @@ public:
* The copying data writer. Copy at most `cnt' elements from `src'. Returns
* the actual number of elements copied.
*/
- size_t write(const void *src, size_t cnt) noexcept;
+ std::size_t write(const void *src, std::size_t cnt) noexcept;
/** Advance the write pointer `cnt' places. */
- void writeAdvance(size_t cnt) noexcept
+ void writeAdvance(std::size_t cnt) noexcept
{ mWritePtr.fetch_add(cnt, std::memory_order_acq_rel); }
- size_t getElemSize() const noexcept { return mElemSize; }
+ std::size_t getElemSize() const noexcept { return mElemSize; }
/**
* Create a new ringbuffer to hold at least `sz' elements of `elem_sz'
@@ -106,7 +105,7 @@ public:
* (even if it is already a power of two, to ensure the requested amount
* can be written).
*/
- static std::unique_ptr<RingBuffer> Create(size_t sz, size_t elem_sz, int limit_writes);
+ static std::unique_ptr<RingBuffer> Create(std::size_t sz, std::size_t elem_sz, int limit_writes);
DEF_FAM_NEWDEL(RingBuffer, mBuffer)
};
diff --git a/common/strutils.cpp b/common/strutils.cpp
index d0418eff..f7868e2e 100644
--- a/common/strutils.cpp
+++ b/common/strutils.cpp
@@ -10,31 +10,32 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
-std::string wstr_to_utf8(const WCHAR *wstr)
+std::string wstr_to_utf8(std::wstring_view wstr)
{
std::string ret;
- int len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
+ int len{WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.length()), nullptr,
+ 0, nullptr, nullptr)};
if(len > 0)
{
ret.resize(len);
- WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &ret[0], len, nullptr, nullptr);
- ret.pop_back();
+ WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.length()), &ret[0], len,
+ nullptr, nullptr);
}
return ret;
}
-std::wstring utf8_to_wstr(const char *str)
+std::wstring utf8_to_wstr(std::string_view str)
{
std::wstring ret;
- int len = MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0);
+ int len{MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.length()), nullptr,
+ 0)};
if(len > 0)
{
ret.resize(len);
- MultiByteToWideChar(CP_UTF8, 0, str, -1, &ret[0], len);
- ret.pop_back();
+ MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.length()), &ret[0], len);
}
return ret;
@@ -43,21 +44,25 @@ std::wstring utf8_to_wstr(const char *str)
namespace al {
-al::optional<std::string> getenv(const char *envname)
+std::optional<std::string> getenv(const char *envname)
{
+#ifdef _GAMING_XBOX
+ const char *str{::getenv(envname)};
+#else
const char *str{std::getenv(envname)};
+#endif
if(str && str[0] != '\0')
return str;
- return al::nullopt;
+ return std::nullopt;
}
#ifdef _WIN32
-al::optional<std::wstring> getenv(const WCHAR *envname)
+std::optional<std::wstring> getenv(const WCHAR *envname)
{
const WCHAR *str{_wgetenv(envname)};
if(str && str[0] != L'\0')
return str;
- return al::nullopt;
+ return std::nullopt;
}
#endif
diff --git a/common/strutils.h b/common/strutils.h
index 0c7a0e22..7eee0c1d 100644
--- a/common/strutils.h
+++ b/common/strutils.h
@@ -1,22 +1,22 @@
#ifndef AL_STRUTILS_H
#define AL_STRUTILS_H
+#include <optional>
#include <string>
-#include "aloptional.h"
-
#ifdef _WIN32
+#include <string_view>
#include <wchar.h>
-std::string wstr_to_utf8(const wchar_t *wstr);
-std::wstring utf8_to_wstr(const char *str);
+std::string wstr_to_utf8(std::wstring_view wstr);
+std::wstring utf8_to_wstr(std::string_view str);
#endif
namespace al {
-al::optional<std::string> getenv(const char *envname);
+std::optional<std::string> getenv(const char *envname);
#ifdef _WIN32
-al::optional<std::wstring> getenv(const wchar_t *envname);
+std::optional<std::wstring> getenv(const wchar_t *envname);
#endif
} // namespace al
diff --git a/common/win_main_utf8.h b/common/win_main_utf8.h
index 077af533..81734242 100644
--- a/common/win_main_utf8.h
+++ b/common/win_main_utf8.h
@@ -20,14 +20,20 @@
#define STATIC_CAST(...) static_cast<__VA_ARGS__>
#define REINTERPRET_CAST(...) reinterpret_cast<__VA_ARGS__>
+#define MAYBE_UNUSED [[maybe_unused]]
#else
#define STATIC_CAST(...) (__VA_ARGS__)
#define REINTERPRET_CAST(...) (__VA_ARGS__)
+#ifdef __GNUC__
+#define MAYBE_UNUSED __attribute__((__unused__))
+#else
+#define MAYBE_UNUSED
+#endif
#endif
-static FILE *my_fopen(const char *fname, const char *mode)
+MAYBE_UNUSED static FILE *my_fopen(const char *fname, const char *mode)
{
wchar_t *wname=NULL, *wmode=NULL;
int namelen, modelen;
@@ -44,10 +50,11 @@ static FILE *my_fopen(const char *fname, const char *mode)
}
#ifdef __cplusplus
- auto strbuf = std::make_unique<wchar_t[]>(static_cast<size_t>(namelen)+modelen);
+ auto strbuf = std::make_unique<wchar_t[]>(static_cast<size_t>(namelen) +
+ static_cast<size_t>(modelen));
wname = strbuf.get();
#else
- wname = (wchar_t*)calloc(sizeof(wchar_t), (size_t)namelen + modelen);
+ wname = (wchar_t*)calloc(sizeof(wchar_t), (size_t)namelen + (size_t)modelen);
#endif
wmode = wname + namelen;
MultiByteToWideChar(CP_UTF8, 0, fname, -1, wname, namelen);
diff --git a/config.h.in b/config.h.in
index 477d8c77..20df5b46 100644
--- a/config.h.in
+++ b/config.h.in
@@ -1,3 +1,6 @@
+/* Define the alignment attribute for externally callable functions. */
+#define FORCE_ALIGN @ALSOFT_FORCE_ALIGN@
+
/* Define if deprecated EAX extensions are enabled */
#cmakedefine ALSOFT_EAX
@@ -117,3 +120,6 @@
/* Define the installation data directory */
#cmakedefine ALSOFT_INSTALL_DATADIR "@ALSOFT_INSTALL_DATADIR@"
+
+/* Define whether build alsoft for winuwp */
+#cmakedefine ALSOFT_UWP
diff --git a/core/ambdec.cpp b/core/ambdec.cpp
index 8ca182c4..f98e1098 100644
--- a/core/ambdec.cpp
+++ b/core/ambdec.cpp
@@ -47,9 +47,9 @@ enum class ReaderScope {
#else
[[gnu::format(printf,2,3)]]
#endif
-al::optional<std::string> make_error(size_t linenum, const char *fmt, ...)
+std::optional<std::string> make_error(size_t linenum, const char *fmt, ...)
{
- al::optional<std::string> ret;
+ std::optional<std::string> ret;
auto &str = ret.emplace();
str.resize(256);
@@ -77,7 +77,7 @@ al::optional<std::string> make_error(size_t linenum, const char *fmt, ...)
AmbDecConf::~AmbDecConf() = default;
-al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
+std::optional<std::string> AmbDecConf::load(const char *fname) noexcept
{
al::ifstream f{fname};
if(!f.is_open())
@@ -139,7 +139,7 @@ al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
{
--toread;
istr >> value;
- if(curgain < al::size(gains))
+ if(curgain < std::size(gains))
gains[curgain++] = value;
}
}
@@ -291,7 +291,7 @@ al::optional<std::string> AmbDecConf::load(const char *fname) noexcept
if(CoeffScale == AmbDecScale::Unset)
return make_error(linenum, "No coefficient scaling defined");
- return al::nullopt;
+ return std::nullopt;
}
else
return make_error(linenum, "Unexpected command: %s", command.c_str());
diff --git a/core/ambdec.h b/core/ambdec.h
index 7f739781..19f68697 100644
--- a/core/ambdec.h
+++ b/core/ambdec.h
@@ -3,9 +3,9 @@
#include <array>
#include <memory>
+#include <optional>
#include <string>
-#include "aloptional.h"
#include "core/ambidefs.h"
/* Helpers to read .ambdec configuration files. */
@@ -49,7 +49,7 @@ struct AmbDecConf {
~AmbDecConf();
- al::optional<std::string> load(const char *fname) noexcept;
+ std::optional<std::string> load(const char *fname) noexcept;
};
#endif /* CORE_AMBDEC_H */
diff --git a/core/ambidefs.cpp b/core/ambidefs.cpp
index 70d6f356..2389ce6b 100644
--- a/core/ambidefs.cpp
+++ b/core/ambidefs.cpp
@@ -21,25 +21,25 @@ constexpr auto inv_sqrt3f = static_cast<float>(1.0/al::numbers::sqrt3);
* will result in that channel being subsequently decoded for second-order as
* if it was a first-order decoder for that same speaker array.
*/
-constexpr std::array<std::array<float,MaxAmbiOrder+1>,MaxAmbiOrder+1> HFScales{{
- {{ 4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f }},
- {{ 4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f }},
- {{ 2.981423970e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f }},
- {{ 2.359168820e+00f, 2.031565936e+00f, 1.444598386e+00f, 7.189495850e-01f }},
- /* 1.947005434e+00f, 1.764337084e+00f, 1.424707344e+00f, 9.755104127e-01f, 4.784482742e-01f */
-}};
+constexpr std::array HFScales{
+ std::array{4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f},
+ std::array{4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f},
+ std::array{2.981423970e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f},
+ std::array{2.359168820e+00f, 2.031565936e+00f, 1.444598386e+00f, 7.189495850e-01f},
+ /*std::array{1.947005434e+00f, 1.764337084e+00f, 1.424707344e+00f, 9.755104127e-01f, 4.784482742e-01f}, */
+};
/* Same as above, but using a 10-point horizontal-only speaker array. Should
* only be used when the device is mixing in 2D B-Format for horizontal-only
* output.
*/
-constexpr std::array<std::array<float,MaxAmbiOrder+1>,MaxAmbiOrder+1> HFScales2D{{
- {{ 2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f }},
- {{ 2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f }},
- {{ 1.825741858e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f }},
- {{ 1.581138830e+00f, 1.460781803e+00f, 1.118033989e+00f, 6.050756345e-01f }},
- /* 1.414213562e+00f, 1.344997024e+00f, 1.144122806e+00f, 8.312538756e-01f, 4.370160244e-01f */
-}};
+constexpr std::array HFScales2D{
+ std::array{2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f},
+ std::array{2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f},
+ std::array{1.825741858e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f},
+ std::array{1.581138830e+00f, 1.460781803e+00f, 1.118033989e+00f, 6.050756345e-01f},
+ /*std::array{1.414213562e+00f, 1.344997024e+00f, 1.144122806e+00f, 8.312538756e-01f, 4.370160244e-01f}, */
+};
/* This calculates a first-order "upsampler" matrix. It combines a first-order
@@ -49,17 +49,17 @@ constexpr std::array<std::array<float,MaxAmbiOrder+1>,MaxAmbiOrder+1> HFScales2D
* signal. While not perfect, this should accurately encode a lower-order
* signal into a higher-order signal.
*/
-constexpr std::array<std::array<float,4>,8> FirstOrderDecoder{{
- {{ 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, }},
- {{ 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, }},
- {{ 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, }},
- {{ 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, }},
- {{ 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, }},
- {{ 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, }},
- {{ 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, }},
- {{ 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,8> FirstOrderEncoder{{
+constexpr std::array FirstOrderDecoder{
+ std::array{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f},
+ std::array{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f},
+ std::array{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f},
+ std::array{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f},
+ std::array{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f},
+ std::array{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f},
+ std::array{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f},
+ std::array{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f},
+};
+constexpr std::array FirstOrderEncoder{
CalcAmbiCoeffs( inv_sqrt3f, inv_sqrt3f, inv_sqrt3f),
CalcAmbiCoeffs( inv_sqrt3f, inv_sqrt3f, -inv_sqrt3f),
CalcAmbiCoeffs(-inv_sqrt3f, inv_sqrt3f, inv_sqrt3f),
@@ -68,25 +68,25 @@ constexpr std::array<AmbiChannelFloatArray,8> FirstOrderEncoder{{
CalcAmbiCoeffs( inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f),
CalcAmbiCoeffs(-inv_sqrt3f, -inv_sqrt3f, inv_sqrt3f),
CalcAmbiCoeffs(-inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f),
-}};
+};
static_assert(FirstOrderDecoder.size() == FirstOrderEncoder.size(), "First-order mismatch");
/* This calculates a 2D first-order "upsampler" matrix. Same as the first-order
* matrix, just using a more optimized speaker array for horizontal-only
* content.
*/
-constexpr std::array<std::array<float,4>,4> FirstOrder2DDecoder{{
- {{ 2.500000000e-01f, 2.041241452e-01f, 0.0f, 2.041241452e-01f, }},
- {{ 2.500000000e-01f, 2.041241452e-01f, 0.0f, -2.041241452e-01f, }},
- {{ 2.500000000e-01f, -2.041241452e-01f, 0.0f, 2.041241452e-01f, }},
- {{ 2.500000000e-01f, -2.041241452e-01f, 0.0f, -2.041241452e-01f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,4> FirstOrder2DEncoder{{
+constexpr std::array FirstOrder2DDecoder{
+ std::array{2.500000000e-01f, 2.041241452e-01f, 0.0f, 2.041241452e-01f},
+ std::array{2.500000000e-01f, 2.041241452e-01f, 0.0f, -2.041241452e-01f},
+ std::array{2.500000000e-01f, -2.041241452e-01f, 0.0f, 2.041241452e-01f},
+ std::array{2.500000000e-01f, -2.041241452e-01f, 0.0f, -2.041241452e-01f},
+};
+constexpr std::array FirstOrder2DEncoder{
CalcAmbiCoeffs( inv_sqrt2f, 0.0f, inv_sqrt2f),
CalcAmbiCoeffs( inv_sqrt2f, 0.0f, -inv_sqrt2f),
CalcAmbiCoeffs(-inv_sqrt2f, 0.0f, inv_sqrt2f),
CalcAmbiCoeffs(-inv_sqrt2f, 0.0f, -inv_sqrt2f),
-}};
+};
static_assert(FirstOrder2DDecoder.size() == FirstOrder2DEncoder.size(), "First-order 2D mismatch");
@@ -94,21 +94,21 @@ static_assert(FirstOrder2DDecoder.size() == FirstOrder2DEncoder.size(), "First-o
* matrix, just using a slightly more dense speaker array suitable for second-
* order content.
*/
-constexpr std::array<std::array<float,9>,12> SecondOrderDecoder{{
- {{ 8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f, }},
- {{ 8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }},
- {{ 8.333333333e-02f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }},
- {{ 8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f, }},
- {{ 8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }},
- {{ 8.333333333e-02f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }},
- {{ 8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f, }},
- {{ 8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }},
- {{ 8.333333333e-02f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }},
- {{ 8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f, }},
- {{ 8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }},
- {{ 8.333333333e-02f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,12> SecondOrderEncoder{{
+constexpr std::array SecondOrderDecoder{
+ std::array{8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f},
+ std::array{8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f},
+ std::array{8.333333333e-02f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f},
+ std::array{8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f},
+ std::array{8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f},
+ std::array{8.333333333e-02f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f},
+ std::array{8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f},
+ std::array{8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f},
+ std::array{8.333333333e-02f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f},
+ std::array{8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f},
+ std::array{8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f},
+ std::array{8.333333333e-02f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f},
+};
+constexpr std::array SecondOrderEncoder{
CalcAmbiCoeffs( 0.000000000e+00f, -5.257311121e-01f, 8.506508084e-01f),
CalcAmbiCoeffs(-8.506508084e-01f, 0.000000000e+00f, 5.257311121e-01f),
CalcAmbiCoeffs(-5.257311121e-01f, 8.506508084e-01f, 0.000000000e+00f),
@@ -121,29 +121,29 @@ constexpr std::array<AmbiChannelFloatArray,12> SecondOrderEncoder{{
CalcAmbiCoeffs( 0.000000000e+00f, 5.257311121e-01f, -8.506508084e-01f),
CalcAmbiCoeffs( 8.506508084e-01f, 0.000000000e+00f, 5.257311121e-01f),
CalcAmbiCoeffs(-5.257311121e-01f, -8.506508084e-01f, 0.000000000e+00f),
-}};
+};
static_assert(SecondOrderDecoder.size() == SecondOrderEncoder.size(), "Second-order mismatch");
/* This calculates a 2D second-order "upsampler" matrix. Same as the second-
* order matrix, just using a more optimized speaker array for horizontal-only
* content.
*/
-constexpr std::array<std::array<float,9>,6> SecondOrder2DDecoder{{
- {{ 1.666666667e-01f, -9.622504486e-02f, 0.0f, 1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }},
- {{ 1.666666667e-01f, -1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f, }},
- {{ 1.666666667e-01f, -9.622504486e-02f, 0.0f, -1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }},
- {{ 1.666666667e-01f, 9.622504486e-02f, 0.0f, -1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }},
- {{ 1.666666667e-01f, 1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f, }},
- {{ 1.666666667e-01f, 9.622504486e-02f, 0.0f, 1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,6> SecondOrder2DEncoder{{
+constexpr std::array SecondOrder2DDecoder{
+ std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, 1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f},
+ std::array{1.666666667e-01f, -1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f},
+ std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, -1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f},
+ std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, -1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f},
+ std::array{1.666666667e-01f, 1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f},
+ std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, 1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f},
+};
+constexpr std::array SecondOrder2DEncoder{
CalcAmbiCoeffs(-0.50000000000f, 0.0f, 0.86602540379f),
CalcAmbiCoeffs(-1.00000000000f, 0.0f, 0.00000000000f),
CalcAmbiCoeffs(-0.50000000000f, 0.0f, -0.86602540379f),
CalcAmbiCoeffs( 0.50000000000f, 0.0f, -0.86602540379f),
CalcAmbiCoeffs( 1.00000000000f, 0.0f, 0.00000000000f),
CalcAmbiCoeffs( 0.50000000000f, 0.0f, 0.86602540379f),
-}};
+};
static_assert(SecondOrder2DDecoder.size() == SecondOrder2DEncoder.size(),
"Second-order 2D mismatch");
@@ -152,29 +152,29 @@ static_assert(SecondOrder2DDecoder.size() == SecondOrder2DEncoder.size(),
* matrix, just using a more dense speaker array suitable for third-order
* content.
*/
-constexpr std::array<std::array<float,16>,20> ThirdOrderDecoder{{
- {{ 5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f, }},
- {{ 5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f, }},
- {{ 5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f, }},
- {{ 5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f, }},
- {{ 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f, }},
- {{ 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f, }},
- {{ 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f, }},
- {{ 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f, }},
- {{ 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, -6.779013272e-02f, 1.659481923e-01f, 4.797944664e-02f, }},
- {{ 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, 6.779013272e-02f, 1.659481923e-01f, -4.797944664e-02f, }},
- {{ 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, -6.779013272e-02f, -1.659481923e-01f, 4.797944664e-02f, }},
- {{ 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, 6.779013272e-02f, -1.659481923e-01f, -4.797944664e-02f, }},
- {{ 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f, }},
- {{ 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f, }},
- {{ 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f, }},
- {{ 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f, }},
- {{ 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f, }},
- {{ 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f, }},
- {{ 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f, }},
- {{ 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,20> ThirdOrderEncoder{{
+constexpr std::array ThirdOrderDecoder{
+ std::array{5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f},
+ std::array{5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f},
+ std::array{5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f},
+ std::array{5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f},
+ std::array{5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f},
+ std::array{5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f},
+ std::array{5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f},
+ std::array{5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f},
+ std::array{5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, -6.779013272e-02f, 1.659481923e-01f, 4.797944664e-02f},
+ std::array{5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, 6.779013272e-02f, 1.659481923e-01f, -4.797944664e-02f},
+ std::array{5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, -6.779013272e-02f, -1.659481923e-01f, 4.797944664e-02f},
+ std::array{5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, 6.779013272e-02f, -1.659481923e-01f, -4.797944664e-02f},
+ std::array{5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f},
+ std::array{5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f},
+ std::array{5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f},
+ std::array{5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f},
+ std::array{5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f},
+ std::array{5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f},
+ std::array{5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f},
+ std::array{5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f},
+};
+constexpr std::array ThirdOrderEncoder{
CalcAmbiCoeffs( 0.35682208976f, 0.93417235897f, 0.00000000000f),
CalcAmbiCoeffs(-0.35682208976f, 0.93417235897f, 0.00000000000f),
CalcAmbiCoeffs( 0.35682208976f, -0.93417235897f, 0.00000000000f),
@@ -195,24 +195,24 @@ constexpr std::array<AmbiChannelFloatArray,20> ThirdOrderEncoder{{
CalcAmbiCoeffs( inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f),
CalcAmbiCoeffs( -inv_sqrt3f, -inv_sqrt3f, inv_sqrt3f),
CalcAmbiCoeffs( -inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f),
-}};
+};
static_assert(ThirdOrderDecoder.size() == ThirdOrderEncoder.size(), "Third-order mismatch");
/* This calculates a 2D third-order "upsampler" matrix. Same as the third-order
* matrix, just using a more optimized speaker array for horizontal-only
* content.
*/
-constexpr std::array<std::array<float,16>,8> ThirdOrder2DDecoder{{
- {{ 1.250000000e-01f, -5.523559567e-02f, 0.0f, 1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f, }},
- {{ 1.250000000e-01f, -1.333505242e-01f, 0.0f, 5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f, }},
- {{ 1.250000000e-01f, -1.333505242e-01f, 0.0f, -5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f, }},
- {{ 1.250000000e-01f, -5.523559567e-02f, 0.0f, -1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f, }},
- {{ 1.250000000e-01f, 5.523559567e-02f, 0.0f, -1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f, }},
- {{ 1.250000000e-01f, 1.333505242e-01f, 0.0f, -5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f, }},
- {{ 1.250000000e-01f, 1.333505242e-01f, 0.0f, 5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f, }},
- {{ 1.250000000e-01f, 5.523559567e-02f, 0.0f, 1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,8> ThirdOrder2DEncoder{{
+constexpr std::array ThirdOrder2DDecoder{
+ std::array{1.250000000e-01f, -5.523559567e-02f, 0.0f, 1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f},
+ std::array{1.250000000e-01f, -1.333505242e-01f, 0.0f, 5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f},
+ std::array{1.250000000e-01f, -1.333505242e-01f, 0.0f, -5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f},
+ std::array{1.250000000e-01f, -5.523559567e-02f, 0.0f, -1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f},
+ std::array{1.250000000e-01f, 5.523559567e-02f, 0.0f, -1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f},
+ std::array{1.250000000e-01f, 1.333505242e-01f, 0.0f, -5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f},
+ std::array{1.250000000e-01f, 1.333505242e-01f, 0.0f, 5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f},
+ std::array{1.250000000e-01f, 5.523559567e-02f, 0.0f, 1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f},
+};
+constexpr std::array ThirdOrder2DEncoder{
CalcAmbiCoeffs(-0.38268343237f, 0.0f, 0.92387953251f),
CalcAmbiCoeffs(-0.92387953251f, 0.0f, 0.38268343237f),
CalcAmbiCoeffs(-0.92387953251f, 0.0f, -0.38268343237f),
@@ -221,7 +221,7 @@ constexpr std::array<AmbiChannelFloatArray,8> ThirdOrder2DEncoder{{
CalcAmbiCoeffs( 0.92387953251f, 0.0f, -0.38268343237f),
CalcAmbiCoeffs( 0.92387953251f, 0.0f, 0.38268343237f),
CalcAmbiCoeffs( 0.38268343237f, 0.0f, 0.92387953251f),
-}};
+};
static_assert(ThirdOrder2DDecoder.size() == ThirdOrder2DEncoder.size(), "Third-order 2D mismatch");
@@ -230,19 +230,19 @@ static_assert(ThirdOrder2DDecoder.size() == ThirdOrder2DEncoder.size(), "Third-o
* the foreseeable future. This is only necessary for mixing horizontal-only
* fourth-order content to 3D.
*/
-constexpr std::array<std::array<float,25>,10> FourthOrder2DDecoder{{
- {{ 1.000000000e-01f, 3.568220898e-02f, 0.0f, 1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }},
- {{ 1.000000000e-01f, 9.341723590e-02f, 0.0f, 6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }},
- {{ 1.000000000e-01f, 1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, -9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f, }},
- {{ 1.000000000e-01f, 9.341723590e-02f, 0.0f, -6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }},
- {{ 1.000000000e-01f, 3.568220898e-02f, 0.0f, -1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }},
- {{ 1.000000000e-01f, -3.568220898e-02f, 0.0f, -1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }},
- {{ 1.000000000e-01f, -9.341723590e-02f, 0.0f, -6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }},
- {{ 1.000000000e-01f, -1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, 9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f, }},
- {{ 1.000000000e-01f, -9.341723590e-02f, 0.0f, 6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }},
- {{ 1.000000000e-01f, -3.568220898e-02f, 0.0f, 1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }},
-}};
-constexpr std::array<AmbiChannelFloatArray,10> FourthOrder2DEncoder{{
+constexpr std::array FourthOrder2DDecoder{
+ std::array{1.000000000e-01f, 3.568220898e-02f, 0.0f, 1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f},
+ std::array{1.000000000e-01f, 9.341723590e-02f, 0.0f, 6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f},
+ std::array{1.000000000e-01f, 1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, -9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f},
+ std::array{1.000000000e-01f, 9.341723590e-02f, 0.0f, -6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f},
+ std::array{1.000000000e-01f, 3.568220898e-02f, 0.0f, -1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f},
+ std::array{1.000000000e-01f, -3.568220898e-02f, 0.0f, -1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f},
+ std::array{1.000000000e-01f, -9.341723590e-02f, 0.0f, -6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f},
+ std::array{1.000000000e-01f, -1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, 9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f},
+ std::array{1.000000000e-01f, -9.341723590e-02f, 0.0f, 6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f},
+ std::array{1.000000000e-01f, -3.568220898e-02f, 0.0f, 1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f},
+};
+constexpr std::array FourthOrder2DEncoder{
CalcAmbiCoeffs( 3.090169944e-01f, 0.000000000e+00f, 9.510565163e-01f),
CalcAmbiCoeffs( 8.090169944e-01f, 0.000000000e+00f, 5.877852523e-01f),
CalcAmbiCoeffs( 1.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f),
@@ -253,12 +253,12 @@ constexpr std::array<AmbiChannelFloatArray,10> FourthOrder2DEncoder{{
CalcAmbiCoeffs(-1.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f),
CalcAmbiCoeffs(-8.090169944e-01f, 0.000000000e+00f, 5.877852523e-01f),
CalcAmbiCoeffs(-3.090169944e-01f, 0.000000000e+00f, 9.510565163e-01f),
-}};
+};
static_assert(FourthOrder2DDecoder.size() == FourthOrder2DEncoder.size(), "Fourth-order 2D mismatch");
template<size_t N, size_t M>
-auto CalcAmbiUpsampler(const std::array<std::array<float,N>,M> &decoder,
+constexpr auto CalcAmbiUpsampler(const std::array<std::array<float,N>,M> &decoder,
const std::array<AmbiChannelFloatArray,M> &encoder)
{
std::array<AmbiChannelFloatArray,N> res{};
@@ -279,13 +279,13 @@ auto CalcAmbiUpsampler(const std::array<std::array<float,N>,M> &decoder,
} // namespace
-const std::array<AmbiChannelFloatArray,4> AmbiScale::FirstOrderUp{CalcAmbiUpsampler(FirstOrderDecoder, FirstOrderEncoder)};
-const std::array<AmbiChannelFloatArray,4> AmbiScale::FirstOrder2DUp{CalcAmbiUpsampler(FirstOrder2DDecoder, FirstOrder2DEncoder)};
-const std::array<AmbiChannelFloatArray,9> AmbiScale::SecondOrderUp{CalcAmbiUpsampler(SecondOrderDecoder, SecondOrderEncoder)};
-const std::array<AmbiChannelFloatArray,9> AmbiScale::SecondOrder2DUp{CalcAmbiUpsampler(SecondOrder2DDecoder, SecondOrder2DEncoder)};
-const std::array<AmbiChannelFloatArray,16> AmbiScale::ThirdOrderUp{CalcAmbiUpsampler(ThirdOrderDecoder, ThirdOrderEncoder)};
-const std::array<AmbiChannelFloatArray,16> AmbiScale::ThirdOrder2DUp{CalcAmbiUpsampler(ThirdOrder2DDecoder, ThirdOrder2DEncoder)};
-const std::array<AmbiChannelFloatArray,25> AmbiScale::FourthOrder2DUp{CalcAmbiUpsampler(FourthOrder2DDecoder, FourthOrder2DEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,4> AmbiScale::FirstOrderUp{CalcAmbiUpsampler(FirstOrderDecoder, FirstOrderEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,4> AmbiScale::FirstOrder2DUp{CalcAmbiUpsampler(FirstOrder2DDecoder, FirstOrder2DEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,9> AmbiScale::SecondOrderUp{CalcAmbiUpsampler(SecondOrderDecoder, SecondOrderEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,9> AmbiScale::SecondOrder2DUp{CalcAmbiUpsampler(SecondOrder2DDecoder, SecondOrder2DEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,16> AmbiScale::ThirdOrderUp{CalcAmbiUpsampler(ThirdOrderDecoder, ThirdOrderEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,16> AmbiScale::ThirdOrder2DUp{CalcAmbiUpsampler(ThirdOrder2DDecoder, ThirdOrder2DEncoder)};
+const std::array<std::array<float,MaxAmbiChannels>,25> AmbiScale::FourthOrder2DUp{CalcAmbiUpsampler(FourthOrder2DDecoder, FourthOrder2DEncoder)};
std::array<float,MaxAmbiOrder+1> AmbiScale::GetHFOrderScales(const uint src_order,
diff --git a/core/ambidefs.h b/core/ambidefs.h
index b7d2bcd1..bea1a312 100644
--- a/core/ambidefs.h
+++ b/core/ambidefs.h
@@ -14,26 +14,26 @@ using uint = unsigned int;
* needed will be (o+1)**2, thus zero-order has 1, first-order has 4, second-
* order has 9, third-order has 16, and fourth-order has 25.
*/
-constexpr uint8_t MaxAmbiOrder{3};
+inline constexpr uint8_t MaxAmbiOrder{3};
constexpr inline size_t AmbiChannelsFromOrder(size_t order) noexcept
{ return (order+1) * (order+1); }
-constexpr size_t MaxAmbiChannels{AmbiChannelsFromOrder(MaxAmbiOrder)};
+inline constexpr size_t MaxAmbiChannels{AmbiChannelsFromOrder(MaxAmbiOrder)};
/* A bitmask of ambisonic channels for 0 to 4th order. This only specifies up
* to 4th order, which is the highest order a 32-bit mask value can specify (a
* 64-bit mask could handle up to 7th order).
*/
-constexpr uint Ambi0OrderMask{0x00000001};
-constexpr uint Ambi1OrderMask{0x0000000f};
-constexpr uint Ambi2OrderMask{0x000001ff};
-constexpr uint Ambi3OrderMask{0x0000ffff};
-constexpr uint Ambi4OrderMask{0x01ffffff};
+inline constexpr uint Ambi0OrderMask{0x00000001};
+inline constexpr uint Ambi1OrderMask{0x0000000f};
+inline constexpr uint Ambi2OrderMask{0x000001ff};
+inline constexpr uint Ambi3OrderMask{0x0000ffff};
+inline constexpr uint Ambi4OrderMask{0x01ffffff};
/* A bitmask of ambisonic channels with height information. If none of these
* channels are used/needed, there's no height (e.g. with most surround sound
* speaker setups). This is ACN ordering, with bit 0 being ACN 0, etc.
*/
-constexpr uint AmbiPeriphonicMask{0xfe7ce4};
+inline constexpr uint AmbiPeriphonicMask{0xfe7ce4};
/* The maximum number of ambisonic channels for 2D (non-periphonic)
* representation. This is 2 per each order above zero-order, plus 1 for zero-
@@ -41,77 +41,61 @@ constexpr uint AmbiPeriphonicMask{0xfe7ce4};
*/
constexpr inline size_t Ambi2DChannelsFromOrder(size_t order) noexcept
{ return order*2 + 1; }
-constexpr size_t MaxAmbi2DChannels{Ambi2DChannelsFromOrder(MaxAmbiOrder)};
+inline constexpr size_t MaxAmbi2DChannels{Ambi2DChannelsFromOrder(MaxAmbiOrder)};
/* NOTE: These are scale factors as applied to Ambisonics content. Decoder
* coefficients should be divided by these values to get proper scalings.
*/
struct AmbiScale {
- static auto& FromN3D() noexcept
- {
- static constexpr const std::array<float,MaxAmbiChannels> ret{{
- 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
- 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
- }};
- return ret;
- }
- static auto& FromSN3D() noexcept
- {
- static constexpr const std::array<float,MaxAmbiChannels> ret{{
- 1.000000000f, /* ACN 0, sqrt(1) */
- 1.732050808f, /* ACN 1, sqrt(3) */
- 1.732050808f, /* ACN 2, sqrt(3) */
- 1.732050808f, /* ACN 3, sqrt(3) */
- 2.236067978f, /* ACN 4, sqrt(5) */
- 2.236067978f, /* ACN 5, sqrt(5) */
- 2.236067978f, /* ACN 6, sqrt(5) */
- 2.236067978f, /* ACN 7, sqrt(5) */
- 2.236067978f, /* ACN 8, sqrt(5) */
- 2.645751311f, /* ACN 9, sqrt(7) */
- 2.645751311f, /* ACN 10, sqrt(7) */
- 2.645751311f, /* ACN 11, sqrt(7) */
- 2.645751311f, /* ACN 12, sqrt(7) */
- 2.645751311f, /* ACN 13, sqrt(7) */
- 2.645751311f, /* ACN 14, sqrt(7) */
- 2.645751311f, /* ACN 15, sqrt(7) */
- }};
- return ret;
- }
- static auto& FromFuMa() noexcept
- {
- static constexpr const std::array<float,MaxAmbiChannels> ret{{
- 1.414213562f, /* ACN 0 (W), sqrt(2) */
- 1.732050808f, /* ACN 1 (Y), sqrt(3) */
- 1.732050808f, /* ACN 2 (Z), sqrt(3) */
- 1.732050808f, /* ACN 3 (X), sqrt(3) */
- 1.936491673f, /* ACN 4 (V), sqrt(15)/2 */
- 1.936491673f, /* ACN 5 (T), sqrt(15)/2 */
- 2.236067978f, /* ACN 6 (R), sqrt(5) */
- 1.936491673f, /* ACN 7 (S), sqrt(15)/2 */
- 1.936491673f, /* ACN 8 (U), sqrt(15)/2 */
- 2.091650066f, /* ACN 9 (Q), sqrt(35/8) */
- 1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
- 2.231093404f, /* ACN 11 (M), sqrt(224/45) */
- 2.645751311f, /* ACN 12 (K), sqrt(7) */
- 2.231093404f, /* ACN 13 (L), sqrt(224/45) */
- 1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
- 2.091650066f, /* ACN 15 (P), sqrt(35/8) */
- }};
- return ret;
- }
- static auto& FromUHJ() noexcept
- {
- static constexpr const std::array<float,MaxAmbiChannels> ret{{
- 1.000000000f, /* ACN 0 (W), sqrt(1) */
- 1.224744871f, /* ACN 1 (Y), sqrt(3/2) */
- 1.224744871f, /* ACN 2 (Z), sqrt(3/2) */
- 1.224744871f, /* ACN 3 (X), sqrt(3/2) */
- /* Higher orders not relevant for UHJ. */
- 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
- }};
- return ret;
- }
+ static inline constexpr std::array<float,MaxAmbiChannels> FromN3D{{
+ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f
+ }};
+ static inline constexpr std::array<float,MaxAmbiChannels> FromSN3D{{
+ 1.000000000f, /* ACN 0, sqrt(1) */
+ 1.732050808f, /* ACN 1, sqrt(3) */
+ 1.732050808f, /* ACN 2, sqrt(3) */
+ 1.732050808f, /* ACN 3, sqrt(3) */
+ 2.236067978f, /* ACN 4, sqrt(5) */
+ 2.236067978f, /* ACN 5, sqrt(5) */
+ 2.236067978f, /* ACN 6, sqrt(5) */
+ 2.236067978f, /* ACN 7, sqrt(5) */
+ 2.236067978f, /* ACN 8, sqrt(5) */
+ 2.645751311f, /* ACN 9, sqrt(7) */
+ 2.645751311f, /* ACN 10, sqrt(7) */
+ 2.645751311f, /* ACN 11, sqrt(7) */
+ 2.645751311f, /* ACN 12, sqrt(7) */
+ 2.645751311f, /* ACN 13, sqrt(7) */
+ 2.645751311f, /* ACN 14, sqrt(7) */
+ 2.645751311f, /* ACN 15, sqrt(7) */
+ }};
+ static inline constexpr std::array<float,MaxAmbiChannels> FromFuMa{{
+ 1.414213562f, /* ACN 0 (W), sqrt(2) */
+ 1.732050808f, /* ACN 1 (Y), sqrt(3) */
+ 1.732050808f, /* ACN 2 (Z), sqrt(3) */
+ 1.732050808f, /* ACN 3 (X), sqrt(3) */
+ 1.936491673f, /* ACN 4 (V), sqrt(15)/2 */
+ 1.936491673f, /* ACN 5 (T), sqrt(15)/2 */
+ 2.236067978f, /* ACN 6 (R), sqrt(5) */
+ 1.936491673f, /* ACN 7 (S), sqrt(15)/2 */
+ 1.936491673f, /* ACN 8 (U), sqrt(15)/2 */
+ 2.091650066f, /* ACN 9 (Q), sqrt(35/8) */
+ 1.972026594f, /* ACN 10 (O), sqrt(35)/3 */
+ 2.231093404f, /* ACN 11 (M), sqrt(224/45) */
+ 2.645751311f, /* ACN 12 (K), sqrt(7) */
+ 2.231093404f, /* ACN 13 (L), sqrt(224/45) */
+ 1.972026594f, /* ACN 14 (N), sqrt(35)/3 */
+ 2.091650066f, /* ACN 15 (P), sqrt(35/8) */
+ }};
+ static inline constexpr std::array<float,MaxAmbiChannels> FromUHJ{{
+ 1.000000000f, /* ACN 0 (W), sqrt(1) */
+ 1.224744871f, /* ACN 1 (Y), sqrt(3/2) */
+ 1.224744871f, /* ACN 2 (Z), sqrt(3/2) */
+ 1.224744871f, /* ACN 3 (X), sqrt(3/2) */
+ /* Higher orders not relevant for UHJ. */
+ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
+ }};
/* Retrieves per-order HF scaling factors for "upsampling" ambisonic data. */
static std::array<float,MaxAmbiOrder+1> GetHFOrderScales(const uint src_order,
@@ -127,72 +111,49 @@ struct AmbiScale {
};
struct AmbiIndex {
- static auto& FromFuMa() noexcept
- {
- static constexpr const std::array<uint8_t,MaxAmbiChannels> ret{{
- 0, /* W */
- 3, /* X */
- 1, /* Y */
- 2, /* Z */
- 6, /* R */
- 7, /* S */
- 5, /* T */
- 8, /* U */
- 4, /* V */
- 12, /* K */
- 13, /* L */
- 11, /* M */
- 14, /* N */
- 10, /* O */
- 15, /* P */
- 9, /* Q */
- }};
- return ret;
- }
- static auto& FromFuMa2D() noexcept
- {
- static constexpr const std::array<uint8_t,MaxAmbi2DChannels> ret{{
- 0, /* W */
- 3, /* X */
- 1, /* Y */
- 8, /* U */
- 4, /* V */
- 15, /* P */
- 9, /* Q */
- }};
- return ret;
- }
-
- static auto& FromACN() noexcept
- {
- static constexpr const std::array<uint8_t,MaxAmbiChannels> ret{{
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15
- }};
- return ret;
- }
- static auto& FromACN2D() noexcept
- {
- static constexpr const std::array<uint8_t,MaxAmbi2DChannels> ret{{
- 0, 1,3, 4,8, 9,15
- }};
- return ret;
- }
-
- static auto& OrderFromChannel() noexcept
- {
- static constexpr const std::array<uint8_t,MaxAmbiChannels> ret{{
- 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3,
- }};
- return ret;
- }
- static auto& OrderFrom2DChannel() noexcept
- {
- static constexpr const std::array<uint8_t,MaxAmbi2DChannels> ret{{
- 0, 1,1, 2,2, 3,3,
- }};
- return ret;
- }
+ static inline constexpr std::array<uint8_t,MaxAmbiChannels> FromFuMa{{
+ 0, /* W */
+ 3, /* X */
+ 1, /* Y */
+ 2, /* Z */
+ 6, /* R */
+ 7, /* S */
+ 5, /* T */
+ 8, /* U */
+ 4, /* V */
+ 12, /* K */
+ 13, /* L */
+ 11, /* M */
+ 14, /* N */
+ 10, /* O */
+ 15, /* P */
+ 9, /* Q */
+ }};
+ static inline constexpr std::array<uint8_t,MaxAmbi2DChannels> FromFuMa2D{{
+ 0, /* W */
+ 3, /* X */
+ 1, /* Y */
+ 8, /* U */
+ 4, /* V */
+ 15, /* P */
+ 9, /* Q */
+ }};
+
+ static inline constexpr std::array<uint8_t,MaxAmbiChannels> FromACN{{
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15
+ }};
+ static inline constexpr std::array<uint8_t,MaxAmbi2DChannels> FromACN2D{{
+ 0, 1,3, 4,8, 9,15
+ }};
+
+
+ static inline constexpr std::array<uint8_t,MaxAmbiChannels> OrderFromChannel{{
+ 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3,
+ }};
+ static inline constexpr std::array<uint8_t,MaxAmbi2DChannels> OrderFrom2DChannel{{
+ 0, 1,1, 2,2, 3,3,
+ }};
};
diff --git a/core/async_event.h b/core/async_event.h
index 5a2f5f91..f1ca0c7b 100644
--- a/core/async_event.h
+++ b/core/async_event.h
@@ -1,6 +1,9 @@
#ifndef CORE_EVENT_H
#define CORE_EVENT_H
+#include <stdint.h>
+#include <variant>
+
#include "almalloc.h"
struct EffectState;
@@ -8,48 +11,53 @@ struct EffectState;
using uint = unsigned int;
-struct AsyncEvent {
- enum : uint {
- /* User event types. */
- SourceStateChange,
- BufferCompleted,
- Disconnected,
- UserEventCount,
-
- /* Internal events, always processed. */
- ReleaseEffectState = 128,
-
- /* End event thread processing. */
- KillThread,
- };
-
- enum class SrcState {
- Reset,
- Stop,
- Play,
- Pause
- };
-
- const uint EnumType;
- union {
- char dummy;
- struct {
- uint id;
- SrcState state;
- } srcstate;
- struct {
- uint id;
- uint count;
- } bufcomp;
- struct {
- char msg[244];
- } disconnect;
- EffectState *mEffectState;
- } u{};
-
- constexpr AsyncEvent(uint type) noexcept : EnumType{type} { }
-
- DISABLE_ALLOC()
+enum class AsyncEnableBits : uint8_t {
+ SourceState,
+ BufferCompleted,
+ Disconnected,
+ Count
+};
+
+
+enum class AsyncSrcState : uint8_t {
+ Reset,
+ Stop,
+ Play,
+ Pause
+};
+
+using AsyncKillThread = std::monostate;
+
+struct AsyncSourceStateEvent {
+ uint mId;
+ AsyncSrcState mState;
};
+struct AsyncBufferCompleteEvent {
+ uint mId;
+ uint mCount;
+};
+
+struct AsyncDisconnectEvent {
+ char msg[244];
+};
+
+struct AsyncEffectReleaseEvent {
+ EffectState *mEffectState;
+};
+
+using AsyncEvent = std::variant<AsyncKillThread,
+ AsyncSourceStateEvent,
+ AsyncBufferCompleteEvent,
+ AsyncEffectReleaseEvent,
+ AsyncDisconnectEvent>;
+
+template<typename T, typename ...Args>
+auto &InitAsyncEvent(std::byte *evtbuf, Args&& ...args)
+{
+ auto *evt = al::construct_at(reinterpret_cast<AsyncEvent*>(evtbuf), std::in_place_type<T>,
+ std::forward<Args>(args)...);
+ return std::get<T>(*evt);
+}
+
#endif
diff --git a/core/bformatdec.cpp b/core/bformatdec.cpp
index 129b9976..a308e185 100644
--- a/core/bformatdec.cpp
+++ b/core/bformatdec.cpp
@@ -16,33 +16,45 @@
#include "opthelpers.h"
+namespace {
+
+template<typename... Ts>
+struct overloaded : Ts... { using Ts::operator()...; };
+
+template<typename... Ts>
+overloaded(Ts...) -> overloaded<Ts...>;
+
+} // namespace
+
BFormatDec::BFormatDec(const size_t inchans, const al::span<const ChannelDec> coeffs,
const al::span<const ChannelDec> coeffslf, const float xover_f0norm,
std::unique_ptr<FrontStablizer> stablizer)
- : mStablizer{std::move(stablizer)}, mDualBand{!coeffslf.empty()}, mChannelDec{inchans}
+ : mStablizer{std::move(stablizer)}
{
- if(!mDualBand)
+ if(coeffslf.empty())
{
- for(size_t j{0};j < mChannelDec.size();++j)
+ auto &decoder = mChannelDec.emplace<std::vector<ChannelDecoderSingle>>(inchans);
+ for(size_t j{0};j < decoder.size();++j)
{
- float *outcoeffs{mChannelDec[j].mGains.Single};
+ float *outcoeffs{decoder[j].mGains};
for(const ChannelDec &incoeffs : coeffs)
*(outcoeffs++) = incoeffs[j];
}
}
else
{
- mChannelDec[0].mXOver.init(xover_f0norm);
- for(size_t j{1};j < mChannelDec.size();++j)
- mChannelDec[j].mXOver = mChannelDec[0].mXOver;
+ auto &decoder = mChannelDec.emplace<std::vector<ChannelDecoderDual>>(inchans);
+ decoder[0].mXOver.init(xover_f0norm);
+ for(size_t j{1};j < decoder.size();++j)
+ decoder[j].mXOver = decoder[0].mXOver;
- for(size_t j{0};j < mChannelDec.size();++j)
+ for(size_t j{0};j < decoder.size();++j)
{
- float *outcoeffs{mChannelDec[j].mGains.Dual[sHFBand]};
+ float *outcoeffs{decoder[j].mGains[sHFBand]};
for(const ChannelDec &incoeffs : coeffs)
*(outcoeffs++) = incoeffs[j];
- outcoeffs = mChannelDec[j].mGains.Dual[sLFBand];
+ outcoeffs = decoder[j].mGains[sLFBand];
for(const ChannelDec &incoeffs : coeffslf)
*(outcoeffs++) = incoeffs[j];
}
@@ -55,30 +67,32 @@ void BFormatDec::process(const al::span<FloatBufferLine> OutBuffer,
{
ASSUME(SamplesToDo > 0);
- if(mDualBand)
+ auto decode_dualband = [=](std::vector<ChannelDecoderDual> &decoder)
{
+ auto *input = InSamples;
const al::span<float> hfSamples{mSamples[sHFBand].data(), SamplesToDo};
const al::span<float> lfSamples{mSamples[sLFBand].data(), SamplesToDo};
- for(auto &chandec : mChannelDec)
+ for(auto &chandec : decoder)
{
- chandec.mXOver.process({InSamples->data(), SamplesToDo}, hfSamples.data(),
+ chandec.mXOver.process({input->data(), SamplesToDo}, hfSamples.data(),
lfSamples.data());
- MixSamples(hfSamples, OutBuffer, chandec.mGains.Dual[sHFBand],
- chandec.mGains.Dual[sHFBand], 0, 0);
- MixSamples(lfSamples, OutBuffer, chandec.mGains.Dual[sLFBand],
- chandec.mGains.Dual[sLFBand], 0, 0);
- ++InSamples;
+ MixSamples(hfSamples, OutBuffer, chandec.mGains[sHFBand], chandec.mGains[sHFBand],0,0);
+ MixSamples(lfSamples, OutBuffer, chandec.mGains[sLFBand], chandec.mGains[sLFBand],0,0);
+ ++input;
}
- }
- else
+ };
+ auto decode_singleband = [=](std::vector<ChannelDecoderSingle> &decoder)
{
- for(auto &chandec : mChannelDec)
+ auto *input = InSamples;
+ for(auto &chandec : decoder)
{
- MixSamples({InSamples->data(), SamplesToDo}, OutBuffer, chandec.mGains.Single,
- chandec.mGains.Single, 0, 0);
- ++InSamples;
+ MixSamples({input->data(), SamplesToDo}, OutBuffer, chandec.mGains, chandec.mGains,
+ 0, 0);
+ ++input;
}
- }
+ };
+
+ std::visit(overloaded{decode_dualband, decode_singleband}, mChannelDec);
}
void BFormatDec::processStablize(const al::span<FloatBufferLine> OutBuffer,
diff --git a/core/bformatdec.h b/core/bformatdec.h
index 7a27a5a4..3bb7f544 100644
--- a/core/bformatdec.h
+++ b/core/bformatdec.h
@@ -4,6 +4,8 @@
#include <array>
#include <cstddef>
#include <memory>
+#include <variant>
+#include <vector>
#include "almalloc.h"
#include "alspan.h"
@@ -11,7 +13,6 @@
#include "bufferline.h"
#include "devformat.h"
#include "filters/splitter.h"
-#include "vector.h"
struct FrontStablizer;
@@ -23,27 +24,20 @@ class BFormatDec {
static constexpr size_t sLFBand{1};
static constexpr size_t sNumBands{2};
- struct ChannelDecoder {
- union MatrixU {
- float Dual[sNumBands][MAX_OUTPUT_CHANNELS];
- float Single[MAX_OUTPUT_CHANNELS];
- } mGains{};
+ struct ChannelDecoderSingle {
+ float mGains[MAX_OUTPUT_CHANNELS];
+ };
- /* NOTE: BandSplitter filter is unused with single-band decoding. */
+ struct ChannelDecoderDual {
BandSplitter mXOver;
+ float mGains[sNumBands][MAX_OUTPUT_CHANNELS];
};
alignas(16) std::array<FloatBufferLine,2> mSamples;
const std::unique_ptr<FrontStablizer> mStablizer;
- const bool mDualBand{false};
-
- /* TODO: This should ideally be a FlexArray, since ChannelDecoder is rather
- * small and only a few are needed (3, 4, 5, 7, typically). But that can
- * only be used in a standard layout struct, and a std::unique_ptr member
- * (mStablizer) causes GCC and Clang to warn it's not.
- */
- al::vector<ChannelDecoder> mChannelDec;
+
+ std::variant<std::vector<ChannelDecoderSingle>,std::vector<ChannelDecoderDual>> mChannelDec;
public:
BFormatDec(const size_t inchans, const al::span<const ChannelDec> coeffs,
diff --git a/core/bsinc_tables.cpp b/core/bsinc_tables.cpp
index 693645f4..41102e9a 100644
--- a/core/bsinc_tables.cpp
+++ b/core/bsinc_tables.cpp
@@ -7,48 +7,50 @@
#include <cmath>
#include <limits>
#include <memory>
+#include <stddef.h>
#include <stdexcept>
#include "alnumbers.h"
-#include "core/mixer/defs.h"
+#include "alnumeric.h"
+#include "bsinc_defs.h"
+#include "resampler_limits.h"
namespace {
using uint = unsigned int;
+#if __cpp_lib_math_special_functions >= 201603L
+using std::cyl_bessel_i;
-/* This is the normalized cardinal sine (sinc) function.
- *
- * sinc(x) = { 1, x = 0
- * { sin(pi x) / (pi x), otherwise.
- */
-constexpr double Sinc(const double x)
-{
- constexpr double epsilon{std::numeric_limits<double>::epsilon()};
- if(!(x > epsilon || x < -epsilon))
- return 1.0;
- return std::sin(al::numbers::pi*x) / (al::numbers::pi*x);
-}
+#else
/* The zero-order modified Bessel function of the first kind, used for the
* Kaiser window.
*
* I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k)
* = sum_{k=0}^inf ((x / 2)^k / k!)^2
+ *
+ * This implementation only handles nu = 0, and isn't the most precise (it
+ * starts with the largest value and accumulates successively smaller values,
+ * compounding the rounding and precision error), but it's good enough.
*/
-constexpr double BesselI_0(const double x) noexcept
+template<typename T, typename U>
+U cyl_bessel_i(T nu, U x)
{
+ if(nu != T{0})
+ throw std::runtime_error{"cyl_bessel_i: nu != 0"};
+
/* Start at k=1 since k=0 is trivial. */
- const double x2{x / 2.0};
+ const double x2{x/2.0};
double term{1.0};
double sum{1.0};
- double last_sum{};
int k{1};
/* Let the integration converge until the term of the sum is no longer
* significant.
*/
+ double last_sum{};
do {
const double y{x2 / k};
++k;
@@ -56,8 +58,21 @@ constexpr double BesselI_0(const double x) noexcept
term *= y * y;
sum += term;
} while(sum != last_sum);
+ return static_cast<U>(sum);
+}
+#endif
- return sum;
+/* This is the normalized cardinal sine (sinc) function.
+ *
+ * sinc(x) = { 1, x = 0
+ * { sin(pi x) / (pi x), otherwise.
+ */
+constexpr double Sinc(const double x)
+{
+ constexpr double epsilon{std::numeric_limits<double>::epsilon()};
+ if(!(x > epsilon || x < -epsilon))
+ return 1.0;
+ return std::sin(al::numbers::pi*x) / (al::numbers::pi*x);
}
/* Calculate a Kaiser window from the given beta value and a normalized k
@@ -78,7 +93,7 @@ constexpr double Kaiser(const double beta, const double k, const double besseli_
{
if(!(k >= -1.0 && k <= 1.0))
return 0.0;
- return BesselI_0(beta * std::sqrt(1.0 - k*k)) / besseli_0_beta;
+ return cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta;
}
/* Calculates the (normalized frequency) transition width of the Kaiser window.
@@ -107,8 +122,6 @@ struct BSincHeader {
double width{};
double beta{};
double scaleBase{};
- double scaleRange{};
- double besseli_0_beta{};
uint a[BSincScaleCount]{};
uint total_size{};
@@ -118,13 +131,11 @@ struct BSincHeader {
width = CalcKaiserWidth(Rejection, Order);
beta = CalcKaiserBeta(Rejection);
scaleBase = width / 2.0;
- scaleRange = 1.0 - scaleBase;
- besseli_0_beta = BesselI_0(beta);
uint num_points{Order+1};
for(uint si{0};si < BSincScaleCount;++si)
{
- const double scale{scaleBase + (scaleRange * (si+1) / BSincScaleCount)};
+ const double scale{lerpd(scaleBase, 1.0, (si+1) / double{BSincScaleCount})};
const uint a_{std::min(static_cast<uint>(num_points / 2.0 / scale), num_points)};
const uint m{2 * a_};
@@ -142,26 +153,6 @@ constexpr BSincHeader bsinc12_hdr{60, 11};
constexpr BSincHeader bsinc24_hdr{60, 23};
-/* NOTE: GCC 5 has an issue with BSincHeader objects being in an anonymous
- * namespace while also being used as non-type template parameters.
- */
-#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6
-
-/* The number of sample points is double the a value (rounded up to a multiple
- * of 4), and scale index 0 includes the doubling for downsampling. bsinc24 is
- * currently the highest quality filter, and will use the most sample points.
- */
-constexpr uint BSincPointsMax{(bsinc24_hdr.a[0]*2 + 3) & ~3u};
-static_assert(BSincPointsMax <= MaxResamplerPadding, "MaxResamplerPadding is too small");
-
-template<size_t total_size>
-struct BSincFilterArray {
- alignas(16) std::array<float, total_size> mTable;
- const BSincHeader &hdr;
-
- BSincFilterArray(const BSincHeader &hdr_) : hdr{hdr_}
- {
-#else
template<const BSincHeader &hdr>
struct BSincFilterArray {
alignas(16) std::array<float, hdr.total_size> mTable{};
@@ -170,10 +161,12 @@ struct BSincFilterArray {
{
constexpr uint BSincPointsMax{(hdr.a[0]*2 + 3) & ~3u};
static_assert(BSincPointsMax <= MaxResamplerPadding, "MaxResamplerPadding is too small");
-#endif
+
using filter_type = double[BSincPhaseCount+1][BSincPointsMax];
auto filter = std::make_unique<filter_type[]>(BSincScaleCount);
+ const double besseli_0_beta{cyl_bessel_i(0, hdr.beta)};
+
/* Calculate the Kaiser-windowed Sinc filter coefficients for each
* scale and phase index.
*/
@@ -181,7 +174,7 @@ struct BSincFilterArray {
{
const uint m{hdr.a[si] * 2};
const size_t o{(BSincPointsMax-m) / 2};
- const double scale{hdr.scaleBase + (hdr.scaleRange * (si+1) / BSincScaleCount)};
+ const double scale{lerpd(hdr.scaleBase, 1.0, (si+1) / double{BSincScaleCount})};
const double cutoff{scale - (hdr.scaleBase * std::max(1.0, scale*2.0))};
const auto a = static_cast<double>(hdr.a[si]);
const double l{a - 1.0/BSincPhaseCount};
@@ -196,7 +189,7 @@ struct BSincFilterArray {
for(uint i{0};i < m;++i)
{
const double x{i - phase};
- filter[si][pi][o+i] = Kaiser(hdr.beta, x/l, hdr.besseli_0_beta) * cutoff *
+ filter[si][pi][o+i] = Kaiser(hdr.beta, x/l, besseli_0_beta) * cutoff *
Sinc(cutoff*x);
}
}
@@ -265,13 +258,8 @@ struct BSincFilterArray {
constexpr const float *getTable() const noexcept { return &mTable.front(); }
};
-#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6
-const BSincFilterArray<bsinc12_hdr.total_size> bsinc12_filter{bsinc12_hdr};
-const BSincFilterArray<bsinc24_hdr.total_size> bsinc24_filter{bsinc24_hdr};
-#else
const BSincFilterArray<bsinc12_hdr> bsinc12_filter{};
const BSincFilterArray<bsinc24_hdr> bsinc24_filter{};
-#endif
template<typename T>
constexpr BSincTable GenerateBSincTable(const T &filter)
@@ -279,7 +267,7 @@ constexpr BSincTable GenerateBSincTable(const T &filter)
BSincTable ret{};
const BSincHeader &hdr = filter.getHeader();
ret.scaleBase = static_cast<float>(hdr.scaleBase);
- ret.scaleRange = static_cast<float>(1.0 / hdr.scaleRange);
+ ret.scaleRange = static_cast<float>(1.0 / (1.0 - hdr.scaleBase));
for(size_t i{0};i < BSincScaleCount;++i)
ret.m[i] = ((hdr.a[i]*2) + 3) & ~3u;
ret.filterOffset[0] = 0;
diff --git a/core/buffer_storage.cpp b/core/buffer_storage.cpp
index 98ca2c1b..6ffab124 100644
--- a/core/buffer_storage.cpp
+++ b/core/buffer_storage.cpp
@@ -12,6 +12,7 @@ const char *NameFromFormat(FmtType type) noexcept
{
case FmtUByte: return "UInt8";
case FmtShort: return "Int16";
+ case FmtInt: return "Int32";
case FmtFloat: return "Float";
case FmtDouble: return "Double";
case FmtMulaw: return "muLaw";
@@ -49,6 +50,7 @@ uint BytesFromFmt(FmtType type) noexcept
{
case FmtUByte: return sizeof(uint8_t);
case FmtShort: return sizeof(int16_t);
+ case FmtInt: return sizeof(int32_t);
case FmtFloat: return sizeof(float);
case FmtDouble: return sizeof(double);
case FmtMulaw: return sizeof(uint8_t);
diff --git a/core/buffer_storage.h b/core/buffer_storage.h
index 282d5b53..3b581b5e 100644
--- a/core/buffer_storage.h
+++ b/core/buffer_storage.h
@@ -2,8 +2,8 @@
#define CORE_BUFFER_STORAGE_H
#include <atomic>
+#include <cstddef>
-#include "albyte.h"
#include "alnumeric.h"
#include "alspan.h"
#include "ambidefs.h"
@@ -15,6 +15,7 @@ using uint = unsigned int;
enum FmtType : unsigned char {
FmtUByte,
FmtShort,
+ FmtInt,
FmtFloat,
FmtDouble,
FmtMulaw,
@@ -85,7 +86,7 @@ struct BufferStorage {
CallbackType mCallback{nullptr};
void *mUserData{nullptr};
- al::span<al::byte> mData;
+ al::span<std::byte> mData;
uint mSampleRate{0u};
FmtChannels mChannels{FmtMono};
diff --git a/core/context.cpp b/core/context.cpp
index d68d8327..2ebbc7b1 100644
--- a/core/context.cpp
+++ b/core/context.cpp
@@ -2,7 +2,10 @@
#include "config.h"
#include <cassert>
+#include <limits>
#include <memory>
+#include <stdexcept>
+#include <utility>
#include "async_event.h"
#include "context.h"
@@ -51,7 +54,7 @@ ContextBase::~ContextBase()
if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)})
{
- al::destroy_n(curarray->end(), curarray->size());
+ std::destroy_n(curarray->end(), curarray->size());
delete curarray;
}
@@ -63,12 +66,14 @@ ContextBase::~ContextBase()
auto evt_vec = mAsyncEvents->getReadVector();
if(evt_vec.first.len > 0)
{
- al::destroy_n(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf), evt_vec.first.len);
+ std::destroy_n(std::launder(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf)),
+ evt_vec.first.len);
count += evt_vec.first.len;
}
if(evt_vec.second.len > 0)
{
- al::destroy_n(reinterpret_cast<AsyncEvent*>(evt_vec.second.buf), evt_vec.second.len);
+ std::destroy_n(std::launder(reinterpret_cast<AsyncEvent*>(evt_vec.second.buf)),
+ evt_vec.second.len);
count += evt_vec.second.len;
}
if(count > 0)
diff --git a/core/context.h b/core/context.h
index 9723eac3..ccb7dd3b 100644
--- a/core/context.h
+++ b/core/context.h
@@ -7,15 +7,15 @@
#include <cstddef>
#include <memory>
#include <thread>
+#include <vector>
#include "almalloc.h"
+#include "alsem.h"
#include "alspan.h"
#include "async_event.h"
#include "atomic.h"
-#include "bufferline.h"
-#include "threads.h"
+#include "opthelpers.h"
#include "vecmat.h"
-#include "vector.h"
struct DeviceBase;
struct EffectSlot;
@@ -25,8 +25,6 @@ struct Voice;
struct VoiceChange;
struct VoicePropsItem;
-using uint = unsigned int;
-
constexpr float SpeedOfSoundMetersPerSec{343.3f};
@@ -137,7 +135,7 @@ struct ContextBase {
std::thread mEventThread;
al::semaphore mEventSem;
std::unique_ptr<RingBuffer> mAsyncEvents;
- using AsyncEventBitset = std::bitset<AsyncEvent::UserEventCount>;
+ using AsyncEventBitset = std::bitset<al::to_underlying(AsyncEnableBits::Count)>;
std::atomic<AsyncEventBitset> mEnabledEvts{0u};
/* Asynchronous voice change actions are processed as a linked list of
@@ -146,20 +144,20 @@ struct ContextBase {
* in clusters that are stored in a vector for easy automatic cleanup.
*/
using VoiceChangeCluster = std::unique_ptr<VoiceChange[]>;
- al::vector<VoiceChangeCluster> mVoiceChangeClusters;
+ std::vector<VoiceChangeCluster> mVoiceChangeClusters;
using VoiceCluster = std::unique_ptr<Voice[]>;
- al::vector<VoiceCluster> mVoiceClusters;
+ std::vector<VoiceCluster> mVoiceClusters;
using VoicePropsCluster = std::unique_ptr<VoicePropsItem[]>;
- al::vector<VoicePropsCluster> mVoicePropClusters;
+ std::vector<VoicePropsCluster> mVoicePropClusters;
static constexpr size_t EffectSlotClusterSize{4};
EffectSlot *getEffectSlot();
using EffectSlotCluster = std::unique_ptr<EffectSlot[]>;
- al::vector<EffectSlotCluster> mEffectSlotClusters;
+ std::vector<EffectSlotCluster> mEffectSlotClusters;
ContextBase(DeviceBase *device);
diff --git a/core/converter.cpp b/core/converter.cpp
index a5141448..5b2f3e15 100644
--- a/core/converter.cpp
+++ b/core/converter.cpp
@@ -6,12 +6,12 @@
#include <algorithm>
#include <cassert>
#include <cmath>
+#include <cstddef>
#include <cstdint>
#include <iterator>
#include <limits.h>
#include "albit.h"
-#include "albyte.h"
#include "alnumeric.h"
#include "fpu_ctrl.h"
@@ -219,7 +219,7 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint
const uint SrcFrameSize{static_cast<uint>(mChan.size()) * mSrcTypeSize};
const uint DstFrameSize{static_cast<uint>(mChan.size()) * mDstTypeSize};
const uint increment{mIncrement};
- auto SamplesIn = static_cast<const al::byte*>(*src);
+ auto SamplesIn = static_cast<const std::byte*>(*src);
uint NumSrcSamples{*srcframes};
FPUCtl mixer_mode{};
@@ -265,8 +265,8 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint
for(size_t chan{0u};chan < mChan.size();chan++)
{
- const al::byte *SrcSamples{SamplesIn + mSrcTypeSize*chan};
- al::byte *DstSamples = static_cast<al::byte*>(dst) + mDstTypeSize*chan;
+ const std::byte *SrcSamples{SamplesIn + mSrcTypeSize*chan};
+ std::byte *DstSamples = static_cast<std::byte*>(dst) + mDstTypeSize*chan;
/* Load the previous samples into the source data first, then the
* new samples from the input buffer.
@@ -299,7 +299,7 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint
SamplesIn += SrcFrameSize*srcread;
NumSrcSamples -= srcread;
- dst = static_cast<al::byte*>(dst) + DstFrameSize*DstSize;
+ dst = static_cast<std::byte*>(dst) + DstFrameSize*DstSize;
pos += DstSize;
}
@@ -309,6 +309,98 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint
return pos;
}
+uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes)
+{
+ const uint increment{mIncrement};
+ uint NumSrcSamples{*srcframes};
+
+ FPUCtl mixer_mode{};
+ uint pos{0};
+ while(pos < dstframes && NumSrcSamples > 0)
+ {
+ const uint prepcount{mSrcPrepCount};
+ const uint readable{minu(NumSrcSamples, BufferLineSize - prepcount)};
+
+ if(prepcount < MaxResamplerPadding && MaxResamplerPadding-prepcount >= readable)
+ {
+ /* Not enough input samples to generate an output sample. Store
+ * what we're given for later.
+ */
+ for(size_t chan{0u};chan < mChan.size();chan++)
+ {
+ LoadSamples(&mChan[chan].PrevSamples[prepcount],
+ static_cast<const std::byte*>(src[chan]), 1, mSrcType, readable);
+ src[chan] = static_cast<const std::byte*>(src[chan]) + mSrcTypeSize*readable;
+ }
+
+ mSrcPrepCount = prepcount + readable;
+ NumSrcSamples = 0;
+ break;
+ }
+
+ float *RESTRICT SrcData{mSrcSamples};
+ float *RESTRICT DstData{mDstSamples};
+ uint DataPosFrac{mFracOffset};
+ uint64_t DataSize64{prepcount};
+ DataSize64 += readable;
+ DataSize64 -= MaxResamplerPadding;
+ DataSize64 <<= MixerFracBits;
+ DataSize64 -= DataPosFrac;
+
+ /* If we have a full prep, we can generate at least one sample. */
+ auto DstSize = static_cast<uint>(
+ clampu64((DataSize64 + increment-1)/increment, 1, BufferLineSize));
+ DstSize = minu(DstSize, dstframes-pos);
+
+ const uint DataPosEnd{DstSize*increment + DataPosFrac};
+ const uint SrcDataEnd{DataPosEnd>>MixerFracBits};
+
+ assert(prepcount+readable >= SrcDataEnd);
+ const uint nextprep{minu(prepcount + readable - SrcDataEnd, MaxResamplerPadding)};
+
+ for(size_t chan{0u};chan < mChan.size();chan++)
+ {
+ /* Load the previous samples into the source data first, then the
+ * new samples from the input buffer.
+ */
+ std::copy_n(mChan[chan].PrevSamples, prepcount, SrcData);
+ LoadSamples(SrcData + prepcount, src[chan], 1, mSrcType, readable);
+
+ /* Store as many prep samples for next time as possible, given the
+ * number of output samples being generated.
+ */
+ std::copy_n(SrcData+SrcDataEnd, nextprep, mChan[chan].PrevSamples);
+ std::fill(std::begin(mChan[chan].PrevSamples)+nextprep,
+ std::end(mChan[chan].PrevSamples), 0.0f);
+
+ /* Now resample, and store the result in the output buffer. */
+ mResample(&mState, SrcData+MaxResamplerEdge, DataPosFrac, increment,
+ {DstData, DstSize});
+
+ std::byte *DstSamples = static_cast<std::byte*>(dst[chan]) + pos*mDstTypeSize;
+ StoreSamples(DstSamples, DstData, 1, mDstType, DstSize);
+ }
+
+ /* Update the number of prep samples still available, as well as the
+ * fractional offset.
+ */
+ mSrcPrepCount = nextprep;
+ mFracOffset = DataPosEnd & MixerFracMask;
+
+ /* Update the src and dst pointers in case there's still more to do. */
+ const uint srcread{minu(NumSrcSamples, SrcDataEnd + mSrcPrepCount - prepcount)};
+ for(size_t chan{0u};chan < mChan.size();chan++)
+ src[chan] = static_cast<const std::byte*>(src[chan]) + mSrcTypeSize*srcread;
+ NumSrcSamples -= srcread;
+
+ pos += DstSize;
+ }
+
+ *srcframes = NumSrcSamples;
+
+ return pos;
+}
+
void ChannelConverter::convert(const void *src, float *dst, uint frames) const
{
diff --git a/core/converter.h b/core/converter.h
index 01becea2..49ca124d 100644
--- a/core/converter.h
+++ b/core/converter.h
@@ -36,6 +36,7 @@ struct SampleConverter {
SampleConverter(size_t numchans) : mChan{numchans} { }
uint convert(const void **src, uint *srcframes, void *dst, uint dstframes);
+ uint convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes);
uint availableOut(uint srcframes) const;
using SampleOffset = std::chrono::duration<int64_t, std::ratio<1,MixerFracOne>>;
diff --git a/core/cpu_caps.cpp b/core/cpu_caps.cpp
index d4b4d86c..1a064cf4 100644
--- a/core/cpu_caps.cpp
+++ b/core/cpu_caps.cpp
@@ -17,6 +17,7 @@
#include <intrin.h>
#endif
+#include <algorithm>
#include <array>
#include <cctype>
#include <string>
@@ -50,14 +51,14 @@ inline std::array<reg_type,4> get_cpuid(unsigned int f)
} // namespace
-al::optional<CPUInfo> GetCPUInfo()
+std::optional<CPUInfo> GetCPUInfo()
{
CPUInfo ret;
#ifdef CAN_GET_CPUID
auto cpuregs = get_cpuid(0);
if(cpuregs[0] == 0)
- return al::nullopt;
+ return std::nullopt;
const reg_type maxfunc{cpuregs[0]};
diff --git a/core/cpu_caps.h b/core/cpu_caps.h
index ffd671d0..0826a49b 100644
--- a/core/cpu_caps.h
+++ b/core/cpu_caps.h
@@ -1,10 +1,9 @@
#ifndef CORE_CPU_CAPS_H
#define CORE_CPU_CAPS_H
+#include <optional>
#include <string>
-#include "aloptional.h"
-
extern int CPUCapFlags;
enum {
@@ -21,6 +20,6 @@ struct CPUInfo {
int mCaps{0};
};
-al::optional<CPUInfo> GetCPUInfo();
+std::optional<CPUInfo> GetCPUInfo();
#endif /* CORE_CPU_CAPS_H */
diff --git a/core/cubic_tables.cpp b/core/cubic_tables.cpp
index 73ec6b3f..5e7aafad 100644
--- a/core/cubic_tables.cpp
+++ b/core/cubic_tables.cpp
@@ -1,24 +1,16 @@
#include "cubic_tables.h"
-#include <algorithm>
#include <array>
-#include <cassert>
-#include <cmath>
-#include <limits>
-#include <memory>
-#include <stdexcept>
+#include <stddef.h>
-#include "alnumbers.h"
-#include "core/mixer/defs.h"
+#include "cubic_defs.h"
namespace {
-using uint = unsigned int;
-
struct SplineFilterArray {
- alignas(16) CubicCoefficients mTable[CubicPhaseCount]{};
+ alignas(16) std::array<CubicCoefficients,CubicPhaseCount> mTable{};
constexpr SplineFilterArray()
{
@@ -49,7 +41,7 @@ struct SplineFilterArray {
mTable[pi].mDeltas[3] = -mTable[pi].mCoeffs[3];
}
- constexpr auto getTable() const noexcept { return al::as_span(mTable); }
+ constexpr auto& getTable() const noexcept { return mTable; }
};
constexpr SplineFilterArray SplineFilter{};
diff --git a/core/dbus_wrap.cpp b/core/dbus_wrap.cpp
index 7f221706..48419566 100644
--- a/core/dbus_wrap.cpp
+++ b/core/dbus_wrap.cpp
@@ -8,20 +8,16 @@
#include <mutex>
#include <type_traits>
+#include "albit.h"
#include "logging.h"
-void *dbus_handle{nullptr};
-#define DECL_FUNC(x) decltype(p##x) p##x{};
-DBUS_FUNCTIONS(DECL_FUNC)
-#undef DECL_FUNC
-
void PrepareDBus()
{
static constexpr char libname[] = "libdbus-1.so.3";
auto load_func = [](auto &f, const char *name) -> void
- { f = reinterpret_cast<std::remove_reference_t<decltype(f)>>(GetSymbol(dbus_handle, name)); };
+ { f = al::bit_cast<std::remove_reference_t<decltype(f)>>(GetSymbol(dbus_handle, name)); };
#define LOAD_FUNC(x) do { \
load_func(p##x, #x); \
if(!p##x) \
diff --git a/core/dbus_wrap.h b/core/dbus_wrap.h
index 09eaacf9..65f08942 100644
--- a/core/dbus_wrap.h
+++ b/core/dbus_wrap.h
@@ -28,8 +28,8 @@ MAGIC(dbus_message_iter_get_arg_type) \
MAGIC(dbus_message_iter_get_basic) \
MAGIC(dbus_set_error_from_message)
-extern void *dbus_handle;
-#define DECL_FUNC(x) extern decltype(x) *p##x;
+inline void *dbus_handle{};
+#define DECL_FUNC(x) inline decltype(x) *p##x{};
DBUS_FUNCTIONS(DECL_FUNC)
#undef DECL_FUNC
diff --git a/core/device.h b/core/device.h
index 9aaf7adb..b1ffc9ce 100644
--- a/core/device.h
+++ b/core/device.h
@@ -1,14 +1,13 @@
#ifndef CORE_DEVICE_H
#define CORE_DEVICE_H
-#include <stddef.h>
-
#include <array>
#include <atomic>
#include <bitset>
#include <chrono>
#include <memory>
-#include <mutex>
+#include <stddef.h>
+#include <stdint.h>
#include <string>
#include "almalloc.h"
@@ -43,20 +42,20 @@ using uint = unsigned int;
#define DEFAULT_NUM_UPDATES 3
-enum class DeviceType : unsigned char {
+enum class DeviceType : uint8_t {
Playback,
Capture,
Loopback
};
-enum class RenderMode : unsigned char {
+enum class RenderMode : uint8_t {
Normal,
Pairwise,
Hrtf
};
-enum class StereoEncoding : unsigned char {
+enum class StereoEncoding : uint8_t {
Basic,
Uhj,
Hrtf,
@@ -95,7 +94,7 @@ struct DistanceComp {
};
-constexpr uint InvalidChannelIndex{~0u};
+constexpr uint8_t InvalidChannelIndex{static_cast<uint8_t>(~0u)};
struct BFChannelConfig {
float Scale;
@@ -113,8 +112,8 @@ struct MixParams {
* source is expected to be a 3D ACN/N3D ambisonic buffer, and for each
* channel [0...count), the given functor is called with the source channel
* index, destination channel index, and the gain for that channel. If the
- * destination channel is INVALID_CHANNEL_INDEX, the given source channel
- * is not used for output.
+ * destination channel is InvalidChannelIndex, the given source channel is
+ * not used for output.
*/
template<typename F>
void setAmbiMixParams(const MixParams &inmix, const float gainbase, F func) const
@@ -123,14 +122,14 @@ struct MixParams {
const size_t numOut{Buffer.size()};
for(size_t i{0};i < numIn;++i)
{
- auto idx = InvalidChannelIndex;
- auto gain = 0.0f;
+ uint8_t idx{InvalidChannelIndex};
+ float gain{0.0f};
for(size_t j{0};j < numOut;++j)
{
if(AmbiMap[j].Index == inmix.AmbiMap[i].Index)
{
- idx = static_cast<uint>(j);
+ idx = static_cast<uint8_t>(j);
gain = AmbiMap[j].Scale * gainbase;
break;
}
@@ -142,7 +141,7 @@ struct MixParams {
struct RealMixParams {
al::span<const InputRemixMap> RemixMap;
- std::array<uint,MaxChannels> ChannelIndex{};
+ std::array<uint8_t,MaxChannels> ChannelIndex{};
al::span<FloatBufferLine> Buffer;
};
@@ -166,6 +165,11 @@ enum {
// ear buds, etc).
DirectEar,
+ /* Specifies if output is using speaker virtualization (e.g. Windows
+ * Spatial Audio).
+ */
+ Virtualization,
+
DeviceFlagsCount
};
@@ -325,9 +329,9 @@ struct DeviceBase {
/**
* Returns the index for the given channel name (e.g. FrontCenter), or
- * INVALID_CHANNEL_INDEX if it doesn't exist.
+ * InvalidChannelIndex if it doesn't exist.
*/
- uint channelIdxByName(Channel chan) const noexcept
+ uint8_t channelIdxByName(Channel chan) const noexcept
{ return RealOut.ChannelIndex[chan]; }
DISABLE_ALLOC()
diff --git a/core/effects/base.h b/core/effects/base.h
index 4ee19f37..83df7cf0 100644
--- a/core/effects/base.h
+++ b/core/effects/base.h
@@ -1,9 +1,9 @@
#ifndef CORE_EFFECTS_BASE_H
#define CORE_EFFECTS_BASE_H
+#include <array>
#include <stddef.h>
-#include "albyte.h"
#include "almalloc.h"
#include "alspan.h"
#include "atomic.h"
@@ -166,6 +166,11 @@ union EffectProps {
struct {
float Gain;
} Dedicated;
+
+ struct {
+ std::array<float,3> OrientAt;
+ std::array<float,3> OrientUp;
+ } Convolution;
};
diff --git a/core/filters/nfc.h b/core/filters/nfc.h
index 33f67a5f..4b8e68b5 100644
--- a/core/filters/nfc.h
+++ b/core/filters/nfc.h
@@ -39,7 +39,7 @@ public:
* w1 = speed_of_sound / (control_distance * sample_rate);
*
* Generally speaking, the control distance should be approximately the
- * average speaker distance, or based on the reference delay if outputing
+ * average speaker distance, or based on the reference delay if outputting
* NFC-HOA. It must not be negative, 0, or infinite. The source distance
* should not be too small relative to the control distance.
*/
diff --git a/core/fmt_traits.h b/core/fmt_traits.h
index f797f836..02473014 100644
--- a/core/fmt_traits.h
+++ b/core/fmt_traits.h
@@ -1,10 +1,9 @@
#ifndef CORE_FMT_TRAITS_H
#define CORE_FMT_TRAITS_H
-#include <stddef.h>
+#include <cstddef>
#include <stdint.h>
-#include "albyte.h"
#include "buffer_storage.h"
@@ -22,36 +21,43 @@ struct FmtTypeTraits<FmtUByte> {
using Type = uint8_t;
template<typename OutT>
- static constexpr inline OutT to(const Type val) noexcept
- { return val*OutT{1.0/128.0} - OutT{1.0}; }
+ static constexpr OutT to(const Type val) noexcept { return val*OutT{1.0/128.0} - OutT{1.0}; }
};
template<>
struct FmtTypeTraits<FmtShort> {
using Type = int16_t;
template<typename OutT>
- static constexpr inline OutT to(const Type val) noexcept { return val*OutT{1.0/32768.0}; }
+ static constexpr OutT to(const Type val) noexcept { return val*OutT{1.0/32768.0}; }
+};
+template<>
+struct FmtTypeTraits<FmtInt> {
+ using Type = int32_t;
+
+ template<typename OutT>
+ static constexpr OutT to(const Type val) noexcept
+ { return static_cast<OutT>(val)*OutT{1.0/2147483648.0}; }
};
template<>
struct FmtTypeTraits<FmtFloat> {
using Type = float;
template<typename OutT>
- static constexpr inline OutT to(const Type val) noexcept { return val; }
+ static constexpr OutT to(const Type val) noexcept { return val; }
};
template<>
struct FmtTypeTraits<FmtDouble> {
using Type = double;
template<typename OutT>
- static constexpr inline OutT to(const Type val) noexcept { return static_cast<OutT>(val); }
+ static constexpr OutT to(const Type val) noexcept { return static_cast<OutT>(val); }
};
template<>
struct FmtTypeTraits<FmtMulaw> {
using Type = uint8_t;
template<typename OutT>
- static constexpr inline OutT to(const Type val) noexcept
+ static constexpr OutT to(const Type val) noexcept
{ return muLawDecompressionTable[val] * OutT{1.0/32768.0}; }
};
template<>
@@ -59,14 +65,14 @@ struct FmtTypeTraits<FmtAlaw> {
using Type = uint8_t;
template<typename OutT>
- static constexpr inline OutT to(const Type val) noexcept
+ static constexpr OutT to(const Type val) noexcept
{ return aLawDecompressionTable[val] * OutT{1.0/32768.0}; }
};
template<FmtType SrcType, typename DstT>
-inline void LoadSampleArray(DstT *RESTRICT dst, const al::byte *src, const size_t srcstep,
- const size_t samples) noexcept
+inline void LoadSampleArray(DstT *RESTRICT dst, const std::byte *src, const std::size_t srcstep,
+ const std::size_t samples) noexcept
{
using TypeTraits = FmtTypeTraits<SrcType>;
using SampleType = typename TypeTraits::Type;
diff --git a/core/fpu_ctrl.cpp b/core/fpu_ctrl.cpp
index 0cf0d6e7..435855ad 100644
--- a/core/fpu_ctrl.cpp
+++ b/core/fpu_ctrl.cpp
@@ -8,38 +8,71 @@
#endif
#ifdef HAVE_SSE_INTRINSICS
#include <emmintrin.h>
-#ifndef _MM_DENORMALS_ZERO_MASK
+#elif defined(HAVE_SSE)
+#include <xmmintrin.h>
+#endif
+
+#if defined(HAVE_SSE) && !defined(_MM_DENORMALS_ZERO_MASK)
/* Some headers seem to be missing these? */
#define _MM_DENORMALS_ZERO_MASK 0x0040u
#define _MM_DENORMALS_ZERO_ON 0x0040u
#endif
-#endif
#include "cpu_caps.h"
+namespace {
-void FPUCtl::enter() noexcept
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+[[gnu::target("sse")]]
+#endif
+[[maybe_unused]]
+void disable_denormals(unsigned int *state [[maybe_unused]])
{
- if(this->in_mode) return;
-
#if defined(HAVE_SSE_INTRINSICS)
- this->sse_state = _mm_getcsr();
- unsigned int sseState{this->sse_state};
+ *state = _mm_getcsr();
+ unsigned int sseState{*state};
sseState &= ~(_MM_FLUSH_ZERO_MASK | _MM_DENORMALS_ZERO_MASK);
sseState |= _MM_FLUSH_ZERO_ON | _MM_DENORMALS_ZERO_ON;
_mm_setcsr(sseState);
-#elif defined(__GNUC__) && defined(HAVE_SSE)
+#elif defined(HAVE_SSE)
- if((CPUCapFlags&CPU_CAP_SSE))
+ *state = _mm_getcsr();
+ unsigned int sseState{*state};
+ sseState &= ~_MM_FLUSH_ZERO_MASK;
+ sseState |= _MM_FLUSH_ZERO_ON;
+ if((CPUCapFlags&CPU_CAP_SSE2))
{
- __asm__ __volatile__("stmxcsr %0" : "=m" (*&this->sse_state));
- unsigned int sseState{this->sse_state};
- sseState |= 0x8000; /* set flush-to-zero */
- if((CPUCapFlags&CPU_CAP_SSE2))
- sseState |= 0x0040; /* set denormals-are-zero */
- __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState));
+ sseState &= ~_MM_DENORMALS_ZERO_MASK;
+ sseState |= _MM_DENORMALS_ZERO_ON;
}
+ _mm_setcsr(sseState);
+#endif
+}
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+[[gnu::target("sse")]]
+#endif
+[[maybe_unused]]
+void reset_fpu(unsigned int state [[maybe_unused]])
+{
+#if defined(HAVE_SSE_INTRINSICS) || defined(HAVE_SSE)
+ _mm_setcsr(state);
+#endif
+}
+
+} // namespace
+
+
+void FPUCtl::enter() noexcept
+{
+ if(this->in_mode) return;
+
+#if defined(HAVE_SSE_INTRINSICS)
+ disable_denormals(&this->sse_state);
+#elif defined(HAVE_SSE)
+ if((CPUCapFlags&CPU_CAP_SSE))
+ disable_denormals(&this->sse_state);
#endif
this->in_mode = true;
@@ -50,12 +83,10 @@ void FPUCtl::leave() noexcept
if(!this->in_mode) return;
#if defined(HAVE_SSE_INTRINSICS)
- _mm_setcsr(this->sse_state);
-
-#elif defined(__GNUC__) && defined(HAVE_SSE)
-
+ reset_fpu(this->sse_state);
+#elif defined(HAVE_SSE)
if((CPUCapFlags&CPU_CAP_SSE))
- __asm__ __volatile__("ldmxcsr %0" : : "m" (*&this->sse_state));
+ reset_fpu(this->sse_state);
#endif
this->in_mode = false;
}
diff --git a/core/helpers.cpp b/core/helpers.cpp
index 99cf009c..5a996eee 100644
--- a/core/helpers.cpp
+++ b/core/helpers.cpp
@@ -3,29 +3,27 @@
#include "helpers.h"
+#if defined(_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
#include <algorithm>
-#include <cerrno>
-#include <cstdarg>
#include <cstdlib>
-#include <cstdio>
#include <cstring>
-#include <mutex>
#include <limits>
+#include <mutex>
+#include <optional>
#include <string>
-#include <tuple>
-#include "almalloc.h"
-#include "alfstream.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "alspan.h"
#include "alstring.h"
#include "logging.h"
#include "strutils.h"
-#include "vector.h"
-/* Mixing thread piority level */
+/* Mixing thread priority level */
int RTPrioLevel{1};
/* Allow reducing the process's RTTime limit for RTKit. */
@@ -34,14 +32,15 @@ bool AllowRTTimeLimit{true};
#ifdef _WIN32
+#include <cctype>
#include <shlobj.h>
const PathNamePair &GetProcBinary()
{
- static al::optional<PathNamePair> procbin;
+ static std::optional<PathNamePair> procbin;
if(procbin) return *procbin;
-
- auto fullpath = al::vector<WCHAR>(256);
+#if !defined(ALSOFT_UWP)
+ auto fullpath = std::vector<WCHAR>(256);
DWORD len{GetModuleFileNameW(nullptr, fullpath.data(), static_cast<DWORD>(fullpath.size()))};
while(len == fullpath.size())
{
@@ -58,7 +57,16 @@ const PathNamePair &GetProcBinary()
fullpath.resize(len);
if(fullpath.back() != 0)
fullpath.push_back(0);
-
+#else
+ auto exePath = __wargv[0];
+ if (!exePath)
+ {
+ ERR("Failed to get process name: error %lu\n", GetLastError());
+ procbin.emplace();
+ return *procbin;
+ }
+ std::vector<WCHAR> fullpath{exePath, exePath + wcslen(exePath) + 1};
+#endif
std::replace(fullpath.begin(), fullpath.end(), '/', '\\');
auto sep = std::find(fullpath.rbegin()+1, fullpath.rend(), '\\');
if(sep != fullpath.rend())
@@ -75,16 +83,16 @@ const PathNamePair &GetProcBinary()
namespace {
-void DirectorySearch(const char *path, const char *ext, al::vector<std::string> *const results)
+void DirectorySearch(const char *path, const char *ext, std::vector<std::string> *const results)
{
std::string pathstr{path};
pathstr += "\\*";
pathstr += ext;
TRACE("Searching %s\n", pathstr.c_str());
- std::wstring wpath{utf8_to_wstr(pathstr.c_str())};
+ std::wstring wpath{utf8_to_wstr(pathstr)};
WIN32_FIND_DATAW fdata;
- HANDLE hdl{FindFirstFileW(wpath.c_str(), &fdata)};
+ HANDLE hdl{FindFirstFileExW(wpath.c_str(), FindExInfoStandard, &fdata, FindExSearchNameMatch, NULL, 0)};
if(hdl == INVALID_HANDLE_VALUE) return;
const auto base = results->size();
@@ -97,7 +105,6 @@ void DirectorySearch(const char *path, const char *ext, al::vector<std::string>
str += wstr_to_utf8(fdata.cFileName);
} while(FindNextFileW(hdl, &fdata));
FindClose(hdl);
-
const al::span<std::string> newlist{results->data()+base, results->size()-base};
std::sort(newlist.begin(), newlist.end());
for(const auto &name : newlist)
@@ -106,16 +113,16 @@ void DirectorySearch(const char *path, const char *ext, al::vector<std::string>
} // namespace
-al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
+std::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
{
- auto is_slash = [](int c) noexcept -> int { return (c == '\\' || c == '/'); };
+ auto is_slash = [](int c) noexcept { return (c == '\\' || c == '/'); };
static std::mutex search_lock;
std::lock_guard<std::mutex> _{search_lock};
/* If the path is absolute, use it directly. */
- al::vector<std::string> results;
- if(isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2]))
+ std::vector<std::string> results;
+ if(std::isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2]))
{
std::string path{subdir};
std::replace(path.begin(), path.end(), '/', '\\');
@@ -149,9 +156,9 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
std::replace(path.begin(), path.end(), '/', '\\');
DirectorySearch(path.c_str(), ext, &results);
+#if !defined(ALSOFT_UWP) && !defined(_GAMING_XBOX)
/* Search the local and global data dirs. */
- static const int ids[2]{ CSIDL_APPDATA, CSIDL_COMMON_APPDATA };
- for(int id : ids)
+ for(auto id : std::array{CSIDL_APPDATA, CSIDL_COMMON_APPDATA})
{
WCHAR buffer[MAX_PATH];
if(SHGetSpecialFolderPathW(nullptr, buffer, id, FALSE) == FALSE)
@@ -165,24 +172,27 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
DirectorySearch(path.c_str(), ext, &results);
}
+#endif
return results;
}
void SetRTPriority(void)
{
+#if !defined(ALSOFT_UWP)
if(RTPrioLevel > 0)
{
if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
ERR("Failed to set priority level for thread\n");
}
+#endif
}
#else
-#include <sys/types.h>
-#include <unistd.h>
+#include <cerrno>
#include <dirent.h>
+#include <unistd.h>
#ifdef __FreeBSD__
#include <sys/sysctl.h>
#endif
@@ -197,7 +207,6 @@ void SetRTPriority(void)
#include <sched.h>
#endif
#ifdef HAVE_RTKIT
-#include <sys/time.h>
#include <sys/resource.h>
#include "dbus_wrap.h"
@@ -209,10 +218,10 @@ void SetRTPriority(void)
const PathNamePair &GetProcBinary()
{
- static al::optional<PathNamePair> procbin;
+ static std::optional<PathNamePair> procbin;
if(procbin) return *procbin;
- al::vector<char> pathname;
+ std::vector<char> pathname;
#ifdef __FreeBSD__
size_t pathlen;
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
@@ -247,7 +256,7 @@ const PathNamePair &GetProcBinary()
#ifndef __SWITCH__
if(pathname.empty())
{
- static const char SelfLinkNames[][32]{
+ const char *SelfLinkNames[]{
"/proc/self/exe",
"/proc/self/file",
"/proc/curproc/exe",
@@ -295,7 +304,7 @@ const PathNamePair &GetProcBinary()
namespace {
-void DirectorySearch(const char *path, const char *ext, al::vector<std::string> *const results)
+void DirectorySearch(const char *path, const char *ext, std::vector<std::string> *const results)
{
TRACE("Searching %s for *%s\n", path, ext);
DIR *dir{opendir(path)};
@@ -331,12 +340,12 @@ void DirectorySearch(const char *path, const char *ext, al::vector<std::string>
} // namespace
-al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
+std::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
{
static std::mutex search_lock;
std::lock_guard<std::mutex> _{search_lock};
- al::vector<std::string> results;
+ std::vector<std::string> results;
if(subdir[0] == '/')
{
DirectorySearch(subdir, ext, &results);
@@ -348,7 +357,7 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
DirectorySearch(localpath->c_str(), ext, &results);
else
{
- al::vector<char> cwdbuf(256);
+ std::vector<char> cwdbuf(256);
while(!getcwd(cwdbuf.data(), cwdbuf.size()))
{
if(errno != ERANGE)
@@ -425,7 +434,7 @@ al::vector<std::string> SearchDataFiles(const char *ext, const char *subdir)
namespace {
-bool SetRTPriorityPthread(int prio)
+bool SetRTPriorityPthread(int prio [[maybe_unused]])
{
int err{ENOTSUP};
#if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
@@ -445,16 +454,12 @@ bool SetRTPriorityPthread(int prio)
#endif
err = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
if(err == 0) return true;
-
-#else
-
- std::ignore = prio;
#endif
WARN("pthread_setschedparam failed: %s (%d)\n", std::strerror(err), err);
return false;
}
-bool SetRTPriorityRTKit(int prio)
+bool SetRTPriorityRTKit(int prio [[maybe_unused]])
{
#ifdef HAVE_RTKIT
if(!HasDBus())
@@ -547,7 +552,6 @@ bool SetRTPriorityRTKit(int prio)
#else
- std::ignore = prio;
WARN("D-Bus not supported\n");
#endif
return false;
diff --git a/core/helpers.h b/core/helpers.h
index f0bfcf1b..df51c116 100644
--- a/core/helpers.h
+++ b/core/helpers.h
@@ -1,18 +1,26 @@
#ifndef CORE_HELPERS_H
#define CORE_HELPERS_H
+#include <utility>
#include <string>
+#include <vector>
-#include "vector.h"
+struct PathNamePair {
+ std::string path, fname;
-struct PathNamePair { std::string path, fname; };
+ PathNamePair() = default;
+ template<typename T, typename U>
+ PathNamePair(T&& path_, U&& fname_)
+ : path{std::forward<T>(path_)}, fname{std::forward<U>(fname_)}
+ { }
+};
const PathNamePair &GetProcBinary(void);
extern int RTPrioLevel;
extern bool AllowRTTimeLimit;
void SetRTPriority(void);
-al::vector<std::string> SearchDataFiles(const char *match, const char *subdir);
+std::vector<std::string> SearchDataFiles(const char *match, const char *subdir);
#endif /* CORE_HELPERS_H */
diff --git a/core/hrtf.cpp b/core/hrtf.cpp
index d5c7573a..9a13a004 100644
--- a/core/hrtf.cpp
+++ b/core/hrtf.cpp
@@ -8,6 +8,7 @@
#include <cassert>
#include <cctype>
#include <cmath>
+#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
@@ -16,16 +17,16 @@
#include <memory>
#include <mutex>
#include <numeric>
+#include <optional>
#include <type_traits>
#include <utility>
+#include <vector>
#include "albit.h"
-#include "albyte.h"
#include "alfstream.h"
#include "almalloc.h"
#include "alnumbers.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "alspan.h"
#include "ambidefs.h"
#include "filters/splitter.h"
@@ -34,7 +35,6 @@
#include "mixer/hrtfdefs.h"
#include "opthelpers.h"
#include "polyphase_resampler.h"
-#include "vector.h"
namespace {
@@ -98,10 +98,10 @@ constexpr char magicMarker03[8]{'M','i','n','P','H','R','0','3'};
constexpr auto PassthruCoeff = static_cast<float>(1.0/al::numbers::sqrt2);
std::mutex LoadedHrtfLock;
-al::vector<LoadedHrtf> LoadedHrtfs;
+std::vector<LoadedHrtf> LoadedHrtfs;
std::mutex EnumeratedHrtfLock;
-al::vector<HrtfEntry> EnumeratedHrtfs;
+std::vector<HrtfEntry> EnumeratedHrtfs;
class databuf final : public std::streambuf {
@@ -289,13 +289,13 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool
mChannels[0].mSplitter.init(static_cast<float>(xover_norm));
for(size_t i{0};i < mChannels.size();++i)
{
- const size_t order{AmbiIndex::OrderFromChannel()[i]};
+ const size_t order{AmbiIndex::OrderFromChannel[i]};
mChannels[i].mSplitter = mChannels[0].mSplitter;
mChannels[i].mHfScale = AmbiOrderHFGain[order];
}
uint min_delay{HrtfHistoryLength*HrirDelayFracOne}, max_delay{0};
- al::vector<ImpulseResponse> impres; impres.reserve(AmbiPoints.size());
+ std::vector<ImpulseResponse> impres; impres.reserve(AmbiPoints.size());
auto calc_res = [Hrtf,&max_delay,&min_delay](const AngularPoint &pt) -> ImpulseResponse
{
auto &field = Hrtf->mFields[0];
@@ -331,7 +331,7 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool
TRACE("Min delay: %.2f, max delay: %.2f, FIR length: %u\n",
min_delay/double{HrirDelayFracOne}, max_delay/double{HrirDelayFracOne}, irSize);
- auto tmpres = al::vector<std::array<double2,HrirLength>>(mChannels.size());
+ auto tmpres = std::vector<std::array<double2,HrirLength>>(mChannels.size());
max_delay = 0;
for(size_t c{0u};c < AmbiPoints.size();++c)
{
@@ -393,7 +393,7 @@ std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize,
{
Hrtf.reset(al::construct_at(static_cast<HrtfStore*>(ptr)));
InitRef(Hrtf->mRef, 1u);
- Hrtf->mSampleRate = rate;
+ Hrtf->mSampleRate = rate & 0xff'ff'ff;
Hrtf->mIrSize = irSize;
/* Set up pointers to storage following the main HRTF struct. */
@@ -425,7 +425,7 @@ std::unique_ptr<HrtfStore> CreateHrtfStore(uint rate, uint8_t irSize,
std::uninitialized_copy_n(delays, irCount, delays_);
/* Finally, assign the storage pointers. */
- Hrtf->mFields = al::as_span(field_, fields.size());
+ Hrtf->mFields = {field_, fields.size()};
Hrtf->mElev = elev_;
Hrtf->mCoeffs = coeffs_;
Hrtf->mDelays = delays_;
@@ -492,10 +492,10 @@ T> readle(std::istream &data)
static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type");
T ret{};
- al::byte b[sizeof(T)]{};
+ std::byte b[sizeof(T)]{};
if(!data.read(reinterpret_cast<char*>(b), num_bits/8))
return static_cast<T>(EOF);
- std::reverse_copy(std::begin(b), std::end(b), reinterpret_cast<al::byte*>(&ret));
+ std::reverse_copy(std::begin(b), std::end(b), reinterpret_cast<std::byte*>(&ret));
return fixsign<num_bits>(ret);
}
@@ -529,7 +529,7 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data, const char *filename)
return nullptr;
}
- auto elevs = al::vector<HrtfStore::Elevation>(evCount);
+ auto elevs = std::vector<HrtfStore::Elevation>(evCount);
for(auto &elev : elevs)
elev.irOffset = readle<uint16_t>(data);
if(!data || data.eof())
@@ -571,8 +571,8 @@ std::unique_ptr<HrtfStore> LoadHrtf00(std::istream &data, const char *filename)
return nullptr;
}
- auto coeffs = al::vector<HrirArray>(irCount, HrirArray{});
- auto delays = al::vector<ubyte2>(irCount);
+ auto coeffs = std::vector<HrirArray>(irCount, HrirArray{});
+ auto delays = std::vector<ubyte2>(irCount);
for(auto &hrir : coeffs)
{
for(auto &val : al::span<float2>{hrir.data(), irSize})
@@ -626,7 +626,7 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data, const char *filename)
return nullptr;
}
- auto elevs = al::vector<HrtfStore::Elevation>(evCount);
+ auto elevs = std::vector<HrtfStore::Elevation>(evCount);
for(auto &elev : elevs)
elev.azCount = readle<uint8_t>(data);
if(!data || data.eof())
@@ -649,8 +649,8 @@ std::unique_ptr<HrtfStore> LoadHrtf01(std::istream &data, const char *filename)
elevs[i].irOffset = static_cast<ushort>(elevs[i-1].irOffset + elevs[i-1].azCount);
const ushort irCount{static_cast<ushort>(elevs.back().irOffset + elevs.back().azCount)};
- auto coeffs = al::vector<HrirArray>(irCount, HrirArray{});
- auto delays = al::vector<ubyte2>(irCount);
+ auto coeffs = std::vector<HrirArray>(irCount, HrirArray{});
+ auto delays = std::vector<ubyte2>(irCount);
for(auto &hrir : coeffs)
{
for(auto &val : al::span<float2>{hrir.data(), irSize})
@@ -722,8 +722,8 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
return nullptr;
}
- auto fields = al::vector<HrtfStore::Field>(fdCount);
- auto elevs = al::vector<HrtfStore::Elevation>{};
+ auto fields = std::vector<HrtfStore::Field>(fdCount);
+ auto elevs = std::vector<HrtfStore::Elevation>{};
for(size_t f{0};f < fdCount;f++)
{
const ushort distance{readle<uint16_t>(data)};
@@ -787,8 +787,8 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
});
const auto irTotal = static_cast<ushort>(elevs.back().azCount + elevs.back().irOffset);
- auto coeffs = al::vector<HrirArray>(irTotal, HrirArray{});
- auto delays = al::vector<ubyte2>(irTotal);
+ auto coeffs = std::vector<HrirArray>(irTotal, HrirArray{});
+ auto delays = std::vector<ubyte2>(irTotal);
if(channelType == ChanType_LeftOnly)
{
if(sampleType == SampleType_S16)
@@ -881,10 +881,10 @@ std::unique_ptr<HrtfStore> LoadHrtf02(std::istream &data, const char *filename)
if(fdCount > 1)
{
- auto fields_ = al::vector<HrtfStore::Field>(fields.size());
- auto elevs_ = al::vector<HrtfStore::Elevation>(elevs.size());
- auto coeffs_ = al::vector<HrirArray>(coeffs.size());
- auto delays_ = al::vector<ubyte2>(delays.size());
+ auto fields_ = std::vector<HrtfStore::Field>(fields.size());
+ auto elevs_ = std::vector<HrtfStore::Elevation>(elevs.size());
+ auto coeffs_ = std::vector<HrirArray>(coeffs.size());
+ auto delays_ = std::vector<ubyte2>(delays.size());
/* Simple reverse for the per-field elements. */
std::reverse_copy(fields.cbegin(), fields.cend(), fields_.begin());
@@ -983,8 +983,8 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data, const char *filename)
return nullptr;
}
- auto fields = al::vector<HrtfStore::Field>(fdCount);
- auto elevs = al::vector<HrtfStore::Elevation>{};
+ auto fields = std::vector<HrtfStore::Field>(fdCount);
+ auto elevs = std::vector<HrtfStore::Elevation>{};
for(size_t f{0};f < fdCount;f++)
{
const ushort distance{readle<uint16_t>(data)};
@@ -1048,8 +1048,8 @@ std::unique_ptr<HrtfStore> LoadHrtf03(std::istream &data, const char *filename)
});
const auto irTotal = static_cast<ushort>(elevs.back().azCount + elevs.back().irOffset);
- auto coeffs = al::vector<HrirArray>(irTotal, HrirArray{});
- auto delays = al::vector<ubyte2>(irTotal);
+ auto coeffs = std::vector<HrirArray>(irTotal, HrirArray{});
+ auto delays = std::vector<ubyte2>(irTotal);
if(channelType == ChanType_LeftOnly)
{
for(auto &hrir : coeffs)
@@ -1221,7 +1221,7 @@ al::span<const char> GetResource(int name)
} // namespace
-al::vector<std::string> EnumerateHrtf(al::optional<std::string> pathopt)
+std::vector<std::string> EnumerateHrtf(std::optional<std::string> pathopt)
{
std::lock_guard<std::mutex> _{EnumeratedHrtfLock};
EnumeratedHrtfs.clear();
@@ -1270,7 +1270,7 @@ al::vector<std::string> EnumerateHrtf(al::optional<std::string> pathopt)
AddBuiltInEntry("Built-In HRTF", IDR_DEFAULT_HRTF_MHR);
}
- al::vector<std::string> list;
+ std::vector<std::string> list;
list.reserve(EnumeratedHrtfs.size());
for(auto &entry : EnumeratedHrtfs)
list.emplace_back(entry.mDispName);
@@ -1368,7 +1368,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate)
TRACE("Resampling HRTF %s (%uhz -> %uhz)\n", name.c_str(), hrtf->mSampleRate, devrate);
/* Calculate the last elevation's index and get the total IR count. */
- const size_t lastEv{std::accumulate(hrtf->mFields.begin(), hrtf->mFields.end(), size_t{0},
+ const size_t lastEv{std::accumulate(hrtf->mFields.begin(), hrtf->mFields.end(), 0_uz,
[](const size_t curval, const HrtfStore::Field &field) noexcept -> size_t
{ return curval + field.evCount; }
) - 1};
@@ -1394,7 +1394,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate)
/* Scale the delays for the new sample rate. */
float max_delay{0.0f};
- auto new_delays = al::vector<float2>(irCount);
+ auto new_delays = std::vector<float2>(irCount);
const float rate_scale{static_cast<float>(devrate)/static_cast<float>(hrtf->mSampleRate)};
for(size_t i{0};i < irCount;++i)
{
@@ -1430,7 +1430,7 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate)
*/
const float newIrSize{std::round(static_cast<float>(hrtf->mIrSize) * rate_scale)};
hrtf->mIrSize = static_cast<uint8_t>(minf(HrirLength, newIrSize));
- hrtf->mSampleRate = devrate;
+ hrtf->mSampleRate = devrate & 0xff'ff'ff;
}
TRACE("Loaded HRTF %s for sample rate %uhz, %u-sample filter\n", name.c_str(),
diff --git a/core/hrtf.h b/core/hrtf.h
index eb18682a..5e6e09a8 100644
--- a/core/hrtf.h
+++ b/core/hrtf.h
@@ -4,17 +4,17 @@
#include <array>
#include <cstddef>
#include <memory>
+#include <optional>
#include <string>
+#include <vector>
#include "almalloc.h"
-#include "aloptional.h"
#include "alspan.h"
#include "atomic.h"
#include "ambidefs.h"
#include "bufferline.h"
#include "mixer/hrtfdefs.h"
#include "intrusive_ptr.h"
-#include "vector.h"
struct HrtfStore {
@@ -83,7 +83,7 @@ struct DirectHrtfState {
};
-al::vector<std::string> EnumerateHrtf(al::optional<std::string> pathopt);
+std::vector<std::string> EnumerateHrtf(std::optional<std::string> pathopt);
HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate);
#endif /* CORE_HRTF_H */
diff --git a/core/logging.cpp b/core/logging.cpp
index 34a95e5a..56ad0a0d 100644
--- a/core/logging.cpp
+++ b/core/logging.cpp
@@ -3,13 +3,17 @@
#include "logging.h"
+#include <cctype>
#include <cstdarg>
#include <cstdio>
+#include <cstring>
+#include <mutex>
+#include <optional>
#include <string>
+#include <vector>
#include "alspan.h"
#include "strutils.h"
-#include "vector.h"
#if defined(_WIN32)
@@ -19,22 +23,74 @@
#include <android/log.h>
#endif
-void al_print(LogLevel level, FILE *logfile, const char *fmt, ...)
+
+FILE *gLogFile{stderr};
+#ifdef _DEBUG
+LogLevel gLogLevel{LogLevel::Warning};
+#else
+LogLevel gLogLevel{LogLevel::Error};
+#endif
+
+
+namespace {
+
+enum class LogState : uint8_t {
+ FirstRun,
+ Ready,
+ Disable
+};
+
+std::mutex LogCallbackMutex;
+LogState gLogState{LogState::FirstRun};
+
+LogCallbackFunc gLogCallback{};
+void *gLogCallbackPtr{};
+
+constexpr std::optional<char> GetLevelCode(LogLevel level)
+{
+ switch(level)
+ {
+ case LogLevel::Disable: break;
+ case LogLevel::Error: return 'E';
+ case LogLevel::Warning: return 'W';
+ case LogLevel::Trace: return 'I';
+ }
+ return std::nullopt;
+}
+
+} // namespace
+
+void al_set_log_callback(LogCallbackFunc callback, void *userptr)
+{
+ auto cblock = std::lock_guard{LogCallbackMutex};
+ gLogCallback = callback;
+ gLogCallbackPtr = callback ? userptr : nullptr;
+ if(gLogState == LogState::FirstRun)
+ {
+ auto extlogopt = al::getenv("ALSOFT_DISABLE_LOG_CALLBACK");
+ if(!extlogopt || *extlogopt != "1")
+ gLogState = LogState::Ready;
+ else
+ gLogState = LogState::Disable;
+ }
+}
+
+void al_print(LogLevel level, const char *fmt, ...)
{
/* Kind of ugly since string literals are const char arrays with a size
* that includes the null terminator, which we want to exclude from the
* span.
*/
- auto prefix = al::as_span("[ALSOFT] (--) ").first<14>();
+ auto prefix = al::span{"[ALSOFT] (--) "}.first<14>();
switch(level)
{
case LogLevel::Disable: break;
- case LogLevel::Error: prefix = al::as_span("[ALSOFT] (EE) ").first<14>(); break;
- case LogLevel::Warning: prefix = al::as_span("[ALSOFT] (WW) ").first<14>(); break;
- case LogLevel::Trace: prefix = al::as_span("[ALSOFT] (II) ").first<14>(); break;
+ case LogLevel::Error: prefix = al::span{"[ALSOFT] (EE) "}.first<14>(); break;
+ case LogLevel::Warning: prefix = al::span{"[ALSOFT] (WW) "}.first<14>(); break;
+ case LogLevel::Trace: prefix = al::span{"[ALSOFT] (II) "}.first<14>(); break;
}
- al::vector<char> dynmsg;
+ std::vector<char> dynmsg;
std::array<char,256> stcmsg{};
char *str{stcmsg.data()};
@@ -45,21 +101,28 @@ void al_print(LogLevel level, FILE *logfile, const char *fmt, ...)
va_start(args, fmt);
va_copy(args2, args);
const int msglen{std::vsnprintf(msg.data(), msg.size(), fmt, args)};
- if(msglen >= 0 && static_cast<size_t>(msglen) >= msg.size()) UNLIKELY
+ if(msglen >= 0)
{
- dynmsg.resize(static_cast<size_t>(msglen)+prefix.size() + 1u);
+ if(static_cast<size_t>(msglen) >= msg.size()) UNLIKELY
+ {
+ dynmsg.resize(static_cast<size_t>(msglen)+prefix.size() + 1u);
- str = dynmsg.data();
- auto prefend2 = std::copy_n(prefix.begin(), prefix.size(), dynmsg.begin());
- msg = {prefend2, dynmsg.end()};
+ str = dynmsg.data();
+ auto prefend2 = std::copy_n(prefix.begin(), prefix.size(), dynmsg.begin());
+ msg = {prefend2, dynmsg.end()};
- std::vsnprintf(msg.data(), msg.size(), fmt, args2);
+ std::vsnprintf(msg.data(), msg.size(), fmt, args2);
+ }
+ msg = msg.first(static_cast<size_t>(msglen));
}
+ else
+ msg = {msg.data(), std::strlen(msg.data())};
va_end(args2);
va_end(args);
if(gLogLevel >= level)
{
+ auto logfile = gLogFile;
fputs(str, logfile);
fflush(logfile);
}
@@ -86,4 +149,21 @@ void al_print(LogLevel level, FILE *logfile, const char *fmt, ...)
};
__android_log_print(android_severity(level), "openal", "%s", str);
#endif
+
+ auto cblock = std::lock_guard{LogCallbackMutex};
+ if(gLogState != LogState::Disable)
+ {
+ while(!msg.empty() && std::isspace(msg.back()))
+ {
+ msg.back() = '\0';
+ msg = msg.first(msg.size()-1);
+ }
+ if(auto logcode = GetLevelCode(level); logcode && !msg.empty())
+ {
+ if(gLogCallback)
+ gLogCallback(gLogCallbackPtr, *logcode, msg.data(), static_cast<int>(msg.size()));
+ else if(gLogState == LogState::FirstRun)
+ gLogState = LogState::Disable;
+ }
+ }
}
diff --git a/core/logging.h b/core/logging.h
index f4b6ab56..06b7cdde 100644
--- a/core/logging.h
+++ b/core/logging.h
@@ -16,36 +16,23 @@ extern LogLevel gLogLevel;
extern FILE *gLogFile;
-#ifdef __USE_MINGW_ANSI_STDIO
-[[gnu::format(gnu_printf,3,4)]]
-#else
-[[gnu::format(printf,3,4)]]
-#endif
-void al_print(LogLevel level, FILE *logfile, const char *fmt, ...);
-#if (!defined(_WIN32) || defined(NDEBUG)) && !defined(__ANDROID__)
-#define TRACE(...) do { \
- if(gLogLevel >= LogLevel::Trace) UNLIKELY \
- al_print(LogLevel::Trace, gLogFile, __VA_ARGS__); \
-} while(0)
+using LogCallbackFunc = void(*)(void *userptr, char level, const char *message, int length) noexcept;
-#define WARN(...) do { \
- if(gLogLevel >= LogLevel::Warning) UNLIKELY \
- al_print(LogLevel::Warning, gLogFile, __VA_ARGS__); \
-} while(0)
+void al_set_log_callback(LogCallbackFunc callback, void *userptr);
-#define ERR(...) do { \
- if(gLogLevel >= LogLevel::Error) UNLIKELY \
- al_print(LogLevel::Error, gLogFile, __VA_ARGS__); \
-} while(0)
+#ifdef __USE_MINGW_ANSI_STDIO
+[[gnu::format(gnu_printf,2,3)]]
#else
+[[gnu::format(printf,2,3)]]
+#endif
+void al_print(LogLevel level, const char *fmt, ...);
-#define TRACE(...) al_print(LogLevel::Trace, gLogFile, __VA_ARGS__)
+#define TRACE(...) al_print(LogLevel::Trace, __VA_ARGS__)
-#define WARN(...) al_print(LogLevel::Warning, gLogFile, __VA_ARGS__)
+#define WARN(...) al_print(LogLevel::Warning, __VA_ARGS__)
-#define ERR(...) al_print(LogLevel::Error, gLogFile, __VA_ARGS__)
-#endif
+#define ERR(...) al_print(LogLevel::Error, __VA_ARGS__)
#endif /* CORE_LOGGING_H */
diff --git a/core/mastering.cpp b/core/mastering.cpp
index 97a4008e..4445719b 100644
--- a/core/mastering.cpp
+++ b/core/mastering.cpp
@@ -382,10 +382,10 @@ std::unique_ptr<Compressor> Compressor::Create(const size_t NumChans, const floa
Compressor::~Compressor()
{
if(mHold)
- al::destroy_at(mHold);
+ std::destroy_at(mHold);
mHold = nullptr;
if(mDelay)
- al::destroy_n(mDelay, mNumChans);
+ std::destroy_n(mDelay, mNumChans);
mDelay = nullptr;
}
diff --git a/core/mixer.cpp b/core/mixer.cpp
index 066c57bd..806ac8b8 100644
--- a/core/mixer.cpp
+++ b/core/mixer.cpp
@@ -82,14 +82,13 @@ std::array<float,MaxAmbiChannels> CalcAmbiCoeffs(const float y, const float z, c
return coeffs;
}
-void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain,
- const al::span<float,MaxAmbiChannels> gains)
+void ComputePanGains(const MixParams *mix, const al::span<const float,MaxAmbiChannels> coeffs,
+ const float ingain, const al::span<float,MaxAmbiChannels> gains)
{
auto ambimap = mix->AmbiMap.cbegin();
auto iter = std::transform(ambimap, ambimap+mix->Buffer.size(), gains.begin(),
[coeffs,ingain](const BFChannelConfig &chanmap) noexcept -> float
- { return chanmap.Scale * coeffs[chanmap.Index] * ingain; }
- );
+ { return chanmap.Scale * coeffs[chanmap.Index] * ingain; });
std::fill(iter, gains.end(), 0.0f);
}
diff --git a/core/mixer.h b/core/mixer.h
index aa7597bb..9062ebac 100644
--- a/core/mixer.h
+++ b/core/mixer.h
@@ -58,7 +58,7 @@ std::array<float,MaxAmbiChannels> CalcAmbiCoeffs(const float y, const float z, c
* vector must be normalized (unit length), and the spread is the angular width
* of the sound (0...tau).
*/
-inline std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const float (&dir)[3],
+inline std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const al::span<const float,3> dir,
const float spread)
{
/* Convert from OpenAL coords to Ambisonics. */
@@ -71,7 +71,7 @@ inline std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const float (&dir)[
* Calculates ambisonic coefficients based on an OpenAL direction vector. The
* vector must be normalized (unit length).
*/
-constexpr std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const float (&dir)[3])
+constexpr std::array<float,MaxAmbiChannels> CalcDirectionCoeffs(const al::span<const float,3> dir)
{
/* Convert from OpenAL coords to Ambisonics. */
return CalcAmbiCoeffs(-dir[0], dir[1], -dir[2]);
@@ -103,7 +103,7 @@ inline std::array<float,MaxAmbiChannels> CalcAngleCoeffs(const float azimuth,
* coeffs are a 'slice' of a transform matrix for the input channel, used to
* scale and orient the sound samples.
*/
-void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain,
- const al::span<float,MaxAmbiChannels> gains);
+void ComputePanGains(const MixParams *mix, const al::span<const float,MaxAmbiChannels> coeffs,
+ const float ingain, const al::span<float,MaxAmbiChannels> gains);
#endif /* CORE_MIXER_H */
diff --git a/core/mixer/mixer_neon.cpp b/core/mixer/mixer_neon.cpp
index ef2936b3..ead775af 100644
--- a/core/mixer/mixer_neon.cpp
+++ b/core/mixer/mixer_neon.cpp
@@ -342,7 +342,7 @@ void Mix_<NEONTag>(const al::span<const float> InSamples, const al::span<FloatBu
{
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
const auto min_len = minz(Counter, InSamples.size());
- const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len;
+ const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len;
for(FloatBufferLine &output : OutBuffer)
MixLine(InSamples, al::assume_aligned<16>(output.data()+OutPos), *CurrentGains++,
@@ -355,7 +355,7 @@ void Mix_<NEONTag>(const al::span<const float> InSamples, float *OutBuffer, floa
{
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
const auto min_len = minz(Counter, InSamples.size());
- const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len;
+ const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len;
MixLine(InSamples, al::assume_aligned<16>(OutBuffer), CurrentGain, TargetGain, delta, min_len,
aligned_len, Counter);
diff --git a/core/mixer/mixer_sse.cpp b/core/mixer/mixer_sse.cpp
index 0aa5d5fb..70f77c14 100644
--- a/core/mixer/mixer_sse.cpp
+++ b/core/mixer/mixer_sse.cpp
@@ -307,7 +307,7 @@ void Mix_<SSETag>(const al::span<const float> InSamples, const al::span<FloatBuf
{
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
const auto min_len = minz(Counter, InSamples.size());
- const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len;
+ const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len;
for(FloatBufferLine &output : OutBuffer)
MixLine(InSamples, al::assume_aligned<16>(output.data()+OutPos), *CurrentGains++,
@@ -320,7 +320,7 @@ void Mix_<SSETag>(const al::span<const float> InSamples, float *OutBuffer, float
{
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
const auto min_len = minz(Counter, InSamples.size());
- const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len;
+ const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len;
MixLine(InSamples, al::assume_aligned<16>(OutBuffer), CurrentGain, TargetGain, delta, min_len,
aligned_len, Counter);
diff --git a/core/uhjfilter.cpp b/core/uhjfilter.cpp
index df50956a..28999e09 100644
--- a/core/uhjfilter.cpp
+++ b/core/uhjfilter.cpp
@@ -9,7 +9,9 @@
#include "alcomplex.h"
#include "alnumeric.h"
#include "opthelpers.h"
+#include "pffft.h"
#include "phase_shifter.h"
+#include "vector.h"
UhjQualityType UhjDecodeQuality{UhjQualityType::Default};
@@ -18,38 +20,141 @@ UhjQualityType UhjEncodeQuality{UhjQualityType::Default};
namespace {
-const PhaseShifterT<UhjLength256> PShiftLq{};
-const PhaseShifterT<UhjLength512> PShiftHq{};
+struct PFFFTSetupDeleter {
+ void operator()(PFFFT_Setup *ptr) { pffft_destroy_setup(ptr); }
+};
+using PFFFTSetupPtr = std::unique_ptr<PFFFT_Setup,PFFFTSetupDeleter>;
+/* Convolution is implemented using a segmented overlap-add method. The filter
+ * response is broken up into multiple segments of 128 samples, and each
+ * segment has an FFT applied with a 256-sample buffer (the latter half left
+ * silent) to get its frequency-domain response.
+ *
+ * Input samples are similarly broken up into 128-sample segments, with a 256-
+ * sample FFT applied to each new incoming segment to get its frequency-domain
+ * response. A history of FFT'd input segments is maintained, equal to the
+ * number of filter response segments.
+ *
+ * To apply the convolution, each filter response segment is convolved with its
+ * paired input segment (using complex multiplies, far cheaper than time-domain
+ * FIRs), accumulating into an FFT buffer. The input history is then shifted to
+ * align with later filter response segments for the next input segment.
+ *
+ * An inverse FFT is then applied to the accumulated FFT buffer to get a 256-
+ * sample time-domain response for output, which is split in two halves. The
+ * first half is the 128-sample output, and the second half is a 128-sample
+ * (really, 127) delayed extension, which gets added to the output next time.
+ * Convolving two time-domain responses of length N results in a time-domain
+ * signal of length N*2 - 1, and this holds true regardless of the convolution
+ * being applied in the frequency domain, so these "overflow" samples need to
+ * be accounted for.
+ */
template<size_t N>
-struct GetPhaseShifter;
-template<>
-struct GetPhaseShifter<UhjLength256> { static auto& Get() noexcept { return PShiftLq; } };
-template<>
-struct GetPhaseShifter<UhjLength512> { static auto& Get() noexcept { return PShiftHq; } };
+struct SegmentedFilter {
+ static constexpr size_t sFftLength{256};
+ static constexpr size_t sSampleLength{sFftLength / 2};
+ static constexpr size_t sNumSegments{N/sSampleLength};
+ static_assert(N >= sFftLength);
+ static_assert((N % sSampleLength) == 0);
+ PFFFTSetupPtr mFft;
+ alignas(16) std::array<float,sFftLength*sNumSegments> mFilterData;
+
+ SegmentedFilter()
+ {
+ mFft = PFFFTSetupPtr{pffft_new_setup(sFftLength, PFFFT_REAL)};
+
+ using complex_d = std::complex<double>;
+ constexpr size_t fft_size{N};
+ constexpr size_t half_size{fft_size / 2};
+
+ /* To set up the filter, we need to generate the desired response.
+ * Start with a pure delay that passes all frequencies through.
+ */
+ auto fftBuffer = std::make_unique<complex_d[]>(fft_size);
+ std::fill_n(fftBuffer.get(), fft_size, complex_d{});
+ fftBuffer[half_size] = 1.0;
+
+ /* Convert to the frequency domain, shift the phase of each bin by +90
+ * degrees, then convert back to the time domain.
+ *
+ * NOTE: The 0- and half-frequency are always real for a real signal.
+ * To maintain that and their phase (0 or pi), they're heavily
+ * attenuated instead of shifted like the others.
+ */
+ forward_fft(al::span{fftBuffer.get(), fft_size});
+ fftBuffer[0] *= std::numeric_limits<double>::epsilon();
+ for(size_t i{1};i < half_size;++i)
+ fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()};
+ fftBuffer[half_size] *= std::numeric_limits<double>::epsilon();
+ for(size_t i{half_size+1};i < fft_size;++i)
+ fftBuffer[i] = std::conj(fftBuffer[fft_size - i]);
+ inverse_fft(al::span{fftBuffer.get(), fft_size});
+
+ /* The segments of the filter are converted back to the frequency
+ * domain, each on their own (0 stuffed).
+ */
+ auto fftBuffer2 = std::make_unique<complex_d[]>(sFftLength);
+ auto fftTmp = al::vector<float,16>(sFftLength);
+ float *filter{mFilterData.data()};
+ for(size_t s{0};s < sNumSegments;++s)
+ {
+ for(size_t i{0};i < sSampleLength;++i)
+ fftBuffer2[i] = fftBuffer[sSampleLength*s + i].real() / double{fft_size};
+ std::fill_n(fftBuffer2.get()+sSampleLength, sSampleLength, complex_d{});
+ forward_fft(al::span{fftBuffer2.get(), sFftLength});
+
+ /* Convert to zdomain data for PFFFT, scaled by the FFT length so
+ * the iFFT result will be normalized.
+ */
+ for(size_t i{0};i < sSampleLength;++i)
+ {
+ fftTmp[i*2 + 0] = static_cast<float>(fftBuffer2[i].real()) / float{sFftLength};
+ fftTmp[i*2 + 1] = static_cast<float>((i == 0) ? fftBuffer2[sSampleLength].real()
+ : fftBuffer2[i].imag()) / float{sFftLength};
+ }
+ pffft_zreorder(mFft.get(), fftTmp.data(), filter, PFFFT_BACKWARD);
+ filter += sFftLength;
+ }
+ }
+};
+
+template<size_t N>
+const SegmentedFilter<N> gSegmentedFilter;
+
+template<size_t N>
+const PhaseShifterT<N> PShifter;
-constexpr float square(float x) noexcept
-{ return x*x; }
/* Filter coefficients for the 'base' all-pass IIR, which applies a frequency-
* dependent phase-shift of N degrees. The output of the filter requires a 1-
* sample delay.
*/
constexpr std::array<float,4> Filter1Coeff{{
- square(0.6923878f), square(0.9360654322959f), square(0.9882295226860f),
- square(0.9987488452737f)
+ 0.479400865589f, 0.876218493539f, 0.976597589508f, 0.997499255936f
}};
/* Filter coefficients for the offset all-pass IIR, which applies a frequency-
* dependent phase-shift of N+90 degrees.
*/
constexpr std::array<float,4> Filter2Coeff{{
- square(0.4021921162426f), square(0.8561710882420f), square(0.9722909545651f),
- square(0.9952884791278f)
+ 0.161758498368f, 0.733028932341f, 0.945349700329f, 0.990599156684f
}};
} // namespace
+void UhjAllPassFilter::processOne(const al::span<const float, 4> coeffs, float x)
+{
+ auto state = mState;
+ for(size_t i{0};i < 4;++i)
+ {
+ const float y{x*coeffs[i] + state[i].z[0]};
+ state[i].z[0] = state[i].z[1];
+ state[i].z[1] = y*coeffs[i] - x;
+ x = y;
+ }
+ mState = state;
+}
+
void UhjAllPassFilter::process(const al::span<const float,4> coeffs,
const al::span<const float> src, const bool updateState, float *RESTRICT dst)
{
@@ -92,7 +197,10 @@ template<size_t N>
void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
const al::span<const float*const,3> InSamples, const size_t SamplesToDo)
{
- const auto &PShift = GetPhaseShifter<N>::Get();
+ static constexpr auto &Filter = gSegmentedFilter<N>;
+ static_assert(sFftLength == Filter.sFftLength);
+ static_assert(sSegmentSize == Filter.sSampleLength);
+ static_assert(sNumSegments == Filter.sNumSegments);
ASSUME(SamplesToDo > 0);
@@ -109,10 +217,71 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
mS[i] = 0.9396926f*mW[i] + 0.1855740f*mX[i];
/* Precompute j(-0.3420201*W + 0.5098604*X) and store in mD. */
- std::transform(winput, winput+SamplesToDo, xinput, mWX.begin() + sWXInOffset,
- [](const float w, const float x) noexcept -> float
- { return -0.3420201f*w + 0.5098604f*x; });
- PShift.process({mD.data(), SamplesToDo}, mWX.data());
+ size_t curseg{mCurrentSegment};
+ for(size_t base{0};base < SamplesToDo;)
+ {
+ const size_t todo{minz(sSegmentSize-mFifoPos, SamplesToDo-base)};
+
+ /* Copy out the samples that were previously processed by the FFT. */
+ std::copy_n(mWXInOut.begin()+mFifoPos, todo, mD.begin()+base);
+
+ /* Transform the non-delayed input and store in the front half of the
+ * filter input.
+ */
+ std::transform(winput+base, winput+base+todo, xinput+base, mWXInOut.begin()+mFifoPos,
+ [](const float w, const float x) noexcept -> float
+ { return -0.3420201f*w + 0.5098604f*x; });
+
+ mFifoPos += todo;
+ base += todo;
+
+ /* Check whether the input buffer is filled with new samples. */
+ if(mFifoPos < sSegmentSize) break;
+ mFifoPos = 0;
+
+ /* Copy the new input to the next history segment, clearing the back
+ * half of the segment, and convert to the frequency domain.
+ */
+ float *input{mWXHistory.data() + curseg*sFftLength};
+ std::copy_n(mWXInOut.begin(), sSegmentSize, input);
+ std::fill_n(input+sSegmentSize, sSegmentSize, 0.0f);
+
+ pffft_transform(Filter.mFft.get(), input, input, mWorkData.data(), PFFFT_FORWARD);
+
+ /* Convolve each input segment with its IR filter counterpart (aligned
+ * in time, from newest to oldest).
+ */
+ mFftBuffer.fill(0.0f);
+ const float *filter{Filter.mFilterData.data()};
+ for(size_t s{curseg};s < sNumSegments;++s)
+ {
+ pffft_zconvolve_accumulate(Filter.mFft.get(), input, filter, mFftBuffer.data());
+ input += sFftLength;
+ filter += sFftLength;
+ }
+ input = mWXHistory.data();
+ for(size_t s{0};s < curseg;++s)
+ {
+ pffft_zconvolve_accumulate(Filter.mFft.get(), input, filter, mFftBuffer.data());
+ input += sFftLength;
+ filter += sFftLength;
+ }
+
+ /* Convert back to samples, writing to the output and storing the extra
+ * for next time.
+ */
+ pffft_transform(Filter.mFft.get(), mFftBuffer.data(), mFftBuffer.data(),
+ mWorkData.data(), PFFFT_BACKWARD);
+
+ for(size_t i{0};i < sSegmentSize;++i)
+ mWXInOut[i] = mFftBuffer[i] + mWXInOut[sSegmentSize+i];
+ for(size_t i{0};i < sSegmentSize;++i)
+ mWXInOut[sSegmentSize+i] = mFftBuffer[sSegmentSize+i];
+
+ /* Shift the input history. */
+ curseg = curseg ? (curseg-1) : (sNumSegments-1);
+ }
+ mCurrentSegment = curseg;
/* D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y */
for(size_t i{0};i < SamplesToDo;++i)
@@ -122,7 +291,6 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
std::copy(mW.cbegin()+SamplesToDo, mW.cbegin()+SamplesToDo+sFilterDelay, mW.begin());
std::copy(mX.cbegin()+SamplesToDo, mX.cbegin()+SamplesToDo+sFilterDelay, mX.begin());
std::copy(mY.cbegin()+SamplesToDo, mY.cbegin()+SamplesToDo+sFilterDelay, mY.begin());
- std::copy(mWX.cbegin()+SamplesToDo, mWX.cbegin()+SamplesToDo+sWXInOffset, mWX.begin());
/* Apply a delay to the existing output to align with the input delay. */
auto *delayBuffer = mDirectDelay.data();
@@ -133,7 +301,7 @@ void UhjEncoder<N>::encode(float *LeftOut, float *RightOut,
float *inout{al::assume_aligned<16>(buffer)};
auto inout_end = inout + SamplesToDo;
- if(SamplesToDo >= sFilterDelay) LIKELY
+ if(SamplesToDo >= sFilterDelay)
{
auto delay_end = std::rotate(inout, inout_end - sFilterDelay, inout_end);
std::swap_ranges(inout, delay_end, distbuf);
@@ -240,7 +408,7 @@ void UhjDecoder<N>::decode(const al::span<float*> samples, const size_t samplesT
{
static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large");
- const auto &PShift = GetPhaseShifter<N>::Get();
+ constexpr auto &PShift = PShifter<N>;
ASSUME(samplesToDo > 0);
@@ -313,11 +481,11 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT
const float *RESTRICT right{al::assume_aligned<16>(samples[1])};
/* S = Left + Right */
- for(size_t i{0};i < samplesToDo;++i)
+ for(size_t i{0};i < samplesToDo+sInputPadding;++i)
mS[i] = left[i] + right[i];
/* D = Left - Right */
- for(size_t i{0};i < samplesToDo;++i)
+ for(size_t i{0};i < samplesToDo+sInputPadding;++i)
mD[i] = left[i] - right[i];
}
@@ -326,14 +494,13 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT
float *RESTRICT youtput{al::assume_aligned<16>(samples[2])};
/* Precompute j(0.828331*D + 0.767820*T) and store in xoutput. */
- std::transform(mD.cbegin(), mD.cbegin()+samplesToDo, youtput, mTemp.begin(),
+ std::transform(mD.cbegin(), mD.cbegin()+sInputPadding+samplesToDo, youtput, mTemp.begin(),
[](const float d, const float t) noexcept { return 0.828331f*d + 0.767820f*t; });
- mFilter2DT.process(Filter2Coeff, {mTemp.data(), samplesToDo}, updateState, xoutput);
+ if(mFirstRun) mFilter2DT.processOne(Filter2Coeff, mTemp[0]);
+ mFilter2DT.process(Filter2Coeff, {mTemp.data()+1, samplesToDo}, updateState, xoutput);
/* Apply filter1 to S and store in mTemp. */
- mTemp[0] = mDelayS;
- mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data()+1);
- if(updateState) LIKELY mDelayS = mTemp[samplesToDo];
+ mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data());
/* W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T) */
for(size_t i{0};i < samplesToDo;++i)
@@ -346,12 +513,11 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT
/* Apply filter1 to (0.795968*D - 0.676392*T) and store in mTemp. */
std::transform(mD.cbegin(), mD.cbegin()+samplesToDo, youtput, youtput,
[](const float d, const float t) noexcept { return 0.795968f*d - 0.676392f*t; });
- mTemp[0] = mDelayDT;
- mFilter1DT.process(Filter1Coeff, {youtput, samplesToDo}, updateState, mTemp.data()+1);
- if(updateState) LIKELY mDelayDT = mTemp[samplesToDo];
+ mFilter1DT.process(Filter1Coeff, {youtput, samplesToDo}, updateState, mTemp.data());
/* Precompute j*S and store in youtput. */
- mFilter2S.process(Filter2Coeff, {mS.data(), samplesToDo}, updateState, youtput);
+ if(mFirstRun) mFilter2S.processOne(Filter2Coeff, mS[0]);
+ mFilter2S.process(Filter2Coeff, {mS.data()+1, samplesToDo}, updateState, youtput);
/* Y = 0.795968*D - 0.676392*T + j(0.186633*S) */
for(size_t i{0};i < samplesToDo;++i)
@@ -363,14 +529,14 @@ void UhjDecoderIIR::decode(const al::span<float*> samples, const size_t samplesT
float *RESTRICT zoutput{al::assume_aligned<16>(samples[3])};
/* Apply filter1 to Q and store in mTemp. */
- mTemp[0] = mDelayQ;
- mFilter1Q.process(Filter1Coeff, {zoutput, samplesToDo}, updateState, mTemp.data()+1);
- if(updateState) LIKELY mDelayQ = mTemp[samplesToDo];
+ mFilter1Q.process(Filter1Coeff, {zoutput, samplesToDo}, updateState, mTemp.data());
/* Z = 1.023332*Q */
for(size_t i{0};i < samplesToDo;++i)
zoutput[i] = 1.023332f*mTemp[i];
}
+
+ mFirstRun = false;
}
@@ -392,7 +558,7 @@ void UhjStereoDecoder<N>::decode(const al::span<float*> samples, const size_t sa
{
static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large");
- const auto &PShift = GetPhaseShifter<N>::Get();
+ constexpr auto &PShift = PShifter<N>;
ASSUME(samplesToDo > 0);
@@ -470,7 +636,7 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa
const float *RESTRICT left{al::assume_aligned<16>(samples[0])};
const float *RESTRICT right{al::assume_aligned<16>(samples[1])};
- for(size_t i{0};i < samplesToDo;++i)
+ for(size_t i{0};i < samplesToDo+sInputPadding;++i)
mS[i] = left[i] + right[i];
/* Pre-apply the width factor to the difference signal D. Smoothly
@@ -480,7 +646,7 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa
const float wcurrent{(mCurrentWidth < 0.0f) ? wtarget : mCurrentWidth};
if(wtarget == wcurrent || !updateState)
{
- for(size_t i{0};i < samplesToDo;++i)
+ for(size_t i{0};i < samplesToDo+sInputPadding;++i)
mD[i] = (left[i] - right[i]) * wcurrent;
mCurrentWidth = wcurrent;
}
@@ -493,6 +659,8 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa
mD[i] = (left[i] - right[i]) * (wcurrent + wstep*fi);
fi += 1.0f;
}
+ for(size_t i{samplesToDo};i < samplesToDo+sInputPadding;++i)
+ mD[i] = (left[i] - right[i]) * wtarget;
mCurrentWidth = wtarget;
}
}
@@ -502,12 +670,11 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa
float *RESTRICT youtput{al::assume_aligned<16>(samples[2])};
/* Apply filter1 to S and store in mTemp. */
- mTemp[0] = mDelayS;
- mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data()+1);
- if(updateState) LIKELY mDelayS = mTemp[samplesToDo];
+ mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data());
/* Precompute j*D and store in xoutput. */
- mFilter2D.process(Filter2Coeff, {mD.data(), samplesToDo}, updateState, xoutput);
+ if(mFirstRun) mFilter2D.processOne(Filter2Coeff, mD[0]);
+ mFilter2D.process(Filter2Coeff, {mD.data()+1, samplesToDo}, updateState, xoutput);
/* W = 0.6098637*S - 0.6896511*j*w*D */
for(size_t i{0};i < samplesToDo;++i)
@@ -517,16 +684,17 @@ void UhjStereoDecoderIIR::decode(const al::span<float*> samples, const size_t sa
xoutput[i] = 0.8624776f*mTemp[i] + 0.7626955f*xoutput[i];
/* Precompute j*S and store in youtput. */
- mFilter2S.process(Filter2Coeff, {mS.data(), samplesToDo}, updateState, youtput);
+ if(mFirstRun) mFilter2S.processOne(Filter2Coeff, mS[0]);
+ mFilter2S.process(Filter2Coeff, {mS.data()+1, samplesToDo}, updateState, youtput);
/* Apply filter1 to D and store in mTemp. */
- mTemp[0] = mDelayD;
- mFilter1D.process(Filter1Coeff, {mD.data(), samplesToDo}, updateState, mTemp.data()+1);
- if(updateState) LIKELY mDelayD = mTemp[samplesToDo];
+ mFilter1D.process(Filter1Coeff, {mD.data(), samplesToDo}, updateState, mTemp.data());
/* Y = 1.6822415*w*D - 0.2156194*j*S */
for(size_t i{0};i < samplesToDo;++i)
youtput[i] = 1.6822415f*mTemp[i] - 0.2156194f*youtput[i];
+
+ mFirstRun = false;
}
diff --git a/core/uhjfilter.h b/core/uhjfilter.h
index df308094..348dc7e1 100644
--- a/core/uhjfilter.h
+++ b/core/uhjfilter.h
@@ -29,6 +29,7 @@ struct UhjAllPassFilter {
};
std::array<AllPassState,4> mState;
+ void processOne(const al::span<const float,4> coeffs, float x);
void process(const al::span<const float,4> coeffs, const al::span<const float> src,
const bool update, float *RESTRICT dst);
};
@@ -50,7 +51,10 @@ struct UhjEncoderBase {
template<size_t N>
struct UhjEncoder final : public UhjEncoderBase {
- static constexpr size_t sFilterDelay{N/2};
+ static constexpr size_t sFftLength{256};
+ static constexpr size_t sSegmentSize{sFftLength/2};
+ static constexpr size_t sNumSegments{N/sSegmentSize};
+ static constexpr size_t sFilterDelay{N/2 + sSegmentSize};
/* Delays and processing storage for the input signal. */
alignas(16) std::array<float,BufferLineSize+sFilterDelay> mW{};
@@ -60,11 +64,12 @@ struct UhjEncoder final : public UhjEncoderBase {
alignas(16) std::array<float,BufferLineSize> mS{};
alignas(16) std::array<float,BufferLineSize> mD{};
- /* History and temp storage for the FIR filter. New samples should be
- * written to index sFilterDelay*2 - 1.
- */
- static constexpr size_t sWXInOffset{sFilterDelay*2 - 1};
- alignas(16) std::array<float,BufferLineSize + sFilterDelay*2> mWX{};
+ /* History and temp storage for the convolution filter. */
+ size_t mFifoPos{}, mCurrentSegment{};
+ alignas(16) std::array<float,sFftLength> mWXInOut{};
+ alignas(16) std::array<float,sFftLength> mFftBuffer{};
+ alignas(16) std::array<float,sFftLength> mWorkData{};
+ alignas(16) std::array<float,sFftLength*sNumSegments> mWXHistory{};
alignas(16) std::array<std::array<float,sFilterDelay>,2> mDirectDelay{};
@@ -77,8 +82,6 @@ struct UhjEncoder final : public UhjEncoderBase {
*/
void encode(float *LeftOut, float *RightOut, const al::span<const float*const,3> InSamples,
const size_t SamplesToDo) override;
-
- DEF_NEWDEL(UhjEncoder)
};
struct UhjEncoderIIR final : public UhjEncoderBase {
@@ -160,17 +163,18 @@ struct UhjDecoder final : public DecoderBase {
};
struct UhjDecoderIIR final : public DecoderBase {
- /* FIXME: These IIR decoder filters actually have a 1-sample delay on the
- * non-filtered components, which is not reflected in the source latency
- * value. sInputPadding is 0, however, because it doesn't need any extra
- * input samples.
+ /* These IIR decoder filters normally have a 1-sample delay on the non-
+ * filtered components. However, the filtered components are made to skip
+ * the first output sample and take one future sample, which puts it ahead
+ * by one sample. The first filtered output sample is cut to align it with
+ * the first non-filtered sample, similar to the FIR filters.
*/
- static constexpr size_t sInputPadding{0};
+ static constexpr size_t sInputPadding{1};
- alignas(16) std::array<float,BufferLineSize> mS{};
- alignas(16) std::array<float,BufferLineSize> mD{};
- alignas(16) std::array<float,BufferLineSize+1> mTemp{};
- float mDelayS{}, mDelayDT{}, mDelayQ{};
+ bool mFirstRun{true};
+ alignas(16) std::array<float,BufferLineSize+sInputPadding> mS{};
+ alignas(16) std::array<float,BufferLineSize+sInputPadding> mD{};
+ alignas(16) std::array<float,BufferLineSize+sInputPadding> mTemp{};
UhjAllPassFilter mFilter1S;
UhjAllPassFilter mFilter2DT;
@@ -211,14 +215,14 @@ struct UhjStereoDecoder final : public DecoderBase {
};
struct UhjStereoDecoderIIR final : public DecoderBase {
- static constexpr size_t sInputPadding{0};
+ static constexpr size_t sInputPadding{1};
+ bool mFirstRun{true};
float mCurrentWidth{-1.0f};
- alignas(16) std::array<float,BufferLineSize> mS{};
- alignas(16) std::array<float,BufferLineSize> mD{};
- alignas(16) std::array<float,BufferLineSize+1> mTemp{};
- float mDelayS{}, mDelayD{};
+ alignas(16) std::array<float,BufferLineSize+sInputPadding> mS{};
+ alignas(16) std::array<float,BufferLineSize+sInputPadding> mD{};
+ alignas(16) std::array<float,BufferLineSize> mTemp{};
UhjAllPassFilter mFilter1S;
UhjAllPassFilter mFilter2D;
diff --git a/core/uiddefs.cpp b/core/uiddefs.cpp
index 244c01a5..9471bba5 100644
--- a/core/uiddefs.cpp
+++ b/core/uiddefs.cpp
@@ -19,12 +19,8 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x
DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x00,0xa0,0xc9,0x25,0xcd,0x16);
DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e);
-DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6);
-DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2);
-DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2);
-DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4,0xde, 0x18,0x5c,0x39,0x5c,0xd3,0x17);
-#ifdef HAVE_WASAPI
+#if defined(HAVE_WASAPI) && !defined(ALSOFT_UWP)
#include <wtypes.h>
#include <devpropdef.h>
#include <propkeydef.h>
diff --git a/core/voice.cpp b/core/voice.cpp
index e8fbcccd..3889c42d 100644
--- a/core/voice.cpp
+++ b/core/voice.cpp
@@ -12,13 +12,12 @@
#include <iterator>
#include <memory>
#include <new>
+#include <optional>
#include <stdlib.h>
#include <utility>
#include <vector>
-#include "albyte.h"
#include "alnumeric.h"
-#include "aloptional.h"
#include "alspan.h"
#include "alstring.h"
#include "ambidefs.h"
@@ -129,7 +128,7 @@ inline HrtfMixerBlendFunc SelectHrtfBlendMixer()
} // namespace
-void Voice::InitMixer(al::optional<std::string> resampler)
+void Voice::InitMixer(std::optional<std::string> resampler)
{
if(resampler)
{
@@ -227,10 +226,9 @@ void SendSourceStoppedEvent(ContextBase *context, uint id)
auto evt_vec = ring->getWriteVector();
if(evt_vec.first.len < 1) return;
- AsyncEvent *evt{al::construct_at(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf),
- AsyncEvent::SourceStateChange)};
- evt->u.srcstate.id = id;
- evt->u.srcstate.state = AsyncEvent::SrcState::Stop;
+ auto &evt = InitAsyncEvent<AsyncSourceStateEvent>(evt_vec.first.buf);
+ evt.mId = id;
+ evt.mState = AsyncSrcState::Stop;
ring->writeAdvance(1);
}
@@ -264,7 +262,7 @@ const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *ds
template<FmtType Type>
-inline void LoadSamples(float *RESTRICT dstSamples, const al::byte *src, const size_t srcChan,
+inline void LoadSamples(float *RESTRICT dstSamples, const std::byte *src, const size_t srcChan,
const size_t srcOffset, const size_t srcStep, const size_t /*samplesPerBlock*/,
const size_t samplesToLoad) noexcept
{
@@ -275,7 +273,7 @@ inline void LoadSamples(float *RESTRICT dstSamples, const al::byte *src, const s
}
template<>
-inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src,
+inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const std::byte *src,
const size_t srcChan, const size_t srcOffset, const size_t srcStep,
const size_t samplesPerBlock, const size_t samplesToLoad) noexcept
{
@@ -289,14 +287,15 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src
/* NOTE: This could probably be optimized better. */
size_t wrote{0};
do {
+ static constexpr int MaxStepIndex{static_cast<int>(std::size(IMAStep_size)) - 1};
/* Each IMA4 block starts with a signed 16-bit sample, and a signed
* 16-bit table index. The table index needs to be clamped.
*/
- int sample{src[srcChan*4] | (src[srcChan*4 + 1] << 8)};
- int index{src[srcChan*4 + 2] | (src[srcChan*4 + 3] << 8)};
+ int sample{int(src[srcChan*4]) | (int(src[srcChan*4 + 1]) << 8)};
+ int index{int(src[srcChan*4 + 2]) | (int(src[srcChan*4 + 3]) << 8)};
sample = (sample^0x8000) - 32768;
- index = clampi((index^0x8000) - 32768, 0, al::size(IMAStep_size)-1);
+ index = clampi((index^0x8000) - 32768, 0, MaxStepIndex);
if(skip == 0)
{
@@ -312,7 +311,7 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src
sample = clampi(sample, -32768, 32767);
index += IMA4Index_adjust[nibble];
- index = clampi(index, 0, al::size(IMAStep_size)-1);
+ index = clampi(index, 0, MaxStepIndex);
return sample;
};
@@ -325,17 +324,17 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src
* always be less than the block size). They need to be decoded despite
* being ignored for proper state on the remaining samples.
*/
- const al::byte *nibbleData{src + (srcStep+srcChan)*4};
+ const std::byte *nibbleData{src + (srcStep+srcChan)*4};
size_t nibbleOffset{0};
const size_t startOffset{skip + 1};
for(;skip;--skip)
{
const size_t byteShift{(nibbleOffset&1) * 4};
- const size_t wordOffset{(nibbleOffset>>1) & ~size_t{3}};
+ const size_t wordOffset{(nibbleOffset>>1) & ~3_uz};
const size_t byteOffset{wordOffset*srcStep + ((nibbleOffset>>1)&3u)};
++nibbleOffset;
- std::ignore = decode_sample((nibbleData[byteOffset]>>byteShift) & 15u);
+ std::ignore = decode_sample(uint(nibbleData[byteOffset]>>byteShift) & 15u);
}
/* Second, decode the rest of the block and write to the output, until
@@ -345,11 +344,11 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src
for(size_t i{0};i < todo;++i)
{
const size_t byteShift{(nibbleOffset&1) * 4};
- const size_t wordOffset{(nibbleOffset>>1) & ~size_t{3}};
+ const size_t wordOffset{(nibbleOffset>>1) & ~3_uz};
const size_t byteOffset{wordOffset*srcStep + ((nibbleOffset>>1)&3u)};
++nibbleOffset;
- const int result{decode_sample((nibbleData[byteOffset]>>byteShift) & 15u)};
+ const int result{decode_sample(uint(nibbleData[byteOffset]>>byteShift) & 15u)};
dstSamples[wrote++] = static_cast<float>(result) / 32768.0f;
}
if(wrote == samplesToLoad)
@@ -360,7 +359,7 @@ inline void LoadSamples<FmtIMA4>(float *RESTRICT dstSamples, const al::byte *src
}
template<>
-inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte *src,
+inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const std::byte *src,
const size_t srcChan, const size_t srcOffset, const size_t srcStep,
const size_t samplesPerBlock, const size_t samplesToLoad) noexcept
{
@@ -377,19 +376,19 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte *
* nibble sample value. This is followed by the two initial 16-bit
* sample history values.
*/
- const al::byte *input{src};
- const uint8_t blockpred{std::min(input[srcChan], uint8_t{6})};
+ const std::byte *input{src};
+ const uint8_t blockpred{std::min(uint8_t(input[srcChan]), uint8_t{6})};
input += srcStep;
- int delta{input[2*srcChan + 0] | (input[2*srcChan + 1] << 8)};
+ int delta{int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1]) << 8)};
input += srcStep*2;
int sampleHistory[2]{};
- sampleHistory[0] = input[2*srcChan + 0] | (input[2*srcChan + 1]<<8);
+ sampleHistory[0] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8);
input += srcStep*2;
- sampleHistory[1] = input[2*srcChan + 0] | (input[2*srcChan + 1]<<8);
+ sampleHistory[1] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8);
input += srcStep*2;
- const auto coeffs = al::as_span(MSADPCMAdaptionCoeff[blockpred]);
+ const al::span coeffs{MSADPCMAdaptionCoeff[blockpred]};
delta = (delta^0x8000) - 32768;
sampleHistory[0] = (sampleHistory[0]^0x8000) - 32768;
sampleHistory[1] = (sampleHistory[1]^0x8000) - 32768;
@@ -439,7 +438,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte *
const size_t byteShift{((nibbleOffset&1)^1) * 4};
nibbleOffset += srcStep;
- std::ignore = decode_sample((input[byteOffset]>>byteShift) & 15);
+ std::ignore = decode_sample(int(input[byteOffset]>>byteShift) & 15);
}
/* Now decode the rest of the block, until the end of the block or the
@@ -452,7 +451,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte *
const size_t byteShift{((nibbleOffset&1)^1) * 4};
nibbleOffset += srcStep;
- const int sample{decode_sample((input[byteOffset]>>byteShift) & 15)};
+ const int sample{decode_sample(int(input[byteOffset]>>byteShift) & 15)};
dstSamples[wrote++] = static_cast<float>(sample) / 32768.0f;
}
if(wrote == samplesToLoad)
@@ -462,7 +461,7 @@ inline void LoadSamples<FmtMSADPCM>(float *RESTRICT dstSamples, const al::byte *
} while(true);
}
-void LoadSamples(float *dstSamples, const al::byte *src, const size_t srcChan,
+void LoadSamples(float *dstSamples, const std::byte *src, const size_t srcChan,
const size_t srcOffset, const FmtType srcType, const size_t srcStep,
const size_t samplesPerBlock, const size_t samplesToLoad) noexcept
{
@@ -475,6 +474,7 @@ void LoadSamples(float *dstSamples, const al::byte *src, const size_t srcChan,
{
HANDLE_FMT(FmtUByte);
HANDLE_FMT(FmtShort);
+ HANDLE_FMT(FmtInt);
HANDLE_FMT(FmtFloat);
HANDLE_FMT(FmtDouble);
HANDLE_FMT(FmtMulaw);
@@ -798,7 +798,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
using ResBufType = decltype(DeviceBase::mResampleData);
static constexpr uint srcSizeMax{static_cast<uint>(ResBufType{}.size()-MaxResamplerEdge)};
- const auto prevSamples = al::as_span(mPrevSamples[chan]);
+ const al::span prevSamples{mPrevSamples[chan]};
const auto resampleBuffer = std::copy(prevSamples.cbegin(), prevSamples.cend(),
Device->mResampleData.begin()) - MaxResamplerEdge;
int intPos{DataPosInt};
@@ -1101,7 +1101,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
{
const size_t byteOffset{blocksDone*mBytesPerBlock};
const size_t byteEnd{mNumCallbackBlocks*mBytesPerBlock};
- al::byte *data{BufferListItem->mSamples};
+ std::byte *data{BufferListItem->mSamples};
std::copy(data+byteOffset, data+byteEnd, data);
mNumCallbackBlocks -= blocksDone;
mCallbackBlockBase += blocksDone;
@@ -1145,16 +1145,15 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
/* Send any events now, after the position/buffer info was updated. */
const auto enabledevt = Context->mEnabledEvts.load(std::memory_order_acquire);
- if(buffers_done > 0 && enabledevt.test(AsyncEvent::BufferCompleted))
+ if(buffers_done > 0 && enabledevt.test(al::to_underlying(AsyncEnableBits::BufferCompleted)))
{
RingBuffer *ring{Context->mAsyncEvents.get()};
auto evt_vec = ring->getWriteVector();
if(evt_vec.first.len > 0)
{
- AsyncEvent *evt{al::construct_at(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf),
- AsyncEvent::BufferCompleted)};
- evt->u.bufcomp.id = SourceID;
- evt->u.bufcomp.count = buffers_done;
+ auto &evt = InitAsyncEvent<AsyncBufferCompleteEvent>(evt_vec.first.buf);
+ evt.mId = SourceID;
+ evt.mCount = buffers_done;
ring->writeAdvance(1);
}
}
@@ -1165,7 +1164,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi
* ensures any residual noise fades to 0 amplitude.
*/
mPlayState.store(Stopping, std::memory_order_release);
- if(enabledevt.test(AsyncEvent::SourceStateChange))
+ if(enabledevt.test(al::to_underlying(AsyncEnableBits::SourceState)))
SendSourceStoppedEvent(Context, SourceID);
}
}
@@ -1275,7 +1274,7 @@ void Voice::prepare(DeviceBase *device)
else if(mAmbiOrder && device->mAmbiOrder > mAmbiOrder)
{
const uint8_t *OrderFromChan{Is2DAmbisonic(mFmtChannels) ?
- AmbiIndex::OrderFrom2DChannel().data() : AmbiIndex::OrderFromChannel().data()};
+ AmbiIndex::OrderFrom2DChannel.data() : AmbiIndex::OrderFromChannel.data()};
const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder,
device->m2DMixing);
diff --git a/core/voice.h b/core/voice.h
index 57ee7b01..a599eda8 100644
--- a/core/voice.h
+++ b/core/voice.h
@@ -5,13 +5,12 @@
#include <atomic>
#include <bitset>
#include <chrono>
+#include <cstddef>
#include <memory>
-#include <stddef.h>
+#include <optional>
#include <string>
-#include "albyte.h"
#include "almalloc.h"
-#include "aloptional.h"
#include "alspan.h"
#include "bufferline.h"
#include "buffer_storage.h"
@@ -100,7 +99,7 @@ struct VoiceBufferItem {
uint mLoopStart{0u};
uint mLoopEnd{0u};
- al::byte *mSamples{nullptr};
+ std::byte *mSamples{nullptr};
};
@@ -270,7 +269,7 @@ struct Voice {
void prepare(DeviceBase *device);
- static void InitMixer(al::optional<std::string> resampler);
+ static void InitMixer(std::optional<std::string> resampler);
DEF_NEWDEL(Voice)
};
diff --git a/docs/ambisonics.txt b/docs/ambisonics.txt
index b1b111d6..7798c8f9 100644
--- a/docs/ambisonics.txt
+++ b/docs/ambisonics.txt
@@ -12,7 +12,7 @@ What Is It?
Originally developed in the 1970s by Michael Gerzon and a team others,
Ambisonics was created as a means of recording and playing back 3D sound.
-Taking advantage of the way sound waves propogate, it is possible to record a
+Taking advantage of the way sound waves propagate, it is possible to record a
fully 3D soundfield using as few as 4 channels (or even just 3, if you don't
mind dropping down to 2 dimensions like many surround sound systems are). This
representation is called B-Format. It was designed to handle audio independent
@@ -63,7 +63,7 @@ remain correct over a larger area around the center of the speakers.
In addition, Ambisonics can encode the near-field effect of sounds, effectively
capturing the sound distance. The near-field effect is a subtle low-frequency
boost as a result of wave-front curvature, and properly compensating for this
-occuring with the output speakers (as well as emulating it with a synthesized
+occurring with the output speakers (as well as emulating it with a synthesized
soundfield) can create an improved sense of distance for sounds that move near
or far.
diff --git a/docs/env-vars.txt b/docs/env-vars.txt
index 815a3098..0c15cbe9 100644
--- a/docs/env-vars.txt
+++ b/docs/env-vars.txt
@@ -78,6 +78,15 @@ Same as for __ALSOFT_REVERSE_Z, but for Y (up/down) panning.
__ALSOFT_REVERSE_X
Same as for __ALSOFT_REVERSE_Z, but for X (left/right) panning.
+__ALSOFT_DEFAULT_ERROR
+Applications that erroneously call alGetError prior to setting a context as
+current may not like that OpenAL Soft returns 0xA004 (AL_INVALID_OPERATION),
+indicating that the call could not be executed as there's no context to get the
+error value from. This can be set to 0 (AL_NO_ERROR) to let such apps pass the
+check despite the problem. Other applications, however, may see AL_NO_ERROR
+returned and assume any previous AL calls succeeded when they actually failed,
+so this should only be set when necessary.
+
__ALSOFT_SUSPEND_CONTEXT
Due to the OpenAL spec not being very clear about them, behavior of the
alcSuspendContext and alcProcessContext methods has varied, and because of
diff --git a/examples/alconvolve.c b/examples/alconvolve.c
index 93fd2eb4..8580d443 100644
--- a/examples/alconvolve.c
+++ b/examples/alconvolve.c
@@ -22,7 +22,7 @@
* THE SOFTWARE.
*/
-/* This file contains an example for applying convolution reverb to a source. */
+/* This file contains an example for applying convolution to a source. */
#include <assert.h>
#include <inttypes.h>
@@ -39,9 +39,9 @@
#include "common/alhelpers.h"
-#ifndef AL_SOFT_convolution_reverb
-#define AL_SOFT_convolution_reverb
-#define AL_EFFECT_CONVOLUTION_REVERB_SOFT 0xA000
+#ifndef AL_SOFT_convolution_effect
+#define AL_SOFT_convolution_effect
+#define AL_EFFECT_CONVOLUTION_SOFT 0xA000
#endif
@@ -278,21 +278,21 @@ static int UpdatePlayer(StreamPlayer *player)
}
-/* CreateEffect creates a new OpenAL effect object with a convolution reverb
- * type, and returns the new effect ID.
+/* CreateEffect creates a new OpenAL effect object with a convolution type, and
+ * returns the new effect ID.
*/
static ALuint CreateEffect(void)
{
ALuint effect = 0;
ALenum err;
- printf("Using Convolution Reverb\n");
+ printf("Using Convolution\n");
- /* Create the effect object and set the convolution reverb effect type. */
+ /* Create the effect object and set the convolution effect type. */
alGenEffects(1, &effect);
- alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_CONVOLUTION_REVERB_SOFT);
+ alEffecti(effect, AL_EFFECT_TYPE, AL_EFFECT_CONVOLUTION_SOFT);
- /* Check if an error occured, and clean up if so. */
+ /* Check if an error occurred, and clean up if so. */
err = alGetError();
if(err != AL_NO_ERROR)
{
@@ -391,7 +391,7 @@ static ALuint LoadSound(const char *filename)
free(membuf);
sf_close(sndfile);
- /* Check if an error occured, and clean up if so. */
+ /* Check if an error occurred, and clean up if so. */
err = alGetError();
if(err != AL_NO_ERROR)
{
@@ -423,10 +423,10 @@ int main(int argc, char **argv)
if(InitAL(&argv, &argc) != 0)
return 1;
- if(!alIsExtensionPresent("AL_SOFTX_convolution_reverb"))
+ if(!alIsExtensionPresent("AL_SOFTX_convolution_effect"))
{
CloseAL();
- fprintf(stderr, "Error: Convolution revern not supported\n");
+ fprintf(stderr, "Error: Convolution effect not supported\n");
return 1;
}
@@ -500,11 +500,11 @@ int main(int argc, char **argv)
alGenAuxiliaryEffectSlots(1, &slot);
/* Set the impulse response sound buffer on the effect slot. This allows
- * effects to access it as needed. In this case, convolution reverb uses it
- * as the filter source. NOTE: Unlike the effect object, the buffer *is*
- * kept referenced and may not be changed or deleted as long as it's set,
- * just like with a source. When another buffer is set, or the effect slot
- * is deleted, the buffer reference is released.
+ * effects to access it as needed. In this case, convolution uses it as the
+ * filter source. NOTE: Unlike the effect object, the buffer *is* kept
+ * referenced and may not be changed or deleted as long as it's set, just
+ * like with a source. When another buffer is set, or the effect slot is
+ * deleted, the buffer reference is released.
*
* The effect slot's gain is reduced because the impulse responses I've
* tested with result in excessively loud reverb. Is that normal? Even with
diff --git a/examples/alffplay.cpp b/examples/alffplay.cpp
index ae40a51a..1f02ef70 100644
--- a/examples/alffplay.cpp
+++ b/examples/alffplay.cpp
@@ -339,14 +339,14 @@ struct AudioState {
}
static void AL_APIENTRY eventCallbackC(ALenum eventType, ALuint object, ALuint param,
- ALsizei length, const ALchar *message, void *userParam)
+ ALsizei length, const ALchar *message, void *userParam) noexcept
{ static_cast<AudioState*>(userParam)->eventCallback(eventType, object, param, length, message); }
void eventCallback(ALenum eventType, ALuint object, ALuint param, ALsizei length,
- const ALchar *message);
+ const ALchar *message) noexcept;
- static ALsizei AL_APIENTRY bufferCallbackC(void *userptr, void *data, ALsizei size)
+ static ALsizei AL_APIENTRY bufferCallbackC(void *userptr, void *data, ALsizei size) noexcept
{ return static_cast<AudioState*>(userptr)->bufferCallback(data, size); }
- ALsizei bufferCallback(void *data, ALsizei size);
+ ALsizei bufferCallback(void *data, ALsizei size) noexcept;
nanoseconds getClockNoLock();
nanoseconds getClock()
@@ -840,7 +840,7 @@ bool AudioState::readAudio(int sample_skip)
void AL_APIENTRY AudioState::eventCallback(ALenum eventType, ALuint object, ALuint param,
- ALsizei length, const ALchar *message)
+ ALsizei length, const ALchar *message) noexcept
{
if(eventType == AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT)
{
@@ -878,7 +878,7 @@ void AL_APIENTRY AudioState::eventCallback(ALenum eventType, ALuint object, ALui
}
}
-ALsizei AudioState::bufferCallback(void *data, ALsizei size)
+ALsizei AudioState::bufferCallback(void *data, ALsizei size) noexcept
{
ALsizei got{0};
diff --git a/examples/alhrtf.c b/examples/alhrtf.c
index d878870e..7ea1b99e 100644
--- a/examples/alhrtf.c
+++ b/examples/alhrtf.c
@@ -121,7 +121,7 @@ static ALuint LoadSound(const char *filename)
free(membuf);
sf_close(sndfile);
- /* Check if an error occured, and clean up if so. */
+ /* Check if an error occurred, and clean up if so. */
err = alGetError();
if(err != AL_NO_ERROR)
{
diff --git a/examples/allatency.c b/examples/allatency.c
index ab4a4ebc..01f4eb69 100644
--- a/examples/allatency.c
+++ b/examples/allatency.c
@@ -124,7 +124,7 @@ static ALuint LoadSound(const char *filename)
free(membuf);
sf_close(sndfile);
- /* Check if an error occured, and clean up if so. */
+ /* Check if an error occurred, and clean up if so. */
err = alGetError();
if(err != AL_NO_ERROR)
{
diff --git a/examples/alloopback.c b/examples/alloopback.c
index 56cd420f..964a0cdb 100644
--- a/examples/alloopback.c
+++ b/examples/alloopback.c
@@ -118,7 +118,7 @@ static ALuint CreateSineWave(void)
alGenBuffers(1, &buffer);
alBufferData(buffer, AL_FORMAT_MONO16, data, sizeof(data), 44100);
- /* Check if an error occured, and clean up if so. */
+ /* Check if an error occurred, and clean up if so. */
err = alGetError();
if(err != AL_NO_ERROR)
{
diff --git a/examples/almultireverb.c b/examples/almultireverb.c
index a77cc59e..dcb76c87 100644
--- a/examples/almultireverb.c
+++ b/examples/almultireverb.c
@@ -137,7 +137,7 @@ static int LoadEffect(ALuint effect, const EFXEAXREVERBPROPERTIES *reverb)
alEffectf(effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, reverb->flRoomRolloffFactor);
alEffecti(effect, AL_EAXREVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit);
- /* Check if an error occured, and return failure if so. */
+ /* Check if an error occurred, and return failure if so. */
if((err=alGetError()) != AL_NO_ERROR)
{
fprintf(stderr, "Error setting up reverb: %s\n", alGetString(err));
@@ -210,7 +210,7 @@ static ALuint LoadSound(const char *filename)
free(membuf);
sf_close(sndfile);
- /* Check if an error occured, and clean up if so. */
+ /* Check if an error occurred, and clean up if so. */
err = alGetError();
if(err != AL_NO_ERROR)
{
@@ -493,7 +493,7 @@ int main(int argc, char **argv)
}
if(argc < 1)
{
- fprintf(stderr, "No filename spacified.\n");
+ fprintf(stderr, "No filename specified.\n");
CloseAL();
return 1;
}
diff --git a/examples/alplay.c b/examples/alplay.c
index 4291cb47..1eabcccd 100644
--- a/examples/alplay.c
+++ b/examples/alplay.c
@@ -266,7 +266,7 @@ static ALuint LoadSound(const char *filename)
free(membuf);
sf_close(sndfile);
- /* Check if an error occured, and clean up if so. */
+ /* Check if an error occurred, and clean up if so. */
err = alGetError();
if(err != AL_NO_ERROR)
{
diff --git a/examples/alreverb.c b/examples/alreverb.c
index 11a3ac6b..ff49db25 100644
--- a/examples/alreverb.c
+++ b/examples/alreverb.c
@@ -132,7 +132,7 @@ static ALuint LoadEffect(const EFXEAXREVERBPROPERTIES *reverb)
alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, reverb->iDecayHFLimit);
}
- /* Check if an error occured, and clean up if so. */
+ /* Check if an error occurred, and clean up if so. */
err = alGetError();
if(err != AL_NO_ERROR)
{
@@ -219,7 +219,7 @@ static ALuint LoadSound(const char *filename)
free(membuf);
sf_close(sndfile);
- /* Check if an error occured, and clean up if so. */
+ /* Check if an error occurred, and clean up if so. */
err = alGetError();
if(err != AL_NO_ERROR)
{
diff --git a/examples/alstreamcb.cpp b/examples/alstreamcb.cpp
index a2e7b659..6cc24034 100644
--- a/examples/alstreamcb.cpp
+++ b/examples/alstreamcb.cpp
@@ -276,9 +276,9 @@ struct StreamPlayer {
* but it allows the callback implementation to have a nice 'this' pointer
* with normal member access.
*/
- static ALsizei AL_APIENTRY bufferCallbackC(void *userptr, void *data, ALsizei size)
+ static ALsizei AL_APIENTRY bufferCallbackC(void *userptr, void *data, ALsizei size) noexcept
{ return static_cast<StreamPlayer*>(userptr)->bufferCallback(data, size); }
- ALsizei bufferCallback(void *data, ALsizei size)
+ ALsizei bufferCallback(void *data, ALsizei size) noexcept
{
/* NOTE: The callback *MUST* be real-time safe! That means no blocking,
* no allocations or deallocations, no I/O, no page faults, or calls to
diff --git a/examples/altonegen.c b/examples/altonegen.c
index 75db2d6b..ec7f77bb 100644
--- a/examples/altonegen.c
+++ b/examples/altonegen.c
@@ -79,63 +79,72 @@ static inline ALuint dither_rng(ALuint *seed)
return *seed;
}
-static void ApplySin(ALfloat *data, ALdouble g, ALuint srate, ALuint freq)
+static void ApplySin(ALfloat *data, ALuint length, ALdouble g, ALuint srate, ALuint freq)
{
- ALdouble smps_per_cycle = (ALdouble)srate / freq;
+ ALdouble cycles_per_sample = (ALdouble)freq / srate;
ALuint i;
- for(i = 0;i < srate;i++)
+ for(i = 0;i < length;i++)
{
ALdouble ival;
- data[i] += (ALfloat)(sin(modf(i/smps_per_cycle, &ival) * 2.0*M_PI) * g);
+ data[i] += (ALfloat)(sin(modf(i*cycles_per_sample, &ival) * 2.0*M_PI) * g);
}
}
/* Generates waveforms using additive synthesis. Each waveform is constructed
* by summing one or more sine waves, up to (and excluding) nyquist.
*/
-static ALuint CreateWave(enum WaveType type, ALuint freq, ALuint srate, ALfloat gain)
+static ALuint CreateWave(enum WaveType type, ALuint seconds, ALuint freq, ALuint srate,
+ ALfloat gain)
{
ALuint seed = 22222;
+ ALuint num_samples;
ALuint data_size;
ALfloat *data;
ALuint buffer;
ALenum err;
ALuint i;
- data_size = (ALuint)(srate * sizeof(ALfloat));
+ if(seconds > INT_MAX / srate / sizeof(ALfloat))
+ {
+ fprintf(stderr, "Too many seconds: %u * %u * %zu > %d\n", seconds, srate, sizeof(ALfloat),
+ INT_MAX);
+ return 0;
+ }
+
+ num_samples = seconds * srate;
+
+ data_size = (ALuint)(num_samples * sizeof(ALfloat));
data = calloc(1, data_size);
switch(type)
{
case WT_Sine:
- ApplySin(data, 1.0, srate, freq);
+ ApplySin(data, num_samples, 1.0, srate, freq);
break;
case WT_Square:
for(i = 1;freq*i < srate/2;i+=2)
- ApplySin(data, 4.0/M_PI * 1.0/i, srate, freq*i);
+ ApplySin(data, num_samples, 4.0/M_PI * 1.0/i, srate, freq*i);
break;
case WT_Sawtooth:
for(i = 1;freq*i < srate/2;i++)
- ApplySin(data, 2.0/M_PI * ((i&1)*2 - 1.0) / i, srate, freq*i);
+ ApplySin(data, num_samples, 2.0/M_PI * ((i&1)*2 - 1.0) / i, srate, freq*i);
break;
case WT_Triangle:
for(i = 1;freq*i < srate/2;i+=2)
- ApplySin(data, 8.0/(M_PI*M_PI) * (1.0 - (i&2)) / (i*i), srate, freq*i);
+ ApplySin(data, num_samples, 8.0/(M_PI*M_PI) * (1.0 - (i&2)) / (i*i), srate, freq*i);
break;
case WT_Impulse:
/* NOTE: Impulse isn't handled using additive synthesis, and is
- * instead just a non-0 sample at a given rate. This can still be
- * useful to test (other than resampling, the ALSOFT_DEFAULT_REVERB
- * environment variable can prove useful here to test the reverb
- * response).
+ * instead just a non-0 sample. This can be useful to test (other
+ * than resampling, the ALSOFT_DEFAULT_REVERB environment variable
+ * can test the reverb response).
*/
- for(i = 0;i < srate;i++)
- data[i] = (i%(srate/freq)) ? 0.0f : 1.0f;
+ data[0] = 1.0f;
break;
case WT_WhiteNoise:
/* NOTE: WhiteNoise is just uniform set of uncorrelated values, and
* is not influenced by the waveform frequency.
*/
- for(i = 0;i < srate;i++)
+ for(i = 0;i < num_samples;i++)
{
ALuint rng0 = dither_rng(&seed);
ALuint rng1 = dither_rng(&seed);
@@ -146,7 +155,7 @@ static ALuint CreateWave(enum WaveType type, ALuint freq, ALuint srate, ALfloat
if(gain != 1.0f)
{
- for(i = 0;i < srate;i++)
+ for(i = 0;i < num_samples;i++)
data[i] *= gain;
}
@@ -156,7 +165,7 @@ static ALuint CreateWave(enum WaveType type, ALuint freq, ALuint srate, ALfloat
alBufferData(buffer, AL_FORMAT_MONO_FLOAT32, data, (ALsizei)data_size, (ALsizei)srate);
free(data);
- /* Check if an error occured, and clean up if so. */
+ /* Check if an error occurred, and clean up if so. */
err = alGetError();
if(err != AL_NO_ERROR)
{
@@ -175,8 +184,8 @@ int main(int argc, char *argv[])
enum WaveType wavetype = WT_Sine;
const char *appname = argv[0];
ALuint source, buffer;
- ALint last_pos, num_loops;
- ALint max_loops = 4;
+ ALint last_pos;
+ ALint seconds = 4;
ALint srate = -1;
ALint tone_freq = 1000;
ALCint dev_rate;
@@ -220,7 +229,9 @@ int main(int argc, char *argv[])
else if(i+1 < argc && strcmp(argv[i], "-t") == 0)
{
i++;
- max_loops = atoi(argv[i]) - 1;
+ seconds = atoi(argv[i]);
+ if(seconds <= 0)
+ seconds = 4;
}
else if(i+1 < argc && (strcmp(argv[i], "--waveform") == 0 || strcmp(argv[i], "-w") == 0))
{
@@ -281,7 +292,7 @@ int main(int argc, char *argv[])
srate = dev_rate;
/* Load the sound into a buffer. */
- buffer = CreateWave(wavetype, (ALuint)tone_freq, (ALuint)srate, gain);
+ buffer = CreateWave(wavetype, (ALuint)seconds, (ALuint)tone_freq, (ALuint)srate, gain);
if(!buffer)
{
CloseAL();
@@ -289,7 +300,7 @@ int main(int argc, char *argv[])
}
printf("Playing %dhz %s-wave tone with %dhz sample rate and %dhz output, for %d second%s...\n",
- tone_freq, GetWaveTypeName(wavetype), srate, dev_rate, max_loops+1, max_loops?"s":"");
+ tone_freq, GetWaveTypeName(wavetype), srate, dev_rate, seconds, (seconds!=1)?"s":"");
fflush(stdout);
/* Create the source to play the sound with. */
@@ -299,21 +310,19 @@ int main(int argc, char *argv[])
assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
/* Play the sound for a while. */
- num_loops = 0;
- last_pos = 0;
- alSourcei(source, AL_LOOPING, (max_loops > 0) ? AL_TRUE : AL_FALSE);
+ last_pos = -1;
alSourcePlay(source);
do {
ALint pos;
al_nssleep(10000000);
- alGetSourcei(source, AL_SAMPLE_OFFSET, &pos);
alGetSourcei(source, AL_SOURCE_STATE, &state);
- if(pos < last_pos && state == AL_PLAYING)
+ alGetSourcei(source, AL_SAMPLE_OFFSET, &pos);
+ pos /= srate;
+
+ if(pos > last_pos)
{
- ++num_loops;
- if(num_loops >= max_loops)
- alSourcei(source, AL_LOOPING, AL_FALSE);
- printf("%d...\n", max_loops - num_loops + 1);
+ last_pos = 0;
+ printf("%d...\n", seconds - pos);
fflush(stdout);
}
last_pos = pos;
diff --git a/include/AL/al.h b/include/AL/al.h
index 5071fa5e..87274184 100644
--- a/include/AL/al.h
+++ b/include/AL/al.h
@@ -3,6 +3,25 @@
#ifdef __cplusplus
extern "C" {
+
+#ifndef AL_DISABLE_NOEXCEPT
+#define AL_API_NOEXCEPT noexcept
+#if __cplusplus >= 201703L
+#define AL_API_NOEXCEPT17 noexcept
+#else
+#define AL_API_NOEXCEPT17
+#endif
+
+#else /* AL_DISABLE_NOEXCEPT */
+
+#define AL_API_NOEXCEPT
+#define AL_API_NOEXCEPT17
+#endif
+
+#else /* __cplusplus */
+
+#define AL_API_NOEXCEPT
+#define AL_API_NOEXCEPT17
#endif
#ifndef AL_API
@@ -455,217 +474,217 @@ typedef void ALvoid;
#ifndef AL_NO_PROTOTYPES
/* Renderer State management. */
-AL_API void AL_APIENTRY alEnable(ALenum capability);
-AL_API void AL_APIENTRY alDisable(ALenum capability);
-AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability);
+AL_API void AL_APIENTRY alEnable(ALenum capability) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alDisable(ALenum capability) AL_API_NOEXCEPT;
+AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability) AL_API_NOEXCEPT;
/* Context state setting. */
-AL_API void AL_APIENTRY alDopplerFactor(ALfloat value);
-AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value);
-AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value);
-AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel);
+AL_API void AL_APIENTRY alDopplerFactor(ALfloat value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel) AL_API_NOEXCEPT;
/* Context state retrieval. */
-AL_API const ALchar* AL_APIENTRY alGetString(ALenum param);
-AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values);
-AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values);
-AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values);
-AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values);
-AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param);
-AL_API ALint AL_APIENTRY alGetInteger(ALenum param);
-AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param);
-AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param);
+AL_API const ALchar* AL_APIENTRY alGetString(ALenum param) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values) AL_API_NOEXCEPT;
+AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param) AL_API_NOEXCEPT;
+AL_API ALint AL_APIENTRY alGetInteger(ALenum param) AL_API_NOEXCEPT;
+AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param) AL_API_NOEXCEPT;
+AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param) AL_API_NOEXCEPT;
/**
* Obtain the first error generated in the AL context since the last call to
* this function.
*/
-AL_API ALenum AL_APIENTRY alGetError(void);
+AL_API ALenum AL_APIENTRY alGetError(void) AL_API_NOEXCEPT;
/** Query for the presence of an extension on the AL context. */
-AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname);
+AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname) AL_API_NOEXCEPT;
/**
* Retrieve the address of a function. The returned function may be context-
* specific.
*/
-AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname);
+AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname) AL_API_NOEXCEPT;
/**
* Retrieve the value of an enum. The returned value may be context-specific.
*/
-AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename);
+AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename) AL_API_NOEXCEPT;
/* Set listener parameters. */
-AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value);
-AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
-AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values);
-AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value);
-AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3);
-AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values);
+AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values) AL_API_NOEXCEPT;
/* Get listener parameters. */
-AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value);
-AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
-AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values);
-AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value);
-AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3);
-AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values);
+AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values) AL_API_NOEXCEPT;
/** Create source objects. */
-AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources);
+AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) AL_API_NOEXCEPT;
/** Delete source objects. */
-AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources);
+AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT;
/** Verify an ID is for a valid source. */
-AL_API ALboolean AL_APIENTRY alIsSource(ALuint source);
+AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) AL_API_NOEXCEPT;
/* Set source parameters. */
-AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value);
-AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
-AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values);
-AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value);
-AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3);
-AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values);
+AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT;
/* Get source parameters. */
-AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value);
-AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
-AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values);
-AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value);
-AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3);
-AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values);
+AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT;
/** Play, restart, or resume a source, setting its state to AL_PLAYING. */
-AL_API void AL_APIENTRY alSourcePlay(ALuint source);
+AL_API void AL_APIENTRY alSourcePlay(ALuint source) AL_API_NOEXCEPT;
/** Stop a source, setting its state to AL_STOPPED if playing or paused. */
-AL_API void AL_APIENTRY alSourceStop(ALuint source);
+AL_API void AL_APIENTRY alSourceStop(ALuint source) AL_API_NOEXCEPT;
/** Rewind a source, setting its state to AL_INITIAL. */
-AL_API void AL_APIENTRY alSourceRewind(ALuint source);
+AL_API void AL_APIENTRY alSourceRewind(ALuint source) AL_API_NOEXCEPT;
/** Pause a source, setting its state to AL_PAUSED if playing. */
-AL_API void AL_APIENTRY alSourcePause(ALuint source);
+AL_API void AL_APIENTRY alSourcePause(ALuint source) AL_API_NOEXCEPT;
/** Play, restart, or resume a list of sources atomically. */
-AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources);
+AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT;
/** Stop a list of sources atomically. */
-AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources);
+AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT;
/** Rewind a list of sources atomically. */
-AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources);
+AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT;
/** Pause a list of sources atomically. */
-AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources);
+AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT;
/** Queue buffers onto a source */
-AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers);
+AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT;
/** Unqueue processed buffers from a source */
-AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers);
+AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT;
/** Create buffer objects */
-AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers);
+AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) AL_API_NOEXCEPT;
/** Delete buffer objects */
-AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers);
+AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT;
/** Verify an ID is a valid buffer (including the NULL buffer) */
-AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer);
+AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer) AL_API_NOEXCEPT;
/**
* Copies data into the buffer, interpreting it using the specified format and
* samplerate.
*/
-AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate);
+AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT;
/* Set buffer parameters. */
-AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value);
-AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
-AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values);
-AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value);
-AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3);
-AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values);
+AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT;
/* Get buffer parameters. */
-AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value);
-AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
-AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values);
-AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value);
-AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3);
-AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values);
+AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT;
#endif /* AL_NO_PROTOTYPES */
/* Pointer-to-function types, useful for storing dynamically loaded AL entry
* points.
*/
-typedef void (AL_APIENTRY *LPALENABLE)(ALenum capability);
-typedef void (AL_APIENTRY *LPALDISABLE)(ALenum capability);
-typedef ALboolean (AL_APIENTRY *LPALISENABLED)(ALenum capability);
-typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param);
-typedef void (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values);
-typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values);
-typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values);
-typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values);
-typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param);
-typedef ALint (AL_APIENTRY *LPALGETINTEGER)(ALenum param);
-typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)(ALenum param);
-typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)(ALenum param);
-typedef ALenum (AL_APIENTRY *LPALGETERROR)(void);
-typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname);
-typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname);
-typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename);
-typedef void (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value);
-typedef void (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
-typedef void (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values);
-typedef void (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value);
-typedef void (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3);
-typedef void (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values);
-typedef void (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value);
-typedef void (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
-typedef void (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values);
-typedef void (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value);
-typedef void (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3);
-typedef void (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values);
-typedef void (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources);
-typedef void (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources);
-typedef ALboolean (AL_APIENTRY *LPALISSOURCE)(ALuint source);
-typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value);
-typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
-typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values);
-typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value);
-typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3);
-typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values);
-typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value);
-typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
-typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values);
-typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value);
-typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3);
-typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values);
-typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources);
-typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources);
-typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources);
-typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources);
-typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source);
-typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source);
-typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source);
-typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source);
-typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers);
-typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers);
-typedef void (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers);
-typedef void (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers);
-typedef ALboolean (AL_APIENTRY *LPALISBUFFER)(ALuint buffer);
-typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate);
-typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value);
-typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3);
-typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values);
-typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value);
-typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3);
-typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values);
-typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value);
-typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3);
-typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values);
-typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value);
-typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3);
-typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values);
-typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value);
-typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value);
-typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value);
-typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel);
+typedef void (AL_APIENTRY *LPALENABLE)(ALenum capability) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDISABLE)(ALenum capability) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISENABLED)(ALenum capability) AL_API_NOEXCEPT17;
+typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param) AL_API_NOEXCEPT17;
+typedef ALint (AL_APIENTRY *LPALGETINTEGER)(ALenum param) AL_API_NOEXCEPT17;
+typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)(ALenum param) AL_API_NOEXCEPT17;
+typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)(ALenum param) AL_API_NOEXCEPT17;
+typedef ALenum (AL_APIENTRY *LPALGETERROR)(void) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname) AL_API_NOEXCEPT17;
+typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname) AL_API_NOEXCEPT17;
+typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISSOURCE)(ALuint source) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISBUFFER)(ALuint buffer) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel) AL_API_NOEXCEPT17;
#ifdef __cplusplus
} /* extern "C" */
diff --git a/include/AL/alc.h b/include/AL/alc.h
index 6d210333..73dcf08f 100644
--- a/include/AL/alc.h
+++ b/include/AL/alc.h
@@ -3,6 +3,25 @@
#ifdef __cplusplus
extern "C" {
+
+#ifndef AL_DISABLE_NOEXCEPT
+#define ALC_API_NOEXCEPT noexcept
+#if __cplusplus >= 201703L
+#define ALC_API_NOEXCEPT17 noexcept
+#else
+#define ALC_API_NOEXCEPT17
+#endif
+
+#else /* AL_DISABLE_NOEXCEPT */
+
+#define ALC_API_NOEXCEPT
+#define ALC_API_NOEXCEPT17
+#endif
+
+#else /* __cplusplus */
+
+#define ALC_API_NOEXCEPT
+#define ALC_API_NOEXCEPT17
#endif
#ifndef ALC_API
@@ -172,34 +191,34 @@ typedef void ALCvoid;
/* Context management. */
/** Create and attach a context to the given device. */
-ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist);
+ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist) ALC_API_NOEXCEPT;
/**
* Makes the given context the active process-wide context. Passing NULL clears
* the active context.
*/
-ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context);
+ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) ALC_API_NOEXCEPT;
/** Resumes processing updates for the given context. */
-ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context);
+ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) ALC_API_NOEXCEPT;
/** Suspends updates for the given context. */
-ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context);
+ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) ALC_API_NOEXCEPT;
/** Remove a context from its device and destroys it. */
-ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context);
+ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) ALC_API_NOEXCEPT;
/** Returns the currently active context. */
-ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void);
+ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) ALC_API_NOEXCEPT;
/** Returns the device that a particular context is attached to. */
-ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context);
+ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context) ALC_API_NOEXCEPT;
/* Device management. */
/** Opens the named playback device. */
-ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename);
+ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename) ALC_API_NOEXCEPT;
/** Closes the given playback device. */
-ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device);
+ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) ALC_API_NOEXCEPT;
/* Error support. */
/** Obtain the most recent Device error. */
-ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device);
+ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) ALC_API_NOEXCEPT;
/* Extension support. */
@@ -207,24 +226,24 @@ ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device);
* Query for the presence of an extension on the device. Pass a NULL device to
* query a device-inspecific extension.
*/
-ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname);
+ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname) ALC_API_NOEXCEPT;
/**
* Retrieve the address of a function. Given a non-NULL device, the returned
* function may be device-specific.
*/
-ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname);
+ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname) ALC_API_NOEXCEPT;
/**
* Retrieve the value of an enum. Given a non-NULL device, the returned value
* may be device-specific.
*/
-ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname);
+ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname) ALC_API_NOEXCEPT;
/* Query functions. */
/** Returns information about the device, and error strings. */
-ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param);
+ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param) ALC_API_NOEXCEPT;
/** Returns information about the device and the version of OpenAL. */
-ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values);
+ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) ALC_API_NOEXCEPT;
/* Capture functions. */
@@ -232,40 +251,40 @@ ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum pa
* Opens the named capture device with the given frequency, format, and buffer
* size.
*/
-ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize);
+ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize) ALC_API_NOEXCEPT;
/** Closes the given capture device. */
-ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device);
+ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) ALC_API_NOEXCEPT;
/** Starts capturing samples into the device buffer. */
-ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device);
+ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) ALC_API_NOEXCEPT;
/** Stops capturing samples. Samples in the device buffer remain available. */
-ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device);
+ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) ALC_API_NOEXCEPT;
/** Reads samples from the device buffer. */
-ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples);
+ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) ALC_API_NOEXCEPT;
#endif /* ALC_NO_PROTOTYPES */
/* Pointer-to-function types, useful for storing dynamically loaded ALC entry
* points.
*/
-typedef ALCcontext* (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist);
-typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context);
-typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context);
-typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context);
-typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context);
-typedef ALCcontext* (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void);
-typedef ALCdevice* (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context);
-typedef ALCdevice* (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename);
-typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device);
-typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device);
-typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname);
-typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname);
-typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname);
-typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param);
-typedef void (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values);
-typedef ALCdevice* (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize);
-typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device);
-typedef void (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device);
-typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device);
-typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples);
+typedef ALCcontext* (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist) ALC_API_NOEXCEPT17;
+typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context) ALC_API_NOEXCEPT17;
+typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context) ALC_API_NOEXCEPT17;
+typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context) ALC_API_NOEXCEPT17;
+typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context) ALC_API_NOEXCEPT17;
+typedef ALCcontext* (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void) ALC_API_NOEXCEPT17;
+typedef ALCdevice* (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context) ALC_API_NOEXCEPT17;
+typedef ALCdevice* (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename) ALC_API_NOEXCEPT17;
+typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device) ALC_API_NOEXCEPT17;
+typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device) ALC_API_NOEXCEPT17;
+typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname) ALC_API_NOEXCEPT17;
+typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname) ALC_API_NOEXCEPT17;
+typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname) ALC_API_NOEXCEPT17;
+typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param) ALC_API_NOEXCEPT17;
+typedef void (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) ALC_API_NOEXCEPT17;
+typedef ALCdevice* (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize) ALC_API_NOEXCEPT17;
+typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device) ALC_API_NOEXCEPT17;
+typedef void (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device) ALC_API_NOEXCEPT17;
+typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device) ALC_API_NOEXCEPT17;
+typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) ALC_API_NOEXCEPT17;
#ifdef __cplusplus
} /* extern "C" */
diff --git a/include/AL/alext.h b/include/AL/alext.h
index d313a999..c75e0770 100644
--- a/include/AL/alext.h
+++ b/include/AL/alext.h
@@ -141,9 +141,9 @@ extern "C" {
#ifndef AL_EXT_STATIC_BUFFER
#define AL_EXT_STATIC_BUFFER 1
-typedef void (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALuint,ALenum,ALvoid*,ALsizei,ALsizei);
+typedef void (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALuint,ALenum,ALvoid*,ALsizei,ALsizei) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq);
+void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT;
#endif
#endif
@@ -159,11 +159,11 @@ void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *
#ifndef ALC_EXT_thread_local_context
#define ALC_EXT_thread_local_context 1
-typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context);
-typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void);
+typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context) ALC_API_NOEXCEPT17;
+typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void) ALC_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context);
-ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void);
+ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) ALC_API_NOEXCEPT;
+ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) ALC_API_NOEXCEPT;
#endif
#endif
@@ -176,9 +176,9 @@ ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void);
#define AL_SOFT_buffer_sub_data 1
#define AL_BYTE_RW_OFFSETS_SOFT 0x1031
#define AL_SAMPLE_RW_OFFSETS_SOFT 0x1032
-typedef void (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei);
+typedef void (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length);
+AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length) AL_API_NOEXCEPT;
#endif
#endif
@@ -195,12 +195,12 @@ AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const AL
#define AL_FOLDBACK_EVENT_STOP 0x4113
#define AL_FOLDBACK_MODE_MONO 0x4101
#define AL_FOLDBACK_MODE_STEREO 0x4102
-typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei);
-typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK);
-typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void);
+typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback);
-AL_API void AL_APIENTRY alRequestFoldbackStop(void);
+AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alRequestFoldbackStop(void) AL_API_NOEXCEPT;
#endif
#endif
@@ -263,15 +263,15 @@ AL_API void AL_APIENTRY alRequestFoldbackStop(void);
#define AL_SAMPLE_LENGTH_SOFT 0x200A
#define AL_SEC_LENGTH_SOFT 0x200B
-typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*);
-typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*);
-typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*);
-typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum);
+typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data);
-AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data);
-AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data);
-AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format);
+AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data) AL_API_NOEXCEPT;
+AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format) AL_API_NOEXCEPT;
#endif
#endif
@@ -302,13 +302,13 @@ AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format);
#define ALC_6POINT1_SOFT 0x1505
#define ALC_7POINT1_SOFT 0x1506
-typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*);
-typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum);
-typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei);
+typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*) ALC_API_NOEXCEPT17;
+typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum) ALC_API_NOEXCEPT17;
+typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei) ALC_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName);
-ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type);
-ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples);
+ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName) AL_API_NOEXCEPT;
+ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type) AL_API_NOEXCEPT;
+ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) AL_API_NOEXCEPT;
#endif
#endif
@@ -328,31 +328,31 @@ ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffe
#define AL_SEC_OFFSET_LATENCY_SOFT 0x1201
typedef _alsoft_int64_t ALint64SOFT;
typedef _alsoft_uint64_t ALuint64SOFT;
-typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble);
-typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble);
-typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*);
-typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*);
-typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*);
-typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*);
-typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT);
-typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT);
-typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*);
-typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*);
-typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*);
-typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*);
+typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value);
-AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3);
-AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values);
-AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value);
-AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3);
-AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values);
-AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value);
-AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3);
-AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values);
-AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value);
-AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3);
-AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values);
+AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values) AL_API_NOEXCEPT;
#endif
#endif
@@ -364,11 +364,11 @@ AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64
#ifndef AL_SOFT_deferred_updates
#define AL_SOFT_deferred_updates 1
#define AL_DEFERRED_UPDATES_SOFT 0xC002
-typedef void (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void);
-typedef void (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void);
+typedef void (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alDeferUpdatesSOFT(void);
-AL_API void AL_APIENTRY alProcessUpdatesSOFT(void);
+AL_API void AL_APIENTRY alDeferUpdatesSOFT(void) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alProcessUpdatesSOFT(void) AL_API_NOEXCEPT;
#endif
#endif
@@ -400,11 +400,11 @@ AL_API void AL_APIENTRY alProcessUpdatesSOFT(void);
#ifndef ALC_SOFT_pause_device
#define ALC_SOFT_pause_device 1
-typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device);
-typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device);
+typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device) ALC_API_NOEXCEPT17;
+typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device) ALC_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device);
-ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device);
+ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) ALC_API_NOEXCEPT;
+ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) ALC_API_NOEXCEPT;
#endif
#endif
@@ -448,11 +448,11 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device);
#define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994
#define ALC_HRTF_SPECIFIER_SOFT 0x1995
#define ALC_HRTF_ID_SOFT 0x1996
-typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index);
-typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs);
+typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index) ALC_API_NOEXCEPT17;
+typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs) ALC_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index);
-ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs);
+ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index) ALC_API_NOEXCEPT;
+ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs) ALC_API_NOEXCEPT;
#endif
#endif
@@ -467,9 +467,9 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi
#define AL_DEFAULT_RESAMPLER_SOFT 0x1211
#define AL_SOURCE_RESAMPLER_SOFT 0x1212
#define AL_RESAMPLER_NAME_SOFT 0x1213
-typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index);
+typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index);
+AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index) AL_API_NOEXCEPT;
#endif
#endif
@@ -493,9 +493,9 @@ typedef _alsoft_uint64_t ALCuint64SOFT;
#define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602
#define AL_SAMPLE_OFFSET_CLOCK_SOFT 0x1202
#define AL_SEC_OFFSET_CLOCK_SOFT 0x1203
-typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
+typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values) ALC_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
+ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values) ALC_API_NOEXCEPT;
#endif
#endif
@@ -552,27 +552,26 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname,
#define AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT 0x19A5
#define AL_EVENT_TYPE_DISCONNECTED_SOFT 0x19A6
typedef void (AL_APIENTRY*ALEVENTPROCSOFT)(ALenum eventType, ALuint object, ALuint param,
- ALsizei length, const ALchar *message,
- void *userParam);
-typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable);
-typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam);
-typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname);
-typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values);
+ ALsizei length, const ALchar *message, void *userParam) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT17;
+typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable);
-AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam);
-AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname);
-AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values);
+AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT;
+AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values) AL_API_NOEXCEPT;
#endif
#endif
#ifndef ALC_SOFT_reopen_device
#define ALC_SOFT_reopen_device
typedef ALCboolean (ALC_APIENTRY*LPALCREOPENDEVICESOFT)(ALCdevice *device,
- const ALCchar *deviceName, const ALCint *attribs);
+ const ALCchar *deviceName, const ALCint *attribs) ALC_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *deviceName,
- const ALCint *attribs);
+ const ALCint *attribs) ALC_API_NOEXCEPT;
#endif
#endif
@@ -580,16 +579,16 @@ ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *de
#define AL_SOFT_callback_buffer
#define AL_BUFFER_CALLBACK_FUNCTION_SOFT 0x19A0
#define AL_BUFFER_CALLBACK_USER_PARAM_SOFT 0x19A1
-typedef ALsizei (AL_APIENTRY*ALBUFFERCALLBACKTYPESOFT)(ALvoid *userptr, ALvoid *sampledata, ALsizei numbytes);
-typedef void (AL_APIENTRY*LPALBUFFERCALLBACKSOFT)(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr);
-typedef void (AL_APIENTRY*LPALGETBUFFERPTRSOFT)(ALuint buffer, ALenum param, ALvoid **value);
-typedef void (AL_APIENTRY*LPALGETBUFFER3PTRSOFT)(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3);
-typedef void (AL_APIENTRY*LPALGETBUFFERPTRVSOFT)(ALuint buffer, ALenum param, ALvoid **values);
+typedef ALsizei (AL_APIENTRY*ALBUFFERCALLBACKTYPESOFT)(ALvoid *userptr, ALvoid *sampledata, ALsizei numbytes) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALBUFFERCALLBACKSOFT)(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETBUFFERPTRSOFT)(ALuint buffer, ALenum param, ALvoid **value) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETBUFFER3PTRSOFT)(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETBUFFERPTRVSOFT)(ALuint buffer, ALenum param, ALvoid **values) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr);
-AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr);
-AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2);
-AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **ptr);
+AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT;
#endif
#endif
@@ -640,11 +639,98 @@ AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid
#ifndef AL_SOFT_source_start_delay
#define AL_SOFT_source_start_delay
-typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMESOFT)(ALuint source, ALint64SOFT start_time);
-typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMEVSOFT)(ALsizei n, const ALuint *sources, ALint64SOFT start_time);
+typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMESOFT)(ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMEVSOFT)(ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-void AL_APIENTRY alSourcePlayAtTimeSOFT(ALuint source, ALint64SOFT start_time);
-void AL_APIENTRY alSourcePlayAtTimevSOFT(ALsizei n, const ALuint *sources, ALint64SOFT start_time);
+void AL_APIENTRY alSourcePlayAtTimeSOFT(ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT;
+void AL_APIENTRY alSourcePlayAtTimevSOFT(ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT;
+#endif
+#endif
+
+#ifndef ALC_EXT_debug
+#define ALC_EXT_debug
+#define ALC_CONTEXT_FLAGS_EXT 0x19CF
+#define ALC_CONTEXT_DEBUG_BIT_EXT 0x0001
+#endif
+
+#ifndef AL_EXT_debug
+#define AL_EXT_debug
+#define AL_DONT_CARE_EXT 0x0002
+#define AL_DEBUG_OUTPUT_EXT 0x19B2
+#define AL_DEBUG_CALLBACK_FUNCTION_EXT 0x19B3
+#define AL_DEBUG_CALLBACK_USER_PARAM_EXT 0x19B4
+#define AL_DEBUG_SOURCE_API_EXT 0x19B5
+#define AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT 0x19B6
+#define AL_DEBUG_SOURCE_THIRD_PARTY_EXT 0x19B7
+#define AL_DEBUG_SOURCE_APPLICATION_EXT 0x19B8
+#define AL_DEBUG_SOURCE_OTHER_EXT 0x19B9
+#define AL_DEBUG_TYPE_ERROR_EXT 0x19BA
+#define AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT 0x19BB
+#define AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT 0x19BC
+#define AL_DEBUG_TYPE_PORTABILITY_EXT 0x19BD
+#define AL_DEBUG_TYPE_PERFORMANCE_EXT 0x19BE
+#define AL_DEBUG_TYPE_MARKER_EXT 0x19BF
+#define AL_DEBUG_TYPE_PUSH_GROUP_EXT 0x19C0
+#define AL_DEBUG_TYPE_POP_GROUP_EXT 0x19C1
+#define AL_DEBUG_TYPE_OTHER_EXT 0x19C2
+#define AL_DEBUG_SEVERITY_HIGH_EXT 0x19C3
+#define AL_DEBUG_SEVERITY_MEDIUM_EXT 0x19C4
+#define AL_DEBUG_SEVERITY_LOW_EXT 0x19C5
+#define AL_DEBUG_SEVERITY_NOTIFICATION_EXT 0x19C6
+#define AL_DEBUG_LOGGED_MESSAGES_EXT 0x19C7
+#define AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT 0x19C8
+#define AL_MAX_DEBUG_MESSAGE_LENGTH_EXT 0x19C9
+#define AL_MAX_DEBUG_LOGGED_MESSAGES_EXT 0x19CA
+#define AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT 0x19CB
+#define AL_MAX_LABEL_LENGTH_EXT 0x19CC
+#define AL_STACK_OVERFLOW_EXT 0x19CD
+#define AL_STACK_UNDERFLOW_EXT 0x19CE
+#define AL_CONTEXT_FLAGS_EXT 0x19CF
+#define AL_BUFFER_EXT 0x1009 /* Same as AL_BUFFER */
+#define AL_SOURCE_EXT 0x19D0
+#define AL_FILTER_EXT 0x19D1
+#define AL_EFFECT_EXT 0x19D2
+#define AL_AUXILIARY_EFFECT_SLOT_EXT 0x19D3
+
+typedef void (AL_APIENTRY*ALDEBUGPROCEXT)(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message, void *userParam) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALDEBUGMESSAGECALLBACKEXT)(ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALDEBUGMESSAGEINSERTEXT)(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALDEBUGMESSAGECONTROLEXT)(ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALPUSHDEBUGGROUPEXT)(ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALPOPDEBUGGROUPEXT)(void) AL_API_NOEXCEPT17;
+typedef ALuint (AL_APIENTRY*LPALGETDEBUGMESSAGELOGEXT)(ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALOBJECTLABELEXT)(ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY*LPALGETOBJECTLABELEXT)(ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT17;
+#ifdef AL_ALEXT_PROTOTYPES
+void AL_APIENTRY alDebugMessageCallbackEXT(ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT;
+void AL_APIENTRY alDebugMessageInsertEXT(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT;
+void AL_APIENTRY alDebugMessageControlEXT(ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT;
+void AL_APIENTRY alPushDebugGroupEXT(ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT;
+void AL_APIENTRY alPopDebugGroupEXT(void) AL_API_NOEXCEPT;
+ALuint AL_APIENTRY alGetDebugMessageLogEXT(ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT;
+void AL_APIENTRY alObjectLabelEXT(ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT;
+void AL_APIENTRY alGetObjectLabelEXT(ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT;
+#endif
+#endif
+
+#ifndef ALC_SOFT_system_events
+#define ALC_SOFT_system_events
+#define ALC_PLAYBACK_DEVICE_SOFT 0x19D4
+#define ALC_CAPTURE_DEVICE_SOFT 0x19D5
+#define ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT 0x19D6
+#define ALC_EVENT_TYPE_DEVICE_ADDED_SOFT 0x19D7
+#define ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT 0x19D8
+#define ALC_EVENT_SUPPORTED_SOFT 0x19D9
+#define ALC_EVENT_NOT_SUPPORTED_SOFT 0x19DA
+typedef void (ALC_APIENTRY*ALCEVENTPROCTYPESOFT)(ALCenum eventType, ALCenum deviceType,
+ ALCdevice *device, ALCsizei length, const ALCchar *message, void *userParam) ALC_API_NOEXCEPT17;
+typedef ALCenum (ALC_APIENTRY*LPALCEVENTISSUPPORTEDSOFT)(ALCenum eventType, ALCenum deviceType) ALC_API_NOEXCEPT17;
+typedef ALCboolean (ALC_APIENTRY*LPALCEVENTCONTROLSOFT)(ALCsizei count, const ALCenum *events, ALCboolean enable) ALC_API_NOEXCEPT17;
+typedef void (ALC_APIENTRY*LPALCEVENTCALLBACKSOFT)(ALCEVENTPROCTYPESOFT callback, void *userParam) ALC_API_NOEXCEPT17;
+#ifdef AL_ALEXT_PROTOTYPES
+ALCenum ALC_APIENTRY alcEventIsSupportedSOFT(ALCenum eventType, ALCenum deviceType) ALC_API_NOEXCEPT;
+ALCboolean ALC_APIENTRY alcEventControlSOFT(ALCsizei count, const ALCenum *events, ALCboolean enable) ALC_API_NOEXCEPT;
+void ALC_APIENTRY alcEventCallbackSOFT(ALCEVENTPROCTYPESOFT callback, void *userParam) ALC_API_NOEXCEPT;
#endif
#endif
diff --git a/include/AL/efx.h b/include/AL/efx.h
index 5ab64a64..f24222c3 100644
--- a/include/AL/efx.h
+++ b/include/AL/efx.h
@@ -204,80 +204,80 @@ extern "C" {
/* Effect object function types. */
-typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*);
-typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*);
-typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint);
-typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint);
-typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*);
-typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat);
-typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*);
-typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*);
-typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*);
-typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*);
-typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17;
/* Filter object function types. */
-typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*);
-typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*);
-typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint);
-typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint);
-typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*);
-typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat);
-typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*);
-typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*);
-typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*);
-typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*);
-typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17;
/* Auxiliary Effect Slot object function types. */
-typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*);
-typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*);
-typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint);
-typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint);
-typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*);
-typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat);
-typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*);
-typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*);
-typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*);
-typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*);
-typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*);
+typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*) AL_API_NOEXCEPT17;
+typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17;
+typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17;
#ifdef AL_ALEXT_PROTOTYPES
-AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects);
-AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects);
-AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect);
-AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue);
-AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues);
-AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue);
-AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues);
-AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue);
-AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues);
-AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue);
-AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues);
-
-AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters);
-AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters);
-AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter);
-AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue);
-AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues);
-AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue);
-AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues);
-AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue);
-AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues);
-AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue);
-AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues);
-
-AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots);
-AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots);
-AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot);
-AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue);
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues);
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue);
-AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues);
-AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue);
-AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues);
-AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue);
-AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues);
+AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects) AL_API_NOEXCEPT;
+AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT;
+
+AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters) AL_API_NOEXCEPT;
+AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT;
+
+AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT;
+AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT;
+AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT;
#endif
/* Filter ranges and defaults. */
diff --git a/router/al.cpp b/router/al.cpp
index 06c314eb..6ed8a626 100644
--- a/router/al.cpp
+++ b/router/al.cpp
@@ -7,33 +7,31 @@
#include "router.h"
-std::atomic<DriverIface*> CurrentCtxDriver{nullptr};
-
-#define DECL_THUNK1(R,n,T1) AL_API R AL_APIENTRY n(T1 a) \
+#define DECL_THUNK1(R,n,T1) AL_API R AL_APIENTRY n(T1 a) noexcept \
{ \
DriverIface *iface = GetThreadDriver(); \
if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \
return iface->n(a); \
}
-#define DECL_THUNK2(R,n,T1,T2) AL_API R AL_APIENTRY n(T1 a, T2 b) \
+#define DECL_THUNK2(R,n,T1,T2) AL_API R AL_APIENTRY n(T1 a, T2 b) noexcept \
{ \
DriverIface *iface = GetThreadDriver(); \
if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \
return iface->n(a, b); \
}
-#define DECL_THUNK3(R,n,T1,T2,T3) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c) \
+#define DECL_THUNK3(R,n,T1,T2,T3) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c) noexcept \
{ \
DriverIface *iface = GetThreadDriver(); \
if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \
return iface->n(a, b, c); \
}
-#define DECL_THUNK4(R,n,T1,T2,T3,T4) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d) \
+#define DECL_THUNK4(R,n,T1,T2,T3,T4) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d) noexcept \
{ \
DriverIface *iface = GetThreadDriver(); \
if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \
return iface->n(a, b, c, d); \
}
-#define DECL_THUNK5(R,n,T1,T2,T3,T4,T5) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d, T5 e) \
+#define DECL_THUNK5(R,n,T1,T2,T3,T4,T5) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \
{ \
DriverIface *iface = GetThreadDriver(); \
if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \
@@ -44,7 +42,7 @@ std::atomic<DriverIface*> CurrentCtxDriver{nullptr};
/* Ugly hack for some apps calling alGetError without a current context, and
* expecting it to be AL_NO_ERROR.
*/
-AL_API ALenum AL_APIENTRY alGetError(void)
+AL_API ALenum AL_APIENTRY alGetError(void) noexcept
{
DriverIface *iface = GetThreadDriver();
if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire);
diff --git a/router/alc.cpp b/router/alc.cpp
index 3aa3382b..fe9faa45 100644
--- a/router/alc.cpp
+++ b/router/alc.cpp
@@ -394,7 +394,7 @@ static void InitCtxFuncs(DriverIface &iface)
}
-ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename)
+ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename) noexcept
{
ALCdevice *device = nullptr;
ALint idx = 0;
@@ -459,7 +459,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename)
return device;
}
-ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
+ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept
{
ALint idx;
@@ -475,7 +475,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
}
-ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist)
+ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist) noexcept
{
ALCcontext *context;
ALint idx;
@@ -498,7 +498,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin
return context;
}
-ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context)
+ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) noexcept
{
ALint idx = -1;
@@ -542,7 +542,7 @@ ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context)
return ALC_TRUE;
}
-ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context)
+ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) noexcept
{
if(context)
{
@@ -553,7 +553,7 @@ ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context)
LastError.store(ALC_INVALID_CONTEXT);
}
-ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context)
+ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) noexcept
{
if(context)
{
@@ -564,7 +564,7 @@ ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context)
LastError.store(ALC_INVALID_CONTEXT);
}
-ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context)
+ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) noexcept
{
ALint idx;
@@ -578,14 +578,14 @@ ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context)
ContextIfaceMap.removeByKey(context);
}
-ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void)
+ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) noexcept
{
DriverIface *iface = GetThreadDriver();
if(!iface) iface = CurrentCtxDriver.load();
return iface ? iface->alcGetCurrentContext() : nullptr;
}
-ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context)
+ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context) noexcept
{
if(context)
{
@@ -598,7 +598,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context)
}
-ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)
+ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) noexcept
{
if(device)
{
@@ -609,7 +609,7 @@ ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)
return LastError.exchange(ALC_NO_ERROR);
}
-ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname)
+ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname) noexcept
{
const char *ptr;
size_t len;
@@ -641,7 +641,7 @@ ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const A
return ALC_FALSE;
}
-ALC_API void* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname)
+ALC_API void* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname) noexcept
{
if(device)
{
@@ -661,7 +661,7 @@ ALC_API void* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *f
return (iter != alcFunctions.cend()) ? iter->address : nullptr;
}
-ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname)
+ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname) noexcept
{
if(device)
{
@@ -681,7 +681,7 @@ ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *e
return (iter != alcEnumerations.cend()) ? iter->value : 0;
}
-ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param)
+ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param) noexcept
{
if(device)
{
@@ -817,7 +817,7 @@ ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum para
return nullptr;
}
-ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
+ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) noexcept
{
if(device)
{
@@ -872,7 +872,7 @@ ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsi
}
-ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize)
+ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize) noexcept
{
ALCdevice *device = nullptr;
ALint idx = 0;
@@ -924,7 +924,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename,
return device;
}
-ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
+ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) noexcept
{
ALint idx;
@@ -939,7 +939,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
return ALC_TRUE;
}
-ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
+ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) noexcept
{
if(device)
{
@@ -950,7 +950,7 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
LastError.store(ALC_INVALID_DEVICE);
}
-ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
+ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) noexcept
{
if(device)
{
@@ -961,7 +961,7 @@ ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
LastError.store(ALC_INVALID_DEVICE);
}
-ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
+ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) noexcept
{
if(device)
{
@@ -973,7 +973,7 @@ ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer,
}
-ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context)
+ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) noexcept
{
ALCenum err = ALC_INVALID_CONTEXT;
ALint idx;
@@ -1009,7 +1009,7 @@ ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context)
return ALC_FALSE;
}
-ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void)
+ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) noexcept
{
DriverIface *iface = GetThreadDriver();
if(iface) return iface->alcGetThreadContext();
diff --git a/router/router.cpp b/router/router.cpp
index 3c891053..e2ba91d1 100644
--- a/router/router.cpp
+++ b/router/router.cpp
@@ -17,18 +17,9 @@
#include "version.h"
-std::vector<DriverIfacePtr> DriverList;
-
-thread_local DriverIface *ThreadCtxDriver;
-
enum LogLevel LogLevel = LogLevel_Error;
FILE *LogFile;
-#ifdef __MINGW32__
-DriverIface *GetThreadDriver() noexcept { return ThreadCtxDriver; }
-void SetThreadDriver(DriverIface *driver) noexcept { ThreadCtxDriver = driver; }
-#endif
-
static void LoadDriverList(void);
@@ -328,13 +319,12 @@ void LoadDriverList(void)
WCHAR cwd_path[MAX_PATH+1] = L"";
WCHAR proc_path[MAX_PATH+1] = L"";
WCHAR sys_path[MAX_PATH+1] = L"";
- int len;
if(GetLoadedModuleDirectory(L"OpenAL32.dll", dll_path, MAX_PATH))
TRACE("Got DLL path %ls\n", dll_path);
GetCurrentDirectoryW(MAX_PATH, cwd_path);
- len = lstrlenW(cwd_path);
+ auto len = wcslen(cwd_path);
if(len > 0 && (cwd_path[len-1] == '\\' || cwd_path[len-1] == '/'))
cwd_path[len-1] = '\0';
TRACE("Got current working directory %ls\n", cwd_path);
@@ -343,7 +333,7 @@ void LoadDriverList(void)
TRACE("Got proc path %ls\n", proc_path);
GetSystemDirectoryW(sys_path, MAX_PATH);
- len = lstrlenW(sys_path);
+ len = wcslen(sys_path);
if(len > 0 && (sys_path[len-1] == '\\' || sys_path[len-1] == '/'))
sys_path[len-1] = '\0';
TRACE("Got system path %ls\n", sys_path);
diff --git a/router/router.h b/router/router.h
index 2a126d42..f5c7f455 100644
--- a/router/router.h
+++ b/router/router.h
@@ -173,21 +173,13 @@ struct DriverIface {
};
using DriverIfacePtr = std::unique_ptr<DriverIface>;
-extern std::vector<DriverIfacePtr> DriverList;
-
-extern thread_local DriverIface *ThreadCtxDriver;
-extern std::atomic<DriverIface*> CurrentCtxDriver;
-
-/* HACK: MinGW generates bad code when accessing an extern thread_local object.
- * Add a wrapper function for it that only accesses it where it's defined.
- */
-#ifdef __MINGW32__
-DriverIface *GetThreadDriver() noexcept;
-void SetThreadDriver(DriverIface *driver) noexcept;
-#else
+inline std::vector<DriverIfacePtr> DriverList;
+
+inline thread_local DriverIface *ThreadCtxDriver{};
+inline std::atomic<DriverIface*> CurrentCtxDriver{};
+
inline DriverIface *GetThreadDriver() noexcept { return ThreadCtxDriver; }
inline void SetThreadDriver(DriverIface *driver) noexcept { ThreadCtxDriver = driver; }
-#endif
class PtrIntMap {
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 00000000..492587e8
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,24 @@
+add_executable(OpenAL_Tests)
+
+include(FetchContent)
+FetchContent_Declare(
+ googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG main
+)
+# For Windows: Prevent overriding the parent project's compiler/linker settings
+set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+FetchContent_MakeAvailable(googletest)
+
+target_link_libraries(OpenAL_Tests PRIVATE
+ OpenAL
+ GTest::gtest_main
+)
+
+target_sources(OpenAL_Tests PRIVATE
+example.t.cpp
+)
+
+# This needs to come last
+include(GoogleTest)
+gtest_discover_tests(OpenAL_Tests)
diff --git a/tests/example.t.cpp b/tests/example.t.cpp
new file mode 100644
index 00000000..30341e8b
--- /dev/null
+++ b/tests/example.t.cpp
@@ -0,0 +1,16 @@
+#include <gtest/gtest.h>
+#include <AL/al.h>
+
+class ExampleTest : public ::testing::Test {
+};
+
+
+TEST_F(ExampleTest, Basic)
+{
+ // just making sure we compile
+ ALuint source, buffer;
+ ALfloat offset;
+ ALenum state;
+}
+
+
diff --git a/utils/makemhr/loaddef.cpp b/utils/makemhr/loaddef.cpp
index e8092363..c8a98511 100644
--- a/utils/makemhr/loaddef.cpp
+++ b/utils/makemhr/loaddef.cpp
@@ -33,11 +33,10 @@
#include <iterator>
#include <limits>
#include <memory>
-#include <cstdarg>
+#include <optional>
#include <vector>
#include "alfstream.h"
-#include "aloptional.h"
#include "alspan.h"
#include "alstring.h"
#include "makemhr.h"
@@ -1451,7 +1450,7 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc
}
if(hData->mChannelType == CT_NONE)
hData->mChannelType = CT_MONO;
- const auto azs = al::as_span(azCounts).first<MAX_FD_COUNT>();
+ const auto azs = al::span{azCounts}.first<MAX_FD_COUNT>();
if(!PrepareHrirData({distances, fdCount}, evCounts, azs, hData))
{
fprintf(stderr, "Error: Out of memory.\n");
@@ -1755,7 +1754,7 @@ static int ProcessSources(TokenReaderT *tr, HrirDataT *hData, const uint outRate
PPhaseResampler onsetResampler;
onsetResampler.init(hData->mIrRate, OnsetRateMultiple*hData->mIrRate);
- al::optional<PPhaseResampler> resampler;
+ std::optional<PPhaseResampler> resampler;
if(outRate && outRate != hData->mIrRate)
resampler.emplace().init(hData->mIrRate, outRate);
const double rateScale{outRate ? static_cast<double>(outRate) / hData->mIrRate : 1.0};
diff --git a/utils/makemhr/loadsofa.cpp b/utils/makemhr/loadsofa.cpp
index dcb0a35e..9bcfc38d 100644
--- a/utils/makemhr/loadsofa.cpp
+++ b/utils/makemhr/loadsofa.cpp
@@ -33,11 +33,11 @@
#include <iterator>
#include <memory>
#include <numeric>
+#include <optional>
#include <string>
#include <thread>
#include <vector>
-#include "aloptional.h"
#include "alspan.h"
#include "makemhr.h"
#include "polyphase_resampler.h"
@@ -87,7 +87,7 @@ static bool PrepareLayout(const uint m, const float *xyzs, HrirDataT *hData)
++fi;
}
fprintf(stdout, "Using %u of %u IRs.\n", ir_total, m);
- const auto azs = al::as_span(azCounts).first<MAX_FD_COUNT>();
+ const auto azs = al::span{azCounts}.first<MAX_FD_COUNT>();
return PrepareHrirData({distances, fi}, evCounts, azs, hData);
}
@@ -265,7 +265,7 @@ static bool LoadResponses(MYSOFA_HRTF *sofaHrtf, HrirDataT *hData, const DelayTy
double *hrirs = hData->mHrirsBase.data();
std::unique_ptr<double[]> restmp;
- al::optional<PPhaseResampler> resampler;
+ std::optional<PPhaseResampler> resampler;
if(outRate && outRate != hData->mIrRate)
{
resampler.emplace().init(hData->mIrRate, outRate);
diff --git a/utils/makemhr/makemhr.cpp b/utils/makemhr/makemhr.cpp
index ae301dc3..98e1b73f 100644
--- a/utils/makemhr/makemhr.cpp
+++ b/utils/makemhr/makemhr.cpp
@@ -297,10 +297,7 @@ static void MinimumPhase(const uint n, double *mags, complex_d *out)
// Remove any DC offset the filter has.
mags[0] = EPSILON;
for(i = 0;i < n;i++)
- {
- auto a = std::exp(complex_d{0.0, out[i].imag()});
- out[i] = a * mags[i];
- }
+ out[i] = std::polar(mags[i], out[i].imag());
}
diff --git a/utils/makemhr/makemhr.h b/utils/makemhr/makemhr.h
index 13b5b2d1..aa18134d 100644
--- a/utils/makemhr/makemhr.h
+++ b/utils/makemhr/makemhr.h
@@ -113,12 +113,12 @@ void MagnitudeResponse(const uint n, const complex_d *in, double *out);
// Performs a forward FFT.
inline void FftForward(const uint n, complex_d *inout)
-{ forward_fft(al::as_span(inout, n)); }
+{ forward_fft(al::span{inout, n}); }
// Performs an inverse FFT.
inline void FftInverse(const uint n, complex_d *inout)
{
- inverse_fft(al::as_span(inout, n));
+ inverse_fft(al::span{inout, n});
double f{1.0 / n};
for(uint i{0};i < n;i++)
inout[i] *= f;
diff --git a/utils/openal-info.c b/utils/openal-info.c
index b646693c..8ef6ebde 100644
--- a/utils/openal-info.c
+++ b/utils/openal-info.c
@@ -167,11 +167,11 @@ static void printHRTFInfo(ALCdevice *device)
alcGetIntegerv(device, ALC_NUM_HRTF_SPECIFIERS_SOFT, 1, &num_hrtfs);
if(!num_hrtfs)
- printf("No HRTFs found\n");
+ printf("No HRTF profiles found\n");
else
{
ALCint i;
- printf("Available HRTFs:\n");
+ printf("Available HRTF profiles:\n");
for(i = 0;i < num_hrtfs;++i)
{
const ALCchar *name = alcGetStringiSOFT(device, ALC_HRTF_SPECIFIER_SOFT, i);
@@ -213,6 +213,65 @@ static void printModeInfo(ALCdevice *device)
alcGetIntegerv(device, ALC_FREQUENCY, 1, &srate);
if(checkALCErrors(device) == ALC_NO_ERROR)
printf("Device sample rate: %dhz\n", srate);
+
+ if(alcIsExtensionPresent(device, "ALC_SOFT_HRTF"))
+ {
+ const ALCchar *hrtfname = "(disabled)";
+ ALCint isenabled = 0;
+
+ alcGetIntegerv(device, ALC_HRTF_SOFT, 1, &isenabled);
+ checkALCErrors(device);
+ if(isenabled == ALC_TRUE)
+ {
+ hrtfname = alcGetString(device, ALC_HRTF_SPECIFIER_SOFT);
+ checkALCErrors(device);
+ }
+ printf("Device HRTF profile: %s\n", hrtfname ? hrtfname : "<null>");
+ }
+}
+
+static void printALCSOFTSystemEventIsSupportedResult(LPALCEVENTISSUPPORTEDSOFT alcEventIsSupportedSOFT, ALCenum eventType, ALCenum deviceType)
+{
+ if (alcEventIsSupportedSOFT == NULL)
+ {
+ printf("ERROR (alcEventIsSupportedSOFT missing)\n");
+ return;
+ }
+ ALCenum supported = alcEventIsSupportedSOFT(eventType, deviceType);
+ if (supported == ALC_EVENT_SUPPORTED_SOFT)
+ {
+ printf("SUPPORTED\n");
+ }
+ else if (supported == ALC_EVENT_NOT_SUPPORTED_SOFT)
+ {
+ printf("NOT SUPPORTED\n");
+ }
+ else
+ {
+ printf("UNEXPECTED VALUE : %d\n", supported);
+ }
+}
+
+static void printALC_SOFT_system_event(void)
+{
+ if(alcIsExtensionPresent(NULL, "ALC_SOFT_system_events"))
+ {
+ LPALCEVENTISSUPPORTEDSOFT alcEventIsSupportedSOFT;
+ alcEventIsSupportedSOFT = FUNCTION_CAST(LPALCEVENTISSUPPORTEDSOFT, alGetProcAddress("alcEventIsSupportedSOFT"));
+ printf("Available events:\n");
+ printf(" ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT for ALC_PLAYBACK_DEVICE_SOFT - ");
+ printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT, ALC_PLAYBACK_DEVICE_SOFT);
+ printf(" ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT for ALC_CAPTURE_DEVICE_SOFT - ");
+ printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT, ALC_CAPTURE_DEVICE_SOFT);
+ printf(" ALC_EVENT_TYPE_DEVICE_ADDED_SOFT for ALC_PLAYBACK_DEVICE_SOFT - ");
+ printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEVICE_ADDED_SOFT, ALC_PLAYBACK_DEVICE_SOFT);
+ printf(" ALC_EVENT_TYPE_DEVICE_ADDED_SOFT for ALC_CAPTURE_DEVICE_SOFT - ");
+ printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEVICE_ADDED_SOFT, ALC_CAPTURE_DEVICE_SOFT);
+ printf(" ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT for ALC_PLAYBACK_DEVICE_SOFT - ");
+ printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT, ALC_PLAYBACK_DEVICE_SOFT);
+ printf(" ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT for ALC_CAPTURE_DEVICE_SOFT - ");
+ printALCSOFTSystemEventIsSupportedResult(alcEventIsSupportedSOFT, ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT, ALC_CAPTURE_DEVICE_SOFT);
+ }
}
static void printALInfo(void)
@@ -420,6 +479,7 @@ int main(int argc, char *argv[])
}
printALCInfo(device);
printHRTFInfo(device);
+ printALC_SOFT_system_event();
context = alcCreateContext(device, NULL);
if(!context || alcMakeContextCurrent(context) == ALC_FALSE)
diff --git a/utils/uhjdecoder.cpp b/utils/uhjdecoder.cpp
index 6d992e30..c7efa376 100644
--- a/utils/uhjdecoder.cpp
+++ b/utils/uhjdecoder.cpp
@@ -26,15 +26,14 @@
#include <array>
#include <complex>
+#include <cstddef>
#include <cstring>
#include <memory>
-#include <stddef.h>
#include <string>
#include <utility>
#include <vector>
#include "albit.h"
-#include "albyte.h"
#include "alcomplex.h"
#include "almalloc.h"
#include "alnumbers.h"
@@ -64,7 +63,7 @@ using ushort = unsigned short;
using uint = unsigned int;
using complex_d = std::complex<double>;
-using byte4 = std::array<al::byte,4>;
+using byte4 = std::array<std::byte,4>;
constexpr ubyte SUBTYPE_BFORMAT_FLOAT[]{
@@ -113,7 +112,7 @@ using FloatBufferSpan = al::span<float,BufferLineSize>;
struct UhjDecoder {
- constexpr static size_t sFilterDelay{1024};
+ constexpr static std::size_t sFilterDelay{1024};
alignas(16) std::array<float,BufferLineSize+sFilterDelay> mS{};
alignas(16) std::array<float,BufferLineSize+sFilterDelay> mD{};
@@ -126,10 +125,10 @@ struct UhjDecoder {
alignas(16) std::array<float,BufferLineSize + sFilterDelay*2> mTemp{};
- void decode(const float *RESTRICT InSamples, const size_t InChannels,
- const al::span<FloatBufferLine> OutSamples, const size_t SamplesToDo);
+ void decode(const float *RESTRICT InSamples, const std::size_t InChannels,
+ const al::span<FloatBufferLine> OutSamples, const std::size_t SamplesToDo);
void decode2(const float *RESTRICT InSamples, const al::span<FloatBufferLine> OutSamples,
- const size_t SamplesToDo);
+ const std::size_t SamplesToDo);
DEF_NEWDEL(UhjDecoder)
};
@@ -210,8 +209,8 @@ const PhaseShifterT<UhjDecoder::sFilterDelay*2> PShift{};
*
* Not halving produces a result matching the original input.
*/
-void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels,
- const al::span<FloatBufferLine> OutSamples, const size_t SamplesToDo)
+void UhjDecoder::decode(const float *RESTRICT InSamples, const std::size_t InChannels,
+ const al::span<FloatBufferLine> OutSamples, const std::size_t SamplesToDo)
{
ASSUME(SamplesToDo > 0);
@@ -224,23 +223,23 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels
*/
/* S = Left + Right */
- for(size_t i{0};i < SamplesToDo;++i)
+ for(std::size_t i{0};i < SamplesToDo;++i)
mS[sFilterDelay+i] = InSamples[i*InChannels + 0] + InSamples[i*InChannels + 1];
/* D = Left - Right */
- for(size_t i{0};i < SamplesToDo;++i)
+ for(std::size_t i{0};i < SamplesToDo;++i)
mD[sFilterDelay+i] = InSamples[i*InChannels + 0] - InSamples[i*InChannels + 1];
if(InChannels > 2)
{
/* T */
- for(size_t i{0};i < SamplesToDo;++i)
+ for(std::size_t i{0};i < SamplesToDo;++i)
mT[sFilterDelay+i] = InSamples[i*InChannels + 2];
}
if(InChannels > 3)
{
/* Q */
- for(size_t i{0};i < SamplesToDo;++i)
+ for(std::size_t i{0};i < SamplesToDo;++i)
mQ[sFilterDelay+i] = InSamples[i*InChannels + 3];
}
@@ -251,7 +250,7 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels
std::copy_n(mTemp.cbegin()+SamplesToDo, mDTHistory.size(), mDTHistory.begin());
PShift.process({xoutput, SamplesToDo}, mTemp.data());
- for(size_t i{0};i < SamplesToDo;++i)
+ for(std::size_t i{0};i < SamplesToDo;++i)
{
/* W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T) */
woutput[i] = 0.981532f*mS[i] + 0.197484f*xoutput[i];
@@ -265,7 +264,7 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels
std::copy_n(mTemp.cbegin()+SamplesToDo, mSHistory.size(), mSHistory.begin());
PShift.process({youtput, SamplesToDo}, mTemp.data());
- for(size_t i{0};i < SamplesToDo;++i)
+ for(std::size_t i{0};i < SamplesToDo;++i)
{
/* Y = 0.795968*D - 0.676392*T + j(0.186633*S) */
youtput[i] = 0.795968f*mD[i] - 0.676392f*mT[i] + 0.186633f*youtput[i];
@@ -275,7 +274,7 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels
{
float *zoutput{OutSamples[3].data()};
/* Z = 1.023332*Q */
- for(size_t i{0};i < SamplesToDo;++i)
+ for(std::size_t i{0};i < SamplesToDo;++i)
zoutput[i] = 1.023332f*mQ[i];
}
@@ -305,7 +304,7 @@ void UhjDecoder::decode(const float *RESTRICT InSamples, const size_t InChannels
* halving here is merely a -6dB reduction in output, but it's still incorrect.
*/
void UhjDecoder::decode2(const float *RESTRICT InSamples,
- const al::span<FloatBufferLine> OutSamples, const size_t SamplesToDo)
+ const al::span<FloatBufferLine> OutSamples, const std::size_t SamplesToDo)
{
ASSUME(SamplesToDo > 0);
@@ -314,11 +313,11 @@ void UhjDecoder::decode2(const float *RESTRICT InSamples,
float *youtput{OutSamples[2].data()};
/* S = Left + Right */
- for(size_t i{0};i < SamplesToDo;++i)
+ for(std::size_t i{0};i < SamplesToDo;++i)
mS[sFilterDelay+i] = InSamples[i*2 + 0] + InSamples[i*2 + 1];
/* D = Left - Right */
- for(size_t i{0};i < SamplesToDo;++i)
+ for(std::size_t i{0};i < SamplesToDo;++i)
mD[sFilterDelay+i] = InSamples[i*2 + 0] - InSamples[i*2 + 1];
/* Precompute j*D and store in xoutput. */
@@ -327,7 +326,7 @@ void UhjDecoder::decode2(const float *RESTRICT InSamples,
std::copy_n(mTemp.cbegin()+SamplesToDo, mDTHistory.size(), mDTHistory.begin());
PShift.process({xoutput, SamplesToDo}, mTemp.data());
- for(size_t i{0};i < SamplesToDo;++i)
+ for(std::size_t i{0};i < SamplesToDo;++i)
{
/* W = 0.981530*S + j*0.163585*D */
woutput[i] = 0.981530f*mS[i] + 0.163585f*xoutput[i];
@@ -341,7 +340,7 @@ void UhjDecoder::decode2(const float *RESTRICT InSamples,
std::copy_n(mTemp.cbegin()+SamplesToDo, mSHistory.size(), mSHistory.begin());
PShift.process({youtput, SamplesToDo}, mTemp.data());
- for(size_t i{0};i < SamplesToDo;++i)
+ for(std::size_t i{0};i < SamplesToDo;++i)
{
/* Y = 0.762956*D + j*0.384230*S */
youtput[i] = 0.762956f*mD[i] + 0.384230f*youtput[i];
@@ -368,7 +367,7 @@ int main(int argc, char **argv)
return 1;
}
- size_t num_files{0}, num_decoded{0};
+ std::size_t num_files{0}, num_decoded{0};
bool use_general{true};
for(int fidx{1};fidx < argc;++fidx)
{
@@ -473,7 +472,7 @@ int main(int argc, char **argv)
* be fed through the decoder after reaching the end of the input file
* to ensure none of the original input is lost.
*/
- size_t LeadIn{UhjDecoder::sFilterDelay};
+ std::size_t LeadIn{UhjDecoder::sFilterDelay};
sf_count_t LeadOut{UhjDecoder::sFilterDelay};
while(LeadOut > 0)
{
@@ -487,7 +486,7 @@ int main(int argc, char **argv)
LeadOut -= remaining;
}
- auto got = static_cast<size_t>(sgot);
+ auto got = static_cast<std::size_t>(sgot);
if(ininfo.channels > 2 || use_general)
decoder->decode(inmem.get(), static_cast<uint>(ininfo.channels), decmem, got);
else
@@ -499,16 +498,16 @@ int main(int argc, char **argv)
}
got -= LeadIn;
- for(size_t i{0};i < got;++i)
+ for(std::size_t i{0};i < got;++i)
{
/* Attenuate by -3dB for FuMa output levels. */
constexpr auto inv_sqrt2 = static_cast<float>(1.0/al::numbers::sqrt2);
- for(size_t j{0};j < outchans;++j)
+ for(std::size_t j{0};j < outchans;++j)
outmem[i*outchans + j] = f32AsLEBytes(decmem[j][LeadIn+i] * inv_sqrt2);
}
LeadIn = 0;
- size_t wrote{fwrite(outmem.get(), sizeof(byte4)*outchans, got, outfile.get())};
+ std::size_t wrote{fwrite(outmem.get(), sizeof(byte4)*outchans, got, outfile.get())};
if(wrote < got)
{
fprintf(stderr, "Error writing wave data: %s (%d)\n", strerror(errno), errno);
diff --git a/utils/uhjencoder.cpp b/utils/uhjencoder.cpp
index 34698993..154a1155 100644
--- a/utils/uhjencoder.cpp
+++ b/utils/uhjencoder.cpp
@@ -25,8 +25,8 @@
#include "config.h"
#include <array>
+#include <cinttypes>
#include <cstring>
-#include <inttypes.h>
#include <memory>
#include <stddef.h>
#include <string>
@@ -325,7 +325,7 @@ int main(int argc, char **argv)
return false;
for(const int id : a)
{
- if(std::find(b.begin(), b.end(), id) != b.end())
+ if(std::find(b.begin(), b.end(), id) == b.end())
return false;
}
return true;
@@ -502,11 +502,9 @@ int main(int argc, char **argv)
got -= LeadIn;
for(size_t c{0};c < uhjchans;++c)
{
- constexpr float max_val{8388607.0f / 8388608.0f};
- auto clamp = [](float v, float mn, float mx) noexcept
- { return std::min(std::max(v, mn), mx); };
+ static constexpr float max_val{8388607.0f / 8388608.0f};
for(size_t i{0};i < got;++i)
- outmem[i*uhjchans + c] = clamp(encmem[c][LeadIn+i], -1.0f, max_val);
+ outmem[i*uhjchans + c] = std::clamp(encmem[c][LeadIn+i], -1.0f, max_val);
}
LeadIn = 0;