mirror of https://github.com/axmolengine/axmol.git
Update openal
This commit is contained in:
parent
529895baff
commit
02cb09c6e2
|
@ -138,7 +138,7 @@
|
||||||
|
|
||||||
## OpenAL Soft
|
## OpenAL Soft
|
||||||
- Upstream: https://github.com/kcat/openal-soft
|
- Upstream: https://github.com/kcat/openal-soft
|
||||||
- Version: 1.21.1 with cmake modification: exclude target 'ex-common'
|
- Version: git 1.21.1-4733c9f (7628)
|
||||||
- License: LGPL-2.1
|
- License: LGPL-2.1
|
||||||
|
|
||||||
## OpenSSL
|
## OpenSSL
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
Portions of this software are licensed under the BSD 3-Clause license.
|
Portions of this software are licensed under the BSD 3-Clause license.
|
||||||
|
|
||||||
Copyright (c) 2015, Archontis Politis
|
Copyright (c) 2015, Archontis Politis
|
||||||
|
Copyright (c) 2019, Anis A. Hireche
|
||||||
Copyright (c) 2019, Christopher Robinson
|
Copyright (c) 2019, Christopher Robinson
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
|
|
|
@ -2,22 +2,24 @@
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.0.2)
|
cmake_minimum_required(VERSION 3.0.2)
|
||||||
|
|
||||||
# The workaround for try_compile failing with code signing
|
if(APPLE)
|
||||||
# since cmake-3.18.2, not required
|
# The workaround for try_compile failing with code signing
|
||||||
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
|
# since cmake-3.18.2, not required
|
||||||
|
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
|
||||||
${CMAKE_TRY_COMPILE_PLATFORM_VARIABLES}
|
${CMAKE_TRY_COMPILE_PLATFORM_VARIABLES}
|
||||||
"CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED"
|
"CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED"
|
||||||
"CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED")
|
"CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED")
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO)
|
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO)
|
||||||
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO)
|
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Fix compile failure with armv7 deployment target >= 11.0, xcode clang will
|
|
||||||
# report:
|
|
||||||
# error: invalid iOS deployment version '--target=armv7-apple-ios13.6',
|
|
||||||
# iOS 10 is the maximum deployment target for 32-bit targets
|
|
||||||
# If CMAKE_OSX_DEPLOYMENT_TARGET is not defined, cmake will choose latest
|
|
||||||
# deployment target
|
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||||
|
# Fix compile failure with armv7 deployment target >= 11.0, xcode clang
|
||||||
|
# will report:
|
||||||
|
# error: invalid iOS deployment version '--target=armv7-apple-ios13.6',
|
||||||
|
# iOS 10 is the maximum deployment target for 32-bit targets
|
||||||
|
# If CMAKE_OSX_DEPLOYMENT_TARGET is not defined, cmake will choose latest
|
||||||
|
# deployment target
|
||||||
if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*armv7.*")
|
if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*armv7.*")
|
||||||
if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET
|
if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET
|
||||||
OR NOT CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "11.0")
|
OR NOT CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "11.0")
|
||||||
|
@ -370,20 +372,25 @@ set(HAVE_SSE3 0)
|
||||||
set(HAVE_SSE4_1 0)
|
set(HAVE_SSE4_1 0)
|
||||||
set(HAVE_NEON 0)
|
set(HAVE_NEON 0)
|
||||||
|
|
||||||
# Check for SSE+SSE2 support
|
# Check for SSE support
|
||||||
option(ALSOFT_REQUIRE_SSE "Require SSE support" OFF)
|
option(ALSOFT_REQUIRE_SSE "Require SSE support" OFF)
|
||||||
option(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF)
|
if(HAVE_XMMINTRIN_H)
|
||||||
if(HAVE_XMMINTRIN_H AND HAVE_EMMINTRIN_H)
|
|
||||||
option(ALSOFT_CPUEXT_SSE "Enable SSE support" ON)
|
option(ALSOFT_CPUEXT_SSE "Enable SSE support" ON)
|
||||||
option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON)
|
if(ALSOFT_CPUEXT_SSE)
|
||||||
if(ALSOFT_CPUEXT_SSE AND ALSOFT_CPUEXT_SSE2)
|
|
||||||
set(HAVE_SSE 1)
|
set(HAVE_SSE 1)
|
||||||
set(HAVE_SSE2 1)
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE)
|
if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE)
|
||||||
message(FATAL_ERROR "Failed to enabled required SSE CPU extensions")
|
message(FATAL_ERROR "Failed to enabled required SSE CPU extensions")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
option(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF)
|
||||||
|
if(HAVE_EMMINTRIN_H)
|
||||||
|
option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON)
|
||||||
|
if(HAVE_SSE AND ALSOFT_CPUEXT_SSE2)
|
||||||
|
set(HAVE_SSE2 1)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
if(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2)
|
if(ALSOFT_REQUIRE_SSE2 AND NOT HAVE_SSE2)
|
||||||
message(FATAL_ERROR "Failed to enable required SSE2 CPU extensions")
|
message(FATAL_ERROR "Failed to enable required SSE2 CPU extensions")
|
||||||
endif()
|
endif()
|
||||||
|
@ -622,11 +629,13 @@ set(COMMON_OBJS
|
||||||
common/alstring.cpp
|
common/alstring.cpp
|
||||||
common/alstring.h
|
common/alstring.h
|
||||||
common/atomic.h
|
common/atomic.h
|
||||||
|
common/comptr.h
|
||||||
common/dynload.cpp
|
common/dynload.cpp
|
||||||
common/dynload.h
|
common/dynload.h
|
||||||
common/intrusive_ptr.h
|
common/intrusive_ptr.h
|
||||||
common/math_defs.h
|
common/math_defs.h
|
||||||
common/opthelpers.h
|
common/opthelpers.h
|
||||||
|
common/phase_shifter.h
|
||||||
common/polyphase_resampler.cpp
|
common/polyphase_resampler.cpp
|
||||||
common/polyphase_resampler.h
|
common/polyphase_resampler.h
|
||||||
common/pragmadefs.h
|
common/pragmadefs.h
|
||||||
|
@ -643,17 +652,29 @@ set(COMMON_OBJS
|
||||||
set(CORE_OBJS
|
set(CORE_OBJS
|
||||||
core/ambdec.cpp
|
core/ambdec.cpp
|
||||||
core/ambdec.h
|
core/ambdec.h
|
||||||
|
core/ambidefs.cpp
|
||||||
core/ambidefs.h
|
core/ambidefs.h
|
||||||
|
core/async_event.h
|
||||||
|
core/bformatdec.cpp
|
||||||
|
core/bformatdec.h
|
||||||
core/bs2b.cpp
|
core/bs2b.cpp
|
||||||
core/bs2b.h
|
core/bs2b.h
|
||||||
core/bsinc_defs.h
|
core/bsinc_defs.h
|
||||||
core/bsinc_tables.cpp
|
core/bsinc_tables.cpp
|
||||||
core/bsinc_tables.h
|
core/bsinc_tables.h
|
||||||
core/bufferline.h
|
core/bufferline.h
|
||||||
|
core/buffer_storage.cpp
|
||||||
|
core/buffer_storage.h
|
||||||
|
core/context.cpp
|
||||||
|
core/context.h
|
||||||
|
core/converter.cpp
|
||||||
|
core/converter.h
|
||||||
core/cpu_caps.cpp
|
core/cpu_caps.cpp
|
||||||
core/cpu_caps.h
|
core/cpu_caps.h
|
||||||
core/devformat.cpp
|
core/devformat.cpp
|
||||||
core/devformat.h
|
core/devformat.h
|
||||||
|
core/device.cpp
|
||||||
|
core/device.h
|
||||||
core/except.cpp
|
core/except.cpp
|
||||||
core/except.h
|
core/except.h
|
||||||
core/filters/biquad.h
|
core/filters/biquad.h
|
||||||
|
@ -666,12 +687,47 @@ set(CORE_OBJS
|
||||||
core/fmt_traits.h
|
core/fmt_traits.h
|
||||||
core/fpu_ctrl.cpp
|
core/fpu_ctrl.cpp
|
||||||
core/fpu_ctrl.h
|
core/fpu_ctrl.h
|
||||||
|
core/front_stablizer.h
|
||||||
|
core/helpers.cpp
|
||||||
|
core/helpers.h
|
||||||
|
core/hrtf.cpp
|
||||||
|
core/hrtf.h
|
||||||
core/logging.cpp
|
core/logging.cpp
|
||||||
core/logging.h
|
core/logging.h
|
||||||
core/mastering.cpp
|
core/mastering.cpp
|
||||||
core/mastering.h
|
core/mastering.h
|
||||||
|
core/mixer.cpp
|
||||||
|
core/mixer.h
|
||||||
|
core/resampler_limits.h
|
||||||
core/uhjfilter.cpp
|
core/uhjfilter.cpp
|
||||||
core/uhjfilter.h
|
core/uhjfilter.h
|
||||||
|
core/uiddefs.cpp
|
||||||
|
core/voice.cpp
|
||||||
|
core/voice.h
|
||||||
|
core/voice_change.h)
|
||||||
|
|
||||||
|
set(HAVE_RTKIT 0)
|
||||||
|
option(ALSOFT_REQUIRE_RTKIT "Require RTKit/D-Bus support" FALSE)
|
||||||
|
find_package(DBus1)
|
||||||
|
if(DBus1_FOUND)
|
||||||
|
option(ALSOFT_RTKIT "Enable RTKit support" ON)
|
||||||
|
if(ALSOFT_RTKIT)
|
||||||
|
set(HAVE_RTKIT 1)
|
||||||
|
set(CORE_OBJS ${CORE_OBJS} core/dbus_wrap.cpp core/dbus_wrap.h core/rtkit.cpp core/rtkit.h)
|
||||||
|
if(WIN32 OR HAVE_DLFCN_H)
|
||||||
|
set(INC_PATHS ${INC_PATHS} ${DBus1_INCLUDE_DIRS})
|
||||||
|
set(CPP_DEFS ${CPP_DEFS} ${DBus1_DEFINITIONS})
|
||||||
|
else()
|
||||||
|
set(EXTRA_LIBS ${EXTRA_LIBS} ${DBus1_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT)
|
||||||
|
message(FATAL_ERROR "Failed to enabled required RTKit support")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Default mixers, always available
|
||||||
|
set(CORE_OBJS ${CORE_OBJS}
|
||||||
core/mixer/defs.h
|
core/mixer/defs.h
|
||||||
core/mixer/hrtfbase.h
|
core/mixer/hrtfbase.h
|
||||||
core/mixer/hrtfdefs.h
|
core/mixer/hrtfdefs.h
|
||||||
|
@ -715,20 +771,14 @@ set(OPENAL_OBJS
|
||||||
# ALC and related routines
|
# ALC and related routines
|
||||||
set(ALC_OBJS
|
set(ALC_OBJS
|
||||||
alc/alc.cpp
|
alc/alc.cpp
|
||||||
alc/alcmain.h
|
|
||||||
alc/alu.cpp
|
alc/alu.cpp
|
||||||
alc/alu.h
|
alc/alu.h
|
||||||
alc/alconfig.cpp
|
alc/alconfig.cpp
|
||||||
alc/alconfig.h
|
alc/alconfig.h
|
||||||
alc/alcontext.h
|
alc/context.cpp
|
||||||
alc/async_event.h
|
alc/context.h
|
||||||
alc/bformatdec.cpp
|
alc/device.cpp
|
||||||
alc/bformatdec.h
|
alc/device.h
|
||||||
alc/buffer_storage.cpp
|
|
||||||
alc/buffer_storage.h
|
|
||||||
alc/compat.h
|
|
||||||
alc/converter.cpp
|
|
||||||
alc/converter.h
|
|
||||||
alc/effectslot.cpp
|
alc/effectslot.cpp
|
||||||
alc/effectslot.h
|
alc/effectslot.h
|
||||||
alc/effects/base.h
|
alc/effects/base.h
|
||||||
|
@ -746,22 +796,18 @@ set(ALC_OBJS
|
||||||
alc/effects/pshifter.cpp
|
alc/effects/pshifter.cpp
|
||||||
alc/effects/reverb.cpp
|
alc/effects/reverb.cpp
|
||||||
alc/effects/vmorpher.cpp
|
alc/effects/vmorpher.cpp
|
||||||
alc/front_stablizer.h
|
|
||||||
alc/helpers.cpp
|
|
||||||
alc/hrtf.cpp
|
|
||||||
alc/hrtf.h
|
|
||||||
alc/inprogext.h
|
alc/inprogext.h
|
||||||
alc/panning.cpp
|
alc/panning.cpp)
|
||||||
alc/uiddefs.cpp
|
|
||||||
alc/voice.cpp
|
|
||||||
alc/voice.h
|
|
||||||
alc/voice_change.h)
|
|
||||||
|
|
||||||
# Include SIMD mixers
|
# Include SIMD mixers
|
||||||
set(CPU_EXTS "Default")
|
set(CPU_EXTS "Default")
|
||||||
|
if(HAVE_SSE)
|
||||||
|
set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse.cpp)
|
||||||
|
set(CPU_EXTS "${CPU_EXTS}, SSE")
|
||||||
|
endif()
|
||||||
if(HAVE_SSE2)
|
if(HAVE_SSE2)
|
||||||
set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse.cpp core/mixer/mixer_sse2.cpp)
|
set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse2.cpp)
|
||||||
set(CPU_EXTS "${CPU_EXTS}, SSE, SSE2")
|
set(CPU_EXTS "${CPU_EXTS}, SSE2")
|
||||||
endif()
|
endif()
|
||||||
if(HAVE_SSE3)
|
if(HAVE_SSE3)
|
||||||
set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse3.cpp)
|
set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse3.cpp)
|
||||||
|
@ -1149,10 +1195,13 @@ if(ALSOFT_EMBED_HRTF_DATA)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if(ALSOFT_UTILS AND NOT ALSOFT_NO_CONFIG_UTIL)
|
if(ALSOFT_UTILS)
|
||||||
|
find_package(MySOFA)
|
||||||
|
if(NOT ALSOFT_NO_CONFIG_UTIL)
|
||||||
find_package(Qt5Widgets)
|
find_package(Qt5Widgets)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(ALSOFT_EXAMPLES)
|
if(ALSOFT_UTILS OR ALSOFT_EXAMPLES)
|
||||||
find_package(SndFile)
|
find_package(SndFile)
|
||||||
find_package(SDL2)
|
find_package(SDL2)
|
||||||
if(SDL2_FOUND)
|
if(SDL2_FOUND)
|
||||||
|
@ -1233,7 +1282,8 @@ else()
|
||||||
router/al.cpp
|
router/al.cpp
|
||||||
)
|
)
|
||||||
target_compile_definitions(OpenAL
|
target_compile_definitions(OpenAL
|
||||||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES ${CPP_DEFS})
|
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_compile_options(OpenAL PRIVATE ${C_FLAGS})
|
||||||
target_link_libraries(OpenAL PRIVATE common ${LINKER_FLAGS})
|
target_link_libraries(OpenAL PRIVATE common ${LINKER_FLAGS})
|
||||||
target_include_directories(OpenAL
|
target_include_directories(OpenAL
|
||||||
|
@ -1309,7 +1359,6 @@ target_include_directories(${IMPL_TARGET}
|
||||||
${INC_PATHS}
|
${INC_PATHS}
|
||||||
${OpenAL_BINARY_DIR}
|
${OpenAL_BINARY_DIR}
|
||||||
${OpenAL_SOURCE_DIR}
|
${OpenAL_SOURCE_DIR}
|
||||||
${OpenAL_SOURCE_DIR}/alc
|
|
||||||
${OpenAL_SOURCE_DIR}/common
|
${OpenAL_SOURCE_DIR}/common
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1318,7 +1367,8 @@ set_target_properties(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME}
|
||||||
SOVERSION ${LIB_MAJOR_VERSION}
|
SOVERSION ${LIB_MAJOR_VERSION}
|
||||||
)
|
)
|
||||||
target_compile_definitions(${IMPL_TARGET}
|
target_compile_definitions(${IMPL_TARGET}
|
||||||
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES ${CPP_DEFS})
|
PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}"
|
||||||
|
${CPP_DEFS})
|
||||||
target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS})
|
target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS})
|
||||||
|
|
||||||
if(TARGET build_version)
|
if(TARGET build_version)
|
||||||
|
@ -1430,7 +1480,16 @@ if(ALSOFT_UTILS)
|
||||||
set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info)
|
set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(MySOFA)
|
if(SNDFILE_FOUND)
|
||||||
|
add_executable(uhjdecoder utils/uhjdecoder.cpp)
|
||||||
|
target_compile_definitions(uhjdecoder PRIVATE ${CPP_DEFS})
|
||||||
|
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
|
||||||
|
PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(MYSOFA_FOUND)
|
if(MYSOFA_FOUND)
|
||||||
set(SOFA_SUPPORT_SRCS
|
set(SOFA_SUPPORT_SRCS
|
||||||
utils/sofa-support.cpp
|
utils/sofa-support.cpp
|
||||||
|
@ -1476,7 +1535,7 @@ if(ALSOFT_UTILS)
|
||||||
message(STATUS "")
|
message(STATUS "")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ALSOFT_EXAMPLES)
|
|
||||||
# Add a static library with common functions used by multiple example targets
|
# Add a static library with common functions used by multiple example targets
|
||||||
add_library(ex-common STATIC EXCLUDE_FROM_ALL
|
add_library(ex-common STATIC EXCLUDE_FROM_ALL
|
||||||
examples/common/alhelpers.c
|
examples/common/alhelpers.c
|
||||||
|
@ -1485,7 +1544,6 @@ target_compile_definitions(ex-common PUBLIC ${CPP_DEFS})
|
||||||
target_include_directories(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
target_include_directories(ex-common PUBLIC ${OpenAL_SOURCE_DIR}/common)
|
||||||
target_compile_options(ex-common PUBLIC ${C_FLAGS})
|
target_compile_options(ex-common PUBLIC ${C_FLAGS})
|
||||||
target_link_libraries(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB})
|
target_link_libraries(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB})
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ALSOFT_EXAMPLES)
|
if(ALSOFT_EXAMPLES)
|
||||||
add_executable(altonegen examples/altonegen.c)
|
add_executable(altonegen examples/altonegen.c)
|
||||||
|
|
|
@ -36,18 +36,18 @@
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "albit.h"
|
#include "albit.h"
|
||||||
#include "alcmain.h"
|
#include "alc/alu.h"
|
||||||
#include "alcontext.h"
|
#include "alc/context.h"
|
||||||
|
#include "alc/device.h"
|
||||||
|
#include "alc/inprogext.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "alspan.h"
|
#include "alspan.h"
|
||||||
#include "alu.h"
|
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "core/except.h"
|
#include "core/except.h"
|
||||||
#include "core/fpu_ctrl.h"
|
#include "core/fpu_ctrl.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "effect.h"
|
#include "effect.h"
|
||||||
#include "inprogext.h"
|
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -308,7 +308,7 @@ void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot)
|
||||||
&& slot->mState == SlotState::Playing) \
|
&& slot->mState == SlotState::Playing) \
|
||||||
slot->updateProps(context.get()); \
|
slot->updateProps(context.get()); \
|
||||||
else \
|
else \
|
||||||
slot->PropsClean.clear(std::memory_order_release); \
|
slot->mPropsDirty.set(std::memory_order_release); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -325,7 +325,7 @@ START_API_FUNC
|
||||||
if UNLIKELY(n <= 0) return;
|
if UNLIKELY(n <= 0) return;
|
||||||
|
|
||||||
std::unique_lock<std::mutex> slotlock{context->mEffectSlotLock};
|
std::unique_lock<std::mutex> slotlock{context->mEffectSlotLock};
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
if(static_cast<ALuint>(n) > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots)
|
if(static_cast<ALuint>(n) > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots)
|
||||||
{
|
{
|
||||||
context->setError(AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)",
|
context->setError(AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)",
|
||||||
|
@ -460,7 +460,7 @@ START_API_FUNC
|
||||||
if(slot->mState == SlotState::Playing)
|
if(slot->mState == SlotState::Playing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
slot->PropsClean.test_and_set(std::memory_order_acq_rel);
|
slot->mPropsDirty.test_and_clear(std::memory_order_acq_rel);
|
||||||
slot->updateProps(context.get());
|
slot->updateProps(context.get());
|
||||||
|
|
||||||
AddActiveEffectSlots({&slot, 1}, context.get());
|
AddActiveEffectSlots({&slot, 1}, context.get());
|
||||||
|
@ -491,7 +491,7 @@ START_API_FUNC
|
||||||
|
|
||||||
if(slot->mState != SlotState::Playing)
|
if(slot->mState != SlotState::Playing)
|
||||||
{
|
{
|
||||||
slot->PropsClean.test_and_set(std::memory_order_acq_rel);
|
slot->mPropsDirty.test_and_clear(std::memory_order_acq_rel);
|
||||||
slot->updateProps(context.get());
|
slot->updateProps(context.get());
|
||||||
}
|
}
|
||||||
slots[i] = slot;
|
slots[i] = slot;
|
||||||
|
@ -571,7 +571,7 @@ START_API_FUNC
|
||||||
switch(param)
|
switch(param)
|
||||||
{
|
{
|
||||||
case AL_EFFECTSLOT_EFFECT:
|
case AL_EFFECTSLOT_EFFECT:
|
||||||
device = context->mDevice.get();
|
device = context->mALDevice.get();
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> ___{device->EffectLock};
|
std::lock_guard<std::mutex> ___{device->EffectLock};
|
||||||
|
@ -587,8 +587,12 @@ START_API_FUNC
|
||||||
}
|
}
|
||||||
if UNLIKELY(slot->mState == SlotState::Initial)
|
if UNLIKELY(slot->mState == SlotState::Initial)
|
||||||
{
|
{
|
||||||
|
slot->mPropsDirty.test_and_clear(std::memory_order_acq_rel);
|
||||||
|
slot->updateProps(context.get());
|
||||||
|
|
||||||
AddActiveEffectSlots({&slot, 1}, context.get());
|
AddActiveEffectSlots({&slot, 1}, context.get());
|
||||||
slot->mState = SlotState::Playing;
|
slot->mState = SlotState::Playing;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -596,6 +600,8 @@ START_API_FUNC
|
||||||
if(!(value == AL_TRUE || value == AL_FALSE))
|
if(!(value == AL_TRUE || value == AL_FALSE))
|
||||||
SETERR_RETURN(context, AL_INVALID_VALUE,,
|
SETERR_RETURN(context, AL_INVALID_VALUE,,
|
||||||
"Effect slot auxiliary send auto out of range");
|
"Effect slot auxiliary send auto out of range");
|
||||||
|
if UNLIKELY(slot->AuxSendAuto == !!value)
|
||||||
|
return;
|
||||||
slot->AuxSendAuto = !!value;
|
slot->AuxSendAuto = !!value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -603,6 +609,8 @@ START_API_FUNC
|
||||||
target = LookupEffectSlot(context.get(), static_cast<ALuint>(value));
|
target = LookupEffectSlot(context.get(), static_cast<ALuint>(value));
|
||||||
if(value && !target)
|
if(value && !target)
|
||||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid effect slot target ID");
|
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid effect slot target ID");
|
||||||
|
if UNLIKELY(slot->Target == target)
|
||||||
|
return;
|
||||||
if(target)
|
if(target)
|
||||||
{
|
{
|
||||||
ALeffectslot *checker{target};
|
ALeffectslot *checker{target};
|
||||||
|
@ -631,12 +639,20 @@ START_API_FUNC
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AL_BUFFER:
|
case AL_BUFFER:
|
||||||
device = context->mDevice.get();
|
device = context->mALDevice.get();
|
||||||
|
|
||||||
if(slot->mState == SlotState::Playing)
|
if(slot->mState == SlotState::Playing)
|
||||||
SETERR_RETURN(context, AL_INVALID_OPERATION,,
|
SETERR_RETURN(context, AL_INVALID_OPERATION,,
|
||||||
"Setting buffer on playing effect slot %u", slot->id);
|
"Setting buffer on playing effect slot %u", slot->id);
|
||||||
|
|
||||||
|
if(ALbuffer *buffer{slot->Buffer})
|
||||||
|
{
|
||||||
|
if UNLIKELY(buffer->id == static_cast<ALuint>(value))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if UNLIKELY(value == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> ___{device->BufferLock};
|
std::lock_guard<std::mutex> ___{device->BufferLock};
|
||||||
ALbuffer *buffer{};
|
ALbuffer *buffer{};
|
||||||
|
@ -720,6 +736,8 @@ START_API_FUNC
|
||||||
case AL_EFFECTSLOT_GAIN:
|
case AL_EFFECTSLOT_GAIN:
|
||||||
if(!(value >= 0.0f && value <= 1.0f))
|
if(!(value >= 0.0f && value <= 1.0f))
|
||||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Effect slot gain out of range");
|
SETERR_RETURN(context, AL_INVALID_VALUE,, "Effect slot gain out of range");
|
||||||
|
if UNLIKELY(slot->Gain == value)
|
||||||
|
return;
|
||||||
slot->Gain = value;
|
slot->Gain = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -884,7 +902,7 @@ END_API_FUNC
|
||||||
|
|
||||||
ALeffectslot::ALeffectslot()
|
ALeffectslot::ALeffectslot()
|
||||||
{
|
{
|
||||||
PropsClean.test_and_set(std::memory_order_relaxed);
|
mPropsDirty.test_and_clear(std::memory_order_relaxed);
|
||||||
|
|
||||||
EffectStateFactory *factory{getFactoryByType(EffectSlotType::None)};
|
EffectStateFactory *factory{getFactoryByType(EffectSlotType::None)};
|
||||||
assert(factory != nullptr);
|
assert(factory != nullptr);
|
||||||
|
@ -928,7 +946,7 @@ ALenum ALeffectslot::initEffect(ALeffect *effect, ALCcontext *context)
|
||||||
}
|
}
|
||||||
al::intrusive_ptr<EffectState> state{factory->create()};
|
al::intrusive_ptr<EffectState> state{factory->create()};
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::unique_lock<std::mutex> statelock{device->StateLock};
|
std::unique_lock<std::mutex> statelock{device->StateLock};
|
||||||
state->mOutTarget = device->Dry.Buffer;
|
state->mOutTarget = device->Dry.Buffer;
|
||||||
{
|
{
|
||||||
|
@ -1004,7 +1022,7 @@ void UpdateAllEffectSlotProps(ALCcontext *context)
|
||||||
usemask &= ~(1_u64 << idx);
|
usemask &= ~(1_u64 << idx);
|
||||||
|
|
||||||
if(slot->mState != SlotState::Stopped
|
if(slot->mState != SlotState::Stopped
|
||||||
&& slot->PropsClean.test_and_set(std::memory_order_acq_rel))
|
&& slot->mPropsDirty.test_and_clear(std::memory_order_acq_rel))
|
||||||
slot->updateProps(context);
|
slot->updateProps(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/device.h"
|
||||||
|
#include "alc/effectslot.h"
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "atomic.h"
|
#include "atomic.h"
|
||||||
#include "effectslot.h"
|
|
||||||
#include "effects/base.h"
|
|
||||||
#include "intrusive_ptr.h"
|
#include "intrusive_ptr.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ struct ALeffectslot {
|
||||||
al::intrusive_ptr<EffectState> State;
|
al::intrusive_ptr<EffectState> State;
|
||||||
} Effect;
|
} Effect;
|
||||||
|
|
||||||
std::atomic_flag PropsClean;
|
al::atomic_invflag mPropsDirty;
|
||||||
|
|
||||||
SlotState mState{SlotState::Initial};
|
SlotState mState{SlotState::Initial};
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
|
@ -43,14 +44,16 @@
|
||||||
|
|
||||||
#include "albit.h"
|
#include "albit.h"
|
||||||
#include "albyte.h"
|
#include "albyte.h"
|
||||||
#include "alcmain.h"
|
#include "alc/context.h"
|
||||||
#include "alcontext.h"
|
#include "alc/device.h"
|
||||||
|
#include "alc/inprogext.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "aloptional.h"
|
#include "aloptional.h"
|
||||||
#include "atomic.h"
|
#include "atomic.h"
|
||||||
#include "core/except.h"
|
#include "core/except.h"
|
||||||
#include "inprogext.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/voice.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -271,6 +274,9 @@ ALuint ChannelsFromUserFmt(UserFmtChannels chans, ALuint ambiorder) noexcept
|
||||||
case UserFmtX71: return 8;
|
case UserFmtX71: return 8;
|
||||||
case UserFmtBFormat2D: return (ambiorder*2) + 1;
|
case UserFmtBFormat2D: return (ambiorder*2) + 1;
|
||||||
case UserFmtBFormat3D: return (ambiorder+1) * (ambiorder+1);
|
case UserFmtBFormat3D: return (ambiorder+1) * (ambiorder+1);
|
||||||
|
case UserFmtUHJ2: return 2;
|
||||||
|
case UserFmtUHJ3: return 3;
|
||||||
|
case UserFmtUHJ4: return 4;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -465,6 +471,9 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
||||||
case UserFmtX71: DstChannels = FmtX71; break;
|
case UserFmtX71: DstChannels = FmtX71; break;
|
||||||
case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
|
case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
|
||||||
case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
|
case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
|
||||||
|
case UserFmtUHJ2: DstChannels = FmtUHJ2; break;
|
||||||
|
case UserFmtUHJ3: DstChannels = FmtUHJ3; break;
|
||||||
|
case UserFmtUHJ4: DstChannels = FmtUHJ4; break;
|
||||||
}
|
}
|
||||||
if UNLIKELY(static_cast<long>(SrcChannels) != static_cast<long>(DstChannels))
|
if UNLIKELY(static_cast<long>(SrcChannels) != static_cast<long>(DstChannels))
|
||||||
SETERR_RETURN(context, AL_INVALID_ENUM, , "Invalid format");
|
SETERR_RETURN(context, AL_INVALID_ENUM, , "Invalid format");
|
||||||
|
@ -501,7 +510,9 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
||||||
unpackalign, NameFromUserFmtType(SrcType));
|
unpackalign, NameFromUserFmtType(SrcType));
|
||||||
|
|
||||||
const ALuint ambiorder{(DstChannels == FmtBFormat2D || DstChannels == FmtBFormat3D) ?
|
const ALuint ambiorder{(DstChannels == FmtBFormat2D || DstChannels == FmtBFormat3D) ?
|
||||||
ALBuf->UnpackAmbiOrder : 0};
|
ALBuf->UnpackAmbiOrder :
|
||||||
|
((DstChannels == FmtUHJ2 || DstChannels == FmtUHJ3 || DstChannels == FmtUHJ4) ? 1 :
|
||||||
|
0)};
|
||||||
|
|
||||||
if((access&AL_PRESERVE_DATA_BIT_SOFT))
|
if((access&AL_PRESERVE_DATA_BIT_SOFT))
|
||||||
{
|
{
|
||||||
|
@ -622,6 +633,9 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
|
||||||
case UserFmtX71: DstChannels = FmtX71; break;
|
case UserFmtX71: DstChannels = FmtX71; break;
|
||||||
case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
|
case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
|
||||||
case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
|
case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
|
||||||
|
case UserFmtUHJ2: DstChannels = FmtUHJ2; break;
|
||||||
|
case UserFmtUHJ3: DstChannels = FmtUHJ3; break;
|
||||||
|
case UserFmtUHJ4: DstChannels = FmtUHJ4; break;
|
||||||
}
|
}
|
||||||
if UNLIKELY(static_cast<long>(SrcChannels) != static_cast<long>(DstChannels))
|
if UNLIKELY(static_cast<long>(SrcChannels) != static_cast<long>(DstChannels))
|
||||||
SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid format");
|
SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid format");
|
||||||
|
@ -643,10 +657,13 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
|
||||||
SETERR_RETURN(context, AL_INVALID_ENUM,, "Unsupported callback format");
|
SETERR_RETURN(context, AL_INVALID_ENUM,, "Unsupported callback format");
|
||||||
|
|
||||||
const ALuint ambiorder{(DstChannels == FmtBFormat2D || DstChannels == FmtBFormat3D) ?
|
const ALuint ambiorder{(DstChannels == FmtBFormat2D || DstChannels == FmtBFormat3D) ?
|
||||||
ALBuf->UnpackAmbiOrder : 0};
|
ALBuf->UnpackAmbiOrder :
|
||||||
|
((DstChannels == FmtUHJ2 || DstChannels == FmtUHJ3 || DstChannels == FmtUHJ4) ? 1 :
|
||||||
|
0)};
|
||||||
|
|
||||||
|
constexpr uint line_size{BufferLineSize + MaxPostVoiceLoad};
|
||||||
al::vector<al::byte,16>(FrameSizeFromFmt(DstChannels, DstType, ambiorder) *
|
al::vector<al::byte,16>(FrameSizeFromFmt(DstChannels, DstType, ambiorder) *
|
||||||
size_t{BufferLineSize + (MaxResamplerPadding>>1)}).swap(ALBuf->mData);
|
size_t{line_size}).swap(ALBuf->mData);
|
||||||
|
|
||||||
ALBuf->mCallback = callback;
|
ALBuf->mCallback = callback;
|
||||||
ALBuf->mUserData = userptr;
|
ALBuf->mUserData = userptr;
|
||||||
|
@ -675,7 +692,7 @@ al::optional<DecompResult> DecomposeUserFormat(ALenum format)
|
||||||
UserFmtChannels channels;
|
UserFmtChannels channels;
|
||||||
UserFmtType type;
|
UserFmtType type;
|
||||||
};
|
};
|
||||||
static const std::array<FormatMap,46> UserFmtList{{
|
static const std::array<FormatMap,55> UserFmtList{{
|
||||||
{ AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte },
|
{ AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte },
|
||||||
{ AL_FORMAT_MONO16, UserFmtMono, UserFmtShort },
|
{ AL_FORMAT_MONO16, UserFmtMono, UserFmtShort },
|
||||||
{ AL_FORMAT_MONO_FLOAT32, UserFmtMono, UserFmtFloat },
|
{ AL_FORMAT_MONO_FLOAT32, UserFmtMono, UserFmtFloat },
|
||||||
|
@ -731,6 +748,18 @@ al::optional<DecompResult> DecomposeUserFormat(ALenum format)
|
||||||
{ AL_FORMAT_BFORMAT3D_16, UserFmtBFormat3D, UserFmtShort },
|
{ AL_FORMAT_BFORMAT3D_16, UserFmtBFormat3D, UserFmtShort },
|
||||||
{ AL_FORMAT_BFORMAT3D_FLOAT32, UserFmtBFormat3D, UserFmtFloat },
|
{ AL_FORMAT_BFORMAT3D_FLOAT32, UserFmtBFormat3D, UserFmtFloat },
|
||||||
{ AL_FORMAT_BFORMAT3D_MULAW, UserFmtBFormat3D, UserFmtMulaw },
|
{ AL_FORMAT_BFORMAT3D_MULAW, UserFmtBFormat3D, UserFmtMulaw },
|
||||||
|
|
||||||
|
{ AL_FORMAT_UHJ2CHN8_SOFT, UserFmtUHJ2, UserFmtUByte },
|
||||||
|
{ AL_FORMAT_UHJ2CHN16_SOFT, UserFmtUHJ2, UserFmtShort },
|
||||||
|
{ AL_FORMAT_UHJ2CHN_FLOAT32_SOFT, UserFmtUHJ2, UserFmtFloat },
|
||||||
|
|
||||||
|
{ AL_FORMAT_UHJ3CHN8_SOFT, UserFmtUHJ3, UserFmtUByte },
|
||||||
|
{ AL_FORMAT_UHJ3CHN16_SOFT, UserFmtUHJ3, UserFmtShort },
|
||||||
|
{ AL_FORMAT_UHJ3CHN_FLOAT32_SOFT, UserFmtUHJ3, UserFmtFloat },
|
||||||
|
|
||||||
|
{ AL_FORMAT_UHJ4CHN8_SOFT, UserFmtUHJ4, UserFmtUByte },
|
||||||
|
{ AL_FORMAT_UHJ4CHN16_SOFT, UserFmtUHJ4, UserFmtShort },
|
||||||
|
{ AL_FORMAT_UHJ4CHN_FLOAT32_SOFT, UserFmtUHJ4, UserFmtFloat },
|
||||||
}};
|
}};
|
||||||
|
|
||||||
for(const auto &fmt : UserFmtList)
|
for(const auto &fmt : UserFmtList)
|
||||||
|
@ -754,7 +783,7 @@ START_API_FUNC
|
||||||
context->setError(AL_INVALID_VALUE, "Generating %d buffers", n);
|
context->setError(AL_INVALID_VALUE, "Generating %d buffers", n);
|
||||||
if UNLIKELY(n <= 0) return;
|
if UNLIKELY(n <= 0) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
if(!EnsureBuffers(device, static_cast<ALuint>(n)))
|
if(!EnsureBuffers(device, static_cast<ALuint>(n)))
|
||||||
{
|
{
|
||||||
|
@ -794,7 +823,7 @@ START_API_FUNC
|
||||||
context->setError(AL_INVALID_VALUE, "Deleting %d buffers", n);
|
context->setError(AL_INVALID_VALUE, "Deleting %d buffers", n);
|
||||||
if UNLIKELY(n <= 0) return;
|
if UNLIKELY(n <= 0) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
/* First try to find any buffers that are invalid or in-use. */
|
/* First try to find any buffers that are invalid or in-use. */
|
||||||
|
@ -834,7 +863,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if LIKELY(context)
|
if LIKELY(context)
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
if(!buffer || LookupBuffer(device, buffer))
|
if(!buffer || LookupBuffer(device, buffer))
|
||||||
return AL_TRUE;
|
return AL_TRUE;
|
||||||
|
@ -855,7 +884,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||||
|
@ -889,7 +918,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return nullptr;
|
if UNLIKELY(!context) return nullptr;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||||
|
@ -942,7 +971,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||||
|
@ -965,7 +994,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||||
|
@ -997,7 +1026,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||||
|
@ -1127,7 +1156,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||||
|
@ -1147,7 +1176,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||||
|
@ -1166,7 +1195,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||||
|
@ -1188,7 +1217,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||||
|
@ -1250,7 +1279,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||||
|
@ -1283,7 +1312,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||||
|
@ -1321,7 +1350,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||||
|
@ -1343,7 +1372,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||||
|
@ -1371,7 +1400,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||||
|
@ -1393,7 +1422,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||||
if UNLIKELY(!albuf)
|
if UNLIKELY(!albuf)
|
||||||
|
@ -1450,7 +1479,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||||
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
||||||
|
@ -1488,7 +1517,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||||
if UNLIKELY(!albuf)
|
if UNLIKELY(!albuf)
|
||||||
|
@ -1516,7 +1545,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
|
|
||||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||||
|
@ -1546,7 +1575,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||||
if UNLIKELY(!albuf)
|
if UNLIKELY(!albuf)
|
||||||
|
@ -1574,7 +1603,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||||
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
||||||
|
@ -1602,7 +1631,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||||
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
|
|
||||||
#include "albyte.h"
|
#include "albyte.h"
|
||||||
|
#include "alc/inprogext.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "atomic.h"
|
#include "atomic.h"
|
||||||
#include "buffer_storage.h"
|
#include "core/buffer_storage.h"
|
||||||
#include "inprogext.h"
|
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,6 +35,9 @@ enum UserFmtChannels : unsigned char {
|
||||||
UserFmtX71 = FmtX71,
|
UserFmtX71 = FmtX71,
|
||||||
UserFmtBFormat2D = FmtBFormat2D,
|
UserFmtBFormat2D = FmtBFormat2D,
|
||||||
UserFmtBFormat3D = FmtBFormat3D,
|
UserFmtBFormat3D = FmtBFormat3D,
|
||||||
|
UserFmtUHJ2 = FmtUHJ2,
|
||||||
|
UserFmtUHJ3 = FmtUHJ3,
|
||||||
|
UserFmtUHJ4 = FmtUHJ4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -39,14 +39,15 @@
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "albit.h"
|
#include "albit.h"
|
||||||
#include "alcmain.h"
|
#include "alc/context.h"
|
||||||
#include "alcontext.h"
|
#include "alc/device.h"
|
||||||
|
#include "alc/effects/base.h"
|
||||||
|
#include "alc/inprogext.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "alstring.h"
|
#include "alstring.h"
|
||||||
#include "core/except.h"
|
#include "core/except.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "effects/base.h"
|
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
|
@ -233,7 +234,7 @@ START_API_FUNC
|
||||||
context->setError(AL_INVALID_VALUE, "Generating %d effects", n);
|
context->setError(AL_INVALID_VALUE, "Generating %d effects", n);
|
||||||
if UNLIKELY(n <= 0) return;
|
if UNLIKELY(n <= 0) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||||
if(!EnsureEffects(device, static_cast<ALuint>(n)))
|
if(!EnsureEffects(device, static_cast<ALuint>(n)))
|
||||||
{
|
{
|
||||||
|
@ -273,7 +274,7 @@ START_API_FUNC
|
||||||
context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
|
context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
|
||||||
if UNLIKELY(n <= 0) return;
|
if UNLIKELY(n <= 0) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||||
|
|
||||||
/* First try to find any effects that are invalid. */
|
/* First try to find any effects that are invalid. */
|
||||||
|
@ -304,7 +305,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if LIKELY(context)
|
if LIKELY(context)
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||||
if(!effect || LookupEffect(device, effect))
|
if(!effect || LookupEffect(device, effect))
|
||||||
return AL_TRUE;
|
return AL_TRUE;
|
||||||
|
@ -319,7 +320,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||||
|
|
||||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
|
@ -369,7 +370,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||||
|
|
||||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
|
@ -392,7 +393,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||||
|
|
||||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
|
@ -415,7 +416,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||||
|
|
||||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
|
@ -438,7 +439,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||||
|
|
||||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
|
@ -470,7 +471,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||||
|
|
||||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
|
@ -493,7 +494,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||||
|
|
||||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
|
@ -516,7 +517,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||||
|
|
||||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "effects/base.h"
|
#include "alc/effects/base.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -1,17 +1,22 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "aloptional.h"
|
#include "aloptional.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
static_assert(ChorusMaxDelay >= AL_CHORUS_MAX_DELAY, "Chorus max delay too small");
|
||||||
|
static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too small");
|
||||||
|
|
||||||
static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
|
static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
|
||||||
static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
|
static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "inprogext.h"
|
#include "alc/inprogext.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/alext.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -12,7 +12,11 @@ class effect_exception final : public al::base_exception {
|
||||||
ALenum mErrorCode;
|
ALenum mErrorCode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
#ifdef __USE_MINGW_ANSI_STDIO
|
||||||
|
[[gnu::format(gnu_printf, 3, 4)]]
|
||||||
|
#else
|
||||||
[[gnu::format(printf, 3, 4)]]
|
[[gnu::format(printf, 3, 4)]]
|
||||||
|
#endif
|
||||||
effect_exception(ALenum code, const char *msg, ...);
|
effect_exception(ALenum code, const char *msg, ...);
|
||||||
|
|
||||||
ALenum errorCode() const noexcept { return mErrorCode; }
|
ALenum errorCode() const noexcept { return mErrorCode; }
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "aloptional.h"
|
#include "aloptional.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "aloptional.h"
|
#include "aloptional.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
#include "aloptional.h"
|
#include "aloptional.h"
|
||||||
#include "effects.h"
|
#include "effects.h"
|
||||||
#include "effects/base.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
|
|
||||||
#include "alcontext.h"
|
#include "alc/context.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "core/except.h"
|
#include "core/except.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
|
|
@ -18,17 +18,17 @@
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
|
|
||||||
#include "albyte.h"
|
#include "albyte.h"
|
||||||
#include "alcontext.h"
|
#include "alc/context.h"
|
||||||
|
#include "alc/effects/base.h"
|
||||||
|
#include "alc/inprogext.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "async_event.h"
|
#include "core/async_event.h"
|
||||||
#include "core/except.h"
|
#include "core/except.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "effects/base.h"
|
#include "core/voice_change.h"
|
||||||
#include "inprogext.h"
|
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "threads.h"
|
#include "threads.h"
|
||||||
#include "voice_change.h"
|
|
||||||
|
|
||||||
|
|
||||||
static int EventThread(ALCcontext *context)
|
static int EventThread(ALCcontext *context)
|
||||||
|
@ -75,25 +75,22 @@ static int EventThread(ALCcontext *context)
|
||||||
msg += " state has changed to ";
|
msg += " state has changed to ";
|
||||||
switch(evt.u.srcstate.state)
|
switch(evt.u.srcstate.state)
|
||||||
{
|
{
|
||||||
case VChangeState::Reset:
|
case AsyncEvent::SrcState::Reset:
|
||||||
msg += "AL_INITIAL";
|
msg += "AL_INITIAL";
|
||||||
state = AL_INITIAL;
|
state = AL_INITIAL;
|
||||||
break;
|
break;
|
||||||
case VChangeState::Stop:
|
case AsyncEvent::SrcState::Stop:
|
||||||
msg += "AL_STOPPED";
|
msg += "AL_STOPPED";
|
||||||
state = AL_STOPPED;
|
state = AL_STOPPED;
|
||||||
break;
|
break;
|
||||||
case VChangeState::Play:
|
case AsyncEvent::SrcState::Play:
|
||||||
msg += "AL_PLAYING";
|
msg += "AL_PLAYING";
|
||||||
state = AL_PLAYING;
|
state = AL_PLAYING;
|
||||||
break;
|
break;
|
||||||
case VChangeState::Pause:
|
case AsyncEvent::SrcState::Pause:
|
||||||
msg += "AL_PAUSED";
|
msg += "AL_PAUSED";
|
||||||
state = AL_PAUSED;
|
state = AL_PAUSED;
|
||||||
break;
|
break;
|
||||||
/* Shouldn't happen */
|
|
||||||
case VChangeState::Restart:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
|
context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
|
||||||
state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
|
|
||||||
#include "alcontext.h"
|
#include "alc/context.h"
|
||||||
#include "alstring.h"
|
#include "alstring.h"
|
||||||
#include "core/except.h"
|
#include "core/except.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
|
|
|
@ -37,8 +37,8 @@
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "albit.h"
|
#include "albit.h"
|
||||||
#include "alcmain.h"
|
#include "alc/context.h"
|
||||||
#include "alcontext.h"
|
#include "alc/device.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "core/except.h"
|
#include "core/except.h"
|
||||||
|
@ -52,7 +52,11 @@ class filter_exception final : public al::base_exception {
|
||||||
ALenum mErrorCode;
|
ALenum mErrorCode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
#ifdef __USE_MINGW_ANSI_STDIO
|
||||||
|
[[gnu::format(gnu_printf, 3, 4)]]
|
||||||
|
#else
|
||||||
[[gnu::format(printf, 3, 4)]]
|
[[gnu::format(printf, 3, 4)]]
|
||||||
|
#endif
|
||||||
filter_exception(ALenum code, const char *msg, ...) : mErrorCode{code}
|
filter_exception(ALenum code, const char *msg, ...) : mErrorCode{code}
|
||||||
{
|
{
|
||||||
std::va_list args;
|
std::va_list args;
|
||||||
|
@ -404,8 +408,8 @@ START_API_FUNC
|
||||||
context->setError(AL_INVALID_VALUE, "Generating %d filters", n);
|
context->setError(AL_INVALID_VALUE, "Generating %d filters", n);
|
||||||
if UNLIKELY(n <= 0) return;
|
if UNLIKELY(n <= 0) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||||
if(!EnsureFilters(device, static_cast<ALuint>(n)))
|
if(!EnsureFilters(device, static_cast<ALuint>(n)))
|
||||||
{
|
{
|
||||||
context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n, (n==1)?"":"s");
|
context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n, (n==1)?"":"s");
|
||||||
|
@ -444,7 +448,7 @@ START_API_FUNC
|
||||||
context->setError(AL_INVALID_VALUE, "Deleting %d filters", n);
|
context->setError(AL_INVALID_VALUE, "Deleting %d filters", n);
|
||||||
if UNLIKELY(n <= 0) return;
|
if UNLIKELY(n <= 0) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||||
|
|
||||||
/* First try to find any filters that are invalid. */
|
/* First try to find any filters that are invalid. */
|
||||||
|
@ -475,7 +479,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if LIKELY(context)
|
if LIKELY(context)
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||||
if(!filter || LookupFilter(device, filter))
|
if(!filter || LookupFilter(device, filter))
|
||||||
return AL_TRUE;
|
return AL_TRUE;
|
||||||
|
@ -491,7 +495,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||||
|
|
||||||
ALfilter *alfilt{LookupFilter(device, filter)};
|
ALfilter *alfilt{LookupFilter(device, filter)};
|
||||||
|
@ -532,7 +536,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||||
|
|
||||||
ALfilter *alfilt{LookupFilter(device, filter)};
|
ALfilter *alfilt{LookupFilter(device, filter)};
|
||||||
|
@ -555,7 +559,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||||
|
|
||||||
ALfilter *alfilt{LookupFilter(device, filter)};
|
ALfilter *alfilt{LookupFilter(device, filter)};
|
||||||
|
@ -578,7 +582,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||||
|
|
||||||
ALfilter *alfilt{LookupFilter(device, filter)};
|
ALfilter *alfilt{LookupFilter(device, filter)};
|
||||||
|
@ -601,7 +605,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||||
|
|
||||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||||
|
@ -636,7 +640,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||||
|
|
||||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||||
|
@ -659,7 +663,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||||
|
|
||||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||||
|
@ -682,7 +686,7 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||||
|
|
||||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "alcontext.h"
|
#include "alc/context.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "atomic.h"
|
#include "atomic.h"
|
||||||
#include "core/except.h"
|
#include "core/except.h"
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
if(!context->mDeferUpdates.load(std::memory_order_acquire)) \
|
if(!context->mDeferUpdates.load(std::memory_order_acquire)) \
|
||||||
UpdateListenerProps(context.get()); \
|
UpdateListenerProps(context.get()); \
|
||||||
else \
|
else \
|
||||||
listener.PropsClean.clear(std::memory_order_release); \
|
listener.mPropsDirty.set(std::memory_order_release); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
|
#include "atomic.h"
|
||||||
|
|
||||||
|
|
||||||
struct ALlistener {
|
struct ALlistener {
|
||||||
|
@ -19,9 +20,9 @@ struct ALlistener {
|
||||||
float Gain{1.0f};
|
float Gain{1.0f};
|
||||||
float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT};
|
float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT};
|
||||||
|
|
||||||
std::atomic_flag PropsClean;
|
al::atomic_invflag mPropsDirty;
|
||||||
|
|
||||||
ALlistener() { PropsClean.test_and_set(std::memory_order_relaxed); }
|
ALlistener() { mPropsDirty.test_and_clear(std::memory_order_relaxed); }
|
||||||
|
|
||||||
DISABLE_ALLOC()
|
DISABLE_ALLOC()
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <stdexcept>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
@ -46,31 +47,31 @@
|
||||||
#include "AL/efx.h"
|
#include "AL/efx.h"
|
||||||
|
|
||||||
#include "albit.h"
|
#include "albit.h"
|
||||||
#include "alcmain.h"
|
#include "alc/alu.h"
|
||||||
#include "alcontext.h"
|
#include "alc/backends/base.h"
|
||||||
|
#include "alc/context.h"
|
||||||
|
#include "alc/device.h"
|
||||||
|
#include "alc/inprogext.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "aloptional.h"
|
#include "aloptional.h"
|
||||||
#include "alspan.h"
|
#include "alspan.h"
|
||||||
#include "alu.h"
|
|
||||||
#include "atomic.h"
|
#include "atomic.h"
|
||||||
#include "auxeffectslot.h"
|
#include "auxeffectslot.h"
|
||||||
#include "backends/base.h"
|
|
||||||
#include "bformatdec.h"
|
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "core/ambidefs.h"
|
#include "core/ambidefs.h"
|
||||||
|
#include "core/bformatdec.h"
|
||||||
#include "core/except.h"
|
#include "core/except.h"
|
||||||
#include "core/filters/nfc.h"
|
#include "core/filters/nfc.h"
|
||||||
#include "core/filters/splitter.h"
|
#include "core/filters/splitter.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/voice_change.h"
|
||||||
#include "event.h"
|
#include "event.h"
|
||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
#include "inprogext.h"
|
|
||||||
#include "math_defs.h"
|
#include "math_defs.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "threads.h"
|
#include "threads.h"
|
||||||
#include "voice_change.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -183,7 +184,7 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context
|
||||||
*/
|
*/
|
||||||
int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
|
int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
const VoiceBufferItem *Current{};
|
const VoiceBufferItem *Current{};
|
||||||
uint64_t readPos{};
|
uint64_t readPos{};
|
||||||
ALuint refcount;
|
ALuint refcount;
|
||||||
|
@ -222,7 +223,7 @@ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds
|
||||||
*/
|
*/
|
||||||
double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
|
double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
const VoiceBufferItem *Current{};
|
const VoiceBufferItem *Current{};
|
||||||
uint64_t readPos{};
|
uint64_t readPos{};
|
||||||
ALuint refcount;
|
ALuint refcount;
|
||||||
|
@ -271,7 +272,7 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl
|
||||||
*/
|
*/
|
||||||
double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
|
double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
const VoiceBufferItem *Current{};
|
const VoiceBufferItem *Current{};
|
||||||
ALuint readPos{};
|
ALuint readPos{};
|
||||||
ALuint readPosFrac{};
|
ALuint readPosFrac{};
|
||||||
|
@ -349,6 +350,57 @@ double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* GetSourceLength
|
||||||
|
*
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
uint64_t length{0};
|
||||||
|
const ALbuffer *BufferFmt{nullptr};
|
||||||
|
for(auto &listitem : source->mQueue)
|
||||||
|
{
|
||||||
|
if(!BufferFmt)
|
||||||
|
BufferFmt = listitem.mBuffer;
|
||||||
|
length += listitem.mSampleLen;
|
||||||
|
}
|
||||||
|
if(length == 0)
|
||||||
|
return 0.0;
|
||||||
|
|
||||||
|
assert(BufferFmt != nullptr);
|
||||||
|
switch(name)
|
||||||
|
{
|
||||||
|
case AL_SEC_LENGTH_SOFT:
|
||||||
|
return static_cast<double>(length) / BufferFmt->mSampleRate;
|
||||||
|
|
||||||
|
case AL_SAMPLE_LENGTH_SOFT:
|
||||||
|
return static_cast<double>(length);
|
||||||
|
|
||||||
|
case AL_BYTE_LENGTH_SOFT:
|
||||||
|
if(BufferFmt->OriginalType == UserFmtIMA4)
|
||||||
|
{
|
||||||
|
ALuint FrameBlockSize{BufferFmt->OriginalAlign};
|
||||||
|
ALuint align{(BufferFmt->OriginalAlign-1)/2 + 4};
|
||||||
|
ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
|
||||||
|
|
||||||
|
/* Round down to nearest ADPCM block */
|
||||||
|
return static_cast<double>(length / FrameBlockSize) * BlockSize;
|
||||||
|
}
|
||||||
|
else if(BufferFmt->OriginalType == UserFmtMSADPCM)
|
||||||
|
{
|
||||||
|
ALuint FrameBlockSize{BufferFmt->OriginalAlign};
|
||||||
|
ALuint align{(FrameBlockSize-2)/2 + 7};
|
||||||
|
ALuint BlockSize{align * BufferFmt->channelsFromFmt()};
|
||||||
|
|
||||||
|
/* Round down to nearest ADPCM block */
|
||||||
|
return static_cast<double>(length / FrameBlockSize) * BlockSize;
|
||||||
|
}
|
||||||
|
return static_cast<double>(length) * BufferFmt->frameSizeFromFmt();
|
||||||
|
}
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct VoicePos {
|
struct VoicePos {
|
||||||
ALuint pos, frac;
|
ALuint pos, frac;
|
||||||
|
@ -439,72 +491,34 @@ void InitVoice(Voice *voice, ALsource *source, ALbufferQueueItem *BufferList, AL
|
||||||
std::memory_order_relaxed);
|
std::memory_order_relaxed);
|
||||||
|
|
||||||
ALbuffer *buffer{BufferList->mBuffer};
|
ALbuffer *buffer{BufferList->mBuffer};
|
||||||
ALuint num_channels{buffer->channelsFromFmt()};
|
ALuint num_channels{(buffer->mChannels==FmtUHJ2) ? 3 : buffer->channelsFromFmt()};
|
||||||
voice->mFrequency = buffer->mSampleRate;
|
voice->mFrequency = buffer->mSampleRate;
|
||||||
voice->mFmtChannels = buffer->mChannels;
|
voice->mFmtChannels = buffer->mChannels;
|
||||||
voice->mFmtType = buffer->mType;
|
voice->mFmtType = buffer->mType;
|
||||||
voice->mSampleSize = buffer->bytesFromFmt();
|
voice->mFrameSize = buffer->frameSizeFromFmt();
|
||||||
voice->mAmbiLayout = buffer->mAmbiLayout;
|
voice->mAmbiLayout = (buffer->mChannels == FmtUHJ2 || buffer->mChannels == FmtUHJ3
|
||||||
voice->mAmbiScaling = buffer->mAmbiScaling;
|
|| voice->mFmtChannels == FmtUHJ4) ? AmbiLayout::FuMa : buffer->mAmbiLayout;
|
||||||
|
voice->mAmbiScaling = (buffer->mChannels == FmtUHJ2 || buffer->mChannels == FmtUHJ3
|
||||||
|
|| voice->mFmtChannels == FmtUHJ4) ? AmbiScaling::FuMa : buffer->mAmbiScaling;
|
||||||
voice->mAmbiOrder = buffer->mAmbiOrder;
|
voice->mAmbiOrder = buffer->mAmbiOrder;
|
||||||
|
|
||||||
if(buffer->mCallback) voice->mFlags |= VoiceIsCallback;
|
if(buffer->mCallback) voice->mFlags |= VoiceIsCallback;
|
||||||
else if(source->SourceType == AL_STATIC) voice->mFlags |= VoiceIsStatic;
|
else if(source->SourceType == AL_STATIC) voice->mFlags |= VoiceIsStatic;
|
||||||
voice->mNumCallbackSamples = 0;
|
voice->mNumCallbackSamples = 0;
|
||||||
|
|
||||||
/* Clear the stepping value explicitly so the mixer knows not to mix this
|
|
||||||
* until the update gets applied.
|
|
||||||
*/
|
|
||||||
voice->mStep = 0;
|
|
||||||
|
|
||||||
if(voice->mChans.capacity() > 2 && num_channels < voice->mChans.capacity())
|
if(voice->mChans.capacity() > 2 && num_channels < voice->mChans.capacity())
|
||||||
al::vector<Voice::ChannelData>{}.swap(voice->mChans);
|
{
|
||||||
|
decltype(voice->mChans){}.swap(voice->mChans);
|
||||||
|
decltype(voice->mVoiceSamples){}.swap(voice->mVoiceSamples);
|
||||||
|
}
|
||||||
voice->mChans.reserve(maxu(2, num_channels));
|
voice->mChans.reserve(maxu(2, num_channels));
|
||||||
voice->mChans.resize(num_channels);
|
voice->mChans.resize(num_channels);
|
||||||
|
voice->mVoiceSamples.reserve(maxu(2, num_channels));
|
||||||
|
voice->mVoiceSamples.resize(num_channels);
|
||||||
|
|
||||||
/* Don't need to set the VOICE_IS_AMBISONIC flag if the device is not
|
voice->prepare(device);
|
||||||
* higher order than the voice. No HF scaling is necessary to mix it.
|
|
||||||
*/
|
|
||||||
if(voice->mAmbiOrder && device->mAmbiOrder > voice->mAmbiOrder)
|
|
||||||
{
|
|
||||||
const uint8_t *OrderFromChan{(voice->mFmtChannels == FmtBFormat2D) ?
|
|
||||||
AmbiIndex::OrderFrom2DChannel().data() :
|
|
||||||
AmbiIndex::OrderFromChannel().data()};
|
|
||||||
const auto scales = BFormatDec::GetHFOrderScales(voice->mAmbiOrder, device->mAmbiOrder);
|
|
||||||
|
|
||||||
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)};
|
source->mPropsDirty.test_and_clear(std::memory_order_acq_rel);
|
||||||
|
|
||||||
for(auto &chandata : voice->mChans)
|
|
||||||
{
|
|
||||||
chandata.mPrevSamples.fill(0.0f);
|
|
||||||
chandata.mAmbiScale = scales[*(OrderFromChan++)];
|
|
||||||
chandata.mAmbiSplitter = splitter;
|
|
||||||
chandata.mDryParams = DirectParams{};
|
|
||||||
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
|
|
||||||
}
|
|
||||||
|
|
||||||
voice->mFlags |= VoiceIsAmbisonic;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Clear previous samples. */
|
|
||||||
for(auto &chandata : voice->mChans)
|
|
||||||
{
|
|
||||||
chandata.mPrevSamples.fill(0.0f);
|
|
||||||
chandata.mDryParams = DirectParams{};
|
|
||||||
std::fill_n(chandata.mWetParams.begin(), device->NumAuxSends, SendParams{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(device->AvgSpeakerDist > 0.0f)
|
|
||||||
{
|
|
||||||
const float w1{SpeedOfSoundMetersPerSec /
|
|
||||||
(device->AvgSpeakerDist * static_cast<float>(device->Frequency))};
|
|
||||||
for(auto &chandata : voice->mChans)
|
|
||||||
chandata.mDryParams.NFCtrlFilter.init(w1);
|
|
||||||
}
|
|
||||||
|
|
||||||
source->PropsClean.test_and_set(std::memory_order_acq_rel);
|
|
||||||
UpdateSourceProps(source, voice, context);
|
UpdateSourceProps(source, voice, context);
|
||||||
|
|
||||||
voice->mSourceID.store(source->id, std::memory_order_release);
|
voice->mSourceID.store(source->id, std::memory_order_release);
|
||||||
|
@ -527,7 +541,7 @@ VoiceChange *GetVoiceChanger(ALCcontext *ctx)
|
||||||
|
|
||||||
void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail)
|
void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail)
|
||||||
{
|
{
|
||||||
ALCdevice *device{ctx->mDevice.get()};
|
ALCdevice *device{ctx->mALDevice.get()};
|
||||||
|
|
||||||
VoiceChange *oldhead{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
|
VoiceChange *oldhead{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
|
||||||
while(VoiceChange *next{oldhead->mNext.load(std::memory_order_relaxed)})
|
while(VoiceChange *next{oldhead->mNext.load(std::memory_order_relaxed)})
|
||||||
|
@ -538,7 +552,11 @@ void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail)
|
||||||
device->waitForMix();
|
device->waitForMix();
|
||||||
if UNLIKELY(!connected)
|
if UNLIKELY(!connected)
|
||||||
{
|
{
|
||||||
/* If the device is disconnected, just ignore all pending changes. */
|
if(ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire))
|
||||||
|
{
|
||||||
|
/* If the device is disconnected and voices are stopped, just
|
||||||
|
* ignore all pending changes.
|
||||||
|
*/
|
||||||
VoiceChange *cur{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
|
VoiceChange *cur{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
|
||||||
while(VoiceChange *next{cur->mNext.load(std::memory_order_acquire)})
|
while(VoiceChange *next{cur->mNext.load(std::memory_order_acquire)})
|
||||||
{
|
{
|
||||||
|
@ -548,6 +566,7 @@ void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail)
|
||||||
}
|
}
|
||||||
ctx->mCurrentVoiceChange.store(cur, std::memory_order_release);
|
ctx->mCurrentVoiceChange.store(cur, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -934,6 +953,11 @@ enum SourceProp : ALenum {
|
||||||
/* AL_EXT_BFORMAT */
|
/* AL_EXT_BFORMAT */
|
||||||
srcOrientation = AL_ORIENTATION,
|
srcOrientation = AL_ORIENTATION,
|
||||||
|
|
||||||
|
/* AL_SOFT_source_length */
|
||||||
|
srcByteLength = AL_BYTE_LENGTH_SOFT,
|
||||||
|
srcSampleLength = AL_SAMPLE_LENGTH_SOFT,
|
||||||
|
srcSecLength = AL_SEC_LENGTH_SOFT,
|
||||||
|
|
||||||
/* AL_SOFT_source_resampler */
|
/* AL_SOFT_source_resampler */
|
||||||
srcResampler = AL_SOURCE_RESAMPLER_SOFT,
|
srcResampler = AL_SOURCE_RESAMPLER_SOFT,
|
||||||
|
|
||||||
|
@ -983,6 +1007,9 @@ ALuint FloatValsByProp(ALenum prop)
|
||||||
case AL_SOURCE_RADIUS:
|
case AL_SOURCE_RADIUS:
|
||||||
case AL_SOURCE_RESAMPLER_SOFT:
|
case AL_SOURCE_RESAMPLER_SOFT:
|
||||||
case AL_SOURCE_SPATIALIZE_SOFT:
|
case AL_SOURCE_SPATIALIZE_SOFT:
|
||||||
|
case AL_BYTE_LENGTH_SOFT:
|
||||||
|
case AL_SAMPLE_LENGTH_SOFT:
|
||||||
|
case AL_SEC_LENGTH_SOFT:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
case AL_STEREO_ANGLES:
|
case AL_STEREO_ANGLES:
|
||||||
|
@ -1045,6 +1072,9 @@ ALuint DoubleValsByProp(ALenum prop)
|
||||||
case AL_SOURCE_RADIUS:
|
case AL_SOURCE_RADIUS:
|
||||||
case AL_SOURCE_RESAMPLER_SOFT:
|
case AL_SOURCE_RESAMPLER_SOFT:
|
||||||
case AL_SOURCE_SPATIALIZE_SOFT:
|
case AL_SOURCE_SPATIALIZE_SOFT:
|
||||||
|
case AL_BYTE_LENGTH_SOFT:
|
||||||
|
case AL_SAMPLE_LENGTH_SOFT:
|
||||||
|
case AL_SEC_LENGTH_SOFT:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
case AL_SEC_OFFSET_LATENCY_SOFT:
|
case AL_SEC_OFFSET_LATENCY_SOFT:
|
||||||
|
@ -1095,7 +1125,7 @@ bool UpdateSourceProps(ALsource *source, ALCcontext *context)
|
||||||
if(SourceShouldUpdate(source, context) && (voice=GetSourceVoice(source, context)) != nullptr)
|
if(SourceShouldUpdate(source, context) && (voice=GetSourceVoice(source, context)) != nullptr)
|
||||||
UpdateSourceProps(source, voice, context);
|
UpdateSourceProps(source, voice, context);
|
||||||
else
|
else
|
||||||
source->PropsClean.clear(std::memory_order_release);
|
source->mPropsDirty.set(std::memory_order_release);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1105,6 +1135,7 @@ bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
||||||
|
|
||||||
switch(prop)
|
switch(prop)
|
||||||
{
|
{
|
||||||
|
case AL_SEC_LENGTH_SOFT:
|
||||||
case AL_SEC_OFFSET_LATENCY_SOFT:
|
case AL_SEC_OFFSET_LATENCY_SOFT:
|
||||||
case AL_SEC_OFFSET_CLOCK_SOFT:
|
case AL_SEC_OFFSET_CLOCK_SOFT:
|
||||||
/* Query only */
|
/* Query only */
|
||||||
|
@ -1223,7 +1254,7 @@ bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
||||||
auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]);
|
auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]);
|
||||||
if(!vpos) SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid offset");
|
if(!vpos) SETERR_RETURN(Context, AL_INVALID_VALUE, false, "Invalid offset");
|
||||||
|
|
||||||
if(SetVoiceOffset(voice, *vpos, Source, Context, Context->mDevice.get()))
|
if(SetVoiceOffset(voice, *vpos, Source, Context, Context->mALDevice.get()))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Source->OffsetType = prop;
|
Source->OffsetType = prop;
|
||||||
|
@ -1298,6 +1329,8 @@ bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
||||||
case AL_DIRECT_CHANNELS_SOFT:
|
case AL_DIRECT_CHANNELS_SOFT:
|
||||||
case AL_SOURCE_RESAMPLER_SOFT:
|
case AL_SOURCE_RESAMPLER_SOFT:
|
||||||
case AL_SOURCE_SPATIALIZE_SOFT:
|
case AL_SOURCE_SPATIALIZE_SOFT:
|
||||||
|
case AL_BYTE_LENGTH_SOFT:
|
||||||
|
case AL_SAMPLE_LENGTH_SOFT:
|
||||||
CHECKSIZE(values, 1);
|
CHECKSIZE(values, 1);
|
||||||
ival = static_cast<int>(values[0]);
|
ival = static_cast<int>(values[0]);
|
||||||
return SetSourceiv(Source, Context, prop, {&ival, 1u});
|
return SetSourceiv(Source, Context, prop, {&ival, 1u});
|
||||||
|
@ -1323,7 +1356,7 @@ bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
||||||
|
|
||||||
bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int> values)
|
bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<const int> values)
|
||||||
{
|
{
|
||||||
ALCdevice *device{Context->mDevice.get()};
|
ALCdevice *device{Context->mALDevice.get()};
|
||||||
ALeffectslot *slot{nullptr};
|
ALeffectslot *slot{nullptr};
|
||||||
al::deque<ALbufferQueueItem> oldlist;
|
al::deque<ALbufferQueueItem> oldlist;
|
||||||
std::unique_lock<std::mutex> slotlock;
|
std::unique_lock<std::mutex> slotlock;
|
||||||
|
@ -1335,6 +1368,8 @@ bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
||||||
case AL_SOURCE_TYPE:
|
case AL_SOURCE_TYPE:
|
||||||
case AL_BUFFERS_QUEUED:
|
case AL_BUFFERS_QUEUED:
|
||||||
case AL_BUFFERS_PROCESSED:
|
case AL_BUFFERS_PROCESSED:
|
||||||
|
case AL_BYTE_LENGTH_SOFT:
|
||||||
|
case AL_SAMPLE_LENGTH_SOFT:
|
||||||
/* Query only */
|
/* Query only */
|
||||||
SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
|
SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
|
||||||
"Setting read-only source property 0x%04x", prop);
|
"Setting read-only source property 0x%04x", prop);
|
||||||
|
@ -1577,7 +1612,7 @@ bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
||||||
*/
|
*/
|
||||||
Voice *voice{GetSourceVoice(Source, Context)};
|
Voice *voice{GetSourceVoice(Source, Context)};
|
||||||
if(voice) UpdateSourceProps(Source, voice, Context);
|
if(voice) UpdateSourceProps(Source, voice, Context);
|
||||||
else Source->PropsClean.clear(std::memory_order_release);
|
else Source->mPropsDirty.set(std::memory_order_release);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1606,6 +1641,7 @@ bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
||||||
case AL_AIR_ABSORPTION_FACTOR:
|
case AL_AIR_ABSORPTION_FACTOR:
|
||||||
case AL_ROOM_ROLLOFF_FACTOR:
|
case AL_ROOM_ROLLOFF_FACTOR:
|
||||||
case AL_SOURCE_RADIUS:
|
case AL_SOURCE_RADIUS:
|
||||||
|
case AL_SEC_LENGTH_SOFT:
|
||||||
CHECKSIZE(values, 1);
|
CHECKSIZE(values, 1);
|
||||||
fvals[0] = static_cast<float>(values[0]);
|
fvals[0] = static_cast<float>(values[0]);
|
||||||
return SetSourcefv(Source, Context, prop, {fvals, 1u});
|
return SetSourcefv(Source, Context, prop, {fvals, 1u});
|
||||||
|
@ -1655,6 +1691,8 @@ bool SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const
|
||||||
case AL_BUFFERS_QUEUED:
|
case AL_BUFFERS_QUEUED:
|
||||||
case AL_BUFFERS_PROCESSED:
|
case AL_BUFFERS_PROCESSED:
|
||||||
case AL_SOURCE_STATE:
|
case AL_SOURCE_STATE:
|
||||||
|
case AL_BYTE_LENGTH_SOFT:
|
||||||
|
case AL_SAMPLE_LENGTH_SOFT:
|
||||||
case AL_SAMPLE_OFFSET_LATENCY_SOFT:
|
case AL_SAMPLE_OFFSET_LATENCY_SOFT:
|
||||||
case AL_SAMPLE_OFFSET_CLOCK_SOFT:
|
case AL_SAMPLE_OFFSET_CLOCK_SOFT:
|
||||||
/* Query only */
|
/* Query only */
|
||||||
|
@ -1716,6 +1754,7 @@ bool SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const
|
||||||
case AL_AIR_ABSORPTION_FACTOR:
|
case AL_AIR_ABSORPTION_FACTOR:
|
||||||
case AL_ROOM_ROLLOFF_FACTOR:
|
case AL_ROOM_ROLLOFF_FACTOR:
|
||||||
case AL_SOURCE_RADIUS:
|
case AL_SOURCE_RADIUS:
|
||||||
|
case AL_SEC_LENGTH_SOFT:
|
||||||
CHECKSIZE(values, 1);
|
CHECKSIZE(values, 1);
|
||||||
fvals[0] = static_cast<float>(values[0]);
|
fvals[0] = static_cast<float>(values[0]);
|
||||||
return SetSourcefv(Source, Context, prop, {fvals, 1u});
|
return SetSourcefv(Source, Context, prop, {fvals, 1u});
|
||||||
|
@ -1761,7 +1800,7 @@ bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const
|
||||||
|
|
||||||
bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<double> values)
|
bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<double> values)
|
||||||
{
|
{
|
||||||
ALCdevice *device{Context->mDevice.get()};
|
ALCdevice *device{Context->mALDevice.get()};
|
||||||
ClockLatency clocktime;
|
ClockLatency clocktime;
|
||||||
nanoseconds srcclock;
|
nanoseconds srcclock;
|
||||||
int ivals[MaxValues];
|
int ivals[MaxValues];
|
||||||
|
@ -1851,6 +1890,13 @@ bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
||||||
values[0] = Source->Radius;
|
values[0] = Source->Radius;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case AL_BYTE_LENGTH_SOFT:
|
||||||
|
case AL_SAMPLE_LENGTH_SOFT:
|
||||||
|
case AL_SEC_LENGTH_SOFT:
|
||||||
|
CHECKSIZE(values, 1);
|
||||||
|
values[0] = GetSourceLength(Source, prop);
|
||||||
|
return true;
|
||||||
|
|
||||||
case AL_STEREO_ANGLES:
|
case AL_STEREO_ANGLES:
|
||||||
CHECKSIZE(values, 2);
|
CHECKSIZE(values, 2);
|
||||||
values[0] = Source->StereoPan[0];
|
values[0] = Source->StereoPan[0];
|
||||||
|
@ -2044,6 +2090,14 @@ bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
||||||
values[0] = ALenumFromDistanceModel(Source->mDistanceModel);
|
values[0] = ALenumFromDistanceModel(Source->mDistanceModel);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case AL_BYTE_LENGTH_SOFT:
|
||||||
|
case AL_SAMPLE_LENGTH_SOFT:
|
||||||
|
case AL_SEC_LENGTH_SOFT:
|
||||||
|
CHECKSIZE(values, 1);
|
||||||
|
values[0] = static_cast<int>(mind(GetSourceLength(Source, prop),
|
||||||
|
std::numeric_limits<int>::max()));
|
||||||
|
return true;
|
||||||
|
|
||||||
case AL_SOURCE_RESAMPLER_SOFT:
|
case AL_SOURCE_RESAMPLER_SOFT:
|
||||||
CHECKSIZE(values, 1);
|
CHECKSIZE(values, 1);
|
||||||
values[0] = static_cast<int>(Source->mResampler);
|
values[0] = static_cast<int>(Source->mResampler);
|
||||||
|
@ -2126,7 +2180,7 @@ bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
||||||
|
|
||||||
bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int64_t> values)
|
bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const al::span<int64_t> values)
|
||||||
{
|
{
|
||||||
ALCdevice *device = Context->mDevice.get();
|
ALCdevice *device{Context->mALDevice.get()};
|
||||||
ClockLatency clocktime;
|
ClockLatency clocktime;
|
||||||
nanoseconds srcclock;
|
nanoseconds srcclock;
|
||||||
double dvals[MaxValues];
|
double dvals[MaxValues];
|
||||||
|
@ -2135,6 +2189,13 @@ bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const
|
||||||
|
|
||||||
switch(prop)
|
switch(prop)
|
||||||
{
|
{
|
||||||
|
case AL_BYTE_LENGTH_SOFT:
|
||||||
|
case AL_SAMPLE_LENGTH_SOFT:
|
||||||
|
case AL_SEC_LENGTH_SOFT:
|
||||||
|
CHECKSIZE(values, 1);
|
||||||
|
values[0] = static_cast<int64_t>(GetSourceLength(Source, prop));
|
||||||
|
return true;
|
||||||
|
|
||||||
case AL_SAMPLE_OFFSET_LATENCY_SOFT:
|
case AL_SAMPLE_OFFSET_LATENCY_SOFT:
|
||||||
CHECKSIZE(values, 2);
|
CHECKSIZE(values, 2);
|
||||||
/* Get the source offset with the clock time first. Then get the clock
|
/* Get the source offset with the clock time first. Then get the clock
|
||||||
|
@ -2277,7 +2338,7 @@ START_API_FUNC
|
||||||
if UNLIKELY(n <= 0) return;
|
if UNLIKELY(n <= 0) return;
|
||||||
|
|
||||||
std::unique_lock<std::mutex> srclock{context->mSourceLock};
|
std::unique_lock<std::mutex> srclock{context->mSourceLock};
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
if(static_cast<ALuint>(n) > device->SourcesMax-context->mNumSources)
|
if(static_cast<ALuint>(n) > device->SourcesMax-context->mNumSources)
|
||||||
{
|
{
|
||||||
context->setError(AL_OUT_OF_MEMORY, "Exceeding %u source limit (%u + %d)",
|
context->setError(AL_OUT_OF_MEMORY, "Exceeding %u source limit (%u + %d)",
|
||||||
|
@ -2869,19 +2930,24 @@ START_API_FUNC
|
||||||
++sources;
|
++sources;
|
||||||
}
|
}
|
||||||
|
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
/* If the device is disconnected, go right to stopped. */
|
/* If the device is disconnected, and voices stop on disconnect, go right
|
||||||
|
* to stopped.
|
||||||
|
*/
|
||||||
if UNLIKELY(!device->Connected.load(std::memory_order_acquire))
|
if UNLIKELY(!device->Connected.load(std::memory_order_acquire))
|
||||||
{
|
{
|
||||||
/* TODO: Send state change event? */
|
if(context->mStopVoicesOnDisconnect.load(std::memory_order_acquire))
|
||||||
|
{
|
||||||
for(ALsource *source : srchandles)
|
for(ALsource *source : srchandles)
|
||||||
{
|
{
|
||||||
|
/* TODO: Send state change event? */
|
||||||
source->Offset = 0.0;
|
source->Offset = 0.0;
|
||||||
source->OffsetType = AL_NONE;
|
source->OffsetType = AL_NONE;
|
||||||
source->state = AL_STOPPED;
|
source->state = AL_STOPPED;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Count the number of reusable voices. */
|
/* Count the number of reusable voices. */
|
||||||
auto voicelist = context->getVoicesSpan();
|
auto voicelist = context->getVoicesSpan();
|
||||||
|
@ -3247,7 +3313,7 @@ START_API_FUNC
|
||||||
SETERR_RETURN(context, AL_INVALID_OPERATION,, "Queueing onto static source %u", src);
|
SETERR_RETURN(context, AL_INVALID_OPERATION,, "Queueing onto static source %u", src);
|
||||||
|
|
||||||
/* Check for a valid Buffer, for its frequency and format */
|
/* Check for a valid Buffer, for its frequency and format */
|
||||||
ALCdevice *device{context->mDevice.get()};
|
ALCdevice *device{context->mALDevice.get()};
|
||||||
ALbuffer *BufferFmt{nullptr};
|
ALbuffer *BufferFmt{nullptr};
|
||||||
for(auto &item : source->mQueue)
|
for(auto &item : source->mQueue)
|
||||||
{
|
{
|
||||||
|
@ -3396,6 +3462,17 @@ START_API_FUNC
|
||||||
END_API_FUNC
|
END_API_FUNC
|
||||||
|
|
||||||
|
|
||||||
|
AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint, ALsizei, const ALuint*)
|
||||||
|
START_API_FUNC
|
||||||
|
{
|
||||||
|
ContextRef context{GetContextRef()};
|
||||||
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
|
context->setError(AL_INVALID_OPERATION, "alSourceQueueBufferLayersSOFT not supported");
|
||||||
|
}
|
||||||
|
END_API_FUNC
|
||||||
|
|
||||||
|
|
||||||
ALsource::ALsource()
|
ALsource::ALsource()
|
||||||
{
|
{
|
||||||
Direct.Gain = 1.0f;
|
Direct.Gain = 1.0f;
|
||||||
|
@ -3413,7 +3490,7 @@ ALsource::ALsource()
|
||||||
send.LFReference = HIGHPASSFREQREF;
|
send.LFReference = HIGHPASSFREQREF;
|
||||||
}
|
}
|
||||||
|
|
||||||
PropsClean.test_and_set(std::memory_order_relaxed);
|
mPropsDirty.test_and_clear(std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
ALsource::~ALsource()
|
ALsource::~ALsource()
|
||||||
|
@ -3440,7 +3517,7 @@ void UpdateAllSourceProps(ALCcontext *context)
|
||||||
ALsource *source = sid ? LookupSource(context, sid) : nullptr;
|
ALsource *source = sid ? LookupSource(context, sid) : nullptr;
|
||||||
if(source && source->VoiceIdx == vidx)
|
if(source && source->VoiceIdx == vidx)
|
||||||
{
|
{
|
||||||
if(!source->PropsClean.test_and_set(std::memory_order_acq_rel))
|
if(source->mPropsDirty.test_and_clear(std::memory_order_acq_rel))
|
||||||
UpdateSourceProps(source, voice, context);
|
UpdateSourceProps(source, voice, context);
|
||||||
}
|
}
|
||||||
++vidx;
|
++vidx;
|
||||||
|
|
|
@ -11,14 +11,15 @@
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
|
|
||||||
#include "alcontext.h"
|
#include "alc/alu.h"
|
||||||
|
#include "alc/context.h"
|
||||||
#include "aldeque.h"
|
#include "aldeque.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "alu.h"
|
#include "atomic.h"
|
||||||
|
#include "core/voice.h"
|
||||||
#include "math_defs.h"
|
#include "math_defs.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
#include "voice.h"
|
|
||||||
|
|
||||||
struct ALbuffer;
|
struct ALbuffer;
|
||||||
struct ALeffectslot;
|
struct ALeffectslot;
|
||||||
|
@ -109,7 +110,7 @@ struct ALsource {
|
||||||
/** Source Buffer Queue head. */
|
/** Source Buffer Queue head. */
|
||||||
al::deque<ALbufferQueueItem> mQueue;
|
al::deque<ALbufferQueueItem> mQueue;
|
||||||
|
|
||||||
std::atomic_flag PropsClean;
|
al::atomic_invflag mPropsDirty;
|
||||||
|
|
||||||
/* Index into the context's Voices array. Lazily updated, only checked and
|
/* Index into the context's Voices array. Lazily updated, only checked and
|
||||||
* reset when looking up the voice.
|
* reset when looking up the voice.
|
||||||
|
|
|
@ -24,26 +24,27 @@
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "AL/al.h"
|
#include "AL/al.h"
|
||||||
#include "AL/alc.h"
|
#include "AL/alc.h"
|
||||||
#include "AL/alext.h"
|
#include "AL/alext.h"
|
||||||
|
|
||||||
#include "alcontext.h"
|
#include "alc/alu.h"
|
||||||
#include "almalloc.h"
|
#include "alc/context.h"
|
||||||
|
#include "alc/inprogext.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "alspan.h"
|
#include "aloptional.h"
|
||||||
#include "alu.h"
|
|
||||||
#include "atomic.h"
|
#include "atomic.h"
|
||||||
|
#include "core/context.h"
|
||||||
#include "core/except.h"
|
#include "core/except.h"
|
||||||
#include "event.h"
|
#include "core/mixer/defs.h"
|
||||||
#include "inprogext.h"
|
#include "core/voice.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
#include "voice.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -142,7 +143,7 @@ END_API_FUNC
|
||||||
if(!context->mDeferUpdates.load(std::memory_order_acquire)) \
|
if(!context->mDeferUpdates.load(std::memory_order_acquire)) \
|
||||||
UpdateContextProps(context.get()); \
|
UpdateContextProps(context.get()); \
|
||||||
else \
|
else \
|
||||||
context->mPropsClean.clear(std::memory_order_release); \
|
context->mPropsDirty.set(std::memory_order_release); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
|
@ -152,12 +153,18 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
||||||
switch(capability)
|
switch(capability)
|
||||||
{
|
{
|
||||||
case AL_SOURCE_DISTANCE_MODEL:
|
case AL_SOURCE_DISTANCE_MODEL:
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||||
context->mSourceDistanceModel = true;
|
context->mSourceDistanceModel = true;
|
||||||
DO_UPDATEPROPS();
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -172,12 +179,18 @@ START_API_FUNC
|
||||||
ContextRef context{GetContextRef()};
|
ContextRef context{GetContextRef()};
|
||||||
if UNLIKELY(!context) return;
|
if UNLIKELY(!context) return;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
|
||||||
switch(capability)
|
switch(capability)
|
||||||
{
|
{
|
||||||
case AL_SOURCE_DISTANCE_MODEL:
|
case AL_SOURCE_DISTANCE_MODEL:
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||||
context->mSourceDistanceModel = false;
|
context->mSourceDistanceModel = false;
|
||||||
DO_UPDATEPROPS();
|
DO_UPDATEPROPS();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
|
||||||
|
context->mStopVoicesOnDisconnect = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -200,6 +213,10 @@ START_API_FUNC
|
||||||
value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE;
|
value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
|
||||||
|
value = context->mStopVoicesOnDisconnect ? AL_TRUE : AL_FALSE;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
|
context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -40,7 +40,7 @@
|
||||||
|
|
||||||
#include "alfstream.h"
|
#include "alfstream.h"
|
||||||
#include "alstring.h"
|
#include "alstring.h"
|
||||||
#include "compat.h"
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cmath>
|
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
@ -37,47 +36,48 @@
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <new>
|
#include <new>
|
||||||
#include <numeric>
|
#include <stdint.h>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "AL/al.h"
|
|
||||||
#include "AL/alc.h"
|
|
||||||
#include "AL/efx.h"
|
|
||||||
|
|
||||||
#include "alcmain.h"
|
|
||||||
#include "alcontext.h"
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "alspan.h"
|
#include "alspan.h"
|
||||||
#include "alstring.h"
|
#include "alstring.h"
|
||||||
#include "async_event.h"
|
|
||||||
#include "atomic.h"
|
#include "atomic.h"
|
||||||
#include "bformatdec.h"
|
|
||||||
#include "core/ambidefs.h"
|
#include "core/ambidefs.h"
|
||||||
|
#include "core/async_event.h"
|
||||||
|
#include "core/bformatdec.h"
|
||||||
#include "core/bs2b.h"
|
#include "core/bs2b.h"
|
||||||
|
#include "core/bsinc_defs.h"
|
||||||
#include "core/bsinc_tables.h"
|
#include "core/bsinc_tables.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
|
#include "core/buffer_storage.h"
|
||||||
|
#include "core/context.h"
|
||||||
#include "core/cpu_caps.h"
|
#include "core/cpu_caps.h"
|
||||||
#include "core/devformat.h"
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
#include "core/filters/biquad.h"
|
#include "core/filters/biquad.h"
|
||||||
#include "core/filters/nfc.h"
|
#include "core/filters/nfc.h"
|
||||||
#include "core/filters/splitter.h"
|
|
||||||
#include "core/fpu_ctrl.h"
|
#include "core/fpu_ctrl.h"
|
||||||
|
#include "core/hrtf.h"
|
||||||
#include "core/mastering.h"
|
#include "core/mastering.h"
|
||||||
|
#include "core/mixer.h"
|
||||||
#include "core/mixer/defs.h"
|
#include "core/mixer/defs.h"
|
||||||
|
#include "core/mixer/hrtfdefs.h"
|
||||||
|
#include "core/resampler_limits.h"
|
||||||
#include "core/uhjfilter.h"
|
#include "core/uhjfilter.h"
|
||||||
|
#include "core/voice.h"
|
||||||
|
#include "core/voice_change.h"
|
||||||
#include "effects/base.h"
|
#include "effects/base.h"
|
||||||
#include "effectslot.h"
|
#include "effectslot.h"
|
||||||
#include "front_stablizer.h"
|
#include "intrusive_ptr.h"
|
||||||
#include "hrtf.h"
|
|
||||||
#include "inprogext.h"
|
|
||||||
#include "math_defs.h"
|
#include "math_defs.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
#include "threads.h"
|
#include "threads.h"
|
||||||
#include "vecmat.h"
|
#include "vecmat.h"
|
||||||
#include "voice.h"
|
#include "vector.h"
|
||||||
#include "voice_change.h"
|
|
||||||
|
|
||||||
struct CTag;
|
struct CTag;
|
||||||
#ifdef HAVE_SSE
|
#ifdef HAVE_SSE
|
||||||
|
@ -92,7 +92,6 @@ struct SSE4Tag;
|
||||||
#ifdef HAVE_NEON
|
#ifdef HAVE_NEON
|
||||||
struct NEONTag;
|
struct NEONTag;
|
||||||
#endif
|
#endif
|
||||||
struct CopyTag;
|
|
||||||
struct PointTag;
|
struct PointTag;
|
||||||
struct LerpTag;
|
struct LerpTag;
|
||||||
struct CubicTag;
|
struct CubicTag;
|
||||||
|
@ -100,12 +99,13 @@ struct BSincTag;
|
||||||
struct FastBSincTag;
|
struct FastBSincTag;
|
||||||
|
|
||||||
|
|
||||||
static_assert(MaxResamplerPadding >= BSincPointsMax, "MaxResamplerPadding is too small");
|
|
||||||
static_assert(!(MaxResamplerPadding&1), "MaxResamplerPadding is not a multiple of two");
|
static_assert(!(MaxResamplerPadding&1), "MaxResamplerPadding is not a multiple of two");
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
|
|
||||||
constexpr uint MaxPitch{10};
|
constexpr uint MaxPitch{10};
|
||||||
|
|
||||||
static_assert((BufferLineSize-1)/MaxPitch > 0, "MaxPitch is too large for BufferLineSize!");
|
static_assert((BufferLineSize-1)/MaxPitch > 0, "MaxPitch is too large for BufferLineSize!");
|
||||||
|
@ -154,9 +154,9 @@ struct ChanMap {
|
||||||
float elevation;
|
float elevation;
|
||||||
};
|
};
|
||||||
|
|
||||||
using HrtfDirectMixerFunc = void(*)(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
|
using HrtfDirectMixerFunc = void(*)(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut,
|
||||||
const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples,
|
const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples, float *TempBuf,
|
||||||
float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize);
|
HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize);
|
||||||
|
|
||||||
HrtfDirectMixerFunc MixDirectHrtf{MixDirectHrtf_<CTag>};
|
HrtfDirectMixerFunc MixDirectHrtf{MixDirectHrtf_<CTag>};
|
||||||
|
|
||||||
|
@ -182,8 +182,8 @@ inline void BsincPrepare(const uint increment, BsincState *state, const BSincTab
|
||||||
|
|
||||||
if(increment > MixerFracOne)
|
if(increment > MixerFracOne)
|
||||||
{
|
{
|
||||||
sf = MixerFracOne / static_cast<float>(increment);
|
sf = MixerFracOne/static_cast<float>(increment) - table->scaleBase;
|
||||||
sf = maxf(0.0f, (BSincScaleCount-1) * (sf-table->scaleBase) * table->scaleRange);
|
sf = maxf(0.0f, BSincScaleCount*sf*table->scaleRange - 1.0f);
|
||||||
si = float2uint(sf);
|
si = float2uint(sf);
|
||||||
/* The interpolation factor is fit to this diagonally-symmetric curve
|
/* The interpolation factor is fit to this diagonally-symmetric curve
|
||||||
* to reduce the transition ripple caused by interpolating different
|
* to reduce the transition ripple caused by interpolating different
|
||||||
|
@ -280,7 +280,7 @@ ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ALCdevice::ProcessHrtf(const size_t SamplesToDo)
|
void DeviceBase::ProcessHrtf(const size_t SamplesToDo)
|
||||||
{
|
{
|
||||||
/* HRTF is stereo output only. */
|
/* HRTF is stereo output only. */
|
||||||
const uint lidx{RealOut.ChannelIndex[FrontLeft]};
|
const uint lidx{RealOut.ChannelIndex[FrontLeft]};
|
||||||
|
@ -290,12 +290,12 @@ void ALCdevice::ProcessHrtf(const size_t SamplesToDo)
|
||||||
mHrtfState->mTemp.data(), mHrtfState->mChannels.data(), mHrtfState->mIrSize, SamplesToDo);
|
mHrtfState->mTemp.data(), mHrtfState->mChannels.data(), mHrtfState->mIrSize, SamplesToDo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALCdevice::ProcessAmbiDec(const size_t SamplesToDo)
|
void DeviceBase::ProcessAmbiDec(const size_t SamplesToDo)
|
||||||
{
|
{
|
||||||
AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo);
|
AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALCdevice::ProcessAmbiDecStablized(const size_t SamplesToDo)
|
void DeviceBase::ProcessAmbiDecStablized(const size_t SamplesToDo)
|
||||||
{
|
{
|
||||||
/* Decode with front image stablization. */
|
/* Decode with front image stablization. */
|
||||||
const uint lidx{RealOut.ChannelIndex[FrontLeft]};
|
const uint lidx{RealOut.ChannelIndex[FrontLeft]};
|
||||||
|
@ -306,18 +306,18 @@ void ALCdevice::ProcessAmbiDecStablized(const size_t SamplesToDo)
|
||||||
SamplesToDo);
|
SamplesToDo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALCdevice::ProcessUhj(const size_t SamplesToDo)
|
void DeviceBase::ProcessUhj(const size_t SamplesToDo)
|
||||||
{
|
{
|
||||||
/* UHJ is stereo output only. */
|
/* UHJ is stereo output only. */
|
||||||
const uint lidx{RealOut.ChannelIndex[FrontLeft]};
|
const uint lidx{RealOut.ChannelIndex[FrontLeft]};
|
||||||
const uint ridx{RealOut.ChannelIndex[FrontRight]};
|
const uint ridx{RealOut.ChannelIndex[FrontRight]};
|
||||||
|
|
||||||
/* Encode to stereo-compatible 2-channel UHJ output. */
|
/* Encode to stereo-compatible 2-channel UHJ output. */
|
||||||
Uhj_Encoder->encode(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer.data(),
|
mUhjEncoder->encode(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer.data(),
|
||||||
SamplesToDo);
|
SamplesToDo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALCdevice::ProcessBs2b(const size_t SamplesToDo)
|
void DeviceBase::ProcessBs2b(const size_t SamplesToDo)
|
||||||
{
|
{
|
||||||
/* First, decode the ambisonic mix to the "real" output. */
|
/* First, decode the ambisonic mix to the "real" output. */
|
||||||
AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo);
|
AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo);
|
||||||
|
@ -365,7 +365,7 @@ inline auto& GetAmbi2DLayout(AmbiLayout layouttype) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CalcContextParams(ALCcontext *ctx)
|
bool CalcContextParams(ContextBase *ctx)
|
||||||
{
|
{
|
||||||
ContextProps *props{ctx->mParams.ContextUpdate.exchange(nullptr, std::memory_order_acq_rel)};
|
ContextProps *props{ctx->mParams.ContextUpdate.exchange(nullptr, std::memory_order_acq_rel)};
|
||||||
if(!props) return false;
|
if(!props) return false;
|
||||||
|
@ -380,7 +380,7 @@ bool CalcContextParams(ALCcontext *ctx)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CalcListenerParams(ALCcontext *ctx)
|
bool CalcListenerParams(ContextBase *ctx)
|
||||||
{
|
{
|
||||||
ListenerProps *props{ctx->mParams.ListenerUpdate.exchange(nullptr,
|
ListenerProps *props{ctx->mParams.ListenerUpdate.exchange(nullptr,
|
||||||
std::memory_order_acq_rel)};
|
std::memory_order_acq_rel)};
|
||||||
|
@ -418,7 +418,7 @@ bool CalcListenerParams(ALCcontext *ctx)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ALCcontext *context)
|
bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBase *context)
|
||||||
{
|
{
|
||||||
EffectSlotProps *props{slot->Update.exchange(nullptr, std::memory_order_acq_rel)};
|
EffectSlotProps *props{slot->Update.exchange(nullptr, std::memory_order_acq_rel)};
|
||||||
if(!props) return false;
|
if(!props) return false;
|
||||||
|
@ -488,7 +488,7 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ALCcontex
|
||||||
output = EffectTarget{&target->Wet, nullptr};
|
output = EffectTarget{&target->Wet, nullptr};
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mDevice.get()};
|
DeviceBase *device{context->mDevice};
|
||||||
output = EffectTarget{&device->Dry, &device->RealOut};
|
output = EffectTarget{&device->Dry, &device->RealOut};
|
||||||
}
|
}
|
||||||
state->update(context, slot, &slot->mEffectProps, output);
|
state->update(context, slot, &slot->mEffectProps, output);
|
||||||
|
@ -678,7 +678,7 @@ struct GainTriplet { float Base, HF, LF; };
|
||||||
void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, const float zpos,
|
void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, const float zpos,
|
||||||
const float Distance, const float Spread, const GainTriplet &DryGain,
|
const float Distance, const float Spread, const GainTriplet &DryGain,
|
||||||
const al::span<const GainTriplet,MAX_SENDS> WetGain, EffectSlot *(&SendSlots)[MAX_SENDS],
|
const al::span<const GainTriplet,MAX_SENDS> WetGain, EffectSlot *(&SendSlots)[MAX_SENDS],
|
||||||
const VoiceProps *props, const ContextParams &Context, const ALCdevice *Device)
|
const VoiceProps *props, const ContextParams &Context, const DeviceBase *Device)
|
||||||
{
|
{
|
||||||
static const ChanMap MonoMap[1]{
|
static const ChanMap MonoMap[1]{
|
||||||
{ FrontCenter, 0.0f, 0.0f }
|
{ FrontCenter, 0.0f, 0.0f }
|
||||||
|
@ -790,16 +790,21 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
|
||||||
|
|
||||||
case FmtBFormat2D:
|
case FmtBFormat2D:
|
||||||
case FmtBFormat3D:
|
case FmtBFormat3D:
|
||||||
|
case FmtUHJ2:
|
||||||
|
case FmtUHJ3:
|
||||||
|
case FmtUHJ4:
|
||||||
DirectChannels = DirectMode::Off;
|
DirectChannels = DirectMode::Off;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
voice->mFlags &= ~(VoiceHasHrtf | VoiceHasNfc);
|
voice->mFlags &= ~(VoiceHasHrtf | VoiceHasNfc);
|
||||||
if(voice->mFmtChannels == FmtBFormat2D || voice->mFmtChannels == FmtBFormat3D)
|
if(voice->mFmtChannels == FmtBFormat2D || voice->mFmtChannels == FmtBFormat3D
|
||||||
|
|| voice->mFmtChannels == FmtUHJ2 || voice->mFmtChannels == FmtUHJ3
|
||||||
|
|| voice->mFmtChannels == FmtUHJ4)
|
||||||
{
|
{
|
||||||
/* Special handling for B-Format sources. */
|
/* Special handling for B-Format sources. */
|
||||||
|
|
||||||
if(Device->AvgSpeakerDist > 0.0f)
|
if(Device->AvgSpeakerDist > 0.0f && voice->mFmtChannels != FmtUHJ2)
|
||||||
{
|
{
|
||||||
if(!(Distance > std::numeric_limits<float>::epsilon()))
|
if(!(Distance > std::numeric_limits<float>::epsilon()))
|
||||||
{
|
{
|
||||||
|
@ -898,7 +903,9 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
|
||||||
/* Convert the rotation matrix for input ordering and scaling, and
|
/* Convert the rotation matrix for input ordering and scaling, and
|
||||||
* whether input is 2D or 3D.
|
* whether input is 2D or 3D.
|
||||||
*/
|
*/
|
||||||
const uint8_t *index_map{(voice->mFmtChannels == FmtBFormat2D) ?
|
const uint8_t *index_map{
|
||||||
|
(voice->mFmtChannels == FmtBFormat2D || voice->mFmtChannels == FmtUHJ2
|
||||||
|
|| voice->mFmtChannels == FmtUHJ3) ?
|
||||||
GetAmbi2DLayout(voice->mAmbiLayout).data() :
|
GetAmbi2DLayout(voice->mAmbiLayout).data() :
|
||||||
GetAmbiLayout(voice->mAmbiLayout).data()};
|
GetAmbiLayout(voice->mAmbiLayout).data()};
|
||||||
|
|
||||||
|
@ -1196,9 +1203,9 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const ALCcontext *context)
|
void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBase *context)
|
||||||
{
|
{
|
||||||
const ALCdevice *Device{context->mDevice.get()};
|
const DeviceBase *Device{context->mDevice};
|
||||||
EffectSlot *SendSlots[MAX_SENDS];
|
EffectSlot *SendSlots[MAX_SENDS];
|
||||||
|
|
||||||
voice->mDirect.Buffer = Device->Dry.Buffer;
|
voice->mDirect.Buffer = Device->Dry.Buffer;
|
||||||
|
@ -1242,9 +1249,9 @@ void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const ALCcon
|
||||||
context->mParams, Device);
|
context->mParams, Device);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ALCcontext *context)
|
void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBase *context)
|
||||||
{
|
{
|
||||||
const ALCdevice *Device{context->mDevice.get()};
|
const DeviceBase *Device{context->mDevice};
|
||||||
const uint NumSends{Device->NumAuxSends};
|
const uint NumSends{Device->NumAuxSends};
|
||||||
|
|
||||||
/* Set mixing buffers and get send parameters. */
|
/* Set mixing buffers and get send parameters. */
|
||||||
|
@ -1542,7 +1549,7 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ALCcontex
|
||||||
context->mParams, Device);
|
context->mParams, Device);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CalcSourceParams(Voice *voice, ALCcontext *context, bool force)
|
void CalcSourceParams(Voice *voice, ContextBase *context, bool force)
|
||||||
{
|
{
|
||||||
VoicePropsItem *props{voice->mUpdate.exchange(nullptr, std::memory_order_acq_rel)};
|
VoicePropsItem *props{voice->mUpdate.exchange(nullptr, std::memory_order_acq_rel)};
|
||||||
if(!props && !force) return;
|
if(!props && !force) return;
|
||||||
|
@ -1555,7 +1562,9 @@ void CalcSourceParams(Voice *voice, ALCcontext *context, bool force)
|
||||||
}
|
}
|
||||||
|
|
||||||
if((voice->mProps.DirectChannels != DirectMode::Off && voice->mFmtChannels != FmtMono
|
if((voice->mProps.DirectChannels != DirectMode::Off && voice->mFmtChannels != FmtMono
|
||||||
&& voice->mFmtChannels != FmtBFormat2D && voice->mFmtChannels != FmtBFormat3D)
|
&& voice->mFmtChannels != FmtBFormat2D && voice->mFmtChannels != FmtBFormat3D
|
||||||
|
&& voice->mFmtChannels != FmtUHJ2 && voice->mFmtChannels != FmtUHJ3
|
||||||
|
&& voice->mFmtChannels != FmtUHJ3)
|
||||||
|| voice->mProps.mSpatializeMode==SpatializeMode::Off
|
|| voice->mProps.mSpatializeMode==SpatializeMode::Off
|
||||||
|| (voice->mProps.mSpatializeMode==SpatializeMode::Auto && voice->mFmtChannels != FmtMono))
|
|| (voice->mProps.mSpatializeMode==SpatializeMode::Auto && voice->mFmtChannels != FmtMono))
|
||||||
CalcNonAttnSourceParams(voice, &voice->mProps, context);
|
CalcNonAttnSourceParams(voice, &voice->mProps, context);
|
||||||
|
@ -1564,7 +1573,7 @@ void CalcSourceParams(Voice *voice, ALCcontext *context, bool force)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SendSourceStateEvent(ALCcontext *context, uint id, VChangeState state)
|
void SendSourceStateEvent(ContextBase *context, uint id, VChangeState state)
|
||||||
{
|
{
|
||||||
RingBuffer *ring{context->mAsyncEvents.get()};
|
RingBuffer *ring{context->mAsyncEvents.get()};
|
||||||
auto evt_vec = ring->getWriteVector();
|
auto evt_vec = ring->getWriteVector();
|
||||||
|
@ -1572,12 +1581,29 @@ void SendSourceStateEvent(ALCcontext *context, uint id, VChangeState state)
|
||||||
|
|
||||||
AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}};
|
AsyncEvent *evt{::new(evt_vec.first.buf) AsyncEvent{EventType_SourceStateChange}};
|
||||||
evt->u.srcstate.id = id;
|
evt->u.srcstate.id = id;
|
||||||
evt->u.srcstate.state = state;
|
switch(state)
|
||||||
|
{
|
||||||
|
case VChangeState::Reset:
|
||||||
|
evt->u.srcstate.state = AsyncEvent::SrcState::Reset;
|
||||||
|
break;
|
||||||
|
case VChangeState::Stop:
|
||||||
|
evt->u.srcstate.state = AsyncEvent::SrcState::Stop;
|
||||||
|
break;
|
||||||
|
case VChangeState::Play:
|
||||||
|
evt->u.srcstate.state = AsyncEvent::SrcState::Play;
|
||||||
|
break;
|
||||||
|
case VChangeState::Pause:
|
||||||
|
evt->u.srcstate.state = AsyncEvent::SrcState::Pause;
|
||||||
|
break;
|
||||||
|
/* Shouldn't happen. */
|
||||||
|
case VChangeState::Restart:
|
||||||
|
ASSUME(0);
|
||||||
|
}
|
||||||
|
|
||||||
ring->writeAdvance(1);
|
ring->writeAdvance(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessVoiceChanges(ALCcontext *ctx)
|
void ProcessVoiceChanges(ContextBase *ctx)
|
||||||
{
|
{
|
||||||
VoiceChange *cur{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
|
VoiceChange *cur{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)};
|
||||||
VoiceChange *next{cur->mNext.load(std::memory_order_acquire)};
|
VoiceChange *next{cur->mNext.load(std::memory_order_acquire)};
|
||||||
|
@ -1672,7 +1698,7 @@ void ProcessVoiceChanges(ALCcontext *ctx)
|
||||||
ctx->mCurrentVoiceChange.store(cur, std::memory_order_release);
|
ctx->mCurrentVoiceChange.store(cur, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessParamUpdates(ALCcontext *ctx, const EffectSlotArray &slots,
|
void ProcessParamUpdates(ContextBase *ctx, const EffectSlotArray &slots,
|
||||||
const al::span<Voice*> voices)
|
const al::span<Voice*> voices)
|
||||||
{
|
{
|
||||||
ProcessVoiceChanges(ctx);
|
ProcessVoiceChanges(ctx);
|
||||||
|
@ -1696,11 +1722,11 @@ void ProcessParamUpdates(ALCcontext *ctx, const EffectSlotArray &slots,
|
||||||
IncrementRef(ctx->mUpdateCount);
|
IncrementRef(ctx->mUpdateCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessContexts(ALCdevice *device, const uint SamplesToDo)
|
void ProcessContexts(DeviceBase *device, const uint SamplesToDo)
|
||||||
{
|
{
|
||||||
ASSUME(SamplesToDo > 0);
|
ASSUME(SamplesToDo > 0);
|
||||||
|
|
||||||
for(ALCcontext *ctx : *device->mContexts.load(std::memory_order_acquire))
|
for(ContextBase *ctx : *device->mContexts.load(std::memory_order_acquire))
|
||||||
{
|
{
|
||||||
const EffectSlotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire);
|
const EffectSlotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire);
|
||||||
const al::span<Voice*> voices{ctx->getVoicesSpanAcquired()};
|
const al::span<Voice*> voices{ctx->getVoicesSpanAcquired()};
|
||||||
|
@ -1902,7 +1928,7 @@ void Write(const al::span<const FloatBufferLine> InBuffer, void *OutBuffer, cons
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ALCdevice::renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep)
|
void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep)
|
||||||
{
|
{
|
||||||
FPUCtl mixer_mode{};
|
FPUCtl mixer_mode{};
|
||||||
for(uint written{0u};written < numSamples;)
|
for(uint written{0u};written < numSamples;)
|
||||||
|
@ -1973,7 +1999,7 @@ void ALCdevice::renderSamples(void *outBuffer, const uint numSamples, const size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ALCdevice::handleDisconnect(const char *msg, ...)
|
void DeviceBase::handleDisconnect(const char *msg, ...)
|
||||||
{
|
{
|
||||||
if(!Connected.exchange(false, std::memory_order_acq_rel))
|
if(!Connected.exchange(false, std::memory_order_acq_rel))
|
||||||
return;
|
return;
|
||||||
|
@ -1989,7 +2015,7 @@ void ALCdevice::handleDisconnect(const char *msg, ...)
|
||||||
evt.u.disconnect.msg[sizeof(evt.u.disconnect.msg)-1] = 0;
|
evt.u.disconnect.msg[sizeof(evt.u.disconnect.msg)-1] = 0;
|
||||||
|
|
||||||
IncrementRef(MixCount);
|
IncrementRef(MixCount);
|
||||||
for(ALCcontext *ctx : *mContexts.load())
|
for(ContextBase *ctx : *mContexts.load())
|
||||||
{
|
{
|
||||||
const uint enabledevt{ctx->mEnabledEvts.load(std::memory_order_acquire)};
|
const uint enabledevt{ctx->mEnabledEvts.load(std::memory_order_acquire)};
|
||||||
if((enabledevt&EventType_Disconnected))
|
if((enabledevt&EventType_Disconnected))
|
||||||
|
@ -2004,6 +2030,12 @@ void ALCdevice::handleDisconnect(const char *msg, ...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!ctx->mStopVoicesOnDisconnect)
|
||||||
|
{
|
||||||
|
ProcessVoiceChanges(ctx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto voicelist = ctx->getVoicesSpanAcquired();
|
auto voicelist = ctx->getVoicesSpanAcquired();
|
||||||
auto stop_voice = [](Voice *voice) -> void
|
auto stop_voice = [](Voice *voice) -> void
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,40 +1,15 @@
|
||||||
#ifndef ALU_H
|
#ifndef ALU_H
|
||||||
#define ALU_H
|
#define ALU_H
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include "alspan.h"
|
|
||||||
#include "core/ambidefs.h"
|
|
||||||
#include "core/bufferline.h"
|
|
||||||
#include "core/devformat.h"
|
|
||||||
|
|
||||||
struct ALCcontext;
|
struct ALCcontext;
|
||||||
struct ALCdevice;
|
struct ALCdevice;
|
||||||
struct EffectSlot;
|
struct EffectSlot;
|
||||||
struct MixParams;
|
|
||||||
|
|
||||||
|
|
||||||
#define MAX_SENDS 6
|
|
||||||
|
|
||||||
|
|
||||||
using MixerFunc = void(*)(const al::span<const float> InSamples,
|
|
||||||
const al::span<FloatBufferLine> OutBuffer, float *CurrentGains, const float *TargetGains,
|
|
||||||
const size_t Counter, const size_t OutPos);
|
|
||||||
|
|
||||||
extern MixerFunc MixSamples;
|
|
||||||
|
|
||||||
|
|
||||||
constexpr float GainMixMax{1000.0f}; /* +60dB */
|
constexpr float GainMixMax{1000.0f}; /* +60dB */
|
||||||
|
|
||||||
constexpr float SpeedOfSoundMetersPerSec{343.3f};
|
|
||||||
constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */
|
constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */
|
||||||
|
|
||||||
/** Target gain for the reverb decay feedback reaching the decay time. */
|
|
||||||
constexpr float ReverbDecayGain{0.001f}; /* -60 dB */
|
|
||||||
|
|
||||||
|
|
||||||
enum HrtfRequestMode {
|
enum HrtfRequestMode {
|
||||||
Hrtf_Default = 0,
|
Hrtf_Default = 0,
|
||||||
|
@ -44,8 +19,6 @@ enum HrtfRequestMode {
|
||||||
|
|
||||||
void aluInit(void);
|
void aluInit(void);
|
||||||
|
|
||||||
void aluInitMixer(void);
|
|
||||||
|
|
||||||
/* aluInitRenderer
|
/* aluInitRenderer
|
||||||
*
|
*
|
||||||
* Set up the appropriate panning method and mixing method given the device
|
* Set up the appropriate panning method and mixing method given the device
|
||||||
|
@ -56,84 +29,6 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, HrtfRequestMode hrtf_appreq
|
||||||
|
|
||||||
void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context);
|
void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context);
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates ambisonic encoder coefficients using the X, Y, and Z direction
|
|
||||||
* components, which must represent a normalized (unit length) vector, and the
|
|
||||||
* spread is the angular width of the sound (0...tau).
|
|
||||||
*
|
|
||||||
* NOTE: The components use ambisonic coordinates. As a result:
|
|
||||||
*
|
|
||||||
* Ambisonic Y = OpenAL -X
|
|
||||||
* Ambisonic Z = OpenAL Y
|
|
||||||
* Ambisonic X = OpenAL -Z
|
|
||||||
*
|
|
||||||
* The components are ordered such that OpenAL's X, Y, and Z are the first,
|
|
||||||
* second, and third parameters respectively -- simply negate X and Z.
|
|
||||||
*/
|
|
||||||
std::array<float,MaxAmbiChannels> CalcAmbiCoeffs(const float y, const float z, const float x,
|
|
||||||
const float spread);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CalcDirectionCoeffs
|
|
||||||
*
|
|
||||||
* Calculates ambisonic coefficients based on an OpenAL direction vector. The
|
|
||||||
* 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],
|
|
||||||
const float spread)
|
|
||||||
{
|
|
||||||
/* Convert from OpenAL coords to Ambisonics. */
|
|
||||||
return CalcAmbiCoeffs(-dir[0], dir[1], -dir[2], spread);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CalcAngleCoeffs
|
|
||||||
*
|
|
||||||
* Calculates ambisonic coefficients based on azimuth and elevation. The
|
|
||||||
* azimuth and elevation parameters are in radians, going right and up
|
|
||||||
* respectively.
|
|
||||||
*/
|
|
||||||
inline std::array<float,MaxAmbiChannels> CalcAngleCoeffs(const float azimuth,
|
|
||||||
const float elevation, const float spread)
|
|
||||||
{
|
|
||||||
const float x{-std::sin(azimuth) * std::cos(elevation)};
|
|
||||||
const float y{ std::sin(elevation)};
|
|
||||||
const float z{ std::cos(azimuth) * std::cos(elevation)};
|
|
||||||
|
|
||||||
return CalcAmbiCoeffs(x, y, z, spread);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ComputePanGains
|
|
||||||
*
|
|
||||||
* Computes panning gains using the given channel decoder coefficients and the
|
|
||||||
* pre-calculated direction or angle coefficients. For B-Format sources, the
|
|
||||||
* 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,MAX_OUTPUT_CHANNELS> gains);
|
|
||||||
|
|
||||||
|
|
||||||
/** Helper to set an identity/pass-through panning for ambisonic mixing (3D input). */
|
|
||||||
template<typename T, typename I, typename F>
|
|
||||||
auto SetAmbiPanIdentity(T iter, I count, F func) -> std::enable_if_t<std::is_integral<I>::value>
|
|
||||||
{
|
|
||||||
if(count < 1) return;
|
|
||||||
|
|
||||||
std::array<float,MaxAmbiChannels> coeffs{{1.0f}};
|
|
||||||
func(*iter, coeffs);
|
|
||||||
++iter;
|
|
||||||
for(I i{1};i < count;++i,++iter)
|
|
||||||
{
|
|
||||||
coeffs[i-1] = 0.0f;
|
|
||||||
coeffs[i ] = 1.0f;
|
|
||||||
func(*iter, coeffs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern const float ConeScale;
|
extern const float ConeScale;
|
||||||
extern const float ZScale;
|
extern const float ZScale;
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/alsa.h"
|
#include "alsa.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
@ -36,12 +36,12 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "albyte.h"
|
#include "albyte.h"
|
||||||
#include "alcmain.h"
|
#include "alc/alconfig.h"
|
||||||
#include "alconfig.h"
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "aloptional.h"
|
#include "aloptional.h"
|
||||||
#include "alu.h"
|
#include "core/device.h"
|
||||||
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "dynload.h"
|
#include "dynload.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
|
@ -407,7 +407,7 @@ int verify_state(snd_pcm_t *handle)
|
||||||
|
|
||||||
|
|
||||||
struct AlsaPlayback final : public BackendBase {
|
struct AlsaPlayback final : public BackendBase {
|
||||||
AlsaPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
AlsaPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~AlsaPlayback() override;
|
~AlsaPlayback() override;
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
@ -635,12 +635,16 @@ void AlsaPlayback::open(const char *name)
|
||||||
name = alsaDevice;
|
name = alsaDevice;
|
||||||
driver = GetConfigValue(nullptr, "alsa", "device", "default");
|
driver = GetConfigValue(nullptr, "alsa", "device", "default");
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Opening device \"%s\"\n", driver);
|
TRACE("Opening device \"%s\"\n", driver);
|
||||||
int err{snd_pcm_open(&mPcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)};
|
|
||||||
|
snd_pcm_t *pcmHandle{};
|
||||||
|
int err{snd_pcm_open(&pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)};
|
||||||
if(err < 0)
|
if(err < 0)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice,
|
throw al::backend_exception{al::backend_error::NoDevice,
|
||||||
"Could not open ALSA device \"%s\"", driver};
|
"Could not open ALSA device \"%s\"", driver};
|
||||||
|
if(mPcmHandle)
|
||||||
|
snd_pcm_close(mPcmHandle);
|
||||||
|
mPcmHandle = pcmHandle;
|
||||||
|
|
||||||
/* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
|
/* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */
|
||||||
snd_config_update_free_global();
|
snd_config_update_free_global();
|
||||||
|
@ -863,7 +867,7 @@ ClockLatency AlsaPlayback::getClockLatency()
|
||||||
|
|
||||||
|
|
||||||
struct AlsaCapture final : public BackendBase {
|
struct AlsaCapture final : public BackendBase {
|
||||||
AlsaCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~AlsaCapture() override;
|
~AlsaCapture() override;
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(const char *name) override;
|
||||||
|
@ -1252,7 +1256,7 @@ std::string AlsaBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr AlsaBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr AlsaBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new AlsaPlayback{device}};
|
return BackendPtr{new AlsaPlayback{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_ALSA_H
|
#ifndef BACKENDS_ALSA_H
|
||||||
#define BACKENDS_ALSA_H
|
#define BACKENDS_ALSA_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct AlsaBackendFactory final : public BackendFactory {
|
struct AlsaBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,21 +3,22 @@
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <mmreg.h>
|
#include <mmreg.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "albit.h"
|
#include "albit.h"
|
||||||
#include "alcmain.h"
|
|
||||||
#include "alnumeric.h"
|
|
||||||
#include "aloptional.h"
|
|
||||||
#include "atomic.h"
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "aloptional.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "atomic.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
|
||||||
|
|
||||||
bool BackendBase::reset()
|
bool BackendBase::reset()
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
#define ALC_BACKENDS_BASE_H
|
#define ALC_BACKENDS_BASE_H
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <cstdarg>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <ratio>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "albyte.h"
|
#include "albyte.h"
|
||||||
#include "alcmain.h"
|
#include "core/device.h"
|
||||||
#include "core/except.h"
|
#include "core/except.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,9 +31,9 @@ struct BackendBase {
|
||||||
|
|
||||||
virtual ClockLatency getClockLatency();
|
virtual ClockLatency getClockLatency();
|
||||||
|
|
||||||
ALCdevice *const mDevice;
|
DeviceBase *const mDevice;
|
||||||
|
|
||||||
BackendBase(ALCdevice *device) noexcept : mDevice{device} { }
|
BackendBase(DeviceBase *device) noexcept : mDevice{device} { }
|
||||||
virtual ~BackendBase() = default;
|
virtual ~BackendBase() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -57,7 +58,7 @@ enum class BackendType {
|
||||||
/* Helper to get the current clock time from the device's ClockBase, and
|
/* Helper to get the current clock time from the device's ClockBase, and
|
||||||
* SamplesDone converted from the sample rate.
|
* SamplesDone converted from the sample rate.
|
||||||
*/
|
*/
|
||||||
inline std::chrono::nanoseconds GetDeviceClockTime(ALCdevice *device)
|
inline std::chrono::nanoseconds GetDeviceClockTime(DeviceBase *device)
|
||||||
{
|
{
|
||||||
using std::chrono::seconds;
|
using std::chrono::seconds;
|
||||||
using std::chrono::nanoseconds;
|
using std::chrono::nanoseconds;
|
||||||
|
@ -69,7 +70,7 @@ inline std::chrono::nanoseconds GetDeviceClockTime(ALCdevice *device)
|
||||||
/* Helper to get the device latency from the backend, including any fixed
|
/* Helper to get the device latency from the backend, including any fixed
|
||||||
* latency from post-processing.
|
* latency from post-processing.
|
||||||
*/
|
*/
|
||||||
inline ClockLatency GetClockLatency(ALCdevice *device)
|
inline ClockLatency GetClockLatency(DeviceBase *device)
|
||||||
{
|
{
|
||||||
BackendBase *backend{device->Backend.get()};
|
BackendBase *backend{device->Backend.get()};
|
||||||
ClockLatency ret{backend->getClockLatency()};
|
ClockLatency ret{backend->getClockLatency()};
|
||||||
|
@ -85,7 +86,7 @@ struct BackendFactory {
|
||||||
|
|
||||||
virtual std::string probe(BackendType type) = 0;
|
virtual std::string probe(BackendType type) = 0;
|
||||||
|
|
||||||
virtual BackendPtr createBackend(ALCdevice *device, BackendType type) = 0;
|
virtual BackendPtr createBackend(DeviceBase *device, BackendType type) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~BackendFactory() = default;
|
virtual ~BackendFactory() = default;
|
||||||
|
@ -103,7 +104,11 @@ class backend_exception final : public base_exception {
|
||||||
backend_error mErrorCode;
|
backend_error mErrorCode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
#ifdef __USE_MINGW_ANSI_STDIO
|
||||||
|
[[gnu::format(gnu_printf, 3, 4)]]
|
||||||
|
#else
|
||||||
[[gnu::format(printf, 3, 4)]]
|
[[gnu::format(printf, 3, 4)]]
|
||||||
|
#endif
|
||||||
backend_exception(backend_error code, const char *msg, ...) : mErrorCode{code}
|
backend_exception(backend_error code, const char *msg, ...) : mErrorCode{code}
|
||||||
{
|
{
|
||||||
std::va_list args;
|
std::va_list args;
|
||||||
|
|
|
@ -20,23 +20,23 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/coreaudio.h"
|
#include "coreaudio.h"
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alnumeric.h"
|
||||||
#include "alu.h"
|
#include "core/converter.h"
|
||||||
#include "ringbuffer.h"
|
#include "core/device.h"
|
||||||
#include "converter.h"
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "backends/base.h"
|
#include "ringbuffer.h"
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <AudioUnit/AudioUnit.h>
|
#include <AudioUnit/AudioUnit.h>
|
||||||
#include <AudioToolbox/AudioToolbox.h>
|
#include <AudioToolbox/AudioToolbox.h>
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ static const char ca_device[] = "CoreAudio Default";
|
||||||
|
|
||||||
|
|
||||||
struct CoreAudioPlayback final : public BackendBase {
|
struct CoreAudioPlayback final : public BackendBase {
|
||||||
CoreAudioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
CoreAudioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~CoreAudioPlayback() override;
|
~CoreAudioPlayback() override;
|
||||||
|
|
||||||
OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
|
OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||||
|
@ -105,7 +105,7 @@ void CoreAudioPlayback::open(const char *name)
|
||||||
/* open the default output unit */
|
/* open the default output unit */
|
||||||
AudioComponentDescription desc{};
|
AudioComponentDescription desc{};
|
||||||
desc.componentType = kAudioUnitType_Output;
|
desc.componentType = kAudioUnitType_Output;
|
||||||
#if TARGET_OS_IOS
|
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||||
#else
|
#else
|
||||||
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||||
|
@ -118,17 +118,27 @@ void CoreAudioPlayback::open(const char *name)
|
||||||
if(comp == nullptr)
|
if(comp == nullptr)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not find audio component"};
|
throw al::backend_exception{al::backend_error::NoDevice, "Could not find audio component"};
|
||||||
|
|
||||||
OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)};
|
AudioUnit audioUnit{};
|
||||||
|
OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)};
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice,
|
throw al::backend_exception{al::backend_error::NoDevice,
|
||||||
"Could not create component instance: %u", err};
|
"Could not create component instance: %u", err};
|
||||||
|
|
||||||
/* init and start the default audio unit... */
|
err = AudioUnitInitialize(audioUnit);
|
||||||
err = AudioUnitInitialize(mAudioUnit);
|
|
||||||
if(err != noErr)
|
if(err != noErr)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Could not initialize audio unit: %u", err};
|
"Could not initialize audio unit: %u", err};
|
||||||
|
|
||||||
|
/* WARNING: I don't know if "valid" audio unit values are guaranteed to be
|
||||||
|
* non-0. If not, this logic is broken.
|
||||||
|
*/
|
||||||
|
if(mAudioUnit)
|
||||||
|
{
|
||||||
|
AudioUnitUninitialize(mAudioUnit);
|
||||||
|
AudioComponentInstanceDispose(mAudioUnit);
|
||||||
|
}
|
||||||
|
mAudioUnit = audioUnit;
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +304,7 @@ void CoreAudioPlayback::stop()
|
||||||
|
|
||||||
|
|
||||||
struct CoreAudioCapture final : public BackendBase {
|
struct CoreAudioCapture final : public BackendBase {
|
||||||
CoreAudioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
CoreAudioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~CoreAudioCapture() override;
|
~CoreAudioCapture() override;
|
||||||
|
|
||||||
OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||||
|
@ -400,7 +410,7 @@ void CoreAudioCapture::open(const char *name)
|
||||||
name};
|
name};
|
||||||
|
|
||||||
desc.componentType = kAudioUnitType_Output;
|
desc.componentType = kAudioUnitType_Output;
|
||||||
#if TARGET_OS_IOS
|
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||||
#else
|
#else
|
||||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||||
|
@ -436,7 +446,7 @@ void CoreAudioCapture::open(const char *name)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Could not enable audio unit input property: %u", err};
|
"Could not enable audio unit input property: %u", err};
|
||||||
|
|
||||||
#if !TARGET_OS_IOS
|
#if !TARGET_OS_IOS && !TARGET_OS_TV
|
||||||
{
|
{
|
||||||
// Get the default input device
|
// Get the default input device
|
||||||
AudioDeviceID inputDevice = kAudioDeviceUnknown;
|
AudioDeviceID inputDevice = kAudioDeviceUnknown;
|
||||||
|
@ -676,7 +686,7 @@ std::string CoreAudioBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr CoreAudioBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr CoreAudioBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new CoreAudioPlayback{device}};
|
return BackendPtr{new CoreAudioPlayback{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_COREAUDIO_H
|
#ifndef BACKENDS_COREAUDIO_H
|
||||||
#define BACKENDS_COREAUDIO_H
|
#define BACKENDS_COREAUDIO_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct CoreAudioBackendFactory final : public BackendFactory {
|
struct CoreAudioBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/dsound.h"
|
#include "dsound.h"
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@ -44,9 +44,10 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alnumeric.h"
|
||||||
#include "alu.h"
|
#include "comptr.h"
|
||||||
#include "compat.h"
|
#include "core/device.h"
|
||||||
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "dynload.h"
|
#include "dynload.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
|
@ -169,20 +170,20 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi
|
||||||
|
|
||||||
|
|
||||||
struct DSoundPlayback final : public BackendBase {
|
struct DSoundPlayback final : public BackendBase {
|
||||||
DSoundPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
DSoundPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~DSoundPlayback() override;
|
~DSoundPlayback() override;
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
|
||||||
void open(const ALCchar *name) override;
|
void open(const char *name) override;
|
||||||
bool reset() override;
|
bool reset() override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
||||||
IDirectSound *mDS{nullptr};
|
ComPtr<IDirectSound> mDS;
|
||||||
IDirectSoundBuffer *mPrimaryBuffer{nullptr};
|
ComPtr<IDirectSoundBuffer> mPrimaryBuffer;
|
||||||
IDirectSoundBuffer *mBuffer{nullptr};
|
ComPtr<IDirectSoundBuffer> mBuffer;
|
||||||
IDirectSoundNotify *mNotifies{nullptr};
|
ComPtr<IDirectSoundNotify> mNotifies;
|
||||||
HANDLE mNotifyEvent{nullptr};
|
HANDLE mNotifyEvent{nullptr};
|
||||||
|
|
||||||
std::atomic<bool> mKillNow{true};
|
std::atomic<bool> mKillNow{true};
|
||||||
|
@ -193,19 +194,11 @@ struct DSoundPlayback final : public BackendBase {
|
||||||
|
|
||||||
DSoundPlayback::~DSoundPlayback()
|
DSoundPlayback::~DSoundPlayback()
|
||||||
{
|
{
|
||||||
if(mNotifies)
|
|
||||||
mNotifies->Release();
|
|
||||||
mNotifies = nullptr;
|
mNotifies = nullptr;
|
||||||
if(mBuffer)
|
|
||||||
mBuffer->Release();
|
|
||||||
mBuffer = nullptr;
|
mBuffer = nullptr;
|
||||||
if(mPrimaryBuffer)
|
|
||||||
mPrimaryBuffer->Release();
|
|
||||||
mPrimaryBuffer = nullptr;
|
mPrimaryBuffer = nullptr;
|
||||||
|
|
||||||
if(mDS)
|
|
||||||
mDS->Release();
|
|
||||||
mDS = nullptr;
|
mDS = nullptr;
|
||||||
|
|
||||||
if(mNotifyEvent)
|
if(mNotifyEvent)
|
||||||
CloseHandle(mNotifyEvent);
|
CloseHandle(mNotifyEvent);
|
||||||
mNotifyEvent = nullptr;
|
mNotifyEvent = nullptr;
|
||||||
|
@ -234,8 +227,8 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
|
||||||
bool Playing{false};
|
bool Playing{false};
|
||||||
DWORD LastCursor{0u};
|
DWORD LastCursor{0u};
|
||||||
mBuffer->GetCurrentPosition(&LastCursor, nullptr);
|
mBuffer->GetCurrentPosition(&LastCursor, nullptr);
|
||||||
while(!mKillNow.load(std::memory_order_acquire) &&
|
while(!mKillNow.load(std::memory_order_acquire)
|
||||||
mDevice->Connected.load(std::memory_order_acquire))
|
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||||
{
|
{
|
||||||
// Get current play cursor
|
// Get current play cursor
|
||||||
DWORD PlayCursor;
|
DWORD PlayCursor;
|
||||||
|
@ -344,31 +337,34 @@ void DSoundPlayback::open(const char *name)
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = DS_OK;
|
hr = DS_OK;
|
||||||
|
if(!mNotifyEvent)
|
||||||
|
{
|
||||||
mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||||
if(!mNotifyEvent) hr = E_FAIL;
|
if(!mNotifyEvent) hr = E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
//DirectSound Init code
|
//DirectSound Init code
|
||||||
|
ComPtr<IDirectSound> ds;
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
hr = DirectSoundCreate(guid, &mDS, nullptr);
|
hr = DirectSoundCreate(guid, ds.getPtr(), nullptr);
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
hr = mDS->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
|
hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
|
||||||
if(FAILED(hr))
|
if(FAILED(hr))
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
|
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
|
||||||
hr};
|
hr};
|
||||||
|
|
||||||
|
mNotifies = nullptr;
|
||||||
|
mBuffer = nullptr;
|
||||||
|
mPrimaryBuffer = nullptr;
|
||||||
|
mDS = std::move(ds);
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DSoundPlayback::reset()
|
bool DSoundPlayback::reset()
|
||||||
{
|
{
|
||||||
if(mNotifies)
|
|
||||||
mNotifies->Release();
|
|
||||||
mNotifies = nullptr;
|
mNotifies = nullptr;
|
||||||
if(mBuffer)
|
|
||||||
mBuffer->Release();
|
|
||||||
mBuffer = nullptr;
|
mBuffer = nullptr;
|
||||||
if(mPrimaryBuffer)
|
|
||||||
mPrimaryBuffer->Release();
|
|
||||||
mPrimaryBuffer = nullptr;
|
mPrimaryBuffer = nullptr;
|
||||||
|
|
||||||
switch(mDevice->FmtType)
|
switch(mDevice->FmtType)
|
||||||
|
@ -465,7 +461,7 @@ retry_open:
|
||||||
DSBUFFERDESC DSBDescription{};
|
DSBUFFERDESC DSBDescription{};
|
||||||
DSBDescription.dwSize = sizeof(DSBDescription);
|
DSBDescription.dwSize = sizeof(DSBDescription);
|
||||||
DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||||
hr = mDS->CreateSoundBuffer(&DSBDescription, &mPrimaryBuffer, nullptr);
|
hr = mDS->CreateSoundBuffer(&DSBDescription, mPrimaryBuffer.getPtr(), nullptr);
|
||||||
}
|
}
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
|
hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
|
||||||
|
@ -485,7 +481,7 @@ retry_open:
|
||||||
DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
|
DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
|
||||||
DSBDescription.lpwfxFormat = &OutputType.Format;
|
DSBDescription.lpwfxFormat = &OutputType.Format;
|
||||||
|
|
||||||
hr = mDS->CreateSoundBuffer(&DSBDescription, &mBuffer, nullptr);
|
hr = mDS->CreateSoundBuffer(&DSBDescription, mBuffer.getPtr(), nullptr);
|
||||||
if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
|
if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
|
||||||
{
|
{
|
||||||
mDevice->FmtType = DevFmtShort;
|
mDevice->FmtType = DevFmtShort;
|
||||||
|
@ -499,7 +495,7 @@ retry_open:
|
||||||
hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
|
hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
mNotifies = static_cast<IDirectSoundNotify*>(ptr);
|
mNotifies = ComPtr<IDirectSoundNotify>{static_cast<IDirectSoundNotify*>(ptr)};
|
||||||
|
|
||||||
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
||||||
assert(num_updates <= MAX_UPDATES);
|
assert(num_updates <= MAX_UPDATES);
|
||||||
|
@ -517,14 +513,8 @@ retry_open:
|
||||||
|
|
||||||
if(FAILED(hr))
|
if(FAILED(hr))
|
||||||
{
|
{
|
||||||
if(mNotifies)
|
|
||||||
mNotifies->Release();
|
|
||||||
mNotifies = nullptr;
|
mNotifies = nullptr;
|
||||||
if(mBuffer)
|
|
||||||
mBuffer->Release();
|
|
||||||
mBuffer = nullptr;
|
mBuffer = nullptr;
|
||||||
if(mPrimaryBuffer)
|
|
||||||
mPrimaryBuffer->Release();
|
|
||||||
mPrimaryBuffer = nullptr;
|
mPrimaryBuffer = nullptr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -558,7 +548,7 @@ void DSoundPlayback::stop()
|
||||||
|
|
||||||
|
|
||||||
struct DSoundCapture final : public BackendBase {
|
struct DSoundCapture final : public BackendBase {
|
||||||
DSoundCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~DSoundCapture() override;
|
~DSoundCapture() override;
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(const char *name) override;
|
||||||
|
@ -567,8 +557,8 @@ struct DSoundCapture final : public BackendBase {
|
||||||
void captureSamples(al::byte *buffer, uint samples) override;
|
void captureSamples(al::byte *buffer, uint samples) override;
|
||||||
uint availableSamples() override;
|
uint availableSamples() override;
|
||||||
|
|
||||||
IDirectSoundCapture *mDSC{nullptr};
|
ComPtr<IDirectSoundCapture> mDSC;
|
||||||
IDirectSoundCaptureBuffer *mDSCbuffer{nullptr};
|
ComPtr<IDirectSoundCaptureBuffer> mDSCbuffer;
|
||||||
DWORD mBufferBytes{0u};
|
DWORD mBufferBytes{0u};
|
||||||
DWORD mCursor{0u};
|
DWORD mCursor{0u};
|
||||||
|
|
||||||
|
@ -582,12 +572,8 @@ DSoundCapture::~DSoundCapture()
|
||||||
if(mDSCbuffer)
|
if(mDSCbuffer)
|
||||||
{
|
{
|
||||||
mDSCbuffer->Stop();
|
mDSCbuffer->Stop();
|
||||||
mDSCbuffer->Release();
|
|
||||||
mDSCbuffer = nullptr;
|
mDSCbuffer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mDSC)
|
|
||||||
mDSC->Release();
|
|
||||||
mDSC = nullptr;
|
mDSC = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -693,20 +679,16 @@ void DSoundCapture::open(const char *name)
|
||||||
DSCBDescription.lpwfxFormat = &InputType.Format;
|
DSCBDescription.lpwfxFormat = &InputType.Format;
|
||||||
|
|
||||||
//DirectSoundCapture Init code
|
//DirectSoundCapture Init code
|
||||||
hr = DirectSoundCaptureCreate(guid, &mDSC, nullptr);
|
hr = DirectSoundCaptureCreate(guid, mDSC.getPtr(), nullptr);
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
mDSC->CreateCaptureBuffer(&DSCBDescription, &mDSCbuffer, nullptr);
|
mDSC->CreateCaptureBuffer(&DSCBDescription, mDSCbuffer.getPtr(), nullptr);
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
mRing = RingBuffer::Create(mDevice->BufferSize, InputType.Format.nBlockAlign, false);
|
mRing = RingBuffer::Create(mDevice->BufferSize, InputType.Format.nBlockAlign, false);
|
||||||
|
|
||||||
if(FAILED(hr))
|
if(FAILED(hr))
|
||||||
{
|
{
|
||||||
mRing = nullptr;
|
mRing = nullptr;
|
||||||
if(mDSCbuffer)
|
|
||||||
mDSCbuffer->Release();
|
|
||||||
mDSCbuffer = nullptr;
|
mDSCbuffer = nullptr;
|
||||||
if(mDSC)
|
|
||||||
mDSC->Release();
|
|
||||||
mDSC = nullptr;
|
mDSC = nullptr;
|
||||||
|
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
|
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
|
||||||
|
@ -858,7 +840,7 @@ std::string DSoundBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr DSoundBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr DSoundBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new DSoundPlayback{device}};
|
return BackendPtr{new DSoundPlayback{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_DSOUND_H
|
#ifndef BACKENDS_DSOUND_H
|
||||||
#define BACKENDS_DSOUND_H
|
#define BACKENDS_DSOUND_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct DSoundBackendFactory final : public BackendFactory {
|
struct DSoundBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/jack.h"
|
#include "jack.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
@ -31,9 +31,10 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/alconfig.h"
|
||||||
#include "alu.h"
|
#include "alnumeric.h"
|
||||||
#include "alconfig.h"
|
#include "core/device.h"
|
||||||
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "dynload.h"
|
#include "dynload.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
|
@ -45,9 +46,6 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr char jackDevice[] = "JACK Default";
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_DYNLOAD
|
#ifdef HAVE_DYNLOAD
|
||||||
#define JACK_FUNCS(MAGIC) \
|
#define JACK_FUNCS(MAGIC) \
|
||||||
MAGIC(jack_client_open); \
|
MAGIC(jack_client_open); \
|
||||||
|
@ -160,21 +158,55 @@ struct DeviceEntry {
|
||||||
al::vector<DeviceEntry> PlaybackList;
|
al::vector<DeviceEntry> PlaybackList;
|
||||||
|
|
||||||
|
|
||||||
void EnumerateDevices(al::vector<DeviceEntry> &list)
|
void EnumerateDevices(jack_client_t *client, al::vector<DeviceEntry> &list)
|
||||||
{
|
{
|
||||||
al::vector<DeviceEntry>{}.swap(list);
|
std::remove_reference_t<decltype(list)>{}.swap(list);
|
||||||
|
|
||||||
list.emplace_back(DeviceEntry{jackDevice, ""});
|
const char **ports{jack_get_ports(client, nullptr, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput)};
|
||||||
|
if(ports)
|
||||||
std::string customList{ConfigValueStr(nullptr, "jack", "custom-devices").value_or("")};
|
|
||||||
size_t strpos{0};
|
|
||||||
while(strpos < customList.size())
|
|
||||||
{
|
{
|
||||||
size_t nextpos{customList.find(';', strpos)};
|
for(size_t i{0};ports[i];++i)
|
||||||
size_t seppos{customList.find('=', strpos)};
|
{
|
||||||
|
const char *sep{std::strchr(ports[i], ':')};
|
||||||
|
if(!sep || ports[i] == sep) continue;
|
||||||
|
|
||||||
|
const al::span<const char> portdev{ports[i], sep};
|
||||||
|
auto check_name = [portdev](const DeviceEntry &entry) -> bool
|
||||||
|
{
|
||||||
|
const size_t len{portdev.size()};
|
||||||
|
return entry.mName.length() == len
|
||||||
|
&& entry.mName.compare(0, len, portdev.data(), len) == 0;
|
||||||
|
};
|
||||||
|
if(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string name{portdev.data(), portdev.size()};
|
||||||
|
list.emplace_back(DeviceEntry{name, name+":"});
|
||||||
|
const auto &entry = list.back();
|
||||||
|
TRACE("Got device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
||||||
|
}
|
||||||
|
/* There are ports but couldn't get device names from them. Add a
|
||||||
|
* generic entry.
|
||||||
|
*/
|
||||||
|
if(ports[0] && list.empty())
|
||||||
|
{
|
||||||
|
WARN("No device names found in available ports, adding a generic name.\n");
|
||||||
|
list.emplace_back(DeviceEntry{"JACK", ""});
|
||||||
|
}
|
||||||
|
jack_free(ports);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto listopt = ConfigValueStr(nullptr, "jack", "custom-devices");
|
||||||
|
if(!listopt) return;
|
||||||
|
|
||||||
|
size_t strpos{0};
|
||||||
|
while(strpos < listopt->size())
|
||||||
|
{
|
||||||
|
size_t nextpos{listopt->find(';', strpos)};
|
||||||
|
size_t seppos{listopt->find('=', strpos)};
|
||||||
if(seppos >= nextpos || seppos == strpos)
|
if(seppos >= nextpos || seppos == strpos)
|
||||||
{
|
{
|
||||||
const std::string entry{customList.substr(strpos, nextpos-strpos)};
|
const std::string entry{listopt->substr(strpos, nextpos-strpos)};
|
||||||
ERR("Invalid device entry: \"%s\"\n", entry.c_str());
|
ERR("Invalid device entry: \"%s\"\n", entry.c_str());
|
||||||
if(nextpos != std::string::npos) ++nextpos;
|
if(nextpos != std::string::npos) ++nextpos;
|
||||||
strpos = nextpos;
|
strpos = nextpos;
|
||||||
|
@ -182,18 +214,18 @@ void EnumerateDevices(al::vector<DeviceEntry> &list)
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t count{1};
|
size_t count{1};
|
||||||
std::string name{customList.substr(strpos, seppos-strpos)};
|
std::string name{listopt->substr(strpos, seppos-strpos)};
|
||||||
auto check_name = [&name](const DeviceEntry &entry) -> bool
|
auto check_name = [&name](const DeviceEntry &entry) -> bool
|
||||||
{ return entry.mName == name; };
|
{ return entry.mName == name; };
|
||||||
while(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend())
|
while(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend())
|
||||||
{
|
{
|
||||||
name = customList.substr(strpos, seppos-strpos);
|
name = listopt->substr(strpos, seppos-strpos);
|
||||||
name += " #";
|
name += " #";
|
||||||
name += std::to_string(++count);
|
name += std::to_string(++count);
|
||||||
}
|
}
|
||||||
|
|
||||||
++seppos;
|
++seppos;
|
||||||
list.emplace_back(DeviceEntry{std::move(name), customList.substr(seppos, nextpos-seppos)});
|
list.emplace_back(DeviceEntry{std::move(name), listopt->substr(seppos, nextpos-seppos)});
|
||||||
const auto &entry = list.back();
|
const auto &entry = list.back();
|
||||||
TRACE("Got custom device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
TRACE("Got custom device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str());
|
||||||
|
|
||||||
|
@ -204,7 +236,7 @@ void EnumerateDevices(al::vector<DeviceEntry> &list)
|
||||||
|
|
||||||
|
|
||||||
struct JackPlayback final : public BackendBase {
|
struct JackPlayback final : public BackendBase {
|
||||||
JackPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
JackPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~JackPlayback() override;
|
~JackPlayback() override;
|
||||||
|
|
||||||
int process(jack_nframes_t numframes) noexcept;
|
int process(jack_nframes_t numframes) noexcept;
|
||||||
|
@ -352,31 +384,16 @@ int JackPlayback::mixerProc()
|
||||||
|
|
||||||
void JackPlayback::open(const char *name)
|
void JackPlayback::open(const char *name)
|
||||||
{
|
{
|
||||||
mPortPattern.clear();
|
if(!mClient)
|
||||||
|
|
||||||
if(!name)
|
|
||||||
name = jackDevice;
|
|
||||||
else if(strcmp(name, jackDevice) != 0)
|
|
||||||
{
|
{
|
||||||
if(PlaybackList.empty())
|
const PathNamePair &binname = GetProcBinary();
|
||||||
EnumerateDevices(PlaybackList);
|
const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
|
||||||
|
|
||||||
auto check_name = [name](const DeviceEntry &entry) -> bool
|
|
||||||
{ return entry.mName == 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};
|
|
||||||
mPortPattern = iter->mPattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *client_name{"alsoft"};
|
|
||||||
jack_status_t status;
|
jack_status_t status;
|
||||||
mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
|
mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
|
||||||
if(mClient == nullptr)
|
if(mClient == nullptr)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Failed to open client connection: 0x%02x", status};
|
"Failed to open client connection: 0x%02x", status};
|
||||||
|
|
||||||
if((status&JackServerStarted))
|
if((status&JackServerStarted))
|
||||||
TRACE("JACK server started\n");
|
TRACE("JACK server started\n");
|
||||||
if((status&JackNameNotUnique))
|
if((status&JackNameNotUnique))
|
||||||
|
@ -384,6 +401,26 @@ void JackPlayback::open(const char *name)
|
||||||
client_name = jack_get_client_name(mClient);
|
client_name = jack_get_client_name(mClient);
|
||||||
TRACE("Client name not unique, got '%s' instead\n", client_name);
|
TRACE("Client name not unique, got '%s' instead\n", client_name);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(PlaybackList.empty())
|
||||||
|
EnumerateDevices(mClient, PlaybackList);
|
||||||
|
|
||||||
|
if(!name && !PlaybackList.empty())
|
||||||
|
{
|
||||||
|
name = PlaybackList[0].mName.c_str();
|
||||||
|
mPortPattern = PlaybackList[0].mPattern;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto check_name = [name](const DeviceEntry &entry) -> bool
|
||||||
|
{ return entry.mName == 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:""};
|
||||||
|
mPortPattern = iter->mPattern;
|
||||||
|
}
|
||||||
|
|
||||||
jack_set_process_callback(mClient, &JackPlayback::processC, this);
|
jack_set_process_callback(mClient, &JackPlayback::processC, this);
|
||||||
|
|
||||||
|
@ -453,8 +490,8 @@ void JackPlayback::start()
|
||||||
const char *devname{mDevice->DeviceName.c_str()};
|
const char *devname{mDevice->DeviceName.c_str()};
|
||||||
if(ConfigValueBool(devname, "jack", "connect-ports").value_or(true))
|
if(ConfigValueBool(devname, "jack", "connect-ports").value_or(true))
|
||||||
{
|
{
|
||||||
const char **ports{jack_get_ports(mClient, mPortPattern.c_str(), nullptr,
|
const char **ports{jack_get_ports(mClient, mPortPattern.c_str(), JACK_DEFAULT_AUDIO_TYPE,
|
||||||
JackPortIsPhysical|JackPortIsInput)};
|
JackPortIsInput)};
|
||||||
if(ports == nullptr)
|
if(ports == nullptr)
|
||||||
{
|
{
|
||||||
jack_deactivate(mClient);
|
jack_deactivate(mClient);
|
||||||
|
@ -547,10 +584,13 @@ bool JackBackendFactory::init()
|
||||||
if(!GetConfigValueBool(nullptr, "jack", "spawn-server", 0))
|
if(!GetConfigValueBool(nullptr, "jack", "spawn-server", 0))
|
||||||
ClientOptions = static_cast<jack_options_t>(ClientOptions | JackNoStartServer);
|
ClientOptions = static_cast<jack_options_t>(ClientOptions | JackNoStartServer);
|
||||||
|
|
||||||
|
const PathNamePair &binname = GetProcBinary();
|
||||||
|
const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
|
||||||
|
|
||||||
void (*old_error_cb)(const char*){&jack_error_callback ? jack_error_callback : nullptr};
|
void (*old_error_cb)(const char*){&jack_error_callback ? jack_error_callback : nullptr};
|
||||||
jack_set_error_function(jack_msg_handler);
|
jack_set_error_function(jack_msg_handler);
|
||||||
jack_status_t status;
|
jack_status_t status;
|
||||||
jack_client_t *client{jack_client_open("alsoft", ClientOptions, &status, nullptr)};
|
jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)};
|
||||||
jack_set_error_function(old_error_cb);
|
jack_set_error_function(old_error_cb);
|
||||||
if(!client)
|
if(!client)
|
||||||
{
|
{
|
||||||
|
@ -575,10 +615,20 @@ std::string JackBackendFactory::probe(BackendType type)
|
||||||
/* Includes null char. */
|
/* Includes null char. */
|
||||||
outnames.append(entry.mName.c_str(), entry.mName.length()+1);
|
outnames.append(entry.mName.c_str(), entry.mName.length()+1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PathNamePair &binname = GetProcBinary();
|
||||||
|
const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()};
|
||||||
|
jack_status_t status;
|
||||||
switch(type)
|
switch(type)
|
||||||
{
|
{
|
||||||
case BackendType::Playback:
|
case BackendType::Playback:
|
||||||
EnumerateDevices(PlaybackList);
|
if(jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)})
|
||||||
|
{
|
||||||
|
EnumerateDevices(client, PlaybackList);
|
||||||
|
jack_client_close(client);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
WARN("jack_client_open() failed, 0x%02x\n", status);
|
||||||
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name);
|
||||||
break;
|
break;
|
||||||
case BackendType::Capture:
|
case BackendType::Capture:
|
||||||
|
@ -587,7 +637,7 @@ std::string JackBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr JackBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr JackBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new JackPlayback{device}};
|
return BackendPtr{new JackPlayback{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_JACK_H
|
#ifndef BACKENDS_JACK_H
|
||||||
#define BACKENDS_JACK_H
|
#define BACKENDS_JACK_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct JackBackendFactory final : public BackendFactory {
|
struct JackBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,18 +20,17 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/loopback.h"
|
#include "loopback.h"
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "core/device.h"
|
||||||
#include "alu.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct LoopbackBackend final : public BackendBase {
|
struct LoopbackBackend final : public BackendBase {
|
||||||
LoopbackBackend(ALCdevice *device) noexcept : BackendBase{device} { }
|
LoopbackBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
|
|
||||||
void open(const ALCchar *name) override;
|
void open(const char *name) override;
|
||||||
bool reset() override;
|
bool reset() override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
@ -40,7 +39,7 @@ struct LoopbackBackend final : public BackendBase {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
void LoopbackBackend::open(const ALCchar *name)
|
void LoopbackBackend::open(const char *name)
|
||||||
{
|
{
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +68,7 @@ bool LoopbackBackendFactory::querySupport(BackendType)
|
||||||
std::string LoopbackBackendFactory::probe(BackendType)
|
std::string LoopbackBackendFactory::probe(BackendType)
|
||||||
{ return std::string{}; }
|
{ return std::string{}; }
|
||||||
|
|
||||||
BackendPtr LoopbackBackendFactory::createBackend(ALCdevice *device, BackendType)
|
BackendPtr LoopbackBackendFactory::createBackend(DeviceBase *device, BackendType)
|
||||||
{ return BackendPtr{new LoopbackBackend{device}}; }
|
{ return BackendPtr{new LoopbackBackend{device}}; }
|
||||||
|
|
||||||
BackendFactory &LoopbackBackendFactory::getFactory()
|
BackendFactory &LoopbackBackendFactory::getFactory()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_LOOPBACK_H
|
#ifndef BACKENDS_LOOPBACK_H
|
||||||
#define BACKENDS_LOOPBACK_H
|
#define BACKENDS_LOOPBACK_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct LoopbackBackendFactory final : public BackendFactory {
|
struct LoopbackBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/null.h"
|
#include "null.h"
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
@ -30,9 +30,9 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "core/device.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alu.h"
|
#include "core/helpers.h"
|
||||||
#include "threads.h"
|
#include "threads.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ constexpr char nullDevice[] = "No Output";
|
||||||
|
|
||||||
|
|
||||||
struct NullBackend final : public BackendBase {
|
struct NullBackend final : public BackendBase {
|
||||||
NullBackend(ALCdevice *device) noexcept : BackendBase{device} { }
|
NullBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ std::string NullBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr NullBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr NullBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new NullBackend{device}};
|
return BackendPtr{new NullBackend{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_NULL_H
|
#ifndef BACKENDS_NULL_H
|
||||||
#define BACKENDS_NULL_H
|
#define BACKENDS_NULL_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct NullBackendFactory final : public BackendFactory {
|
struct NullBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "alu.h"
|
#include "alnumeric.h"
|
||||||
|
#include "core/device.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
|
||||||
#include "oboe/Oboe.h"
|
#include "oboe/Oboe.h"
|
||||||
|
@ -18,7 +20,7 @@ constexpr char device_name[] = "Oboe Default";
|
||||||
|
|
||||||
|
|
||||||
struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback {
|
struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback {
|
||||||
OboePlayback(ALCdevice *device) : BackendBase{device} { }
|
OboePlayback(DeviceBase *device) : BackendBase{device} { }
|
||||||
|
|
||||||
oboe::ManagedStream mStream;
|
oboe::ManagedStream mStream;
|
||||||
|
|
||||||
|
@ -64,9 +66,10 @@ void OboePlayback::open(const char *name)
|
||||||
name};
|
name};
|
||||||
|
|
||||||
/* Open a basic output stream, just to ensure it can work. */
|
/* Open a basic output stream, just to ensure it can work. */
|
||||||
|
oboe::ManagedStream stream;
|
||||||
oboe::Result result{oboe::AudioStreamBuilder{}.setDirection(oboe::Direction::Output)
|
oboe::Result result{oboe::AudioStreamBuilder{}.setDirection(oboe::Direction::Output)
|
||||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||||
->openManagedStream(mStream)};
|
->openManagedStream(stream)};
|
||||||
if(result != oboe::Result::OK)
|
if(result != oboe::Result::OK)
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
|
throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
|
||||||
oboe::convertToText(result)};
|
oboe::convertToText(result)};
|
||||||
|
@ -220,7 +223,7 @@ void OboePlayback::stop()
|
||||||
|
|
||||||
|
|
||||||
struct OboeCapture final : public BackendBase {
|
struct OboeCapture final : public BackendBase {
|
||||||
OboeCapture(ALCdevice *device) : BackendBase{device} { }
|
OboeCapture(DeviceBase *device) : BackendBase{device} { }
|
||||||
|
|
||||||
oboe::ManagedStream mStream;
|
oboe::ManagedStream mStream;
|
||||||
|
|
||||||
|
@ -367,7 +370,7 @@ std::string OboeBackendFactory::probe(BackendType type)
|
||||||
return std::string{};
|
return std::string{};
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr OboeBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr OboeBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new OboePlayback{device}};
|
return BackendPtr{new OboePlayback{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_OBOE_H
|
#ifndef BACKENDS_OBOE_H
|
||||||
#define BACKENDS_OBOE_H
|
#define BACKENDS_OBOE_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct OboeBackendFactory final : public BackendFactory {
|
struct OboeBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/opensl.h"
|
#include "opensl.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
@ -33,9 +33,9 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "albit.h"
|
#include "albit.h"
|
||||||
#include "alcmain.h"
|
#include "alnumeric.h"
|
||||||
#include "alu.h"
|
#include "core/device.h"
|
||||||
#include "compat.h"
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
|
@ -151,7 +151,7 @@ const char *res_str(SLresult result) noexcept
|
||||||
|
|
||||||
|
|
||||||
struct OpenSLPlayback final : public BackendBase {
|
struct OpenSLPlayback final : public BackendBase {
|
||||||
OpenSLPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
OpenSLPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~OpenSLPlayback() override;
|
~OpenSLPlayback() override;
|
||||||
|
|
||||||
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
||||||
|
@ -316,6 +316,9 @@ void OpenSLPlayback::open(const char *name)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||||
name};
|
name};
|
||||||
|
|
||||||
|
/* There's only one device, so if it's already open, there's nothing to do. */
|
||||||
|
if(mEngineObj) return;
|
||||||
|
|
||||||
// create engine
|
// create engine
|
||||||
SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
|
SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
|
||||||
PRINTERR(result, "slCreateEngine");
|
PRINTERR(result, "slCreateEngine");
|
||||||
|
@ -629,7 +632,7 @@ ClockLatency OpenSLPlayback::getClockLatency()
|
||||||
|
|
||||||
|
|
||||||
struct OpenSLCapture final : public BackendBase {
|
struct OpenSLCapture final : public BackendBase {
|
||||||
OpenSLCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
OpenSLCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~OpenSLCapture() override;
|
~OpenSLCapture() override;
|
||||||
|
|
||||||
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
||||||
|
@ -959,7 +962,7 @@ std::string OSLBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr OSLBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr OSLBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new OpenSLPlayback{device}};
|
return BackendPtr{new OpenSLPlayback{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_OSL_H
|
#ifndef BACKENDS_OSL_H
|
||||||
#define BACKENDS_OSL_H
|
#define BACKENDS_OSL_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct OSLBackendFactory final : public BackendFactory {
|
struct OSLBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/oss.h"
|
#include "oss.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
@ -41,13 +41,13 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "alcmain.h"
|
|
||||||
#include "alconfig.h"
|
|
||||||
#include "albyte.h"
|
#include "albyte.h"
|
||||||
|
#include "alc/alconfig.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "aloptional.h"
|
#include "aloptional.h"
|
||||||
#include "alu.h"
|
#include "core/device.h"
|
||||||
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "threads.h"
|
#include "threads.h"
|
||||||
|
@ -226,7 +226,7 @@ uint log2i(uint x)
|
||||||
|
|
||||||
|
|
||||||
struct OSSPlayback final : public BackendBase {
|
struct OSSPlayback final : public BackendBase {
|
||||||
OSSPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
OSSPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~OSSPlayback() override;
|
~OSSPlayback() override;
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
@ -249,7 +249,7 @@ struct OSSPlayback final : public BackendBase {
|
||||||
OSSPlayback::~OSSPlayback()
|
OSSPlayback::~OSSPlayback()
|
||||||
{
|
{
|
||||||
if(mFd != -1)
|
if(mFd != -1)
|
||||||
close(mFd);
|
::close(mFd);
|
||||||
mFd = -1;
|
mFd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,11 +328,15 @@ void OSSPlayback::open(const char *name)
|
||||||
devname = iter->device_name.c_str();
|
devname = iter->device_name.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
mFd = ::open(devname, O_WRONLY);
|
int fd{::open(devname, O_WRONLY)};
|
||||||
if(mFd == -1)
|
if(fd == -1)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname,
|
throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname,
|
||||||
strerror(errno)};
|
strerror(errno)};
|
||||||
|
|
||||||
|
if(mFd != -1)
|
||||||
|
::close(mFd);
|
||||||
|
mFd = fd;
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,7 +442,7 @@ void OSSPlayback::stop()
|
||||||
|
|
||||||
|
|
||||||
struct OSScapture final : public BackendBase {
|
struct OSScapture final : public BackendBase {
|
||||||
OSScapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
OSScapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~OSScapture() override;
|
~OSScapture() override;
|
||||||
|
|
||||||
int recordProc();
|
int recordProc();
|
||||||
|
@ -676,7 +680,7 @@ std::string OSSBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr OSSBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr OSSBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new OSSPlayback{device}};
|
return BackendPtr{new OSSPlayback{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_OSS_H
|
#ifndef BACKENDS_OSS_H
|
||||||
#define BACKENDS_OSS_H
|
#define BACKENDS_OSS_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct OSSBackendFactory final : public BackendFactory {
|
struct OSSBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,15 +20,15 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/portaudio.h"
|
#include "portaudio.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/alconfig.h"
|
||||||
#include "alu.h"
|
#include "alnumeric.h"
|
||||||
#include "alconfig.h"
|
#include "core/device.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "dynload.h"
|
#include "dynload.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
|
@ -72,7 +72,7 @@ MAKE_FUNC(Pa_GetStreamInfo);
|
||||||
|
|
||||||
|
|
||||||
struct PortPlayback final : public BackendBase {
|
struct PortPlayback final : public BackendBase {
|
||||||
PortPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~PortPlayback() override;
|
~PortPlayback() override;
|
||||||
|
|
||||||
int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
||||||
|
@ -123,53 +123,58 @@ void PortPlayback::open(const char *name)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||||
name};
|
name};
|
||||||
|
|
||||||
mUpdateSize = mDevice->UpdateSize;
|
PaStreamParameters params{};
|
||||||
|
|
||||||
auto devidopt = ConfigValueInt(nullptr, "port", "device");
|
auto devidopt = ConfigValueInt(nullptr, "port", "device");
|
||||||
if(devidopt && *devidopt >= 0) mParams.device = *devidopt;
|
if(devidopt && *devidopt >= 0) params.device = *devidopt;
|
||||||
else mParams.device = Pa_GetDefaultOutputDevice();
|
else params.device = Pa_GetDefaultOutputDevice();
|
||||||
mParams.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
|
params.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
|
||||||
mParams.hostApiSpecificStreamInfo = nullptr;
|
params.hostApiSpecificStreamInfo = nullptr;
|
||||||
|
|
||||||
mParams.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
params.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||||
|
|
||||||
switch(mDevice->FmtType)
|
switch(mDevice->FmtType)
|
||||||
{
|
{
|
||||||
case DevFmtByte:
|
case DevFmtByte:
|
||||||
mParams.sampleFormat = paInt8;
|
params.sampleFormat = paInt8;
|
||||||
break;
|
break;
|
||||||
case DevFmtUByte:
|
case DevFmtUByte:
|
||||||
mParams.sampleFormat = paUInt8;
|
params.sampleFormat = paUInt8;
|
||||||
break;
|
break;
|
||||||
case DevFmtUShort:
|
case DevFmtUShort:
|
||||||
/* fall-through */
|
/* fall-through */
|
||||||
case DevFmtShort:
|
case DevFmtShort:
|
||||||
mParams.sampleFormat = paInt16;
|
params.sampleFormat = paInt16;
|
||||||
break;
|
break;
|
||||||
case DevFmtUInt:
|
case DevFmtUInt:
|
||||||
/* fall-through */
|
/* fall-through */
|
||||||
case DevFmtInt:
|
case DevFmtInt:
|
||||||
mParams.sampleFormat = paInt32;
|
params.sampleFormat = paInt32;
|
||||||
break;
|
break;
|
||||||
case DevFmtFloat:
|
case DevFmtFloat:
|
||||||
mParams.sampleFormat = paFloat32;
|
params.sampleFormat = paFloat32;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
retry_open:
|
retry_open:
|
||||||
PaError err{Pa_OpenStream(&mStream, nullptr, &mParams, mDevice->Frequency, mDevice->UpdateSize,
|
PaStream *stream{};
|
||||||
|
PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency, mDevice->UpdateSize,
|
||||||
paNoFlag, &PortPlayback::writeCallbackC, this)};
|
paNoFlag, &PortPlayback::writeCallbackC, this)};
|
||||||
if(err != paNoError)
|
if(err != paNoError)
|
||||||
{
|
{
|
||||||
if(mParams.sampleFormat == paFloat32)
|
if(params.sampleFormat == paFloat32)
|
||||||
{
|
{
|
||||||
mParams.sampleFormat = paInt16;
|
params.sampleFormat = paInt16;
|
||||||
goto retry_open;
|
goto retry_open;
|
||||||
}
|
}
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
|
throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
|
||||||
Pa_GetErrorText(err)};
|
Pa_GetErrorText(err)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pa_CloseStream(mStream);
|
||||||
|
mStream = stream;
|
||||||
|
mParams = params;
|
||||||
|
mUpdateSize = mDevice->UpdateSize;
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +231,7 @@ void PortPlayback::stop()
|
||||||
|
|
||||||
|
|
||||||
struct PortCapture final : public BackendBase {
|
struct PortCapture final : public BackendBase {
|
||||||
PortCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
PortCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~PortCapture() override;
|
~PortCapture() override;
|
||||||
|
|
||||||
int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
||||||
|
@ -426,7 +431,7 @@ std::string PortBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr PortBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr PortBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new PortPlayback{device}};
|
return BackendPtr{new PortPlayback{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_PORTAUDIO_H
|
#ifndef BACKENDS_PORTAUDIO_H
|
||||||
#define BACKENDS_PORTAUDIO_H
|
#define BACKENDS_PORTAUDIO_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct PortBackendFactory final : public BackendFactory {
|
struct PortBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,33 +21,49 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/pulseaudio.h"
|
#include "pulseaudio.h"
|
||||||
|
|
||||||
#include <poll.h>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <atomic>
|
|
||||||
#include <thread>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
|
#include <bitset>
|
||||||
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <cstring>
|
||||||
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
|
#include <mutex>
|
||||||
|
#include <new>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "albyte.h"
|
||||||
#include "alu.h"
|
#include "alc/alconfig.h"
|
||||||
#include "alconfig.h"
|
#include "almalloc.h"
|
||||||
#include "compat.h"
|
#include "alnumeric.h"
|
||||||
|
#include "aloptional.h"
|
||||||
|
#include "alspan.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "dynload.h"
|
#include "dynload.h"
|
||||||
|
#include "opthelpers.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
|
#include "vector.h"
|
||||||
|
|
||||||
#include <pulse/pulseaudio.h>
|
#include <pulse/pulseaudio.h>
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
|
|
||||||
#ifdef HAVE_DYNLOAD
|
#ifdef HAVE_DYNLOAD
|
||||||
#define PULSE_FUNCS(MAGIC) \
|
#define PULSE_FUNCS(MAGIC) \
|
||||||
MAGIC(pa_mainloop_new); \
|
MAGIC(pa_mainloop_new); \
|
||||||
|
@ -282,7 +298,7 @@ al::optional<Channel> ChannelFromPulse(pa_channel_position_t chan)
|
||||||
return al::nullopt;
|
return al::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetChannelOrderFromMap(ALCdevice *device, const pa_channel_map &chanmap)
|
void SetChannelOrderFromMap(DeviceBase *device, const pa_channel_map &chanmap)
|
||||||
{
|
{
|
||||||
device->RealOut.ChannelIndex.fill(INVALID_CHANNEL_INDEX);
|
device->RealOut.ChannelIndex.fill(INVALID_CHANNEL_INDEX);
|
||||||
for(uint i{0};i < chanmap.channels;++i)
|
for(uint i{0};i < chanmap.channels;++i)
|
||||||
|
@ -497,19 +513,13 @@ public:
|
||||||
|
|
||||||
pa_context *PulseMainloop::connectContext(std::unique_lock<std::mutex> &plock)
|
pa_context *PulseMainloop::connectContext(std::unique_lock<std::mutex> &plock)
|
||||||
{
|
{
|
||||||
const char *name{"OpenAL Soft"};
|
|
||||||
|
|
||||||
const PathNamePair &binname = GetProcBinary();
|
|
||||||
if(!binname.fname.empty())
|
|
||||||
name = binname.fname.c_str();
|
|
||||||
|
|
||||||
if(!mMainloop)
|
if(!mMainloop)
|
||||||
{
|
{
|
||||||
mThread = std::thread{std::mem_fn(&PulseMainloop::mainloop_proc), this};
|
mThread = std::thread{std::mem_fn(&PulseMainloop::mainloop_proc), this};
|
||||||
mCondVar.wait(plock, [this]() noexcept { return mMainloop; });
|
mCondVar.wait(plock, [this]() noexcept { return mMainloop; });
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_context *context{pa_context_new(pa_mainloop_get_api(mMainloop), name)};
|
pa_context *context{pa_context_new(pa_mainloop_get_api(mMainloop), nullptr)};
|
||||||
if(!context) throw al::backend_exception{al::backend_error::OutOfMemory,
|
if(!context) throw al::backend_exception{al::backend_error::OutOfMemory,
|
||||||
"pa_context_new() failed"};
|
"pa_context_new() failed"};
|
||||||
|
|
||||||
|
@ -661,7 +671,7 @@ PulseMainloop gGlobalMainloop;
|
||||||
|
|
||||||
|
|
||||||
struct PulsePlayback final : public BackendBase {
|
struct PulsePlayback final : public BackendBase {
|
||||||
PulsePlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
PulsePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~PulsePlayback() override;
|
~PulsePlayback() override;
|
||||||
|
|
||||||
void bufferAttrCallback(pa_stream *stream) noexcept;
|
void bufferAttrCallback(pa_stream *stream) noexcept;
|
||||||
|
@ -846,6 +856,7 @@ void PulsePlayback::open(const char *name)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto plock = mMainloop.getUniqueLock();
|
auto plock = mMainloop.getUniqueLock();
|
||||||
|
if(!mContext)
|
||||||
mContext = mMainloop.connectContext(plock);
|
mContext = mMainloop.connectContext(plock);
|
||||||
|
|
||||||
pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
|
pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
|
||||||
|
@ -864,8 +875,18 @@ void PulsePlayback::open(const char *name)
|
||||||
if(defname) pulse_name = defname->c_str();
|
if(defname) pulse_name = defname->c_str();
|
||||||
}
|
}
|
||||||
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
|
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
|
||||||
mStream = mMainloop.connectStream(pulse_name, plock, mContext, flags, nullptr, &spec, nullptr,
|
pa_stream *stream{mMainloop.connectStream(pulse_name, plock, mContext, flags, nullptr, &spec,
|
||||||
BackendType::Playback);
|
nullptr, BackendType::Playback)};
|
||||||
|
if(mStream)
|
||||||
|
{
|
||||||
|
pa_stream_set_state_callback(mStream, nullptr, nullptr);
|
||||||
|
pa_stream_set_moved_callback(mStream, nullptr, nullptr);
|
||||||
|
pa_stream_set_write_callback(mStream, nullptr, nullptr);
|
||||||
|
pa_stream_set_buffer_attr_callback(mStream, nullptr, nullptr);
|
||||||
|
pa_stream_disconnect(mStream);
|
||||||
|
pa_stream_unref(mStream);
|
||||||
|
}
|
||||||
|
mStream = stream;
|
||||||
|
|
||||||
pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
|
pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
|
||||||
mFrameSize = static_cast<uint>(pa_frame_size(pa_stream_get_sample_spec(mStream)));
|
mFrameSize = static_cast<uint>(pa_frame_size(pa_stream_get_sample_spec(mStream)));
|
||||||
|
@ -1029,34 +1050,34 @@ void PulsePlayback::start()
|
||||||
{
|
{
|
||||||
auto plock = mMainloop.getUniqueLock();
|
auto plock = mMainloop.getUniqueLock();
|
||||||
|
|
||||||
pa_stream_set_write_callback(mStream, &PulsePlayback::streamWriteCallbackC, this);
|
/* Write some (silent) samples to fill the buffer before we start feeding
|
||||||
pa_operation *op{pa_stream_cork(mStream, 0, &PulseMainloop::streamSuccessCallbackC,
|
* it newly mixed samples.
|
||||||
&mMainloop)};
|
*/
|
||||||
|
if(size_t todo{pa_stream_writable_size(mStream)})
|
||||||
/* Write some (silent) samples to fill the prebuf amount if needed. */
|
|
||||||
if(size_t prebuf{mAttr.prebuf})
|
|
||||||
{
|
{
|
||||||
prebuf = minz(prebuf, pa_stream_writable_size(mStream));
|
void *buf{pa_xmalloc(todo)};
|
||||||
|
|
||||||
void *buf{pa_xmalloc(prebuf)};
|
|
||||||
switch(mSpec.format)
|
switch(mSpec.format)
|
||||||
{
|
{
|
||||||
case PA_SAMPLE_U8:
|
case PA_SAMPLE_U8:
|
||||||
std::fill_n(static_cast<uint8_t*>(buf), prebuf, 0x80);
|
std::fill_n(static_cast<uint8_t*>(buf), todo, 0x80);
|
||||||
break;
|
break;
|
||||||
case PA_SAMPLE_ALAW:
|
case PA_SAMPLE_ALAW:
|
||||||
std::fill_n(static_cast<uint8_t*>(buf), prebuf, 0xD5);
|
std::fill_n(static_cast<uint8_t*>(buf), todo, 0xD5);
|
||||||
break;
|
break;
|
||||||
case PA_SAMPLE_ULAW:
|
case PA_SAMPLE_ULAW:
|
||||||
std::fill_n(static_cast<uint8_t*>(buf), prebuf, 0x7f);
|
std::fill_n(static_cast<uint8_t*>(buf), todo, 0x7f);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
std::fill_n(static_cast<uint8_t*>(buf), prebuf, 0x00);
|
std::fill_n(static_cast<uint8_t*>(buf), todo, 0x00);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pa_stream_write(mStream, buf, prebuf, pa_xfree, 0, PA_SEEK_RELATIVE);
|
pa_stream_write(mStream, buf, todo, pa_xfree, 0, PA_SEEK_RELATIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pa_stream_set_write_callback(mStream, &PulsePlayback::streamWriteCallbackC, this);
|
||||||
|
pa_operation *op{pa_stream_cork(mStream, 0, &PulseMainloop::streamSuccessCallbackC,
|
||||||
|
&mMainloop)};
|
||||||
|
|
||||||
mMainloop.waitForOperation(op, plock);
|
mMainloop.waitForOperation(op, plock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1103,7 +1124,7 @@ ClockLatency PulsePlayback::getClockLatency()
|
||||||
|
|
||||||
|
|
||||||
struct PulseCapture final : public BackendBase {
|
struct PulseCapture final : public BackendBase {
|
||||||
PulseCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
PulseCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~PulseCapture() override;
|
~PulseCapture() override;
|
||||||
|
|
||||||
void streamStateCallback(pa_stream *stream) noexcept;
|
void streamStateCallback(pa_stream *stream) noexcept;
|
||||||
|
@ -1505,7 +1526,7 @@ std::string PulseBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr PulseBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr PulseBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new PulsePlayback{device}};
|
return BackendPtr{new PulsePlayback{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_PULSEAUDIO_H
|
#ifndef BACKENDS_PULSEAUDIO_H
|
||||||
#define BACKENDS_PULSEAUDIO_H
|
#define BACKENDS_PULSEAUDIO_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
class PulseBackendFactory final : public BackendFactory {
|
class PulseBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,16 +20,15 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/sdl2.h"
|
#include "sdl2.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "alcmain.h"
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alu.h"
|
#include "core/device.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
@ -46,7 +45,7 @@ namespace {
|
||||||
constexpr char defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
|
constexpr char defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
|
||||||
|
|
||||||
struct Sdl2Backend final : public BackendBase {
|
struct Sdl2Backend final : public BackendBase {
|
||||||
Sdl2Backend(ALCdevice *device) noexcept : BackendBase{device} { }
|
Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~Sdl2Backend() override;
|
~Sdl2Backend() override;
|
||||||
|
|
||||||
void audioCallback(Uint8 *stream, int len) noexcept;
|
void audioCallback(Uint8 *stream, int len) noexcept;
|
||||||
|
@ -106,32 +105,34 @@ void Sdl2Backend::open(const char *name)
|
||||||
/* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't
|
/* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't
|
||||||
* necessarily the first in the list.
|
* necessarily the first in the list.
|
||||||
*/
|
*/
|
||||||
|
SDL_AudioDeviceID devid;
|
||||||
if(!name || strcmp(name, defaultDeviceName) == 0)
|
if(!name || strcmp(name, defaultDeviceName) == 0)
|
||||||
mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have,
|
devid = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const size_t prefix_len = strlen(DEVNAME_PREFIX);
|
const size_t prefix_len = strlen(DEVNAME_PREFIX);
|
||||||
if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
|
if(strncmp(name, DEVNAME_PREFIX, prefix_len) == 0)
|
||||||
mDeviceID = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
|
devid = SDL_OpenAudioDevice(name+prefix_len, SDL_FALSE, &want, &have,
|
||||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||||
else
|
else
|
||||||
mDeviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have,
|
devid = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
|
||||||
}
|
}
|
||||||
if(mDeviceID == 0)
|
if(!devid)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()};
|
throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()};
|
||||||
|
|
||||||
mDevice->Frequency = static_cast<uint>(have.freq);
|
DevFmtChannels devchans{};
|
||||||
|
|
||||||
if(have.channels == 1)
|
if(have.channels == 1)
|
||||||
mDevice->FmtChans = DevFmtMono;
|
devchans = DevFmtMono;
|
||||||
else if(have.channels == 2)
|
else if(have.channels == 2)
|
||||||
mDevice->FmtChans = DevFmtStereo;
|
devchans = DevFmtStereo;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
SDL_CloseAudioDevice(devid);
|
||||||
throw al::backend_exception{al::backend_error::DeviceError,
|
throw al::backend_exception{al::backend_error::DeviceError,
|
||||||
"Unhandled SDL channel count: %d", int{have.channels}};
|
"Unhandled SDL channel count: %d", int{have.channels}};
|
||||||
|
}
|
||||||
|
|
||||||
|
DevFmtType devtype{};
|
||||||
switch(have.format)
|
switch(have.format)
|
||||||
{
|
{
|
||||||
case AUDIO_U8: mDevice->FmtType = DevFmtUByte; break;
|
case AUDIO_U8: mDevice->FmtType = DevFmtUByte; break;
|
||||||
|
@ -141,17 +142,20 @@ void Sdl2Backend::open(const char *name)
|
||||||
case AUDIO_S32SYS: mDevice->FmtType = DevFmtInt; break;
|
case AUDIO_S32SYS: mDevice->FmtType = DevFmtInt; break;
|
||||||
case AUDIO_F32SYS: mDevice->FmtType = DevFmtFloat; break;
|
case AUDIO_F32SYS: mDevice->FmtType = DevFmtFloat; break;
|
||||||
default:
|
default:
|
||||||
|
SDL_CloseAudioDevice(devid);
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Unhandled SDL format: 0x%04x",
|
throw al::backend_exception{al::backend_error::DeviceError, "Unhandled SDL format: 0x%04x",
|
||||||
have.format};
|
have.format};
|
||||||
}
|
}
|
||||||
mDevice->UpdateSize = have.samples;
|
|
||||||
mDevice->BufferSize = have.samples * 2; /* SDL always (tries to) use two periods. */
|
|
||||||
|
|
||||||
mFrameSize = mDevice->frameSizeFromFmt();
|
if(mDeviceID)
|
||||||
mFrequency = mDevice->Frequency;
|
SDL_CloseAudioDevice(mDeviceID);
|
||||||
mFmtChans = mDevice->FmtChans;
|
mDeviceID = devid;
|
||||||
mFmtType = mDevice->FmtType;
|
|
||||||
mUpdateSize = mDevice->UpdateSize;
|
mFrameSize = FrameSizeFromDevFmt(devchans, devtype, 0);
|
||||||
|
mFrequency = static_cast<uint>(have.freq);
|
||||||
|
mFmtChans = devchans;
|
||||||
|
mFmtType = devtype;
|
||||||
|
mUpdateSize = have.samples;
|
||||||
|
|
||||||
mDevice->DeviceName = name ? name : defaultDeviceName;
|
mDevice->DeviceName = name ? name : defaultDeviceName;
|
||||||
}
|
}
|
||||||
|
@ -162,7 +166,7 @@ bool Sdl2Backend::reset()
|
||||||
mDevice->FmtChans = mFmtChans;
|
mDevice->FmtChans = mFmtChans;
|
||||||
mDevice->FmtType = mFmtType;
|
mDevice->FmtType = mFmtType;
|
||||||
mDevice->UpdateSize = mUpdateSize;
|
mDevice->UpdateSize = mUpdateSize;
|
||||||
mDevice->BufferSize = mUpdateSize * 2;
|
mDevice->BufferSize = mUpdateSize * 2; /* SDL always (tries to) use two periods. */
|
||||||
setDefaultWFXChannelOrder();
|
setDefaultWFXChannelOrder();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -208,7 +212,7 @@ std::string SDL2BackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr SDL2BackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr SDL2BackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new Sdl2Backend{device}};
|
return BackendPtr{new Sdl2Backend{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_SDL2_H
|
#ifndef BACKENDS_SDL2_H
|
||||||
#define BACKENDS_SDL2_H
|
#define BACKENDS_SDL2_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct SDL2BackendFactory final : public BackendFactory {
|
struct SDL2BackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/sndio.h"
|
#include "sndio.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -29,8 +29,9 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alnumeric.h"
|
||||||
#include "alu.h"
|
#include "core/device.h"
|
||||||
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "threads.h"
|
#include "threads.h"
|
||||||
|
@ -45,7 +46,7 @@ static const char sndio_device[] = "SndIO Default";
|
||||||
|
|
||||||
|
|
||||||
struct SndioPlayback final : public BackendBase {
|
struct SndioPlayback final : public BackendBase {
|
||||||
SndioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
SndioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~SndioPlayback() override;
|
~SndioPlayback() override;
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
@ -122,10 +123,14 @@ void SndioPlayback::open(const char *name)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||||
name};
|
name};
|
||||||
|
|
||||||
mSndHandle = sio_open(nullptr, SIO_PLAY, 0);
|
sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)};
|
||||||
if(mSndHandle == nullptr)
|
if(!sndHandle)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not open backend device"};
|
throw al::backend_exception{al::backend_error::NoDevice, "Could not open backend device"};
|
||||||
|
|
||||||
|
if(mSndHandle)
|
||||||
|
sio_close(mSndHandle);
|
||||||
|
mSndHandle = sndHandle;
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,7 +293,7 @@ void SndioPlayback::stop()
|
||||||
|
|
||||||
|
|
||||||
struct SndioCapture final : public BackendBase {
|
struct SndioCapture final : public BackendBase {
|
||||||
SndioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
SndioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~SndioCapture() override;
|
~SndioCapture() override;
|
||||||
|
|
||||||
int recordProc();
|
int recordProc();
|
||||||
|
@ -507,7 +512,7 @@ std::string SndIOBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr SndIOBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr SndIOBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new SndioPlayback{device}};
|
return BackendPtr{new SndioPlayback{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_SNDIO_H
|
#ifndef BACKENDS_SNDIO_H
|
||||||
#define BACKENDS_SNDIO_H
|
#define BACKENDS_SNDIO_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct SndIOBackendFactory final : public BackendFactory {
|
struct SndIOBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/solaris.h"
|
#include "solaris.h"
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
@ -39,11 +39,10 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "alcmain.h"
|
|
||||||
#include "albyte.h"
|
#include "albyte.h"
|
||||||
#include "alu.h"
|
#include "alc/alconfig.h"
|
||||||
#include "alconfig.h"
|
#include "core/device.h"
|
||||||
#include "compat.h"
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "threads.h"
|
#include "threads.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
@ -59,7 +58,7 @@ std::string solaris_driver{"/dev/audio"};
|
||||||
|
|
||||||
|
|
||||||
struct SolarisBackend final : public BackendBase {
|
struct SolarisBackend final : public BackendBase {
|
||||||
SolarisBackend(ALCdevice *device) noexcept : BackendBase{device} { }
|
SolarisBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~SolarisBackend() override;
|
~SolarisBackend() override;
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
@ -148,11 +147,15 @@ void SolarisBackend::open(const char *name)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||||
name};
|
name};
|
||||||
|
|
||||||
mFd = ::open(solaris_driver.c_str(), O_WRONLY);
|
int fd{::open(solaris_driver.c_str(), O_WRONLY)};
|
||||||
if(mFd == -1)
|
if(fd == -1)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s",
|
throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s",
|
||||||
solaris_driver.c_str(), strerror(errno)};
|
solaris_driver.c_str(), strerror(errno)};
|
||||||
|
|
||||||
|
if(mFd != -1)
|
||||||
|
::close(mFd);
|
||||||
|
mFd = fd;
|
||||||
|
|
||||||
mDevice->DeviceName = name;
|
mDevice->DeviceName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +294,7 @@ std::string SolarisBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr SolarisBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr SolarisBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new SolarisBackend{device}};
|
return BackendPtr{new SolarisBackend{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_SOLARIS_H
|
#ifndef BACKENDS_SOLARIS_H
|
||||||
#define BACKENDS_SOLARIS_H
|
#define BACKENDS_SOLARIS_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct SolarisBackendFactory final : public BackendFactory {
|
struct SolarisBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/wasapi.h"
|
#include "wasapi.h"
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@ -56,10 +56,11 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "albit.h"
|
#include "albit.h"
|
||||||
#include "alcmain.h"
|
#include "alnumeric.h"
|
||||||
#include "alu.h"
|
#include "comptr.h"
|
||||||
#include "compat.h"
|
#include "core/converter.h"
|
||||||
#include "converter.h"
|
#include "core/device.h"
|
||||||
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
|
@ -129,69 +130,6 @@ inline uint RefTime2Samples(const ReferenceTime &val, uint srate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class ComPtr {
|
|
||||||
T *mPtr{nullptr};
|
|
||||||
|
|
||||||
public:
|
|
||||||
ComPtr() noexcept = default;
|
|
||||||
ComPtr(const ComPtr &rhs) : 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)
|
|
||||||
{
|
|
||||||
if(!rhs.mPtr)
|
|
||||||
{
|
|
||||||
if(mPtr)
|
|
||||||
mPtr->Release();
|
|
||||||
mPtr = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rhs.mPtr->AddRef();
|
|
||||||
try {
|
|
||||||
if(mPtr)
|
|
||||||
mPtr->Release();
|
|
||||||
mPtr = rhs.mPtr;
|
|
||||||
}
|
|
||||||
catch(...) {
|
|
||||||
rhs.mPtr->Release();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
ComPtr& operator=(ComPtr&& rhs)
|
|
||||||
{
|
|
||||||
if(mPtr)
|
|
||||||
mPtr->Release();
|
|
||||||
mPtr = rhs.mPtr;
|
|
||||||
rhs.mPtr = nullptr;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
T *ret{mPtr};
|
|
||||||
mPtr = nullptr;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void swap(ComPtr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
|
|
||||||
void swap(ComPtr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class GuidPrinter {
|
class GuidPrinter {
|
||||||
char mMsg[64];
|
char mMsg[64];
|
||||||
|
|
||||||
|
@ -238,10 +176,9 @@ struct DevMap {
|
||||||
|
|
||||||
bool checkName(const al::vector<DevMap> &list, const std::string &name)
|
bool checkName(const al::vector<DevMap> &list, const std::string &name)
|
||||||
{
|
{
|
||||||
return std::find_if(list.cbegin(), list.cend(),
|
auto match_name = [&name](const DevMap &entry) -> bool
|
||||||
[&name](const DevMap &entry) -> bool
|
{ return entry.name == name; };
|
||||||
{ return entry.name == name; }
|
return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
|
||||||
) != list.cend();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
al::vector<DevMap> PlaybackDevices;
|
al::vector<DevMap> PlaybackDevices;
|
||||||
|
@ -297,26 +234,26 @@ NameGUIDPair get_device_name_and_guid(IMMDevice *device)
|
||||||
return std::make_pair(std::move(name), std::move(guid));
|
return std::make_pair(std::move(name), std::move(guid));
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor)
|
EndpointFormFactor get_device_formfactor(IMMDevice *device)
|
||||||
{
|
{
|
||||||
ComPtr<IPropertyStore> ps;
|
ComPtr<IPropertyStore> ps;
|
||||||
HRESULT hr = device->OpenPropertyStore(STGM_READ, ps.getPtr());
|
HRESULT hr{device->OpenPropertyStore(STGM_READ, ps.getPtr())};
|
||||||
if(FAILED(hr))
|
if(FAILED(hr))
|
||||||
{
|
{
|
||||||
WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
|
WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
|
||||||
return;
|
return UnknownFormFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EndpointFormFactor formfactor{UnknownFormFactor};
|
||||||
PropVariant pvform;
|
PropVariant pvform;
|
||||||
hr = ps->GetValue(reinterpret_cast<const PROPERTYKEY&>(PKEY_AudioEndpoint_FormFactor), pvform.get());
|
hr = ps->GetValue(PKEY_AudioEndpoint_FormFactor, pvform.get());
|
||||||
if(FAILED(hr))
|
if(FAILED(hr))
|
||||||
WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
|
WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
|
||||||
else if(pvform->vt == VT_UI4)
|
else if(pvform->vt == VT_UI4)
|
||||||
*formfactor = static_cast<EndpointFormFactor>(pvform->ulVal);
|
formfactor = static_cast<EndpointFormFactor>(pvform->ulVal);
|
||||||
else if(pvform->vt == VT_EMPTY)
|
else if(pvform->vt != VT_EMPTY)
|
||||||
*formfactor = UnknownFormFactor;
|
|
||||||
else
|
|
||||||
WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt);
|
WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt);
|
||||||
|
return formfactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -484,26 +421,27 @@ void TraceFormat(const char *msg, const WAVEFORMATEX *format)
|
||||||
|
|
||||||
enum class MsgType {
|
enum class MsgType {
|
||||||
OpenDevice,
|
OpenDevice,
|
||||||
|
ReopenDevice,
|
||||||
ResetDevice,
|
ResetDevice,
|
||||||
StartDevice,
|
StartDevice,
|
||||||
StopDevice,
|
StopDevice,
|
||||||
CloseDevice,
|
CloseDevice,
|
||||||
EnumeratePlayback,
|
EnumeratePlayback,
|
||||||
EnumerateCapture,
|
EnumerateCapture,
|
||||||
QuitThread,
|
|
||||||
|
|
||||||
Count
|
Count,
|
||||||
|
QuitThread = Count
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{
|
constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{
|
||||||
"Open Device",
|
"Open Device",
|
||||||
|
"Reopen Device",
|
||||||
"Reset Device",
|
"Reset Device",
|
||||||
"Start Device",
|
"Start Device",
|
||||||
"Stop Device",
|
"Stop Device",
|
||||||
"Close Device",
|
"Close Device",
|
||||||
"Enumerate Playback",
|
"Enumerate Playback",
|
||||||
"Enumerate Capture",
|
"Enumerate Capture"
|
||||||
"Quit"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -511,7 +449,7 @@ constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{
|
||||||
struct WasapiProxy {
|
struct WasapiProxy {
|
||||||
virtual ~WasapiProxy() = default;
|
virtual ~WasapiProxy() = default;
|
||||||
|
|
||||||
virtual HRESULT openProxy() = 0;
|
virtual HRESULT openProxy(const char *name) = 0;
|
||||||
virtual void closeProxy() = 0;
|
virtual void closeProxy() = 0;
|
||||||
|
|
||||||
virtual HRESULT resetProxy() = 0;
|
virtual HRESULT resetProxy() = 0;
|
||||||
|
@ -521,19 +459,22 @@ struct WasapiProxy {
|
||||||
struct Msg {
|
struct Msg {
|
||||||
MsgType mType;
|
MsgType mType;
|
||||||
WasapiProxy *mProxy;
|
WasapiProxy *mProxy;
|
||||||
|
const char *mParam;
|
||||||
std::promise<HRESULT> mPromise;
|
std::promise<HRESULT> mPromise;
|
||||||
|
|
||||||
|
operator bool() const noexcept { return mType != MsgType::QuitThread; }
|
||||||
};
|
};
|
||||||
static std::deque<Msg> mMsgQueue;
|
static std::deque<Msg> mMsgQueue;
|
||||||
static std::mutex mMsgQueueLock;
|
static std::mutex mMsgQueueLock;
|
||||||
static std::condition_variable mMsgQueueCond;
|
static std::condition_variable mMsgQueueCond;
|
||||||
|
|
||||||
std::future<HRESULT> pushMessage(MsgType type)
|
std::future<HRESULT> pushMessage(MsgType type, const char *param=nullptr)
|
||||||
{
|
{
|
||||||
std::promise<HRESULT> promise;
|
std::promise<HRESULT> promise;
|
||||||
std::future<HRESULT> future{promise.get_future()};
|
std::future<HRESULT> future{promise.get_future()};
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> _{mMsgQueueLock};
|
std::lock_guard<std::mutex> _{mMsgQueueLock};
|
||||||
mMsgQueue.emplace_back(Msg{type, this, std::move(promise)});
|
mMsgQueue.emplace_back(Msg{type, this, param, std::move(promise)});
|
||||||
}
|
}
|
||||||
mMsgQueueCond.notify_one();
|
mMsgQueueCond.notify_one();
|
||||||
return future;
|
return future;
|
||||||
|
@ -545,19 +486,19 @@ struct WasapiProxy {
|
||||||
std::future<HRESULT> future{promise.get_future()};
|
std::future<HRESULT> future{promise.get_future()};
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> _{mMsgQueueLock};
|
std::lock_guard<std::mutex> _{mMsgQueueLock};
|
||||||
mMsgQueue.emplace_back(Msg{type, nullptr, std::move(promise)});
|
mMsgQueue.emplace_back(Msg{type, nullptr, nullptr, std::move(promise)});
|
||||||
}
|
}
|
||||||
mMsgQueueCond.notify_one();
|
mMsgQueueCond.notify_one();
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool popMessage(Msg &msg)
|
static Msg popMessage()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock{mMsgQueueLock};
|
std::unique_lock<std::mutex> lock{mMsgQueueLock};
|
||||||
mMsgQueueCond.wait(lock, []{return !mMsgQueue.empty();});
|
mMsgQueueCond.wait(lock, []{return !mMsgQueue.empty();});
|
||||||
msg = std::move(mMsgQueue.front());
|
Msg msg{std::move(mMsgQueue.front())};
|
||||||
mMsgQueue.pop_front();
|
mMsgQueue.pop_front();
|
||||||
return msg.mType != MsgType::QuitThread;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int messageHandler(std::promise<HRESULT> *promise);
|
static int messageHandler(std::promise<HRESULT> *promise);
|
||||||
|
@ -597,12 +538,11 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
|
||||||
|
|
||||||
TRACE("Starting message loop\n");
|
TRACE("Starting message loop\n");
|
||||||
uint deviceCount{0};
|
uint deviceCount{0};
|
||||||
Msg msg;
|
while(Msg msg{popMessage()})
|
||||||
while(popMessage(msg))
|
|
||||||
{
|
{
|
||||||
TRACE("Got message \"%s\" (0x%04x, this=%p)\n",
|
TRACE("Got message \"%s\" (0x%04x, this=%p, param=%p)\n",
|
||||||
MessageStr[static_cast<size_t>(msg.mType)], static_cast<uint>(msg.mType),
|
MessageStr[static_cast<size_t>(msg.mType)], static_cast<uint>(msg.mType),
|
||||||
decltype(std::declval<void*>()){msg.mProxy});
|
static_cast<void*>(msg.mProxy), static_cast<const void*>(msg.mParam));
|
||||||
|
|
||||||
switch(msg.mType)
|
switch(msg.mType)
|
||||||
{
|
{
|
||||||
|
@ -611,7 +551,7 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
|
||||||
if(++deviceCount == 1)
|
if(++deviceCount == 1)
|
||||||
hr = cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
hr = cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
hr = msg.mProxy->openProxy();
|
hr = msg.mProxy->openProxy(msg.mParam);
|
||||||
msg.mPromise.set_value(hr);
|
msg.mPromise.set_value(hr);
|
||||||
|
|
||||||
if(FAILED(hr))
|
if(FAILED(hr))
|
||||||
|
@ -621,6 +561,11 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
case MsgType::ReopenDevice:
|
||||||
|
hr = msg.mProxy->openProxy(msg.mParam);
|
||||||
|
msg.mPromise.set_value(hr);
|
||||||
|
continue;
|
||||||
|
|
||||||
case MsgType::ResetDevice:
|
case MsgType::ResetDevice:
|
||||||
hr = msg.mProxy->resetProxy();
|
hr = msg.mProxy->resetProxy();
|
||||||
msg.mPromise.set_value(hr);
|
msg.mPromise.set_value(hr);
|
||||||
|
@ -669,11 +614,11 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
default:
|
case MsgType::QuitThread:
|
||||||
|
break;
|
||||||
|
}
|
||||||
ERR("Unexpected message: %u\n", static_cast<uint>(msg.mType));
|
ERR("Unexpected message: %u\n", static_cast<uint>(msg.mType));
|
||||||
msg.mPromise.set_value(E_FAIL);
|
msg.mPromise.set_value(E_FAIL);
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
TRACE("Message loop finished\n");
|
TRACE("Message loop finished\n");
|
||||||
|
|
||||||
|
@ -682,13 +627,13 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
|
||||||
|
|
||||||
|
|
||||||
struct WasapiPlayback final : public BackendBase, WasapiProxy {
|
struct WasapiPlayback final : public BackendBase, WasapiProxy {
|
||||||
WasapiPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
WasapiPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~WasapiPlayback() override;
|
~WasapiPlayback() override;
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(const char *name) override;
|
||||||
HRESULT openProxy() override;
|
HRESULT openProxy(const char *name) override;
|
||||||
void closeProxy() override;
|
void closeProxy() override;
|
||||||
|
|
||||||
bool reset() override;
|
bool reset() override;
|
||||||
|
@ -700,8 +645,6 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy {
|
||||||
|
|
||||||
ClockLatency getClockLatency() override;
|
ClockLatency getClockLatency() override;
|
||||||
|
|
||||||
std::wstring mDevId;
|
|
||||||
|
|
||||||
HRESULT mOpenStatus{E_FAIL};
|
HRESULT mOpenStatus{E_FAIL};
|
||||||
ComPtr<IMMDevice> mMMDev{nullptr};
|
ComPtr<IMMDevice> mMMDev{nullptr};
|
||||||
ComPtr<IAudioClient> mClient{nullptr};
|
ComPtr<IAudioClient> mClient{nullptr};
|
||||||
|
@ -796,21 +739,40 @@ void WasapiPlayback::open(const char *name)
|
||||||
{
|
{
|
||||||
HRESULT hr{S_OK};
|
HRESULT hr{S_OK};
|
||||||
|
|
||||||
|
if(!mNotifyEvent)
|
||||||
|
{
|
||||||
mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||||
if(mNotifyEvent == nullptr)
|
if(mNotifyEvent == nullptr)
|
||||||
{
|
{
|
||||||
ERR("Failed to create notify events: %lu\n", GetLastError());
|
ERR("Failed to create notify events: %lu\n", GetLastError());
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
if(name)
|
if(name && PlaybackDevices.empty())
|
||||||
{
|
|
||||||
if(PlaybackDevices.empty())
|
|
||||||
pushMessage(MsgType::EnumeratePlayback).wait();
|
pushMessage(MsgType::EnumeratePlayback).wait();
|
||||||
|
|
||||||
hr = E_FAIL;
|
if(SUCCEEDED(mOpenStatus))
|
||||||
|
hr = pushMessage(MsgType::ReopenDevice, name).get();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hr = pushMessage(MsgType::OpenDevice, name).get();
|
||||||
|
mOpenStatus = hr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(FAILED(hr))
|
||||||
|
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
|
||||||
|
hr};
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT WasapiPlayback::openProxy(const char *name)
|
||||||
|
{
|
||||||
|
const wchar_t *devid{nullptr};
|
||||||
|
if(name)
|
||||||
|
{
|
||||||
auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
|
auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(),
|
||||||
[name](const DevMap &entry) -> bool
|
[name](const DevMap &entry) -> bool
|
||||||
{ return entry.name == name || entry.endpoint_guid == name; });
|
{ return entry.name == name || entry.endpoint_guid == name; });
|
||||||
|
@ -822,57 +784,36 @@ void WasapiPlayback::open(const char *name)
|
||||||
{ return entry.devid == wname; });
|
{ return entry.devid == wname; });
|
||||||
}
|
}
|
||||||
if(iter == PlaybackDevices.cend())
|
if(iter == PlaybackDevices.cend())
|
||||||
|
{
|
||||||
WARN("Failed to find device name matching \"%s\"\n", name);
|
WARN("Failed to find device name matching \"%s\"\n", name);
|
||||||
else
|
return E_FAIL;
|
||||||
{
|
|
||||||
mDevId = iter->devid;
|
|
||||||
mDevice->DeviceName = iter->name;
|
|
||||||
hr = S_OK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
name = iter->name.c_str();
|
||||||
|
devid = iter->devid.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SUCCEEDED(hr))
|
|
||||||
hr = pushMessage(MsgType::OpenDevice).get();
|
|
||||||
mOpenStatus = hr;
|
|
||||||
|
|
||||||
if(FAILED(hr))
|
|
||||||
{
|
|
||||||
if(mNotifyEvent != nullptr)
|
|
||||||
CloseHandle(mNotifyEvent);
|
|
||||||
mNotifyEvent = nullptr;
|
|
||||||
|
|
||||||
mDevId.clear();
|
|
||||||
|
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
|
|
||||||
hr};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT WasapiPlayback::openProxy()
|
|
||||||
{
|
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
ComPtr<IMMDevice> mmdev;
|
||||||
HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
|
HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
|
||||||
IID_IMMDeviceEnumerator, &ptr)};
|
IID_IMMDeviceEnumerator, &ptr)};
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
|
ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
|
||||||
if(mDevId.empty())
|
if(!devid)
|
||||||
hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, mMMDev.getPtr());
|
hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, mmdev.getPtr());
|
||||||
else
|
else
|
||||||
hr = enumerator->GetDevice(mDevId.c_str(), mMMDev.getPtr());
|
hr = enumerator->GetDevice(devid, mmdev.getPtr());
|
||||||
}
|
}
|
||||||
if(SUCCEEDED(hr))
|
if(FAILED(hr))
|
||||||
hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
|
|
||||||
if(SUCCEEDED(hr))
|
|
||||||
{
|
{
|
||||||
mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)};
|
WARN("Failed to open device \"%s\"\n", name?name:"(default)");
|
||||||
if(mDevice->DeviceName.empty())
|
return hr;
|
||||||
mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(FAILED(hr))
|
mClient = nullptr;
|
||||||
mMMDev = nullptr;
|
mMMDev = std::move(mmdev);
|
||||||
|
if(name) mDevice->DeviceName = name;
|
||||||
|
else mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first;
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
@ -1105,8 +1046,7 @@ HRESULT WasapiPlayback::resetProxy()
|
||||||
}
|
}
|
||||||
mFrameStep = OutputType.Format.nChannels;
|
mFrameStep = OutputType.Format.nChannels;
|
||||||
|
|
||||||
EndpointFormFactor formfactor{UnknownFormFactor};
|
const EndpointFormFactor formfactor{get_device_formfactor(mMMDev.get())};
|
||||||
get_device_formfactor(mMMDev.get(), &formfactor);
|
|
||||||
mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo
|
mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo
|
||||||
&& (formfactor == Headphones || formfactor == Headset));
|
&& (formfactor == Headphones || formfactor == Headset));
|
||||||
|
|
||||||
|
@ -1220,13 +1160,13 @@ ClockLatency WasapiPlayback::getClockLatency()
|
||||||
|
|
||||||
|
|
||||||
struct WasapiCapture final : public BackendBase, WasapiProxy {
|
struct WasapiCapture final : public BackendBase, WasapiProxy {
|
||||||
WasapiCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
WasapiCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~WasapiCapture() override;
|
~WasapiCapture() override;
|
||||||
|
|
||||||
int recordProc();
|
int recordProc();
|
||||||
|
|
||||||
void open(const char *name) override;
|
void open(const char *name) override;
|
||||||
HRESULT openProxy() override;
|
HRESULT openProxy(const char *name) override;
|
||||||
void closeProxy() override;
|
void closeProxy() override;
|
||||||
|
|
||||||
HRESULT resetProxy() override;
|
HRESULT resetProxy() override;
|
||||||
|
@ -1238,8 +1178,6 @@ struct WasapiCapture final : public BackendBase, WasapiProxy {
|
||||||
void captureSamples(al::byte *buffer, uint samples) override;
|
void captureSamples(al::byte *buffer, uint samples) override;
|
||||||
uint availableSamples() override;
|
uint availableSamples() override;
|
||||||
|
|
||||||
std::wstring mDevId;
|
|
||||||
|
|
||||||
HRESULT mOpenStatus{E_FAIL};
|
HRESULT mOpenStatus{E_FAIL};
|
||||||
ComPtr<IMMDevice> mMMDev{nullptr};
|
ComPtr<IMMDevice> mMMDev{nullptr};
|
||||||
ComPtr<IAudioClient> mClient{nullptr};
|
ComPtr<IAudioClient> mClient{nullptr};
|
||||||
|
@ -1373,12 +1311,30 @@ void WasapiCapture::open(const char *name)
|
||||||
|
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
|
if(name && CaptureDevices.empty())
|
||||||
|
pushMessage(MsgType::EnumerateCapture).wait();
|
||||||
|
hr = pushMessage(MsgType::OpenDevice, name).get();
|
||||||
|
}
|
||||||
|
mOpenStatus = hr;
|
||||||
|
|
||||||
|
if(FAILED(hr))
|
||||||
|
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
|
||||||
|
hr};
|
||||||
|
|
||||||
|
hr = pushMessage(MsgType::ResetDevice).get();
|
||||||
|
if(FAILED(hr))
|
||||||
|
{
|
||||||
|
if(hr == E_OUTOFMEMORY)
|
||||||
|
throw al::backend_exception{al::backend_error::OutOfMemory, "Out of memory"};
|
||||||
|
throw al::backend_exception{al::backend_error::DeviceError, "Device reset failed"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT WasapiCapture::openProxy(const char *name)
|
||||||
|
{
|
||||||
|
const wchar_t *devid{nullptr};
|
||||||
if(name)
|
if(name)
|
||||||
{
|
{
|
||||||
if(CaptureDevices.empty())
|
|
||||||
pushMessage(MsgType::EnumerateCapture).wait();
|
|
||||||
|
|
||||||
hr = E_FAIL;
|
|
||||||
auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
|
auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
|
||||||
[name](const DevMap &entry) -> bool
|
[name](const DevMap &entry) -> bool
|
||||||
{ return entry.name == name || entry.endpoint_guid == name; });
|
{ return entry.name == name || entry.endpoint_guid == name; });
|
||||||
|
@ -1390,65 +1346,34 @@ void WasapiCapture::open(const char *name)
|
||||||
{ return entry.devid == wname; });
|
{ return entry.devid == wname; });
|
||||||
}
|
}
|
||||||
if(iter == CaptureDevices.cend())
|
if(iter == CaptureDevices.cend())
|
||||||
|
{
|
||||||
WARN("Failed to find device name matching \"%s\"\n", name);
|
WARN("Failed to find device name matching \"%s\"\n", name);
|
||||||
else
|
return E_FAIL;
|
||||||
{
|
|
||||||
mDevId = iter->devid;
|
|
||||||
mDevice->DeviceName = iter->name;
|
|
||||||
hr = S_OK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
name = iter->name.c_str();
|
||||||
|
devid = iter->devid.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SUCCEEDED(hr))
|
|
||||||
hr = pushMessage(MsgType::OpenDevice).get();
|
|
||||||
mOpenStatus = hr;
|
|
||||||
|
|
||||||
if(FAILED(hr))
|
|
||||||
{
|
|
||||||
if(mNotifyEvent != nullptr)
|
|
||||||
CloseHandle(mNotifyEvent);
|
|
||||||
mNotifyEvent = nullptr;
|
|
||||||
|
|
||||||
mDevId.clear();
|
|
||||||
|
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
|
|
||||||
hr};
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = pushMessage(MsgType::ResetDevice).get();
|
|
||||||
if(FAILED(hr))
|
|
||||||
{
|
|
||||||
if(hr == E_OUTOFMEMORY)
|
|
||||||
throw al::backend_exception{al::backend_error::OutOfMemory, "Out of memory"};
|
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "Device reset failed"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT WasapiCapture::openProxy()
|
|
||||||
{
|
|
||||||
void *ptr;
|
void *ptr;
|
||||||
HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
|
HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
|
||||||
IID_IMMDeviceEnumerator, &ptr)};
|
IID_IMMDeviceEnumerator, &ptr)};
|
||||||
if(SUCCEEDED(hr))
|
if(SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
|
ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
|
||||||
if(mDevId.empty())
|
if(!devid)
|
||||||
hr = enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, mMMDev.getPtr());
|
hr = enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, mMMDev.getPtr());
|
||||||
else
|
else
|
||||||
hr = enumerator->GetDevice(mDevId.c_str(), mMMDev.getPtr());
|
hr = enumerator->GetDevice(devid, mMMDev.getPtr());
|
||||||
}
|
}
|
||||||
if(SUCCEEDED(hr))
|
if(FAILED(hr))
|
||||||
hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
|
|
||||||
if(SUCCEEDED(hr))
|
|
||||||
{
|
{
|
||||||
mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)};
|
WARN("Failed to open device \"%s\"\n", name?name:"(default)");
|
||||||
if(mDevice->DeviceName.empty())
|
return hr;
|
||||||
mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(FAILED(hr))
|
mClient = nullptr;
|
||||||
mMMDev = nullptr;
|
if(name) mDevice->DeviceName = name;
|
||||||
|
else mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first;
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
@ -1567,7 +1492,7 @@ HRESULT WasapiCapture::resetProxy()
|
||||||
CoTaskMemFree(wfx);
|
CoTaskMemFree(wfx);
|
||||||
wfx = nullptr;
|
wfx = nullptr;
|
||||||
|
|
||||||
auto validate_fmt = [](ALCdevice *device, uint32_t chancount, DWORD chanmask) noexcept
|
auto validate_fmt = [](DeviceBase *device, uint32_t chancount, DWORD chanmask) noexcept
|
||||||
-> bool
|
-> bool
|
||||||
{
|
{
|
||||||
switch(device->FmtChans)
|
switch(device->FmtChans)
|
||||||
|
@ -1832,7 +1757,7 @@ std::string WasapiBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr WasapiBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr WasapiBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new WasapiPlayback{device}};
|
return BackendPtr{new WasapiPlayback{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_WASAPI_H
|
#ifndef BACKENDS_WASAPI_H
|
||||||
#define BACKENDS_WASAPI_H
|
#define BACKENDS_WASAPI_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct WasapiBackendFactory final : public BackendFactory {
|
struct WasapiBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/wave.h"
|
#include "wave.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
@ -35,12 +35,11 @@
|
||||||
|
|
||||||
#include "albit.h"
|
#include "albit.h"
|
||||||
#include "albyte.h"
|
#include "albyte.h"
|
||||||
#include "alcmain.h"
|
#include "alc/alconfig.h"
|
||||||
#include "alconfig.h"
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "alu.h"
|
#include "core/device.h"
|
||||||
#include "compat.h"
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
|
@ -93,7 +92,7 @@ void fwrite32le(uint val, FILE *f)
|
||||||
|
|
||||||
|
|
||||||
struct WaveBackend final : public BackendBase {
|
struct WaveBackend final : public BackendBase {
|
||||||
WaveBackend(ALCdevice *device) noexcept : BackendBase{device} { }
|
WaveBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~WaveBackend() override;
|
~WaveBackend() override;
|
||||||
|
|
||||||
int mixerProc();
|
int mixerProc();
|
||||||
|
@ -132,8 +131,8 @@ int WaveBackend::mixerProc()
|
||||||
|
|
||||||
int64_t done{0};
|
int64_t done{0};
|
||||||
auto start = std::chrono::steady_clock::now();
|
auto start = std::chrono::steady_clock::now();
|
||||||
while(!mKillNow.load(std::memory_order_acquire) &&
|
while(!mKillNow.load(std::memory_order_acquire)
|
||||||
mDevice->Connected.load(std::memory_order_acquire))
|
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||||
{
|
{
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
@ -214,9 +213,12 @@ void WaveBackend::open(const char *name)
|
||||||
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
|
||||||
name};
|
name};
|
||||||
|
|
||||||
|
/* There's only one "device", so if it's already open, we're done. */
|
||||||
|
if(mFile) return;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
{
|
{
|
||||||
std::wstring wname = utf8_to_wstr(fname);
|
std::wstring wname{utf8_to_wstr(fname)};
|
||||||
mFile = _wfopen(wname.c_str(), L"wb");
|
mFile = _wfopen(wname.c_str(), L"wb");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -392,7 +394,7 @@ std::string WaveBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr WaveBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr WaveBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new WaveBackend{device}};
|
return BackendPtr{new WaveBackend{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_WAVE_H
|
#ifndef BACKENDS_WAVE_H
|
||||||
#define BACKENDS_WAVE_H
|
#define BACKENDS_WAVE_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct WaveBackendFactory final : public BackendFactory {
|
struct WaveBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "backends/winmm.h"
|
#include "winmm.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -38,9 +38,9 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alnumeric.h"
|
||||||
#include "alu.h"
|
#include "core/device.h"
|
||||||
#include "compat.h"
|
#include "core/helpers.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
#include "strutils.h"
|
#include "strutils.h"
|
||||||
|
@ -125,7 +125,7 @@ void ProbeCaptureDevices(void)
|
||||||
|
|
||||||
|
|
||||||
struct WinMMPlayback final : public BackendBase {
|
struct WinMMPlayback final : public BackendBase {
|
||||||
WinMMPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
WinMMPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~WinMMPlayback() override;
|
~WinMMPlayback() override;
|
||||||
|
|
||||||
void CALLBACK waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR param1, DWORD_PTR param2) noexcept;
|
void CALLBACK waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR param1, DWORD_PTR param2) noexcept;
|
||||||
|
@ -224,27 +224,28 @@ void WinMMPlayback::open(const char *name)
|
||||||
auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
|
auto DeviceID = static_cast<UINT>(std::distance(PlaybackDevices.cbegin(), iter));
|
||||||
|
|
||||||
retry_open:
|
retry_open:
|
||||||
mFormat = WAVEFORMATEX{};
|
WAVEFORMATEX format{};
|
||||||
if(mDevice->FmtType == DevFmtFloat)
|
if(mDevice->FmtType == DevFmtFloat)
|
||||||
{
|
{
|
||||||
mFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||||
mFormat.wBitsPerSample = 32;
|
format.wBitsPerSample = 32;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mFormat.wFormatTag = WAVE_FORMAT_PCM;
|
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||||
if(mDevice->FmtType == DevFmtUByte || mDevice->FmtType == DevFmtByte)
|
if(mDevice->FmtType == DevFmtUByte || mDevice->FmtType == DevFmtByte)
|
||||||
mFormat.wBitsPerSample = 8;
|
format.wBitsPerSample = 8;
|
||||||
else
|
else
|
||||||
mFormat.wBitsPerSample = 16;
|
format.wBitsPerSample = 16;
|
||||||
}
|
}
|
||||||
mFormat.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||||
mFormat.nBlockAlign = static_cast<WORD>(mFormat.wBitsPerSample * mFormat.nChannels / 8);
|
format.nBlockAlign = static_cast<WORD>(format.wBitsPerSample * format.nChannels / 8);
|
||||||
mFormat.nSamplesPerSec = mDevice->Frequency;
|
format.nSamplesPerSec = mDevice->Frequency;
|
||||||
mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
|
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
||||||
mFormat.cbSize = 0;
|
format.cbSize = 0;
|
||||||
|
|
||||||
MMRESULT res{waveOutOpen(&mOutHdl, DeviceID, &mFormat,
|
HWAVEOUT outHandle{};
|
||||||
|
MMRESULT res{waveOutOpen(&outHandle, DeviceID, &format,
|
||||||
reinterpret_cast<DWORD_PTR>(&WinMMPlayback::waveOutProcC),
|
reinterpret_cast<DWORD_PTR>(&WinMMPlayback::waveOutProcC),
|
||||||
reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
|
reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
|
||||||
if(res != MMSYSERR_NOERROR)
|
if(res != MMSYSERR_NOERROR)
|
||||||
|
@ -257,6 +258,11 @@ retry_open:
|
||||||
throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: %u", res};
|
throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: %u", res};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(mOutHdl)
|
||||||
|
waveOutClose(mOutHdl);
|
||||||
|
mOutHdl = outHandle;
|
||||||
|
mFormat = format;
|
||||||
|
|
||||||
mDevice->DeviceName = PlaybackDevices[DeviceID];
|
mDevice->DeviceName = PlaybackDevices[DeviceID];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,7 +370,7 @@ void WinMMPlayback::stop()
|
||||||
|
|
||||||
|
|
||||||
struct WinMMCapture final : public BackendBase {
|
struct WinMMCapture final : public BackendBase {
|
||||||
WinMMCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
WinMMCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||||
~WinMMCapture() override;
|
~WinMMCapture() override;
|
||||||
|
|
||||||
void CALLBACK waveInProc(HWAVEIN device, UINT msg, DWORD_PTR param1, DWORD_PTR param2) noexcept;
|
void CALLBACK waveInProc(HWAVEIN device, UINT msg, DWORD_PTR param1, DWORD_PTR param2) noexcept;
|
||||||
|
@ -615,7 +621,7 @@ std::string WinMMBackendFactory::probe(BackendType type)
|
||||||
return outnames;
|
return outnames;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendPtr WinMMBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
BackendPtr WinMMBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||||
{
|
{
|
||||||
if(type == BackendType::Playback)
|
if(type == BackendType::Playback)
|
||||||
return BackendPtr{new WinMMPlayback{device}};
|
return BackendPtr{new WinMMPlayback{device}};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BACKENDS_WINMM_H
|
#ifndef BACKENDS_WINMM_H
|
||||||
#define BACKENDS_WINMM_H
|
#define BACKENDS_WINMM_H
|
||||||
|
|
||||||
#include "backends/base.h"
|
#include "base.h"
|
||||||
|
|
||||||
struct WinMMBackendFactory final : public BackendFactory {
|
struct WinMMBackendFactory final : public BackendFactory {
|
||||||
public:
|
public:
|
||||||
|
@ -11,7 +11,7 @@ public:
|
||||||
|
|
||||||
std::string probe(BackendType type) override;
|
std::string probe(BackendType type) override;
|
||||||
|
|
||||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||||
|
|
||||||
static BackendFactory &getFactory();
|
static BackendFactory &getFactory();
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,387 @@
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <limits>
|
||||||
|
#include <numeric>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "AL/efx.h"
|
||||||
|
|
||||||
|
#include "al/auxeffectslot.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 "core/async_event.h"
|
||||||
|
#include "core/device.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "core/voice.h"
|
||||||
|
#include "core/voice_change.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "effectslot.h"
|
||||||
|
#include "ringbuffer.h"
|
||||||
|
#include "vecmat.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
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_STEREO_ANGLES "
|
||||||
|
"AL_LOKI_quadriphonic "
|
||||||
|
"AL_SOFT_bformat_ex "
|
||||||
|
"AL_SOFTX_bformat_hoa "
|
||||||
|
"AL_SOFT_block_alignment "
|
||||||
|
"AL_SOFTX_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_SOFTX_filter_gain_ex "
|
||||||
|
"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_SOFTX_UHJ";
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
|
||||||
|
|
||||||
|
thread_local ALCcontext *ALCcontext::sLocalContext{nullptr};
|
||||||
|
ALCcontext::ThreadCtx::~ThreadCtx()
|
||||||
|
{
|
||||||
|
if(ALCcontext *ctx{ALCcontext::sLocalContext})
|
||||||
|
{
|
||||||
|
const bool result{ctx->releaseIfNoDelete()};
|
||||||
|
ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
|
||||||
|
result ? "" : ", leak detected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
|
||||||
|
|
||||||
|
ALeffect ALCcontext::sDefaultEffect;
|
||||||
|
|
||||||
|
|
||||||
|
ContextBase::ContextBase(DeviceBase *device) : mDevice{device}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
ContextBase::~ContextBase()
|
||||||
|
{
|
||||||
|
size_t count{0};
|
||||||
|
ContextProps *cprops{mParams.ContextUpdate.exchange(nullptr, std::memory_order_relaxed)};
|
||||||
|
if(cprops)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
delete cprops;
|
||||||
|
}
|
||||||
|
cprops = mFreeContextProps.exchange(nullptr, std::memory_order_acquire);
|
||||||
|
while(cprops)
|
||||||
|
{
|
||||||
|
std::unique_ptr<ContextProps> old{cprops};
|
||||||
|
cprops = old->next.load(std::memory_order_relaxed);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
TRACE("Freed %zu context property object%s\n", count, (count==1)?"":"s");
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
EffectSlotProps *eprops{mFreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)};
|
||||||
|
while(eprops)
|
||||||
|
{
|
||||||
|
std::unique_ptr<EffectSlotProps> old{eprops};
|
||||||
|
eprops = old->next.load(std::memory_order_relaxed);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
TRACE("Freed %zu AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
|
||||||
|
|
||||||
|
if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)})
|
||||||
|
{
|
||||||
|
al::destroy_n(curarray->end(), curarray->size());
|
||||||
|
delete curarray;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
VoicePropsItem *vprops{mFreeVoiceProps.exchange(nullptr, std::memory_order_acquire)};
|
||||||
|
while(vprops)
|
||||||
|
{
|
||||||
|
std::unique_ptr<VoicePropsItem> old{vprops};
|
||||||
|
vprops = old->next.load(std::memory_order_relaxed);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
TRACE("Freed %zu voice property object%s\n", count, (count==1)?"":"s");
|
||||||
|
|
||||||
|
delete mVoices.exchange(nullptr, std::memory_order_relaxed);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
ListenerProps *lprops{mParams.ListenerUpdate.exchange(nullptr, std::memory_order_relaxed)};
|
||||||
|
if(lprops)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
delete lprops;
|
||||||
|
}
|
||||||
|
lprops = mFreeListenerProps.exchange(nullptr, std::memory_order_acquire);
|
||||||
|
while(lprops)
|
||||||
|
{
|
||||||
|
std::unique_ptr<ListenerProps> old{lprops};
|
||||||
|
lprops = old->next.load(std::memory_order_relaxed);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
TRACE("Freed %zu listener property object%s\n", count, (count==1)?"":"s");
|
||||||
|
|
||||||
|
if(mAsyncEvents)
|
||||||
|
{
|
||||||
|
count = 0;
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
count += evt_vec.second.len;
|
||||||
|
}
|
||||||
|
if(count > 0)
|
||||||
|
TRACE("Destructed %zu orphaned event%s\n", count, (count==1)?"":"s");
|
||||||
|
mAsyncEvents->readAdvance(count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextBase::allocVoiceChanges(size_t addcount)
|
||||||
|
{
|
||||||
|
constexpr size_t clustersize{128};
|
||||||
|
/* Convert element count to cluster count. */
|
||||||
|
addcount = (addcount+(clustersize-1)) / clustersize;
|
||||||
|
while(addcount)
|
||||||
|
{
|
||||||
|
VoiceChangeCluster cluster{std::make_unique<VoiceChange[]>(clustersize)};
|
||||||
|
for(size_t i{1};i < clustersize;++i)
|
||||||
|
cluster[i-1].mNext.store(std::addressof(cluster[i]), std::memory_order_relaxed);
|
||||||
|
cluster[clustersize-1].mNext.store(mVoiceChangeTail, std::memory_order_relaxed);
|
||||||
|
mVoiceChangeClusters.emplace_back(std::move(cluster));
|
||||||
|
mVoiceChangeTail = mVoiceChangeClusters.back().get();
|
||||||
|
--addcount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContextBase::allocVoices(size_t addcount)
|
||||||
|
{
|
||||||
|
constexpr size_t clustersize{32};
|
||||||
|
/* Convert element count to cluster count. */
|
||||||
|
addcount = (addcount+(clustersize-1)) / clustersize;
|
||||||
|
|
||||||
|
if(addcount >= std::numeric_limits<int>::max()/clustersize - mVoiceClusters.size())
|
||||||
|
throw std::runtime_error{"Allocating too many voices"};
|
||||||
|
const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize};
|
||||||
|
TRACE("Increasing allocated voices to %zu\n", totalcount);
|
||||||
|
|
||||||
|
auto newarray = VoiceArray::Create(totalcount);
|
||||||
|
while(addcount)
|
||||||
|
{
|
||||||
|
mVoiceClusters.emplace_back(std::make_unique<Voice[]>(clustersize));
|
||||||
|
--addcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto voice_iter = newarray->begin();
|
||||||
|
for(VoiceCluster &cluster : mVoiceClusters)
|
||||||
|
{
|
||||||
|
for(size_t i{0};i < clustersize;++i)
|
||||||
|
*(voice_iter++) = &cluster[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(auto *oldvoices = mVoices.exchange(newarray.release(), std::memory_order_acq_rel))
|
||||||
|
{
|
||||||
|
mDevice->waitForMix();
|
||||||
|
delete oldvoices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device)
|
||||||
|
: ContextBase{device.get()}, mALDevice{std::move(device)}
|
||||||
|
{
|
||||||
|
mPropsDirty.test_and_clear(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 cur, const SourceSubList &sublist) noexcept -> size_t
|
||||||
|
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
|
||||||
|
if(count > 0)
|
||||||
|
WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
|
||||||
|
mSourceList.clear();
|
||||||
|
mNumSources = 0;
|
||||||
|
|
||||||
|
mDefaultSlot = nullptr;
|
||||||
|
count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u},
|
||||||
|
[](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
|
||||||
|
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
|
||||||
|
if(count > 0)
|
||||||
|
WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
|
||||||
|
mEffectSlotList.clear();
|
||||||
|
mNumEffectSlots = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ALCcontext::init()
|
||||||
|
{
|
||||||
|
if(sDefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback)
|
||||||
|
{
|
||||||
|
mDefaultSlot = std::make_unique<ALeffectslot>();
|
||||||
|
aluInitEffectPanning(&mDefaultSlot->mSlot, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectSlotArray *auxslots;
|
||||||
|
if(!mDefaultSlot)
|
||||||
|
auxslots = EffectSlot::CreatePtrArray(0);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auxslots = EffectSlot::CreatePtrArray(1);
|
||||||
|
(*auxslots)[0] = &mDefaultSlot->mSlot;
|
||||||
|
mDefaultSlot->mState = SlotState::Playing;
|
||||||
|
}
|
||||||
|
mActiveAuxSlots.store(auxslots, std::memory_order_relaxed);
|
||||||
|
|
||||||
|
allocVoiceChanges(1);
|
||||||
|
{
|
||||||
|
VoiceChange *cur{mVoiceChangeTail};
|
||||||
|
while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
|
||||||
|
cur = next;
|
||||||
|
mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
mExtensionList = alExtList;
|
||||||
|
|
||||||
|
|
||||||
|
mParams.Matrix = alu::Matrix::Identity();
|
||||||
|
mParams.Velocity = alu::Vector{};
|
||||||
|
mParams.Gain = mListener.Gain;
|
||||||
|
mParams.MetersPerUnit = mListener.mMetersPerUnit;
|
||||||
|
mParams.DopplerFactor = mDopplerFactor;
|
||||||
|
mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity;
|
||||||
|
mParams.SourceDistanceModel = mSourceDistanceModel;
|
||||||
|
mParams.mDistanceModel = mDistanceModel;
|
||||||
|
|
||||||
|
|
||||||
|
mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false);
|
||||||
|
StartEventThrd(this);
|
||||||
|
|
||||||
|
|
||||||
|
allocVoices(256);
|
||||||
|
mActiveVoiceCount.store(64, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ALCcontext::deinit()
|
||||||
|
{
|
||||||
|
if(sLocalContext == this)
|
||||||
|
{
|
||||||
|
WARN("%p released while current on thread\n", voidp{this});
|
||||||
|
sThreadContext.set(nullptr);
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
|
||||||
|
ALCcontext *origctx{this};
|
||||||
|
if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
|
||||||
|
release();
|
||||||
|
|
||||||
|
bool ret{};
|
||||||
|
/* First make sure this context exists in the device's list. */
|
||||||
|
auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
|
||||||
|
if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
|
||||||
|
{
|
||||||
|
using ContextArray = al::FlexArray<ContextBase*>;
|
||||||
|
auto alloc_ctx_array = [](const size_t count) -> ContextArray*
|
||||||
|
{
|
||||||
|
if(count == 0) return &DeviceBase::sEmptyContextArray;
|
||||||
|
return ContextArray::Create(count).release();
|
||||||
|
};
|
||||||
|
auto *newarray = alloc_ctx_array(oldarray->size() - toremove);
|
||||||
|
|
||||||
|
/* Copy the current/old context handles to the new array, excluding the
|
||||||
|
* given context.
|
||||||
|
*/
|
||||||
|
std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
|
||||||
|
std::bind(std::not_equal_to<>{}, _1, this));
|
||||||
|
|
||||||
|
/* Store the new context array in the device. Wait for any current mix
|
||||||
|
* to finish before deleting the old array.
|
||||||
|
*/
|
||||||
|
mDevice->mContexts.store(newarray);
|
||||||
|
if(oldarray != &DeviceBase::sEmptyContextArray)
|
||||||
|
{
|
||||||
|
mDevice->waitForMix();
|
||||||
|
delete oldarray;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = !newarray->empty();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = !oldarray->empty();
|
||||||
|
|
||||||
|
StopEventThrd(this);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ALCcontext::processUpdates()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> _{mPropLock};
|
||||||
|
if(mDeferUpdates.exchange(false, std::memory_order_acq_rel))
|
||||||
|
{
|
||||||
|
/* Tell the mixer to stop applying updates, then wait for any active
|
||||||
|
* updating to finish, before providing updates.
|
||||||
|
*/
|
||||||
|
mHoldUpdates.store(true, std::memory_order_release);
|
||||||
|
while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
|
||||||
|
/* busy-wait */
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mPropsDirty.test_and_clear(std::memory_order_acq_rel))
|
||||||
|
UpdateContextProps(this);
|
||||||
|
if(mListener.mPropsDirty.test_and_clear(std::memory_order_acq_rel))
|
||||||
|
UpdateListenerProps(this);
|
||||||
|
UpdateAllEffectSlotProps(this);
|
||||||
|
UpdateAllSourceProps(this);
|
||||||
|
|
||||||
|
/* Now with all updates declared, let the mixer continue applying them
|
||||||
|
* so they all happen at once.
|
||||||
|
*/
|
||||||
|
mHoldUpdates.store(false, std::memory_order_release);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
#ifndef ALC_CONTEXT_H
|
||||||
|
#define ALC_CONTEXT_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "AL/al.h"
|
||||||
|
#include "AL/alc.h"
|
||||||
|
#include "AL/alext.h"
|
||||||
|
|
||||||
|
#include "al/listener.h"
|
||||||
|
#include "almalloc.h"
|
||||||
|
#include "alnumeric.h"
|
||||||
|
#include "atomic.h"
|
||||||
|
#include "core/context.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
|
#include "vector.h"
|
||||||
|
|
||||||
|
struct ALeffect;
|
||||||
|
struct ALeffectslot;
|
||||||
|
struct ALsource;
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
|
|
||||||
|
|
||||||
|
struct SourceSubList {
|
||||||
|
uint64_t FreeMask{~0_u64};
|
||||||
|
ALsource *Sources{nullptr}; /* 64 */
|
||||||
|
|
||||||
|
SourceSubList() noexcept = default;
|
||||||
|
SourceSubList(const SourceSubList&) = delete;
|
||||||
|
SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
|
||||||
|
{ rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
|
||||||
|
~SourceSubList();
|
||||||
|
|
||||||
|
SourceSubList& operator=(const SourceSubList&) = delete;
|
||||||
|
SourceSubList& operator=(SourceSubList&& rhs) noexcept
|
||||||
|
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EffectSlotSubList {
|
||||||
|
uint64_t FreeMask{~0_u64};
|
||||||
|
ALeffectslot *EffectSlots{nullptr}; /* 64 */
|
||||||
|
|
||||||
|
EffectSlotSubList() noexcept = default;
|
||||||
|
EffectSlotSubList(const EffectSlotSubList&) = delete;
|
||||||
|
EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
|
||||||
|
: FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
|
||||||
|
{ rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
|
||||||
|
~EffectSlotSubList();
|
||||||
|
|
||||||
|
EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
|
||||||
|
EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
|
||||||
|
{ std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
|
||||||
|
const al::intrusive_ptr<ALCdevice> mALDevice;
|
||||||
|
|
||||||
|
/* Wet buffers used by effect slots. */
|
||||||
|
al::vector<WetBufferPtr> mWetBuffers;
|
||||||
|
|
||||||
|
|
||||||
|
al::atomic_invflag mPropsDirty;
|
||||||
|
std::atomic<bool> mDeferUpdates{false};
|
||||||
|
|
||||||
|
std::mutex mPropLock;
|
||||||
|
|
||||||
|
std::atomic<ALenum> mLastError{AL_NO_ERROR};
|
||||||
|
|
||||||
|
DistanceModel mDistanceModel{DistanceModel::Default};
|
||||||
|
bool mSourceDistanceModel{false};
|
||||||
|
|
||||||
|
float mDopplerFactor{1.0f};
|
||||||
|
float mDopplerVelocity{1.0f};
|
||||||
|
float mSpeedOfSound{SpeedOfSoundMetersPerSec};
|
||||||
|
|
||||||
|
std::mutex mEventCbLock;
|
||||||
|
ALEVENTPROCSOFT mEventCb{};
|
||||||
|
void *mEventParam{nullptr};
|
||||||
|
|
||||||
|
ALlistener mListener{};
|
||||||
|
|
||||||
|
al::vector<SourceSubList> mSourceList;
|
||||||
|
ALuint mNumSources{0};
|
||||||
|
std::mutex mSourceLock;
|
||||||
|
|
||||||
|
al::vector<EffectSlotSubList> mEffectSlotList;
|
||||||
|
ALuint mNumEffectSlots{0u};
|
||||||
|
std::mutex mEffectSlotLock;
|
||||||
|
|
||||||
|
/* Default effect slot */
|
||||||
|
std::unique_ptr<ALeffectslot> mDefaultSlot;
|
||||||
|
|
||||||
|
const char *mExtensionList{nullptr};
|
||||||
|
|
||||||
|
|
||||||
|
ALCcontext(al::intrusive_ptr<ALCdevice> device);
|
||||||
|
ALCcontext(const ALCcontext&) = delete;
|
||||||
|
ALCcontext& operator=(const ALCcontext&) = delete;
|
||||||
|
~ALCcontext();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
/**
|
||||||
|
* Removes the context from its device and removes it from being current on
|
||||||
|
* the running thread or globally. Returns true if other contexts still
|
||||||
|
* exist on the device.
|
||||||
|
*/
|
||||||
|
bool deinit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defers/suspends updates for the given context's listener and sources.
|
||||||
|
* This does *NOT* stop mixing, but rather prevents certain property
|
||||||
|
* changes from taking effect.
|
||||||
|
*/
|
||||||
|
void deferUpdates() noexcept { mDeferUpdates.exchange(true, std::memory_order_acq_rel); }
|
||||||
|
|
||||||
|
/** Resumes update processing after being deferred. */
|
||||||
|
void processUpdates();
|
||||||
|
|
||||||
|
#ifdef __USE_MINGW_ANSI_STDIO
|
||||||
|
[[gnu::format(gnu_printf, 3, 4)]]
|
||||||
|
#else
|
||||||
|
[[gnu::format(printf, 3, 4)]]
|
||||||
|
#endif
|
||||||
|
void setError(ALenum errorCode, const char *msg, ...);
|
||||||
|
|
||||||
|
/* Process-wide current context */
|
||||||
|
static std::atomic<ALCcontext*> sGlobalContext;
|
||||||
|
|
||||||
|
/* Thread-local current context. */
|
||||||
|
static 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.
|
||||||
|
*/
|
||||||
|
class ThreadCtx {
|
||||||
|
public:
|
||||||
|
~ThreadCtx();
|
||||||
|
void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
|
||||||
|
};
|
||||||
|
static thread_local ThreadCtx sThreadContext;
|
||||||
|
|
||||||
|
/* Default effect that applies to sources that don't have an effect on send 0. */
|
||||||
|
static ALeffect sDefaultEffect;
|
||||||
|
|
||||||
|
DEF_NEWDEL(ALCcontext)
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SETERR_RETURN(ctx, err, retval, ...) do { \
|
||||||
|
(ctx)->setError((err), __VA_ARGS__); \
|
||||||
|
return retval; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
|
using ContextRef = al::intrusive_ptr<ALCcontext>;
|
||||||
|
|
||||||
|
ContextRef GetContextRef(void);
|
||||||
|
|
||||||
|
void UpdateContextProps(ALCcontext *context);
|
||||||
|
|
||||||
|
|
||||||
|
extern bool TrapALError;
|
||||||
|
|
||||||
|
#endif /* ALC_CONTEXT_H */
|
|
@ -0,0 +1,76 @@
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
#include <numeric>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "albit.h"
|
||||||
|
#include "alconfig.h"
|
||||||
|
#include "backends/base.h"
|
||||||
|
#include "core/bformatdec.h"
|
||||||
|
#include "core/bs2b.h"
|
||||||
|
#include "core/front_stablizer.h"
|
||||||
|
#include "core/hrtf.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "core/mastering.h"
|
||||||
|
#include "core/uhjfilter.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using voidp = void*;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
/* This should be in core/device.cpp. */
|
||||||
|
DeviceBase::DeviceBase(DeviceType type) : Type{type}, mContexts{&sEmptyContextArray}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceBase::~DeviceBase()
|
||||||
|
{
|
||||||
|
auto *oldarray = mContexts.exchange(nullptr, std::memory_order_relaxed);
|
||||||
|
if(oldarray != &sEmptyContextArray) delete oldarray;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ALCdevice::~ALCdevice()
|
||||||
|
{
|
||||||
|
TRACE("Freeing device %p\n", voidp{this});
|
||||||
|
|
||||||
|
Backend = nullptr;
|
||||||
|
|
||||||
|
size_t count{std::accumulate(BufferList.cbegin(), BufferList.cend(), size_t{0u},
|
||||||
|
[](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},
|
||||||
|
[](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},
|
||||||
|
[](size_t cur, const FilterSubList &sublist) noexcept -> size_t
|
||||||
|
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
|
||||||
|
if(count > 0)
|
||||||
|
WARN("%zu Filter%s not deleted\n", count, (count==1)?"":"s");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ALCdevice::enumerateHrtfs()
|
||||||
|
{
|
||||||
|
mHrtfList = EnumerateHrtf(ConfigValueStr(DeviceName.c_str(), nullptr, "hrtf-paths"));
|
||||||
|
if(auto defhrtfopt = ConfigValueStr(DeviceName.c_str(), nullptr, "default-hrtf"))
|
||||||
|
{
|
||||||
|
auto iter = std::find(mHrtfList.begin(), mHrtfList.end(), *defhrtfopt);
|
||||||
|
if(iter == mHrtfList.end())
|
||||||
|
WARN("Failed to find default HRTF \"%s\"\n", defhrtfopt->c_str());
|
||||||
|
else if(iter != mHrtfList.begin())
|
||||||
|
std::rotate(mHrtfList.begin(), iter, iter+1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
#ifndef ALC_DEVICE_H
|
||||||
|
#define ALC_DEVICE_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "AL/alc.h"
|
||||||
|
#include "AL/alext.h"
|
||||||
|
|
||||||
|
#include "almalloc.h"
|
||||||
|
#include "alnumeric.h"
|
||||||
|
#include "core/device.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
|
#include "vector.h"
|
||||||
|
|
||||||
|
struct ALbuffer;
|
||||||
|
struct ALeffect;
|
||||||
|
struct ALfilter;
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
|
|
||||||
|
|
||||||
|
struct BufferSubList {
|
||||||
|
uint64_t FreeMask{~0_u64};
|
||||||
|
ALbuffer *Buffers{nullptr}; /* 64 */
|
||||||
|
|
||||||
|
BufferSubList() noexcept = default;
|
||||||
|
BufferSubList(const BufferSubList&) = delete;
|
||||||
|
BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers}
|
||||||
|
{ rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; }
|
||||||
|
~BufferSubList();
|
||||||
|
|
||||||
|
BufferSubList& operator=(const BufferSubList&) = delete;
|
||||||
|
BufferSubList& operator=(BufferSubList&& rhs) noexcept
|
||||||
|
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EffectSubList {
|
||||||
|
uint64_t FreeMask{~0_u64};
|
||||||
|
ALeffect *Effects{nullptr}; /* 64 */
|
||||||
|
|
||||||
|
EffectSubList() noexcept = default;
|
||||||
|
EffectSubList(const EffectSubList&) = delete;
|
||||||
|
EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects}
|
||||||
|
{ rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; }
|
||||||
|
~EffectSubList();
|
||||||
|
|
||||||
|
EffectSubList& operator=(const EffectSubList&) = delete;
|
||||||
|
EffectSubList& operator=(EffectSubList&& rhs) noexcept
|
||||||
|
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FilterSubList {
|
||||||
|
uint64_t FreeMask{~0_u64};
|
||||||
|
ALfilter *Filters{nullptr}; /* 64 */
|
||||||
|
|
||||||
|
FilterSubList() noexcept = default;
|
||||||
|
FilterSubList(const FilterSubList&) = delete;
|
||||||
|
FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters}
|
||||||
|
{ rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; }
|
||||||
|
~FilterSubList();
|
||||||
|
|
||||||
|
FilterSubList& operator=(const FilterSubList&) = delete;
|
||||||
|
FilterSubList& operator=(FilterSubList&& rhs) noexcept
|
||||||
|
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase {
|
||||||
|
ALCuint NumMonoSources{};
|
||||||
|
ALCuint NumStereoSources{};
|
||||||
|
|
||||||
|
// Maximum number of sources that can be created
|
||||||
|
uint SourcesMax{};
|
||||||
|
// Maximum number of slots that can be created
|
||||||
|
uint AuxiliaryEffectSlotMax{};
|
||||||
|
|
||||||
|
std::string mHrtfName;
|
||||||
|
al::vector<std::string> mHrtfList;
|
||||||
|
ALCenum mHrtfStatus{ALC_FALSE};
|
||||||
|
|
||||||
|
ALCenum LimiterState{ALC_DONT_CARE_SOFT};
|
||||||
|
|
||||||
|
std::atomic<ALCenum> LastError{ALC_NO_ERROR};
|
||||||
|
|
||||||
|
// Map of Buffers for this device
|
||||||
|
std::mutex BufferLock;
|
||||||
|
al::vector<BufferSubList> BufferList;
|
||||||
|
|
||||||
|
// Map of Effects for this device
|
||||||
|
std::mutex EffectLock;
|
||||||
|
al::vector<EffectSubList> EffectList;
|
||||||
|
|
||||||
|
// Map of Filters for this device
|
||||||
|
std::mutex FilterLock;
|
||||||
|
al::vector<FilterSubList> FilterList;
|
||||||
|
|
||||||
|
|
||||||
|
ALCdevice(DeviceType type) : DeviceBase{type} { }
|
||||||
|
~ALCdevice();
|
||||||
|
|
||||||
|
void enumerateHrtfs();
|
||||||
|
|
||||||
|
DEF_NEWDEL(ALCdevice)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -20,16 +20,26 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "alc/effects/base.h"
|
||||||
|
#include "alc/effectslot.h"
|
||||||
|
#include "almalloc.h"
|
||||||
|
#include "alnumeric.h"
|
||||||
|
#include "alspan.h"
|
||||||
|
#include "core/ambidefs.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
|
#include "core/context.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
|
#include "core/mixer.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
|
#include "math_defs.h"
|
||||||
|
|
||||||
#include "alcmain.h"
|
|
||||||
#include "alcontext.h"
|
|
||||||
#include "core/filters/biquad.h"
|
|
||||||
#include "effectslot.h"
|
|
||||||
#include "vecmat.h"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -69,8 +79,8 @@ struct AutowahState final : public EffectState {
|
||||||
alignas(16) float mBufferOut[BufferLineSize];
|
alignas(16) float mBufferOut[BufferLineSize];
|
||||||
|
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -78,7 +88,7 @@ struct AutowahState final : public EffectState {
|
||||||
DEF_NEWDEL(AutowahState)
|
DEF_NEWDEL(AutowahState)
|
||||||
};
|
};
|
||||||
|
|
||||||
void AutowahState::deviceUpdate(const ALCdevice*, const Buffer&)
|
void AutowahState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||||
{
|
{
|
||||||
/* (Re-)initializing parameters and clear the buffers. */
|
/* (Re-)initializing parameters and clear the buffers. */
|
||||||
|
|
||||||
|
@ -104,10 +114,10 @@ void AutowahState::deviceUpdate(const ALCdevice*, const Buffer&)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutowahState::update(const ALCcontext *context, const EffectSlot *slot,
|
void AutowahState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
const EffectProps *props, const EffectTarget target)
|
const EffectProps *props, const EffectTarget target)
|
||||||
{
|
{
|
||||||
const ALCdevice *device{context->mDevice.get()};
|
const DeviceBase *device{context->mDevice};
|
||||||
const auto frequency = static_cast<float>(device->Frequency);
|
const auto frequency = static_cast<float>(device->Frequency);
|
||||||
|
|
||||||
const float ReleaseTime{clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f)};
|
const float ReleaseTime{clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f)};
|
||||||
|
|
|
@ -1,24 +1,37 @@
|
||||||
#ifndef EFFECTS_BASE_H
|
#ifndef EFFECTS_BASE_H
|
||||||
#define EFFECTS_BASE_H
|
#define EFFECTS_BASE_H
|
||||||
|
|
||||||
#include <cstddef>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include "albyte.h"
|
#include "albyte.h"
|
||||||
#include "alcmain.h"
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alspan.h"
|
#include "alspan.h"
|
||||||
#include "atomic.h"
|
#include "atomic.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
#include "intrusive_ptr.h"
|
#include "intrusive_ptr.h"
|
||||||
|
|
||||||
struct EffectSlot;
|
|
||||||
struct BufferStorage;
|
struct BufferStorage;
|
||||||
|
struct ContextBase;
|
||||||
|
struct DeviceBase;
|
||||||
|
struct EffectSlot;
|
||||||
|
struct MixParams;
|
||||||
|
struct RealMixParams;
|
||||||
|
|
||||||
|
|
||||||
|
/** Target gain for the reverb decay feedback reaching the decay time. */
|
||||||
|
constexpr float ReverbDecayGain{0.001f}; /* -60 dB */
|
||||||
|
|
||||||
|
constexpr float ReverbMaxReflectionsDelay{0.3f};
|
||||||
|
constexpr float ReverbMaxLateReverbDelay{0.1f};
|
||||||
|
|
||||||
enum class ChorusWaveform {
|
enum class ChorusWaveform {
|
||||||
Sinusoid,
|
Sinusoid,
|
||||||
Triangle
|
Triangle
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr float ChorusMaxDelay{0.016f};
|
||||||
|
constexpr float FlangerMaxDelay{0.004f};
|
||||||
|
|
||||||
constexpr float EchoMaxDelay{0.207f};
|
constexpr float EchoMaxDelay{0.207f};
|
||||||
constexpr float EchoMaxLRDelay{0.404f};
|
constexpr float EchoMaxLRDelay{0.404f};
|
||||||
|
|
||||||
|
@ -175,8 +188,8 @@ struct EffectState : public al::intrusive_ref<EffectState> {
|
||||||
|
|
||||||
virtual ~EffectState() = default;
|
virtual ~EffectState() = default;
|
||||||
|
|
||||||
virtual void deviceUpdate(const ALCdevice *device, const Buffer &buffer) = 0;
|
virtual void deviceUpdate(const DeviceBase *device, const Buffer &buffer) = 0;
|
||||||
virtual void update(const ALCcontext *context, const EffectSlot *slot,
|
virtual void update(const ContextBase *context, const EffectSlot *slot,
|
||||||
const EffectProps *props, const EffectTarget target) = 0;
|
const EffectProps *props, const EffectTarget target) = 0;
|
||||||
virtual void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
virtual void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) = 0;
|
const al::span<FloatBufferLine> samplesOut) = 0;
|
||||||
|
|
|
@ -21,20 +21,24 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/effects/base.h"
|
||||||
#include "alcontext.h"
|
#include "alc/effectslot.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "alspan.h"
|
#include "alspan.h"
|
||||||
#include "alu.h"
|
#include "core/bufferline.h"
|
||||||
#include "core/ambidefs.h"
|
#include "core/context.h"
|
||||||
#include "effects/base.h"
|
#include "core/devformat.h"
|
||||||
#include "effectslot.h"
|
#include "core/device.h"
|
||||||
|
#include "core/mixer.h"
|
||||||
|
#include "core/mixer/defs.h"
|
||||||
|
#include "core/resampler_limits.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
#include "math_defs.h"
|
#include "math_defs.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
@ -42,6 +46,8 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
|
|
||||||
#define MAX_UPDATE_SAMPLES 256
|
#define MAX_UPDATE_SAMPLES 256
|
||||||
|
|
||||||
struct ChorusState final : public EffectState {
|
struct ChorusState final : public EffectState {
|
||||||
|
@ -68,8 +74,8 @@ struct ChorusState final : public EffectState {
|
||||||
void getTriangleDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo);
|
void getTriangleDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo);
|
||||||
void getSinusoidDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo);
|
void getSinusoidDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo);
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -77,9 +83,9 @@ struct ChorusState final : public EffectState {
|
||||||
DEF_NEWDEL(ChorusState)
|
DEF_NEWDEL(ChorusState)
|
||||||
};
|
};
|
||||||
|
|
||||||
void ChorusState::deviceUpdate(const ALCdevice *Device, const Buffer&)
|
void ChorusState::deviceUpdate(const DeviceBase *Device, const Buffer&)
|
||||||
{
|
{
|
||||||
constexpr float max_delay{maxf(AL_CHORUS_MAX_DELAY, AL_FLANGER_MAX_DELAY)};
|
constexpr float max_delay{maxf(ChorusMaxDelay, FlangerMaxDelay)};
|
||||||
|
|
||||||
const auto frequency = static_cast<float>(Device->Frequency);
|
const auto frequency = static_cast<float>(Device->Frequency);
|
||||||
const size_t maxlen{NextPowerOf2(float2uint(max_delay*2.0f*frequency) + 1u)};
|
const size_t maxlen{NextPowerOf2(float2uint(max_delay*2.0f*frequency) + 1u)};
|
||||||
|
@ -94,7 +100,7 @@ void ChorusState::deviceUpdate(const ALCdevice *Device, const Buffer&)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChorusState::update(const ALCcontext *Context, const EffectSlot *Slot,
|
void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot,
|
||||||
const EffectProps *props, const EffectTarget target)
|
const EffectProps *props, const EffectTarget target)
|
||||||
{
|
{
|
||||||
constexpr int mindelay{(MaxResamplerPadding>>1) << MixerFracBits};
|
constexpr int mindelay{(MaxResamplerPadding>>1) << MixerFracBits};
|
||||||
|
@ -102,7 +108,7 @@ void ChorusState::update(const ALCcontext *Context, const EffectSlot *Slot,
|
||||||
/* The LFO depth is scaled to be relative to the sample delay. Clamp the
|
/* The LFO depth is scaled to be relative to the sample delay. Clamp the
|
||||||
* delay and depth to allow enough padding for resampling.
|
* delay and depth to allow enough padding for resampling.
|
||||||
*/
|
*/
|
||||||
const ALCdevice *device{Context->mDevice.get()};
|
const DeviceBase *device{Context->mDevice};
|
||||||
const auto frequency = static_cast<float>(device->Frequency);
|
const auto frequency = static_cast<float>(device->Frequency);
|
||||||
|
|
||||||
mWaveform = props->Chorus.Waveform;
|
mWaveform = props->Chorus.Waveform;
|
||||||
|
@ -247,7 +253,7 @@ void ChorusState::process(const size_t samplesToDo, const al::span<const FloatBu
|
||||||
++offset;
|
++offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(ALsizei c{0};c < 2;++c)
|
for(size_t c{0};c < 2;++c)
|
||||||
MixSamples({temps[c], todo}, samplesOut, mGains[c].Current, mGains[c].Target,
|
MixSamples({temps[c], todo}, samplesOut, mGains[c].Current, mGains[c].Target,
|
||||||
samplesToDo-base, base);
|
samplesToDo-base, base);
|
||||||
|
|
||||||
|
|
|
@ -1,32 +1,56 @@
|
||||||
/**
|
/**
|
||||||
* OpenAL cross platform audio library
|
* This file is part of the OpenAL Soft cross platform audio library
|
||||||
|
*
|
||||||
* Copyright (C) 2013 by Anis A. Hireche
|
* Copyright (C) 2013 by Anis A. Hireche
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* modification, are permitted provided that the following conditions are met:
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Library General Public
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
* License along with this library; if not, write to the
|
* this list of conditions and the following disclaimer.
|
||||||
* Free Software Foundation, Inc.,
|
*
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of Spherical-Harmonic-Transform nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/effects/base.h"
|
||||||
#include "alcontext.h"
|
#include "alc/effectslot.h"
|
||||||
#include "alu.h"
|
#include "almalloc.h"
|
||||||
#include "effectslot.h"
|
#include "alnumeric.h"
|
||||||
#include "vecmat.h"
|
#include "alspan.h"
|
||||||
|
#include "core/ambidefs.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
|
#include "core/mixer.h"
|
||||||
|
#include "core/mixer/defs.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
|
|
||||||
|
struct ContextBase;
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -49,8 +73,8 @@ struct CompressorState final : public EffectState {
|
||||||
float mEnvFollower{1.0f};
|
float mEnvFollower{1.0f};
|
||||||
|
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -58,7 +82,7 @@ struct CompressorState final : public EffectState {
|
||||||
DEF_NEWDEL(CompressorState)
|
DEF_NEWDEL(CompressorState)
|
||||||
};
|
};
|
||||||
|
|
||||||
void CompressorState::deviceUpdate(const ALCdevice *device, const Buffer&)
|
void CompressorState::deviceUpdate(const DeviceBase *device, const Buffer&)
|
||||||
{
|
{
|
||||||
/* Number of samples to do a full attack and release (non-integer sample
|
/* Number of samples to do a full attack and release (non-integer sample
|
||||||
* counts are okay).
|
* counts are okay).
|
||||||
|
@ -73,7 +97,7 @@ void CompressorState::deviceUpdate(const ALCdevice *device, const Buffer&)
|
||||||
mReleaseMult = std::pow(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
|
mReleaseMult = std::pow(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompressorState::update(const ALCcontext*, const EffectSlot *slot,
|
void CompressorState::update(const ContextBase*, const EffectSlot *slot,
|
||||||
const EffectProps *props, const EffectTarget target)
|
const EffectProps *props, const EffectTarget target)
|
||||||
{
|
{
|
||||||
mEnabled = props->Compressor.OnOff;
|
mEnabled = props->Compressor.OnOff;
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <complex>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#ifdef HAVE_SSE_INTRINSICS
|
#ifdef HAVE_SSE_INTRINSICS
|
||||||
#include <xmmintrin.h>
|
#include <xmmintrin.h>
|
||||||
|
@ -9,21 +17,26 @@
|
||||||
#include <arm_neon.h>
|
#include <arm_neon.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "albyte.h"
|
||||||
#include "alcomplex.h"
|
#include "alcomplex.h"
|
||||||
#include "alcontext.h"
|
#include "alc/effectslot.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
|
#include "alnumeric.h"
|
||||||
#include "alspan.h"
|
#include "alspan.h"
|
||||||
#include "bformatdec.h"
|
#include "base.h"
|
||||||
#include "buffer_storage.h"
|
|
||||||
#include "core/ambidefs.h"
|
#include "core/ambidefs.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
|
#include "core/buffer_storage.h"
|
||||||
|
#include "core/context.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
#include "core/filters/splitter.h"
|
#include "core/filters/splitter.h"
|
||||||
#include "core/fmt_traits.h"
|
#include "core/fmt_traits.h"
|
||||||
#include "core/logging.h"
|
#include "core/mixer.h"
|
||||||
#include "effects/base.h"
|
#include "intrusive_ptr.h"
|
||||||
#include "effectslot.h"
|
|
||||||
#include "math_defs.h"
|
#include "math_defs.h"
|
||||||
#include "polyphase_resampler.h"
|
#include "polyphase_resampler.h"
|
||||||
|
#include "vector.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -190,8 +203,8 @@ struct ConvolutionState final : public EffectState {
|
||||||
void (ConvolutionState::*mMix)(const al::span<FloatBufferLine>,const size_t)
|
void (ConvolutionState::*mMix)(const al::span<FloatBufferLine>,const size_t)
|
||||||
{&ConvolutionState::NormalMix};
|
{&ConvolutionState::NormalMix};
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -219,7 +232,7 @@ void ConvolutionState::UpsampleMix(const al::span<FloatBufferLine> samplesOut,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ConvolutionState::deviceUpdate(const ALCdevice *device, const Buffer &buffer)
|
void ConvolutionState::deviceUpdate(const DeviceBase *device, const Buffer &buffer)
|
||||||
{
|
{
|
||||||
constexpr uint MaxConvolveAmbiOrder{1u};
|
constexpr uint MaxConvolveAmbiOrder{1u};
|
||||||
|
|
||||||
|
@ -316,7 +329,7 @@ void ConvolutionState::deviceUpdate(const ALCdevice *device, const Buffer &buffe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ConvolutionState::update(const ALCcontext *context, const EffectSlot *slot,
|
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
|
/* NOTE: Stereo and Rear are slightly different from normal mixing (as
|
||||||
|
@ -374,13 +387,31 @@ void ConvolutionState::update(const ALCcontext *context, const EffectSlot *slot,
|
||||||
for(auto &chan : *mChans)
|
for(auto &chan : *mChans)
|
||||||
std::fill(std::begin(chan.Target), std::end(chan.Target), 0.0f);
|
std::fill(std::begin(chan.Target), std::end(chan.Target), 0.0f);
|
||||||
const float gain{slot->Gain};
|
const float gain{slot->Gain};
|
||||||
if(mChannels == FmtBFormat3D || mChannels == FmtBFormat2D)
|
/* TODO: UHJ should be decoded to B-Format and processed that way, since
|
||||||
|
* there's no telling if it can ever do a direct-out mix (even if the
|
||||||
|
* device is outputing UHJ, the effect slot can feed another effect that's
|
||||||
|
* not UHJ).
|
||||||
|
*
|
||||||
|
* Not that UHJ should really ever be used for convolution, but it's a
|
||||||
|
* valid format regardless.
|
||||||
|
*/
|
||||||
|
if((mChannels == FmtUHJ2 || mChannels == FmtUHJ3 || mChannels == FmtUHJ4) && target.RealOut
|
||||||
|
&& target.RealOut->ChannelIndex[FrontLeft] != INVALID_CHANNEL_INDEX
|
||||||
|
&& target.RealOut->ChannelIndex[FrontRight] != INVALID_CHANNEL_INDEX)
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mDevice.get()};
|
mOutTarget = target.RealOut->Buffer;
|
||||||
|
const uint lidx = target.RealOut->ChannelIndex[FrontLeft];
|
||||||
|
const uint ridx = target.RealOut->ChannelIndex[FrontRight];
|
||||||
|
(*mChans)[0].Target[lidx] = gain;
|
||||||
|
(*mChans)[1].Target[ridx] = gain;
|
||||||
|
}
|
||||||
|
else if(mChannels == FmtBFormat3D || mChannels == FmtBFormat2D)
|
||||||
|
{
|
||||||
|
DeviceBase *device{context->mDevice};
|
||||||
if(device->mAmbiOrder > mAmbiOrder)
|
if(device->mAmbiOrder > mAmbiOrder)
|
||||||
{
|
{
|
||||||
mMix = &ConvolutionState::UpsampleMix;
|
mMix = &ConvolutionState::UpsampleMix;
|
||||||
const auto scales = BFormatDec::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder);
|
const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder);
|
||||||
(*mChans)[0].mHfScale = scales[0];
|
(*mChans)[0].mHfScale = scales[0];
|
||||||
for(size_t i{1};i < mChans->size();++i)
|
for(size_t i{1};i < mChans->size();++i)
|
||||||
(*mChans)[i].mHfScale = scales[1];
|
(*mChans)[i].mHfScale = scales[1];
|
||||||
|
@ -403,7 +434,7 @@ void ConvolutionState::update(const ALCcontext *context, const EffectSlot *slot,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mDevice.get()};
|
DeviceBase *device{context->mDevice};
|
||||||
al::span<const ChanMap> chanmap{};
|
al::span<const ChanMap> chanmap{};
|
||||||
switch(mChannels)
|
switch(mChannels)
|
||||||
{
|
{
|
||||||
|
@ -416,6 +447,9 @@ void ConvolutionState::update(const ALCcontext *context, const EffectSlot *slot,
|
||||||
case FmtX71: chanmap = X71Map; break;
|
case FmtX71: chanmap = X71Map; break;
|
||||||
case FmtBFormat2D:
|
case FmtBFormat2D:
|
||||||
case FmtBFormat3D:
|
case FmtBFormat3D:
|
||||||
|
case FmtUHJ2:
|
||||||
|
case FmtUHJ3:
|
||||||
|
case FmtUHJ4:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,25 +20,35 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cmath>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/effects/base.h"
|
||||||
#include "alcontext.h"
|
#include "alc/effectslot.h"
|
||||||
#include "alu.h"
|
#include "almalloc.h"
|
||||||
#include "effectslot.h"
|
#include "alspan.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
|
#include "core/mixer.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
|
|
||||||
|
struct ContextBase;
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
|
|
||||||
struct DedicatedState final : public EffectState {
|
struct DedicatedState final : public EffectState {
|
||||||
float mCurrentGains[MAX_OUTPUT_CHANNELS];
|
float mCurrentGains[MAX_OUTPUT_CHANNELS];
|
||||||
float mTargetGains[MAX_OUTPUT_CHANNELS];
|
float mTargetGains[MAX_OUTPUT_CHANNELS];
|
||||||
|
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -46,12 +56,12 @@ struct DedicatedState final : public EffectState {
|
||||||
DEF_NEWDEL(DedicatedState)
|
DEF_NEWDEL(DedicatedState)
|
||||||
};
|
};
|
||||||
|
|
||||||
void DedicatedState::deviceUpdate(const ALCdevice*, const Buffer&)
|
void DedicatedState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||||
{
|
{
|
||||||
std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f);
|
std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DedicatedState::update(const ALCcontext*, const EffectSlot *slot,
|
void DedicatedState::update(const ContextBase*, const EffectSlot *slot,
|
||||||
const EffectProps *props, const EffectTarget target)
|
const EffectProps *props, const EffectTarget target)
|
||||||
{
|
{
|
||||||
std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
|
std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
|
||||||
|
|
|
@ -21,13 +21,24 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <array>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/effects/base.h"
|
||||||
#include "alcontext.h"
|
#include "alc/effectslot.h"
|
||||||
|
#include "almalloc.h"
|
||||||
|
#include "alnumeric.h"
|
||||||
|
#include "alspan.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
|
#include "core/context.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
#include "core/filters/biquad.h"
|
#include "core/filters/biquad.h"
|
||||||
#include "effectslot.h"
|
#include "core/mixer.h"
|
||||||
|
#include "core/mixer/defs.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
|
#include "math_defs.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -45,8 +56,8 @@ struct DistortionState final : public EffectState {
|
||||||
float mBuffer[2][BufferLineSize]{};
|
float mBuffer[2][BufferLineSize]{};
|
||||||
|
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -54,16 +65,16 @@ struct DistortionState final : public EffectState {
|
||||||
DEF_NEWDEL(DistortionState)
|
DEF_NEWDEL(DistortionState)
|
||||||
};
|
};
|
||||||
|
|
||||||
void DistortionState::deviceUpdate(const ALCdevice*, const Buffer&)
|
void DistortionState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||||
{
|
{
|
||||||
mLowpass.clear();
|
mLowpass.clear();
|
||||||
mBandpass.clear();
|
mBandpass.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DistortionState::update(const ALCcontext *context, const EffectSlot *slot,
|
void DistortionState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
const EffectProps *props, const EffectTarget target)
|
const EffectProps *props, const EffectTarget target)
|
||||||
{
|
{
|
||||||
const ALCdevice *device{context->mDevice.get()};
|
const DeviceBase *device{context->mDevice};
|
||||||
|
|
||||||
/* Store waveshaper edge settings. */
|
/* Store waveshaper edge settings. */
|
||||||
const float edge{minf(std::sin(al::MathDefs<float>::Pi()*0.5f * props->Distortion.Edge),
|
const float edge{minf(std::sin(al::MathDefs<float>::Pi()*0.5f * props->Distortion.Edge),
|
||||||
|
|
|
@ -20,20 +20,32 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iterator>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/effects/base.h"
|
||||||
#include "alcontext.h"
|
#include "alc/effectslot.h"
|
||||||
|
#include "almalloc.h"
|
||||||
|
#include "alnumeric.h"
|
||||||
|
#include "alspan.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
|
#include "core/context.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
#include "core/filters/biquad.h"
|
#include "core/filters/biquad.h"
|
||||||
#include "effectslot.h"
|
#include "core/mixer.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
|
#include "opthelpers.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
|
|
||||||
constexpr float LowpassFreqRef{5000.0f};
|
constexpr float LowpassFreqRef{5000.0f};
|
||||||
|
|
||||||
struct EchoState final : public EffectState {
|
struct EchoState final : public EffectState {
|
||||||
|
@ -57,8 +69,8 @@ struct EchoState final : public EffectState {
|
||||||
|
|
||||||
alignas(16) float mTempBuffer[2][BufferLineSize];
|
alignas(16) float mTempBuffer[2][BufferLineSize];
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -66,7 +78,7 @@ struct EchoState final : public EffectState {
|
||||||
DEF_NEWDEL(EchoState)
|
DEF_NEWDEL(EchoState)
|
||||||
};
|
};
|
||||||
|
|
||||||
void EchoState::deviceUpdate(const ALCdevice *Device, const Buffer&)
|
void EchoState::deviceUpdate(const DeviceBase *Device, const Buffer&)
|
||||||
{
|
{
|
||||||
const auto frequency = static_cast<float>(Device->Frequency);
|
const auto frequency = static_cast<float>(Device->Frequency);
|
||||||
|
|
||||||
|
@ -85,10 +97,10 @@ void EchoState::deviceUpdate(const ALCdevice *Device, const Buffer&)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EchoState::update(const ALCcontext *context, const EffectSlot *slot,
|
void EchoState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
const EffectProps *props, const EffectTarget target)
|
const EffectProps *props, const EffectTarget target)
|
||||||
{
|
{
|
||||||
const ALCdevice *device{context->mDevice.get()};
|
const DeviceBase *device{context->mDevice};
|
||||||
const auto frequency = static_cast<float>(device->Frequency);
|
const auto frequency = static_cast<float>(device->Frequency);
|
||||||
|
|
||||||
mTap[0].delay = maxu(float2uint(props->Echo.Delay*frequency + 0.5f), 1);
|
mTap[0].delay = maxu(float2uint(props->Echo.Delay*frequency + 0.5f), 1);
|
||||||
|
@ -148,7 +160,7 @@ void EchoState::process(const size_t samplesToDo, const al::span<const FloatBuff
|
||||||
mFilter.setComponents(z1, z2);
|
mFilter.setComponents(z1, z2);
|
||||||
mOffset = offset;
|
mOffset = offset;
|
||||||
|
|
||||||
for(ALsizei c{0};c < 2;c++)
|
for(size_t c{0};c < 2;c++)
|
||||||
MixSamples({mTempBuffer[c], samplesToDo}, samplesOut, mGains[c].Current, mGains[c].Target,
|
MixSamples({mTempBuffer[c], samplesToDo}, samplesOut, mGains[c].Current, mGains[c].Target,
|
||||||
samplesToDo, 0);
|
samplesToDo, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,17 +20,25 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdlib>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/effects/base.h"
|
||||||
#include "alcontext.h"
|
#include "alc/effectslot.h"
|
||||||
|
#include "almalloc.h"
|
||||||
|
#include "alspan.h"
|
||||||
|
#include "core/ambidefs.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
|
#include "core/context.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
#include "core/filters/biquad.h"
|
#include "core/filters/biquad.h"
|
||||||
#include "effectslot.h"
|
#include "core/mixer.h"
|
||||||
#include "vecmat.h"
|
#include "intrusive_ptr.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -90,8 +98,8 @@ struct EqualizerState final : public EffectState {
|
||||||
FloatBufferLine mSampleBuffer{};
|
FloatBufferLine mSampleBuffer{};
|
||||||
|
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -99,7 +107,7 @@ struct EqualizerState final : public EffectState {
|
||||||
DEF_NEWDEL(EqualizerState)
|
DEF_NEWDEL(EqualizerState)
|
||||||
};
|
};
|
||||||
|
|
||||||
void EqualizerState::deviceUpdate(const ALCdevice*, const Buffer&)
|
void EqualizerState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||||
{
|
{
|
||||||
for(auto &e : mChans)
|
for(auto &e : mChans)
|
||||||
{
|
{
|
||||||
|
@ -108,10 +116,10 @@ void EqualizerState::deviceUpdate(const ALCdevice*, const Buffer&)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EqualizerState::update(const ALCcontext *context, const EffectSlot *slot,
|
void EqualizerState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
const EffectProps *props, const EffectTarget target)
|
const EffectProps *props, const EffectTarget target)
|
||||||
{
|
{
|
||||||
const ALCdevice *device{context->mDevice.get()};
|
const DeviceBase *device{context->mDevice};
|
||||||
auto frequency = static_cast<float>(device->Frequency);
|
auto frequency = static_cast<float>(device->Frequency);
|
||||||
float gain, f0norm;
|
float gain, f0norm;
|
||||||
|
|
||||||
|
|
|
@ -20,22 +20,32 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <array>
|
|
||||||
#include <complex>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
#include <complex>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/effects/base.h"
|
||||||
|
#include "alc/effectslot.h"
|
||||||
#include "alcomplex.h"
|
#include "alcomplex.h"
|
||||||
#include "alcontext.h"
|
#include "almalloc.h"
|
||||||
#include "alu.h"
|
#include "alnumeric.h"
|
||||||
#include "effectslot.h"
|
#include "alspan.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
|
#include "core/context.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
|
#include "core/mixer.h"
|
||||||
|
#include "core/mixer/defs.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
#include "math_defs.h"
|
#include "math_defs.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
using complex_d = std::complex<double>;
|
using complex_d = std::complex<double>;
|
||||||
|
|
||||||
#define HIL_SIZE 1024
|
#define HIL_SIZE 1024
|
||||||
|
@ -84,8 +94,8 @@ struct FshifterState final : public EffectState {
|
||||||
} mGains[2];
|
} mGains[2];
|
||||||
|
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -93,7 +103,7 @@ struct FshifterState final : public EffectState {
|
||||||
DEF_NEWDEL(FshifterState)
|
DEF_NEWDEL(FshifterState)
|
||||||
};
|
};
|
||||||
|
|
||||||
void FshifterState::deviceUpdate(const ALCdevice*, const Buffer&)
|
void FshifterState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||||
{
|
{
|
||||||
/* (Re-)initializing parameters and clear the buffers. */
|
/* (Re-)initializing parameters and clear the buffers. */
|
||||||
mCount = 0;
|
mCount = 0;
|
||||||
|
@ -114,10 +124,10 @@ void FshifterState::deviceUpdate(const ALCdevice*, const Buffer&)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FshifterState::update(const ALCcontext *context, const EffectSlot *slot,
|
void FshifterState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
const EffectProps *props, const EffectTarget target)
|
const EffectProps *props, const EffectTarget target)
|
||||||
{
|
{
|
||||||
const ALCdevice *device{context->mDevice.get()};
|
const DeviceBase *device{context->mDevice};
|
||||||
|
|
||||||
const float step{props->Fshifter.Frequency / static_cast<float>(device->Frequency)};
|
const float step{props->Fshifter.Frequency / static_cast<float>(device->Frequency)};
|
||||||
mPhaseStep[0] = mPhaseStep[1] = fastf2u(minf(step, 1.0f) * MixerFracOne);
|
mPhaseStep[0] = mPhaseStep[1] = fastf2u(minf(step, 1.0f) * MixerFracOne);
|
||||||
|
|
|
@ -20,21 +20,31 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/effects/base.h"
|
||||||
#include "alcontext.h"
|
#include "alc/effectslot.h"
|
||||||
|
#include "almalloc.h"
|
||||||
|
#include "alnumeric.h"
|
||||||
|
#include "alspan.h"
|
||||||
|
#include "core/ambidefs.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
|
#include "core/context.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
#include "core/filters/biquad.h"
|
#include "core/filters/biquad.h"
|
||||||
#include "effectslot.h"
|
#include "core/mixer.h"
|
||||||
#include "vecmat.h"
|
#include "intrusive_ptr.h"
|
||||||
|
#include "math_defs.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
|
|
||||||
#define MAX_UPDATE_SAMPLES 128
|
#define MAX_UPDATE_SAMPLES 128
|
||||||
|
|
||||||
#define WAVEFORM_FRACBITS 24
|
#define WAVEFORM_FRACBITS 24
|
||||||
|
@ -81,8 +91,8 @@ struct ModulatorState final : public EffectState {
|
||||||
} mChans[MaxAmbiChannels];
|
} mChans[MaxAmbiChannels];
|
||||||
|
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -90,7 +100,7 @@ struct ModulatorState final : public EffectState {
|
||||||
DEF_NEWDEL(ModulatorState)
|
DEF_NEWDEL(ModulatorState)
|
||||||
};
|
};
|
||||||
|
|
||||||
void ModulatorState::deviceUpdate(const ALCdevice*, const Buffer&)
|
void ModulatorState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||||
{
|
{
|
||||||
for(auto &e : mChans)
|
for(auto &e : mChans)
|
||||||
{
|
{
|
||||||
|
@ -99,10 +109,10 @@ void ModulatorState::deviceUpdate(const ALCdevice*, const Buffer&)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModulatorState::update(const ALCcontext *context, const EffectSlot *slot,
|
void ModulatorState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
const EffectProps *props, const EffectTarget target)
|
const EffectProps *props, const EffectTarget target)
|
||||||
{
|
{
|
||||||
const ALCdevice *device{context->mDevice.get()};
|
const DeviceBase *device{context->mDevice};
|
||||||
|
|
||||||
const float step{props->Modulator.Frequency / static_cast<float>(device->Frequency)};
|
const float step{props->Modulator.Frequency / static_cast<float>(device->Frequency)};
|
||||||
mStep = fastf2u(clampf(step*WAVEFORM_FRACONE, 0.0f, float{WAVEFORM_FRACONE-1}));
|
mStep = fastf2u(clampf(step*WAVEFORM_FRACONE, 0.0f, float{WAVEFORM_FRACONE-1}));
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include <stddef.h>
|
||||||
#include "alcontext.h"
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alspan.h"
|
#include "alspan.h"
|
||||||
#include "effects/base.h"
|
#include "base.h"
|
||||||
#include "effectslot.h"
|
#include "core/bufferline.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
|
|
||||||
|
struct ContextBase;
|
||||||
|
struct DeviceBase;
|
||||||
|
struct EffectSlot;
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -15,8 +20,8 @@ struct NullState final : public EffectState {
|
||||||
NullState();
|
NullState();
|
||||||
~NullState() override;
|
~NullState() override;
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -39,14 +44,14 @@ NullState::~NullState() = default;
|
||||||
* format) have been changed. Will always be followed by a call to the update
|
* format) have been changed. Will always be followed by a call to the update
|
||||||
* method, if successful.
|
* method, if successful.
|
||||||
*/
|
*/
|
||||||
void NullState::deviceUpdate(const ALCdevice* /*device*/, const Buffer& /*buffer*/)
|
void NullState::deviceUpdate(const DeviceBase* /*device*/, const Buffer& /*buffer*/)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This updates the effect state with new properties. This is called any time
|
/* This updates the effect state with new properties. This is called any time
|
||||||
* the effect is (re)loaded into a slot.
|
* the effect is (re)loaded into a slot.
|
||||||
*/
|
*/
|
||||||
void NullState::update(const ALCcontext* /*context*/, const EffectSlot* /*slot*/,
|
void NullState::update(const ContextBase* /*context*/, const EffectSlot* /*slot*/,
|
||||||
const EffectProps* /*props*/, const EffectTarget /*target*/)
|
const EffectProps* /*props*/, const EffectTarget /*target*/)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,23 +20,33 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <array>
|
|
||||||
#include <complex>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
#include <complex>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/effects/base.h"
|
||||||
|
#include "alc/effectslot.h"
|
||||||
#include "alcomplex.h"
|
#include "alcomplex.h"
|
||||||
#include "alcontext.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "alu.h"
|
#include "alspan.h"
|
||||||
#include "effectslot.h"
|
#include "core/bufferline.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
|
#include "core/mixer.h"
|
||||||
|
#include "core/mixer/defs.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
#include "math_defs.h"
|
#include "math_defs.h"
|
||||||
|
|
||||||
|
struct ContextBase;
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
using complex_d = std::complex<double>;
|
using complex_d = std::complex<double>;
|
||||||
|
|
||||||
#define STFT_SIZE 1024
|
#define STFT_SIZE 1024
|
||||||
|
@ -93,8 +103,8 @@ struct PshifterState final : public EffectState {
|
||||||
float mTargetGains[MAX_OUTPUT_CHANNELS];
|
float mTargetGains[MAX_OUTPUT_CHANNELS];
|
||||||
|
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -102,7 +112,7 @@ struct PshifterState final : public EffectState {
|
||||||
DEF_NEWDEL(PshifterState)
|
DEF_NEWDEL(PshifterState)
|
||||||
};
|
};
|
||||||
|
|
||||||
void PshifterState::deviceUpdate(const ALCdevice*, const Buffer&)
|
void PshifterState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||||
{
|
{
|
||||||
/* (Re-)initializing parameters and clear the buffers. */
|
/* (Re-)initializing parameters and clear the buffers. */
|
||||||
mCount = 0;
|
mCount = 0;
|
||||||
|
@ -122,7 +132,7 @@ void PshifterState::deviceUpdate(const ALCdevice*, const Buffer&)
|
||||||
std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
|
std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PshifterState::update(const ALCcontext*, const EffectSlot *slot,
|
void PshifterState::update(const ContextBase*, const EffectSlot *slot,
|
||||||
const EffectProps *props, const EffectTarget target)
|
const EffectProps *props, const EffectTarget target)
|
||||||
{
|
{
|
||||||
const int tune{props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune};
|
const int tune{props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune};
|
||||||
|
|
|
@ -20,23 +20,33 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <numeric>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdio>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <iterator>
|
||||||
|
#include <numeric>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/effects/base.h"
|
||||||
#include "alcontext.h"
|
#include "alc/effectslot.h"
|
||||||
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "bformatdec.h"
|
#include "alspan.h"
|
||||||
|
#include "core/ambidefs.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
|
#include "core/context.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
#include "core/filters/biquad.h"
|
#include "core/filters/biquad.h"
|
||||||
#include "effectslot.h"
|
#include "core/filters/splitter.h"
|
||||||
#include "vector.h"
|
#include "core/mixer.h"
|
||||||
|
#include "core/mixer/defs.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
|
#include "math_defs.h"
|
||||||
|
#include "opthelpers.h"
|
||||||
#include "vecmat.h"
|
#include "vecmat.h"
|
||||||
|
#include "vector.h"
|
||||||
|
|
||||||
/* This is a user config option for modifying the overall output of the reverb
|
/* This is a user config option for modifying the overall output of the reverb
|
||||||
* effect.
|
* effect.
|
||||||
|
@ -45,6 +55,11 @@ float ReverbBoost = 1.0f;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
|
|
||||||
|
constexpr float MaxModulationTime{4.0f};
|
||||||
|
constexpr float DefaultModulationTime{0.25f};
|
||||||
|
|
||||||
#define MOD_FRACBITS 24
|
#define MOD_FRACBITS 24
|
||||||
#define MOD_FRACONE (1<<MOD_FRACBITS)
|
#define MOD_FRACONE (1<<MOD_FRACBITS)
|
||||||
#define MOD_FRACMASK (MOD_FRACONE-1)
|
#define MOD_FRACMASK (MOD_FRACONE-1)
|
||||||
|
@ -379,15 +394,15 @@ struct ReverbState final : public EffectState {
|
||||||
/* Calculated parameters which indicate if cross-fading is needed after
|
/* Calculated parameters which indicate if cross-fading is needed after
|
||||||
* an update.
|
* an update.
|
||||||
*/
|
*/
|
||||||
float Density{AL_EAXREVERB_DEFAULT_DENSITY};
|
float Density{1.0f};
|
||||||
float Diffusion{AL_EAXREVERB_DEFAULT_DIFFUSION};
|
float Diffusion{1.0f};
|
||||||
float DecayTime{AL_EAXREVERB_DEFAULT_DECAY_TIME};
|
float DecayTime{1.49f};
|
||||||
float HFDecayTime{AL_EAXREVERB_DEFAULT_DECAY_HFRATIO * AL_EAXREVERB_DEFAULT_DECAY_TIME};
|
float HFDecayTime{0.83f * 1.49f};
|
||||||
float LFDecayTime{AL_EAXREVERB_DEFAULT_DECAY_LFRATIO * AL_EAXREVERB_DEFAULT_DECAY_TIME};
|
float LFDecayTime{1.0f * 1.49f};
|
||||||
float ModulationTime{AL_EAXREVERB_DEFAULT_MODULATION_TIME};
|
float ModulationTime{0.25f};
|
||||||
float ModulationDepth{AL_EAXREVERB_DEFAULT_MODULATION_DEPTH};
|
float ModulationDepth{0.0f};
|
||||||
float HFReference{AL_EAXREVERB_DEFAULT_HFREFERENCE};
|
float HFReference{5000.0f};
|
||||||
float LFReference{AL_EAXREVERB_DEFAULT_LFREFERENCE};
|
float LFReference{250.0f};
|
||||||
} mParams;
|
} mParams;
|
||||||
|
|
||||||
/* Master effect filters */
|
/* Master effect filters */
|
||||||
|
@ -527,8 +542,8 @@ struct ReverbState final : public EffectState {
|
||||||
void lateFaded(const size_t offset, const size_t todo, const float fade,
|
void lateFaded(const size_t offset, const size_t todo, const float fade,
|
||||||
const float fadeStep);
|
const float fadeStep);
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -556,16 +571,17 @@ void ReverbState::allocLines(const float frequency)
|
||||||
/* Multiplier for the maximum density value, i.e. density=1, which is
|
/* Multiplier for the maximum density value, i.e. density=1, which is
|
||||||
* actually the least density...
|
* actually the least density...
|
||||||
*/
|
*/
|
||||||
const float multiplier{CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY)};
|
const float multiplier{CalcDelayLengthMult(1.0f)};
|
||||||
|
|
||||||
/* The main delay length includes the maximum early reflection delay, the
|
/* The main delay length includes the maximum early reflection delay, the
|
||||||
* largest early tap width, the maximum late reverb delay, and the
|
* largest early tap width, the maximum late reverb delay, and the
|
||||||
* largest late tap width. Finally, it must also be extended by the
|
* largest late tap width. Finally, it must also be extended by the
|
||||||
* update size (BufferLineSize) for block processing.
|
* update size (BufferLineSize) for block processing.
|
||||||
*/
|
*/
|
||||||
float length{AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS.back()*multiplier +
|
constexpr float LateLineDiffAvg{(LATE_LINE_LENGTHS.back()-LATE_LINE_LENGTHS.front()) /
|
||||||
AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
|
float{NUM_LINES}};
|
||||||
(LATE_LINE_LENGTHS.back() - LATE_LINE_LENGTHS.front())/float{NUM_LINES}*multiplier};
|
float length{ReverbMaxReflectionsDelay + EARLY_TAP_LENGTHS.back()*multiplier +
|
||||||
|
ReverbMaxLateReverbDelay + LateLineDiffAvg*multiplier};
|
||||||
totalSamples += mDelay.calcLineLength(length, totalSamples, frequency, BufferLineSize);
|
totalSamples += mDelay.calcLineLength(length, totalSamples, frequency, BufferLineSize);
|
||||||
|
|
||||||
/* The early vector all-pass line. */
|
/* The early vector all-pass line. */
|
||||||
|
@ -584,7 +600,7 @@ void ReverbState::allocLines(const float frequency)
|
||||||
* time and depth coefficient, and halfed for the low-to-high frequency
|
* time and depth coefficient, and halfed for the low-to-high frequency
|
||||||
* swing.
|
* swing.
|
||||||
*/
|
*/
|
||||||
constexpr float max_mod_delay{AL_EAXREVERB_MAX_MODULATION_TIME*MODULATION_DEPTH_COEFF / 2.0f};
|
constexpr float max_mod_delay{MaxModulationTime*MODULATION_DEPTH_COEFF / 2.0f};
|
||||||
|
|
||||||
/* The late delay lines are calculated from the largest maximum density
|
/* The late delay lines are calculated from the largest maximum density
|
||||||
* line length, and the maximum modulation delay. An additional sample is
|
* line length, and the maximum modulation delay. An additional sample is
|
||||||
|
@ -607,18 +623,18 @@ void ReverbState::allocLines(const float frequency)
|
||||||
mLate.Delay.realizeLineOffset(mSampleBuffer.data());
|
mLate.Delay.realizeLineOffset(mSampleBuffer.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReverbState::deviceUpdate(const ALCdevice *device, const Buffer&)
|
void ReverbState::deviceUpdate(const DeviceBase *device, const Buffer&)
|
||||||
{
|
{
|
||||||
const auto frequency = static_cast<float>(device->Frequency);
|
const auto frequency = static_cast<float>(device->Frequency);
|
||||||
|
|
||||||
/* Allocate the delay lines. */
|
/* Allocate the delay lines. */
|
||||||
allocLines(frequency);
|
allocLines(frequency);
|
||||||
|
|
||||||
const float multiplier{CalcDelayLengthMult(AL_EAXREVERB_MAX_DENSITY)};
|
const float multiplier{CalcDelayLengthMult(1.0f)};
|
||||||
|
|
||||||
/* The late feed taps are set a fixed position past the latest delay tap. */
|
/* The late feed taps are set a fixed position past the latest delay tap. */
|
||||||
mLateFeedTap = float2uint(
|
mLateFeedTap = float2uint((ReverbMaxReflectionsDelay + EARLY_TAP_LENGTHS.back()*multiplier) *
|
||||||
(AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS.back()*multiplier) * frequency);
|
frequency);
|
||||||
|
|
||||||
/* Clear filters and gain coefficients since the delay lines were all just
|
/* Clear filters and gain coefficients since the delay lines were all just
|
||||||
* cleared (if not reallocated).
|
* cleared (if not reallocated).
|
||||||
|
@ -665,7 +681,7 @@ void ReverbState::deviceUpdate(const ALCdevice *device, const Buffer&)
|
||||||
if(device->mAmbiOrder > 1)
|
if(device->mAmbiOrder > 1)
|
||||||
{
|
{
|
||||||
mMixOut = &ReverbState::MixOutAmbiUp;
|
mMixOut = &ReverbState::MixOutAmbiUp;
|
||||||
mOrderScales = BFormatDec::GetHFOrderScales(1, device->mAmbiOrder);
|
mOrderScales = AmbiScale::GetHFOrderScales(1, device->mAmbiOrder);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -813,15 +829,14 @@ void Modulation::updateModulator(float modTime, float modDepth, float frequency)
|
||||||
* (half of it is spent decreasing the frequency, half is spent increasing
|
* (half of it is spent decreasing the frequency, half is spent increasing
|
||||||
* it).
|
* it).
|
||||||
*/
|
*/
|
||||||
if(modTime >= AL_EAXREVERB_DEFAULT_MODULATION_TIME)
|
if(modTime >= DefaultModulationTime)
|
||||||
{
|
{
|
||||||
/* To cancel the effects of a long period modulation on the late
|
/* To cancel the effects of a long period modulation on the late
|
||||||
* reverberation, the amount of pitch should be varied (decreased)
|
* reverberation, the amount of pitch should be varied (decreased)
|
||||||
* according to the modulation time. The natural form is varying
|
* according to the modulation time. The natural form is varying
|
||||||
* inversely, in fact resulting in an invariant.
|
* inversely, in fact resulting in an invariant.
|
||||||
*/
|
*/
|
||||||
Depth[1] = MODULATION_DEPTH_COEFF / 4.0f * AL_EAXREVERB_DEFAULT_MODULATION_TIME *
|
Depth[1] = MODULATION_DEPTH_COEFF / 4.0f * DefaultModulationTime * modDepth * frequency;
|
||||||
modDepth * frequency;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Depth[1] = MODULATION_DEPTH_COEFF / 4.0f * modTime * modDepth * frequency;
|
Depth[1] = MODULATION_DEPTH_COEFF / 4.0f * modTime * modDepth * frequency;
|
||||||
|
@ -835,7 +850,8 @@ void LateReverb::updateLines(const float density_mult, const float diffusion,
|
||||||
/* Scaling factor to convert the normalized reference frequencies from
|
/* Scaling factor to convert the normalized reference frequencies from
|
||||||
* representing 0...freq to 0...max_reference.
|
* representing 0...freq to 0...max_reference.
|
||||||
*/
|
*/
|
||||||
const float norm_weight_factor{frequency / AL_EAXREVERB_MAX_HFREFERENCE};
|
constexpr float MaxHFReference{20000.0f};
|
||||||
|
const float norm_weight_factor{frequency / MaxHFReference};
|
||||||
|
|
||||||
const float late_allpass_avg{
|
const float late_allpass_avg{
|
||||||
std::accumulate(LATE_ALLPASS_LENGTHS.begin(), LATE_ALLPASS_LENGTHS.end(), 0.0f) /
|
std::accumulate(LATE_ALLPASS_LENGTHS.begin(), LATE_ALLPASS_LENGTHS.end(), 0.0f) /
|
||||||
|
@ -985,10 +1001,10 @@ void ReverbState::update3DPanning(const float *ReflectionsPan, const float *Late
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReverbState::update(const ALCcontext *Context, const EffectSlot *Slot,
|
void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot,
|
||||||
const EffectProps *props, const EffectTarget target)
|
const EffectProps *props, const EffectTarget target)
|
||||||
{
|
{
|
||||||
const ALCdevice *Device{Context->mDevice.get()};
|
const DeviceBase *Device{Context->mDevice};
|
||||||
const auto frequency = static_cast<float>(Device->Frequency);
|
const auto frequency = static_cast<float>(Device->Frequency);
|
||||||
|
|
||||||
/* Calculate the master filters */
|
/* Calculate the master filters */
|
||||||
|
@ -1024,10 +1040,10 @@ void ReverbState::update(const ALCcontext *Context, const EffectSlot *Slot,
|
||||||
props->Reverb.DecayTime);
|
props->Reverb.DecayTime);
|
||||||
|
|
||||||
/* Calculate the LF/HF decay times. */
|
/* Calculate the LF/HF decay times. */
|
||||||
const float lfDecayTime{clampf(props->Reverb.DecayTime * props->Reverb.DecayLFRatio,
|
constexpr float MinDecayTime{0.1f}, MaxDecayTime{20.0f};
|
||||||
AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME)};
|
const float lfDecayTime{clampf(props->Reverb.DecayTime*props->Reverb.DecayLFRatio,
|
||||||
const float hfDecayTime{clampf(props->Reverb.DecayTime * hfRatio,
|
MinDecayTime, MaxDecayTime)};
|
||||||
AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME)};
|
const float hfDecayTime{clampf(props->Reverb.DecayTime*hfRatio, MinDecayTime, MaxDecayTime)};
|
||||||
|
|
||||||
/* Update the modulator rate and depth. */
|
/* Update the modulator rate and depth. */
|
||||||
mLate.Mod.updateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth,
|
mLate.Mod.updateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth,
|
||||||
|
|
|
@ -1,39 +1,62 @@
|
||||||
/**
|
/**
|
||||||
* OpenAL cross platform audio library
|
* This file is part of the OpenAL Soft cross platform audio library
|
||||||
|
*
|
||||||
* Copyright (C) 2019 by Anis A. Hireche
|
* Copyright (C) 2019 by Anis A. Hireche
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Library General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2 of the License, or (at your option) any later version.
|
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* modification, are permitted provided that the following conditions are met:
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Library General Public
|
* * Redistributions of source code must retain the above copyright notice,
|
||||||
* License along with this library; if not, write to the
|
* this list of conditions and the following disclaimer.
|
||||||
* Free Software Foundation, Inc.,
|
*
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of Spherical-Harmonic-Transform nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdlib>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
#include "alcmain.h"
|
#include "alc/effects/base.h"
|
||||||
#include "alcontext.h"
|
#include "alc/effectslot.h"
|
||||||
#include "alu.h"
|
#include "almalloc.h"
|
||||||
#include "effectslot.h"
|
#include "alnumeric.h"
|
||||||
|
#include "alspan.h"
|
||||||
|
#include "core/ambidefs.h"
|
||||||
|
#include "core/bufferline.h"
|
||||||
|
#include "core/context.h"
|
||||||
|
#include "core/devformat.h"
|
||||||
|
#include "core/device.h"
|
||||||
|
#include "core/mixer.h"
|
||||||
|
#include "intrusive_ptr.h"
|
||||||
#include "math_defs.h"
|
#include "math_defs.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using uint = unsigned int;
|
||||||
|
|
||||||
#define MAX_UPDATE_SAMPLES 256
|
#define MAX_UPDATE_SAMPLES 256
|
||||||
#define NUM_FORMANTS 4
|
#define NUM_FORMANTS 4
|
||||||
#define NUM_FILTERS 2
|
#define NUM_FILTERS 2
|
||||||
|
@ -138,8 +161,8 @@ struct VmorpherState final : public EffectState {
|
||||||
alignas(16) float mSampleBufferB[MAX_UPDATE_SAMPLES]{};
|
alignas(16) float mSampleBufferB[MAX_UPDATE_SAMPLES]{};
|
||||||
alignas(16) float mLfo[MAX_UPDATE_SAMPLES]{};
|
alignas(16) float mLfo[MAX_UPDATE_SAMPLES]{};
|
||||||
|
|
||||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||||
const EffectTarget target) override;
|
const EffectTarget target) override;
|
||||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||||
const al::span<FloatBufferLine> samplesOut) override;
|
const al::span<FloatBufferLine> samplesOut) override;
|
||||||
|
@ -202,7 +225,7 @@ std::array<FormantFilter,4> VmorpherState::getFiltersByPhoneme(VMorpherPhenome p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void VmorpherState::deviceUpdate(const ALCdevice*, const Buffer&)
|
void VmorpherState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||||
{
|
{
|
||||||
for(auto &e : mChans)
|
for(auto &e : mChans)
|
||||||
{
|
{
|
||||||
|
@ -214,10 +237,10 @@ void VmorpherState::deviceUpdate(const ALCdevice*, const Buffer&)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VmorpherState::update(const ALCcontext *context, const EffectSlot *slot,
|
void VmorpherState::update(const ContextBase *context, const EffectSlot *slot,
|
||||||
const EffectProps *props, const EffectTarget target)
|
const EffectProps *props, const EffectTarget target)
|
||||||
{
|
{
|
||||||
const ALCdevice *device{context->mDevice.get()};
|
const DeviceBase *device{context->mDevice};
|
||||||
const float frequency{static_cast<float>(device->Frequency)};
|
const float frequency{static_cast<float>(device->Frequency)};
|
||||||
const float step{props->Vmorpher.Rate / frequency};
|
const float step{props->Vmorpher.Rate / frequency};
|
||||||
mStep = fastf2u(clampf(step*WAVEFORM_FRACONE, 0.0f, float{WAVEFORM_FRACONE-1}));
|
mStep = fastf2u(clampf(step*WAVEFORM_FRACONE, 0.0f, float{WAVEFORM_FRACONE-1}));
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include "alcontext.h"
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
|
||||||
EffectSlotArray *EffectSlot::CreatePtrArray(size_t count) noexcept
|
EffectSlotArray *EffectSlot::CreatePtrArray(size_t count) noexcept
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
#ifndef EFFECTSLOT_H
|
#ifndef EFFECTSLOT_H
|
||||||
#define EFFECTSLOT_H
|
#define EFFECTSLOT_H
|
||||||
|
|
||||||
#include <atomic.h>
|
#include <atomic>
|
||||||
|
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alcmain.h"
|
#include "core/device.h"
|
||||||
#include "effects/base.h"
|
#include "effects/base.h"
|
||||||
#include "intrusive_ptr.h"
|
#include "intrusive_ptr.h"
|
||||||
|
|
||||||
|
|
||||||
struct EffectSlot;
|
struct EffectSlot;
|
||||||
struct WetBuffer;
|
struct WetBuffer;
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,34 @@ AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *
|
||||||
#endif
|
#endif
|
||||||
#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);
|
||||||
|
#ifdef AL_ALEXT_PROTOTYPES
|
||||||
|
ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *deviceName,
|
||||||
|
const ALCint *attribs);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef AL_SOFT_UHJ
|
||||||
|
#define AL_SOFT_UHJ
|
||||||
|
#define AL_FORMAT_UHJ2CHN8_SOFT 0x19A2
|
||||||
|
#define AL_FORMAT_UHJ2CHN16_SOFT 0x19A3
|
||||||
|
#define AL_FORMAT_UHJ2CHN_FLOAT32_SOFT 0x19A4
|
||||||
|
#define AL_FORMAT_UHJ3CHN8_SOFT 0x19A5
|
||||||
|
#define AL_FORMAT_UHJ3CHN16_SOFT 0x19A6
|
||||||
|
#define AL_FORMAT_UHJ3CHN_FLOAT32_SOFT 0x19A7
|
||||||
|
#define AL_FORMAT_UHJ4CHN8_SOFT 0x19A8
|
||||||
|
#define AL_FORMAT_UHJ4CHN16_SOFT 0x19A9
|
||||||
|
#define AL_FORMAT_UHJ4CHN_FLOAT32_SOFT 0x19AA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef AL_SOFT_hold_on_disconnect
|
||||||
|
#define AL_SOFT_hold_on_disconnect
|
||||||
|
#define AL_STOP_SOURCES_ON_DISCONNECT_SOFT 0x19AB
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -38,24 +38,24 @@
|
||||||
#include "AL/alext.h"
|
#include "AL/alext.h"
|
||||||
|
|
||||||
#include "al/auxeffectslot.h"
|
#include "al/auxeffectslot.h"
|
||||||
#include "alcmain.h"
|
|
||||||
#include "alconfig.h"
|
#include "alconfig.h"
|
||||||
#include "alcontext.h"
|
#include "alc/context.h"
|
||||||
#include "almalloc.h"
|
#include "almalloc.h"
|
||||||
#include "alnumeric.h"
|
#include "alnumeric.h"
|
||||||
#include "aloptional.h"
|
#include "aloptional.h"
|
||||||
#include "alspan.h"
|
#include "alspan.h"
|
||||||
#include "alstring.h"
|
#include "alstring.h"
|
||||||
#include "alu.h"
|
#include "alu.h"
|
||||||
#include "bformatdec.h"
|
|
||||||
#include "core/ambdec.h"
|
#include "core/ambdec.h"
|
||||||
#include "core/ambidefs.h"
|
#include "core/ambidefs.h"
|
||||||
|
#include "core/bformatdec.h"
|
||||||
#include "core/bs2b.h"
|
#include "core/bs2b.h"
|
||||||
#include "core/devformat.h"
|
#include "core/devformat.h"
|
||||||
|
#include "core/front_stablizer.h"
|
||||||
|
#include "core/hrtf.h"
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
#include "core/uhjfilter.h"
|
#include "core/uhjfilter.h"
|
||||||
#include "front_stablizer.h"
|
#include "device.h"
|
||||||
#include "hrtf.h"
|
|
||||||
#include "math_defs.h"
|
#include "math_defs.h"
|
||||||
#include "opthelpers.h"
|
#include "opthelpers.h"
|
||||||
|
|
||||||
|
@ -486,11 +486,6 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
|
||||||
decoder = X71Config;
|
decoder = X71Config;
|
||||||
break;
|
break;
|
||||||
case DevFmtAmbi3D:
|
case DevFmtAmbi3D:
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(device->FmtChans == DevFmtAmbi3D)
|
|
||||||
{
|
|
||||||
const char *devname{device->DeviceName.c_str()};
|
const char *devname{device->DeviceName.c_str()};
|
||||||
auto&& acnmap = GetAmbiLayout(device->mAmbiLayout);
|
auto&& acnmap = GetAmbiLayout(device->mAmbiLayout);
|
||||||
auto&& n3dscale = GetAmbiScales(device->mAmbiScale);
|
auto&& n3dscale = GetAmbiScales(device->mAmbiScale);
|
||||||
|
@ -499,17 +494,16 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
|
||||||
const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)};
|
const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)};
|
||||||
std::transform(acnmap.begin(), acnmap.begin()+count, std::begin(device->Dry.AmbiMap),
|
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}; }
|
{ return BFChannelConfig{1.0f/n3dscale[acn], acn}; });
|
||||||
);
|
|
||||||
AllocChannels(device, count, 0);
|
AllocChannels(device, count, 0);
|
||||||
|
|
||||||
float nfc_delay{ConfigValueFloat(devname, "decoder", "nfc-ref-delay").value_or(0.0f)};
|
float nfc_delay{ConfigValueFloat(devname, "decoder", "nfc-ref-delay").value_or(0.0f)};
|
||||||
if(nfc_delay > 0.0f)
|
if(nfc_delay > 0.0f)
|
||||||
InitNearFieldCtrl(device, nfc_delay * SpeedOfSoundMetersPerSec, device->mAmbiOrder,
|
InitNearFieldCtrl(device, nfc_delay * SpeedOfSoundMetersPerSec, device->mAmbiOrder,
|
||||||
true);
|
true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
const bool dual_band{hqdec && !decoder.mCoeffsLF.empty()};
|
const bool dual_band{hqdec && !decoder.mCoeffsLF.empty()};
|
||||||
al::vector<ChannelDec> chancoeffs, chancoeffslf;
|
al::vector<ChannelDec> chancoeffs, chancoeffslf;
|
||||||
for(size_t i{0u};i < decoder.mChannels.size();++i)
|
for(size_t i{0u};i < decoder.mChannels.size();++i)
|
||||||
|
@ -524,28 +518,26 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
|
||||||
|
|
||||||
chancoeffs.resize(maxz(chancoeffs.size(), idx+1u), ChannelDec{});
|
chancoeffs.resize(maxz(chancoeffs.size(), idx+1u), ChannelDec{});
|
||||||
al::span<float,MaxAmbiChannels> coeffs{chancoeffs[idx]};
|
al::span<float,MaxAmbiChannels> coeffs{chancoeffs[idx]};
|
||||||
size_t start{0};
|
size_t ambichan{0};
|
||||||
for(uint o{0};o <= decoder.mOrder;++o)
|
for(uint o{0};o < decoder.mOrder+1;++o)
|
||||||
{
|
{
|
||||||
size_t count{o ? 2u : 1u};
|
const float order_gain{decoder.mOrderGain[o]};
|
||||||
do {
|
const size_t order_max{Ambi2DChannelsFromOrder(o)};
|
||||||
coeffs[start] = decoder.mCoeffs[i][start] * decoder.mOrderGain[o];
|
for(;ambichan < order_max;++ambichan)
|
||||||
++start;
|
coeffs[ambichan] = decoder.mCoeffs[i][ambichan] * order_gain;
|
||||||
} while(--count);
|
|
||||||
}
|
}
|
||||||
if(!dual_band)
|
if(!dual_band)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
chancoeffslf.resize(maxz(chancoeffslf.size(), idx+1u), ChannelDec{});
|
chancoeffslf.resize(maxz(chancoeffslf.size(), idx+1u), ChannelDec{});
|
||||||
coeffs = chancoeffslf[idx];
|
coeffs = chancoeffslf[idx];
|
||||||
start = 0;
|
ambichan = 0;
|
||||||
for(uint o{0};o <= decoder.mOrder;++o)
|
for(uint o{0};o < decoder.mOrder+1;++o)
|
||||||
{
|
{
|
||||||
size_t count{o ? 2u : 1u};
|
const float order_gain{decoder.mOrderGainLF[o]};
|
||||||
do {
|
const size_t order_max{Ambi2DChannelsFromOrder(o)};
|
||||||
coeffs[start] = decoder.mCoeffsLF[i][start] * decoder.mOrderGainLF[o];
|
for(;ambichan < order_max;++ambichan)
|
||||||
++start;
|
coeffs[ambichan] = decoder.mCoeffsLF[i][ambichan] * order_gain;
|
||||||
} while(--count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,8 +548,7 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
|
||||||
const size_t ambicount{Ambi2DChannelsFromOrder(decoder.mOrder)};
|
const size_t ambicount{Ambi2DChannelsFromOrder(decoder.mOrder)};
|
||||||
std::transform(AmbiIndex::FromACN2D().begin(), AmbiIndex::FromACN2D().begin()+ambicount,
|
std::transform(AmbiIndex::FromACN2D().begin(), AmbiIndex::FromACN2D().begin()+ambicount,
|
||||||
std::begin(device->Dry.AmbiMap),
|
std::begin(device->Dry.AmbiMap),
|
||||||
[](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; }
|
[](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; });
|
||||||
);
|
|
||||||
AllocChannels(device, ambicount, device->channelsFromFmt());
|
AllocChannels(device, ambicount, device->channelsFromFmt());
|
||||||
|
|
||||||
std::unique_ptr<FrontStablizer> stablizer;
|
std::unique_ptr<FrontStablizer> stablizer;
|
||||||
|
@ -592,7 +583,6 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
|
||||||
"");
|
"");
|
||||||
device->AmbiDecoder = BFormatDec::Create(ambicount, chancoeffs, chancoeffslf,
|
device->AmbiDecoder = BFormatDec::Create(ambicount, chancoeffs, chancoeffslf,
|
||||||
std::move(stablizer));
|
std::move(stablizer));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitCustomPanning(ALCdevice *device, const bool hqdec, const bool stablize,
|
void InitCustomPanning(ALCdevice *device, const bool hqdec, const bool stablize,
|
||||||
|
@ -750,7 +740,9 @@ void InitHrtfPanning(ALCdevice *device)
|
||||||
static const float AmbiOrderHFGain1O[MaxAmbiOrder+1]{
|
static const float AmbiOrderHFGain1O[MaxAmbiOrder+1]{
|
||||||
2.000000000e+00f, 1.154700538e+00f
|
2.000000000e+00f, 1.154700538e+00f
|
||||||
}, AmbiOrderHFGain2O[MaxAmbiOrder+1]{
|
}, AmbiOrderHFGain2O[MaxAmbiOrder+1]{
|
||||||
2.357022604e+00f, 1.825741858e+00f, 9.428090416e-01f
|
/*AMP 1.000000000e+00f, 7.745966692e-01f, 4.000000000e-01f*/
|
||||||
|
/*RMS*/ 9.128709292e-01f, 7.071067812e-01f, 3.651483717e-01f
|
||||||
|
/*ENRGY 2.357022604e+00f, 1.825741858e+00f, 9.428090416e-01f*/
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(al::size(AmbiPoints1O) == al::size(AmbiMatrix1O), "First-Order Ambisonic HRTF mismatch");
|
static_assert(al::size(AmbiPoints1O) == al::size(AmbiMatrix1O), "First-Order Ambisonic HRTF mismatch");
|
||||||
|
@ -798,7 +790,7 @@ void InitHrtfPanning(ALCdevice *device)
|
||||||
((ambi_order%10) == 2) ? "nd" :
|
((ambi_order%10) == 2) ? "nd" :
|
||||||
((ambi_order%10) == 3) ? "rd" : "th",
|
((ambi_order%10) == 3) ? "rd" : "th",
|
||||||
(device->mRenderMode == RenderMode::Hrtf) ? "+ Full " : "",
|
(device->mRenderMode == RenderMode::Hrtf) ? "+ Full " : "",
|
||||||
device->HrtfName.c_str());
|
device->mHrtfName.c_str());
|
||||||
|
|
||||||
al::span<const AngularPoint> AmbiPoints{AmbiPoints1O};
|
al::span<const AngularPoint> AmbiPoints{AmbiPoints1O};
|
||||||
const float (*AmbiMatrix)[MaxAmbiChannels]{AmbiMatrix1O};
|
const float (*AmbiMatrix)[MaxAmbiChannels]{AmbiMatrix1O};
|
||||||
|
@ -854,7 +846,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, HrtfRequestMode hrtf_appreq
|
||||||
device->mHrtfState = nullptr;
|
device->mHrtfState = nullptr;
|
||||||
device->mHrtf = nullptr;
|
device->mHrtf = nullptr;
|
||||||
device->mIrSize = 0;
|
device->mIrSize = 0;
|
||||||
device->HrtfName.clear();
|
device->mHrtfName.clear();
|
||||||
device->mXOverFreq = 400.0f;
|
device->mXOverFreq = 400.0f;
|
||||||
device->mRenderMode = RenderMode::Normal;
|
device->mRenderMode = RenderMode::Normal;
|
||||||
|
|
||||||
|
@ -862,7 +854,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, HrtfRequestMode hrtf_appreq
|
||||||
{
|
{
|
||||||
old_hrtf = nullptr;
|
old_hrtf = nullptr;
|
||||||
if(hrtf_appreq == Hrtf_Enable)
|
if(hrtf_appreq == Hrtf_Enable)
|
||||||
device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
|
device->mHrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
|
||||||
|
|
||||||
const char *layout{nullptr};
|
const char *layout{nullptr};
|
||||||
switch(device->FmtChans)
|
switch(device->FmtChans)
|
||||||
|
@ -943,42 +935,42 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, HrtfRequestMode hrtf_appreq
|
||||||
(hrtf_appreq == Hrtf_Enable);
|
(hrtf_appreq == Hrtf_Enable);
|
||||||
if(!usehrtf) goto no_hrtf;
|
if(!usehrtf) goto no_hrtf;
|
||||||
|
|
||||||
device->HrtfStatus = ALC_HRTF_ENABLED_SOFT;
|
device->mHrtfStatus = ALC_HRTF_ENABLED_SOFT;
|
||||||
if(headphones && hrtf_appreq != Hrtf_Disable)
|
if(headphones && hrtf_appreq != Hrtf_Disable)
|
||||||
device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
|
device->mHrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(hrtf_userreq != Hrtf_Enable)
|
if(hrtf_userreq != Hrtf_Enable)
|
||||||
{
|
{
|
||||||
if(hrtf_appreq == Hrtf_Enable)
|
if(hrtf_appreq == Hrtf_Enable)
|
||||||
device->HrtfStatus = ALC_HRTF_DENIED_SOFT;
|
device->mHrtfStatus = ALC_HRTF_DENIED_SOFT;
|
||||||
goto no_hrtf;
|
goto no_hrtf;
|
||||||
}
|
}
|
||||||
device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT;
|
device->mHrtfStatus = ALC_HRTF_REQUIRED_SOFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(device->HrtfList.empty())
|
if(device->mHrtfList.empty())
|
||||||
device->HrtfList = EnumerateHrtf(device->DeviceName.c_str());
|
device->enumerateHrtfs();
|
||||||
|
|
||||||
if(hrtf_id >= 0 && static_cast<uint>(hrtf_id) < device->HrtfList.size())
|
if(hrtf_id >= 0 && static_cast<uint>(hrtf_id) < device->mHrtfList.size())
|
||||||
{
|
{
|
||||||
const std::string &hrtfname = device->HrtfList[static_cast<uint>(hrtf_id)];
|
const std::string &hrtfname = device->mHrtfList[static_cast<uint>(hrtf_id)];
|
||||||
if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->Frequency)})
|
if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->Frequency)})
|
||||||
{
|
{
|
||||||
device->mHrtf = std::move(hrtf);
|
device->mHrtf = std::move(hrtf);
|
||||||
device->HrtfName = hrtfname;
|
device->mHrtfName = hrtfname;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!device->mHrtf)
|
if(!device->mHrtf)
|
||||||
{
|
{
|
||||||
for(const auto &hrtfname : device->HrtfList)
|
for(const auto &hrtfname : device->mHrtfList)
|
||||||
{
|
{
|
||||||
if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->Frequency)})
|
if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->Frequency)})
|
||||||
{
|
{
|
||||||
device->mHrtf = std::move(hrtf);
|
device->mHrtf = std::move(hrtf);
|
||||||
device->HrtfName = hrtfname;
|
device->mHrtfName = hrtfname;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1000,7 +992,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, HrtfRequestMode hrtf_appreq
|
||||||
device->PostProcess = &ALCdevice::ProcessHrtf;
|
device->PostProcess = &ALCdevice::ProcessHrtf;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
|
device->mHrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
|
||||||
|
|
||||||
no_hrtf:
|
no_hrtf:
|
||||||
old_hrtf = nullptr;
|
old_hrtf = nullptr;
|
||||||
|
@ -1034,7 +1026,7 @@ no_hrtf:
|
||||||
}
|
}
|
||||||
if(device->mRenderMode == RenderMode::Normal)
|
if(device->mRenderMode == RenderMode::Normal)
|
||||||
{
|
{
|
||||||
device->Uhj_Encoder = std::make_unique<Uhj2Encoder>();
|
device->mUhjEncoder = std::make_unique<UhjEncoder>();
|
||||||
TRACE("UHJ enabled\n");
|
TRACE("UHJ enabled\n");
|
||||||
InitUhjPanning(device);
|
InitUhjPanning(device);
|
||||||
device->PostProcess = &ALCdevice::ProcessUhj;
|
device->PostProcess = &ALCdevice::ProcessUhj;
|
||||||
|
@ -1049,7 +1041,7 @@ no_hrtf:
|
||||||
|
|
||||||
void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context)
|
void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context)
|
||||||
{
|
{
|
||||||
ALCdevice *device{context->mDevice.get()};
|
DeviceBase *device{context->mDevice};
|
||||||
const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)};
|
const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)};
|
||||||
|
|
||||||
auto wetbuffer_iter = context->mWetBuffers.end();
|
auto wetbuffer_iter = context->mWetBuffers.end();
|
||||||
|
@ -1098,113 +1090,3 @@ void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context)
|
||||||
std::fill(iter, slot->Wet.AmbiMap.end(), BFChannelConfig{});
|
std::fill(iter, slot->Wet.AmbiMap.end(), BFChannelConfig{});
|
||||||
slot->Wet.Buffer = wetbuffer->mBuffer;
|
slot->Wet.Buffer = wetbuffer->mBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::array<float,MaxAmbiChannels> CalcAmbiCoeffs(const float y, const float z, const float x,
|
|
||||||
const float spread)
|
|
||||||
{
|
|
||||||
std::array<float,MaxAmbiChannels> coeffs;
|
|
||||||
|
|
||||||
/* Zeroth-order */
|
|
||||||
coeffs[0] = 1.0f; /* ACN 0 = 1 */
|
|
||||||
/* First-order */
|
|
||||||
coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */
|
|
||||||
coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */
|
|
||||||
coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */
|
|
||||||
/* Second-order */
|
|
||||||
const float xx{x*x}, yy{y*y}, zz{z*z}, xy{x*y}, yz{y*z}, xz{x*z};
|
|
||||||
coeffs[4] = 3.872983346f * xy; /* ACN 4 = sqrt(15) * X * Y */
|
|
||||||
coeffs[5] = 3.872983346f * yz; /* ACN 5 = sqrt(15) * Y * Z */
|
|
||||||
coeffs[6] = 1.118033989f * (3.0f*zz - 1.0f); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */
|
|
||||||
coeffs[7] = 3.872983346f * xz; /* ACN 7 = sqrt(15) * X * Z */
|
|
||||||
coeffs[8] = 1.936491673f * (xx - yy); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */
|
|
||||||
/* Third-order */
|
|
||||||
coeffs[9] = 2.091650066f * (y*(3.0f*xx - yy)); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */
|
|
||||||
coeffs[10] = 10.246950766f * (z*xy); /* ACN 10 = sqrt(105) * Z * X * Y */
|
|
||||||
coeffs[11] = 1.620185175f * (y*(5.0f*zz - 1.0f)); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */
|
|
||||||
coeffs[12] = 1.322875656f * (z*(5.0f*zz - 3.0f)); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */
|
|
||||||
coeffs[13] = 1.620185175f * (x*(5.0f*zz - 1.0f)); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */
|
|
||||||
coeffs[14] = 5.123475383f * (z*(xx - yy)); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
|
|
||||||
coeffs[15] = 2.091650066f * (x*(xx - 3.0f*yy)); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
|
|
||||||
/* Fourth-order */
|
|
||||||
/* ACN 16 = sqrt(35)*3/2 * X * Y * (X*X - Y*Y) */
|
|
||||||
/* ACN 17 = sqrt(35/2)*3/2 * (3*X*X - Y*Y) * Y * Z */
|
|
||||||
/* ACN 18 = sqrt(5)*3/2 * X * Y * (7*Z*Z - 1) */
|
|
||||||
/* ACN 19 = sqrt(5/2)*3/2 * Y * Z * (7*Z*Z - 3) */
|
|
||||||
/* ACN 20 = 3/8 * (35*Z*Z*Z*Z - 30*Z*Z + 3) */
|
|
||||||
/* ACN 21 = sqrt(5/2)*3/2 * X * Z * (7*Z*Z - 3) */
|
|
||||||
/* ACN 22 = sqrt(5)*3/4 * (X*X - Y*Y) * (7*Z*Z - 1) */
|
|
||||||
/* ACN 23 = sqrt(35/2)*3/2 * (X*X - 3*Y*Y) * X * Z */
|
|
||||||
/* ACN 24 = sqrt(35)*3/8 * (X*X*X*X - 6*X*X*Y*Y + Y*Y*Y*Y) */
|
|
||||||
|
|
||||||
if(spread > 0.0f)
|
|
||||||
{
|
|
||||||
/* Implement the spread by using a spherical source that subtends the
|
|
||||||
* angle spread. See:
|
|
||||||
* http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
|
|
||||||
*
|
|
||||||
* When adjusted for N3D normalization instead of SN3D, these
|
|
||||||
* calculations are:
|
|
||||||
*
|
|
||||||
* ZH0 = -sqrt(pi) * (-1+ca);
|
|
||||||
* ZH1 = 0.5*sqrt(pi) * sa*sa;
|
|
||||||
* ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1);
|
|
||||||
* ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1);
|
|
||||||
* ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3);
|
|
||||||
* ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
|
|
||||||
*
|
|
||||||
* The gain of the source is compensated for size, so that the
|
|
||||||
* loudness doesn't depend on the spread. Thus:
|
|
||||||
*
|
|
||||||
* ZH0 = 1.0f;
|
|
||||||
* ZH1 = 0.5f * (ca+1.0f);
|
|
||||||
* ZH2 = 0.5f * (ca+1.0f)*ca;
|
|
||||||
* ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f);
|
|
||||||
* ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca;
|
|
||||||
* ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
|
|
||||||
*/
|
|
||||||
const float ca{std::cos(spread * 0.5f)};
|
|
||||||
/* Increase the source volume by up to +3dB for a full spread. */
|
|
||||||
const float scale{std::sqrt(1.0f + spread/al::MathDefs<float>::Tau())};
|
|
||||||
|
|
||||||
const float ZH0_norm{scale};
|
|
||||||
const float ZH1_norm{scale * 0.5f * (ca+1.f)};
|
|
||||||
const float ZH2_norm{scale * 0.5f * (ca+1.f)*ca};
|
|
||||||
const float ZH3_norm{scale * 0.125f * (ca+1.f)*(5.f*ca*ca-1.f)};
|
|
||||||
|
|
||||||
/* Zeroth-order */
|
|
||||||
coeffs[0] *= ZH0_norm;
|
|
||||||
/* First-order */
|
|
||||||
coeffs[1] *= ZH1_norm;
|
|
||||||
coeffs[2] *= ZH1_norm;
|
|
||||||
coeffs[3] *= ZH1_norm;
|
|
||||||
/* Second-order */
|
|
||||||
coeffs[4] *= ZH2_norm;
|
|
||||||
coeffs[5] *= ZH2_norm;
|
|
||||||
coeffs[6] *= ZH2_norm;
|
|
||||||
coeffs[7] *= ZH2_norm;
|
|
||||||
coeffs[8] *= ZH2_norm;
|
|
||||||
/* Third-order */
|
|
||||||
coeffs[9] *= ZH3_norm;
|
|
||||||
coeffs[10] *= ZH3_norm;
|
|
||||||
coeffs[11] *= ZH3_norm;
|
|
||||||
coeffs[12] *= ZH3_norm;
|
|
||||||
coeffs[13] *= ZH3_norm;
|
|
||||||
coeffs[14] *= ZH3_norm;
|
|
||||||
coeffs[15] *= ZH3_norm;
|
|
||||||
}
|
|
||||||
|
|
||||||
return coeffs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain,
|
|
||||||
const al::span<float,MAX_OUTPUT_CHANNELS> 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; }
|
|
||||||
);
|
|
||||||
std::fill(iter, gains.end(), 0.0f);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef AL_BIT_H
|
#ifndef AL_BIT_H
|
||||||
#define AL_BIT_H
|
#define AL_BIT_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#if !defined(__GNUC__) && (defined(_WIN32) || defined(_WIN64))
|
#if !defined(__GNUC__) && (defined(_WIN32) || defined(_WIN64))
|
||||||
|
@ -73,6 +74,17 @@ int> countr_zero(T val) noexcept
|
||||||
* they're good enough if the GCC built-ins aren't available.
|
* they're good enough if the GCC built-ins aren't available.
|
||||||
*/
|
*/
|
||||||
namespace detail_ {
|
namespace detail_ {
|
||||||
|
template<typename T, size_t = std::numeric_limits<T>::digits>
|
||||||
|
struct fast_utype { };
|
||||||
|
template<typename T>
|
||||||
|
struct fast_utype<T,8> { using type = std::uint_fast8_t; };
|
||||||
|
template<typename T>
|
||||||
|
struct fast_utype<T,16> { using type = std::uint_fast16_t; };
|
||||||
|
template<typename T>
|
||||||
|
struct fast_utype<T,32> { using type = std::uint_fast32_t; };
|
||||||
|
template<typename T>
|
||||||
|
struct fast_utype<T,64> { using type = std::uint_fast64_t; };
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
constexpr T repbits(unsigned char bits) noexcept
|
constexpr T repbits(unsigned char bits) noexcept
|
||||||
{
|
{
|
||||||
|
@ -85,17 +97,18 @@ namespace detail_ {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
constexpr std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
|
constexpr std::enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value,
|
||||||
int> popcount(T v) noexcept
|
int> popcount(T val) noexcept
|
||||||
{
|
{
|
||||||
constexpr T m55{detail_::repbits<T>(0x55)};
|
using fast_type = typename detail_::fast_utype<T>::type;
|
||||||
constexpr T m33{detail_::repbits<T>(0x33)};
|
constexpr fast_type m55{detail_::repbits<fast_type>(0x55)};
|
||||||
constexpr T m0f{detail_::repbits<T>(0x0f)};
|
constexpr fast_type m33{detail_::repbits<fast_type>(0x33)};
|
||||||
constexpr T m01{detail_::repbits<T>(0x01)};
|
constexpr fast_type m0f{detail_::repbits<fast_type>(0x0f)};
|
||||||
|
constexpr fast_type m01{detail_::repbits<fast_type>(0x01)};
|
||||||
|
|
||||||
v = v - ((v >> 1) & m55);
|
auto v = val - ((fast_type{val} >> 1) & m55);
|
||||||
v = (v & m33) + ((v >> 2) & m33);
|
v = (v & m33) + ((v >> 2) & m33);
|
||||||
v = (v + (v >> 4)) & m0f;
|
v = (v + (v >> 4)) & m0f;
|
||||||
return static_cast<int>((v * m01) >> ((sizeof(T)-1)*8));
|
return static_cast<int>(((v * m01) >> ((sizeof(T)-1)*8)) & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
|
|
|
@ -2,23 +2,10 @@
|
||||||
#define AL_STRING_H
|
#define AL_STRING_H
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "almalloc.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace al {
|
namespace al {
|
||||||
|
|
||||||
template<typename T, typename Tr=std::char_traits<T>>
|
|
||||||
using basic_string = std::basic_string<T, Tr, al::allocator<T>>;
|
|
||||||
|
|
||||||
using string = basic_string<char>;
|
|
||||||
using wstring = basic_string<wchar_t>;
|
|
||||||
using u16string = basic_string<char16_t>;
|
|
||||||
using u32string = basic_string<char32_t>;
|
|
||||||
|
|
||||||
|
|
||||||
/* These would be better served by using a string_view-like span/view with
|
/* These would be better served by using a string_view-like span/view with
|
||||||
* case-insensitive char traits.
|
* case-insensitive char traits.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,8 +2,29 @@
|
||||||
#define AL_ATOMIC_H
|
#define AL_ATOMIC_H
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
|
namespace al {
|
||||||
|
|
||||||
|
struct atomic_invflag : protected std::atomic_flag {
|
||||||
|
atomic_invflag() noexcept = default;
|
||||||
|
template<typename T>
|
||||||
|
atomic_invflag(T&& arg) noexcept : std::atomic_flag{std::forward<T>(arg)} { }
|
||||||
|
|
||||||
|
inline bool test_and_clear(std::memory_order m=std::memory_order_seq_cst) noexcept
|
||||||
|
{ return !test_and_set(m); }
|
||||||
|
inline bool test_and_clear(std::memory_order m=std::memory_order_seq_cst) volatile noexcept
|
||||||
|
{ return !test_and_set(m); }
|
||||||
|
|
||||||
|
inline void set(std::memory_order m=std::memory_order_seq_cst) noexcept
|
||||||
|
{ clear(m); }
|
||||||
|
inline void set(std::memory_order m=std::memory_order_seq_cst) volatile noexcept
|
||||||
|
{ clear(m); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace al
|
||||||
|
|
||||||
using RefCount = std::atomic<unsigned int>;
|
using RefCount = std::atomic<unsigned int>;
|
||||||
|
|
||||||
inline void InitRef(RefCount &ref, unsigned int value)
|
inline void InitRef(RefCount &ref, unsigned int value)
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
#ifndef COMMON_COMPTR_H
|
||||||
|
#define COMMON_COMPTR_H
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class ComPtr {
|
||||||
|
T *mPtr{nullptr};
|
||||||
|
|
||||||
|
public:
|
||||||
|
ComPtr() noexcept = default;
|
||||||
|
ComPtr(const ComPtr &rhs) : 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)
|
||||||
|
{
|
||||||
|
if(!rhs.mPtr)
|
||||||
|
{
|
||||||
|
if(mPtr)
|
||||||
|
mPtr->Release();
|
||||||
|
mPtr = nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rhs.mPtr->AddRef();
|
||||||
|
try {
|
||||||
|
if(mPtr)
|
||||||
|
mPtr->Release();
|
||||||
|
mPtr = rhs.mPtr;
|
||||||
|
}
|
||||||
|
catch(...) {
|
||||||
|
rhs.mPtr->Release();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ComPtr& operator=(ComPtr&& rhs)
|
||||||
|
{
|
||||||
|
if(mPtr)
|
||||||
|
mPtr->Release();
|
||||||
|
mPtr = rhs.mPtr;
|
||||||
|
rhs.mPtr = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
T *ret{mPtr};
|
||||||
|
mPtr = nullptr;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(ComPtr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
|
||||||
|
void swap(ComPtr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,347 @@
|
||||||
|
#ifndef PHASE_SHIFTER_H
|
||||||
|
#define PHASE_SHIFTER_H
|
||||||
|
|
||||||
|
#ifdef HAVE_SSE_INTRINSICS
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#elif defined(HAVE_NEON)
|
||||||
|
#include <arm_neon.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "alcomplex.h"
|
||||||
|
#include "alspan.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
template<size_t FilterSize>
|
||||||
|
struct PhaseShifterT {
|
||||||
|
static_assert(FilterSize >= 16, "FilterSize needs to be at least 16");
|
||||||
|
static_assert((FilterSize&(FilterSize-1)) == 0, "FilterSize needs to be power-of-two");
|
||||||
|
|
||||||
|
alignas(16) std::array<float,FilterSize/2> mCoeffs{};
|
||||||
|
|
||||||
|
/* Some notes on this filter construction.
|
||||||
|
*
|
||||||
|
* A wide-band phase-shift filter needs a delay to maintain linearity. A
|
||||||
|
* dirac impulse in the center of a time-domain buffer represents a filter
|
||||||
|
* passing all frequencies through as-is with a pure delay. Converting that
|
||||||
|
* to the frequency domain, adjusting the phase of each frequency bin by
|
||||||
|
* +90 degrees, then converting back to the time domain, results in a FIR
|
||||||
|
* filter that applies a +90 degree wide-band phase-shift.
|
||||||
|
*
|
||||||
|
* A particularly notable aspect of the time-domain filter response is that
|
||||||
|
* every other coefficient is 0. This allows doubling the effective size of
|
||||||
|
* the filter, by storing only the non-0 coefficients and double-stepping
|
||||||
|
* over the input to apply it.
|
||||||
|
*
|
||||||
|
* Additionally, the resulting filter is independent of the sample rate.
|
||||||
|
* The same filter can be applied regardless of the device's sample rate
|
||||||
|
* and achieve the same effect.
|
||||||
|
*/
|
||||||
|
PhaseShifterT()
|
||||||
|
{
|
||||||
|
using complex_d = std::complex<double>;
|
||||||
|
constexpr size_t fft_size{FilterSize};
|
||||||
|
constexpr size_t half_size{fft_size / 2};
|
||||||
|
|
||||||
|
auto fftBuffer = std::make_unique<complex_d[]>(fft_size);
|
||||||
|
std::fill_n(fftBuffer.get(), fft_size, complex_d{});
|
||||||
|
fftBuffer[half_size] = 1.0;
|
||||||
|
|
||||||
|
forward_fft({fftBuffer.get(), fft_size});
|
||||||
|
for(size_t i{0};i < half_size+1;++i)
|
||||||
|
fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()};
|
||||||
|
for(size_t i{half_size+1};i < fft_size;++i)
|
||||||
|
fftBuffer[i] = std::conj(fftBuffer[fft_size - i]);
|
||||||
|
inverse_fft({fftBuffer.get(), fft_size});
|
||||||
|
|
||||||
|
auto fftiter = fftBuffer.get() + half_size + (FilterSize/2 - 1);
|
||||||
|
for(float &coeff : mCoeffs)
|
||||||
|
{
|
||||||
|
coeff = static_cast<float>(fftiter->real() / double{fft_size});
|
||||||
|
fftiter -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void process(al::span<float> dst, const float *RESTRICT src) const;
|
||||||
|
void processAccum(al::span<float> dst, const float *RESTRICT src) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<size_t S>
|
||||||
|
inline void PhaseShifterT<S>::process(al::span<float> dst, const float *RESTRICT src) const
|
||||||
|
{
|
||||||
|
#ifdef HAVE_SSE_INTRINSICS
|
||||||
|
if(size_t todo{dst.size()>>1})
|
||||||
|
{
|
||||||
|
auto *out = reinterpret_cast<__m64*>(dst.data());
|
||||||
|
do {
|
||||||
|
__m128 r04{_mm_setzero_ps()};
|
||||||
|
__m128 r14{_mm_setzero_ps()};
|
||||||
|
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||||
|
{
|
||||||
|
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||||
|
const __m128 s0{_mm_loadu_ps(&src[j*2])};
|
||||||
|
const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])};
|
||||||
|
|
||||||
|
__m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))};
|
||||||
|
r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs));
|
||||||
|
|
||||||
|
s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1));
|
||||||
|
r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs));
|
||||||
|
}
|
||||||
|
src += 2;
|
||||||
|
|
||||||
|
__m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))};
|
||||||
|
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||||
|
|
||||||
|
_mm_storel_pi(out, r4);
|
||||||
|
++out;
|
||||||
|
} while(--todo);
|
||||||
|
}
|
||||||
|
if((dst.size()&1))
|
||||||
|
{
|
||||||
|
__m128 r4{_mm_setzero_ps()};
|
||||||
|
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||||
|
{
|
||||||
|
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||||
|
const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||||
|
r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs));
|
||||||
|
}
|
||||||
|
r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
|
||||||
|
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||||
|
|
||||||
|
dst.back() = _mm_cvtss_f32(r4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(HAVE_NEON)
|
||||||
|
|
||||||
|
size_t pos{0};
|
||||||
|
if(size_t todo{dst.size()>>1})
|
||||||
|
{
|
||||||
|
/* There doesn't seem to be NEON intrinsics to do this kind of stipple
|
||||||
|
* shuffling, so there's two custom methods for it.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
auto unpacklo = [](float32x4_t a, float32x4_t b)
|
||||||
|
{
|
||||||
|
float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))};
|
||||||
|
return vcombine_f32(result.val[0], result.val[1]);
|
||||||
|
};
|
||||||
|
auto unpackhi = [](float32x4_t a, float32x4_t b)
|
||||||
|
{
|
||||||
|
float32x2x2_t result{vzip_f32(vget_high_f32(a), vget_high_f32(b))};
|
||||||
|
return vcombine_f32(result.val[0], result.val[1]);
|
||||||
|
};
|
||||||
|
do {
|
||||||
|
float32x4_t r04{vdupq_n_f32(0.0f)};
|
||||||
|
float32x4_t r14{vdupq_n_f32(0.0f)};
|
||||||
|
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||||
|
{
|
||||||
|
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])};
|
||||||
|
|
||||||
|
r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs);
|
||||||
|
r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs);
|
||||||
|
}
|
||||||
|
src += 2;
|
||||||
|
|
||||||
|
float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))};
|
||||||
|
float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))};
|
||||||
|
|
||||||
|
vst1_f32(&dst[pos], r2);
|
||||||
|
pos += 2;
|
||||||
|
} while(--todo);
|
||||||
|
}
|
||||||
|
if((dst.size()&1))
|
||||||
|
{
|
||||||
|
auto load4 = [](float32_t a, float32_t b, float32_t c, float32_t d)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
float32x4_t r4{vdupq_n_f32(0.0f)};
|
||||||
|
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||||
|
{
|
||||||
|
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
|
||||||
|
const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||||
|
r4 = vmlaq_f32(r4, s, coeffs);
|
||||||
|
}
|
||||||
|
r4 = vaddq_f32(r4, vrev64q_f32(r4));
|
||||||
|
dst[pos] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
for(float &output : dst)
|
||||||
|
{
|
||||||
|
float ret{0.0f};
|
||||||
|
for(size_t j{0};j < mCoeffs.size();++j)
|
||||||
|
ret += src[j*2] * mCoeffs[j];
|
||||||
|
|
||||||
|
output = ret;
|
||||||
|
++src;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t S>
|
||||||
|
inline void PhaseShifterT<S>::processAccum(al::span<float> dst, const float *RESTRICT src) const
|
||||||
|
{
|
||||||
|
#ifdef HAVE_SSE_INTRINSICS
|
||||||
|
if(size_t todo{dst.size()>>1})
|
||||||
|
{
|
||||||
|
auto *out = reinterpret_cast<__m64*>(dst.data());
|
||||||
|
do {
|
||||||
|
__m128 r04{_mm_setzero_ps()};
|
||||||
|
__m128 r14{_mm_setzero_ps()};
|
||||||
|
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||||
|
{
|
||||||
|
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||||
|
const __m128 s0{_mm_loadu_ps(&src[j*2])};
|
||||||
|
const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])};
|
||||||
|
|
||||||
|
__m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))};
|
||||||
|
r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs));
|
||||||
|
|
||||||
|
s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1));
|
||||||
|
r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs));
|
||||||
|
}
|
||||||
|
src += 2;
|
||||||
|
|
||||||
|
__m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))};
|
||||||
|
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||||
|
|
||||||
|
_mm_storel_pi(out, _mm_add_ps(_mm_loadl_pi(_mm_undefined_ps(), out), r4));
|
||||||
|
++out;
|
||||||
|
} while(--todo);
|
||||||
|
}
|
||||||
|
if((dst.size()&1))
|
||||||
|
{
|
||||||
|
__m128 r4{_mm_setzero_ps()};
|
||||||
|
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||||
|
{
|
||||||
|
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||||
|
/* NOTE: This could alternatively be done with two unaligned loads
|
||||||
|
* and a shuffle. Which would be better?
|
||||||
|
*/
|
||||||
|
const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||||
|
r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs));
|
||||||
|
}
|
||||||
|
r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
|
||||||
|
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||||
|
|
||||||
|
dst.back() += _mm_cvtss_f32(r4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(HAVE_NEON)
|
||||||
|
|
||||||
|
size_t pos{0};
|
||||||
|
if(size_t todo{dst.size()>>1})
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
auto unpacklo = [](float32x4_t a, float32x4_t b)
|
||||||
|
{
|
||||||
|
float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))};
|
||||||
|
return vcombine_f32(result.val[0], result.val[1]);
|
||||||
|
};
|
||||||
|
auto unpackhi = [](float32x4_t a, float32x4_t b)
|
||||||
|
{
|
||||||
|
float32x2x2_t result{vzip_f32(vget_high_f32(a), vget_high_f32(b))};
|
||||||
|
return vcombine_f32(result.val[0], result.val[1]);
|
||||||
|
};
|
||||||
|
do {
|
||||||
|
float32x4_t r04{vdupq_n_f32(0.0f)};
|
||||||
|
float32x4_t r14{vdupq_n_f32(0.0f)};
|
||||||
|
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||||
|
{
|
||||||
|
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])};
|
||||||
|
|
||||||
|
r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs);
|
||||||
|
r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs);
|
||||||
|
}
|
||||||
|
src += 2;
|
||||||
|
|
||||||
|
float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))};
|
||||||
|
float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))};
|
||||||
|
|
||||||
|
vst1_f32(&dst[pos], vadd_f32(vld1_f32(&dst[pos]), r2));
|
||||||
|
pos += 2;
|
||||||
|
} while(--todo);
|
||||||
|
}
|
||||||
|
if((dst.size()&1))
|
||||||
|
{
|
||||||
|
auto load4 = [](float32_t a, float32_t b, float32_t c, float32_t d)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
float32x4_t r4{vdupq_n_f32(0.0f)};
|
||||||
|
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||||
|
{
|
||||||
|
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
|
||||||
|
const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||||
|
r4 = vmlaq_f32(r4, s, coeffs);
|
||||||
|
}
|
||||||
|
r4 = vaddq_f32(r4, vrev64q_f32(r4));
|
||||||
|
dst[pos] += vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
for(float &output : dst)
|
||||||
|
{
|
||||||
|
float ret{0.0f};
|
||||||
|
for(size_t j{0};j < mCoeffs.size();++j)
|
||||||
|
ret += src[j*2] * mCoeffs[j];
|
||||||
|
|
||||||
|
output += ret;
|
||||||
|
++src;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* PHASE_SHIFTER_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue