Update openal

This commit is contained in:
halx99 2021-05-14 10:15:42 +08:00
parent 529895baff
commit 02cb09c6e2
145 changed files with 9092 additions and 2375 deletions

2
external/README.md vendored
View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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);
} }
} }

View File

@ -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};

View File

@ -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);

View File

@ -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,
}; };

View File

@ -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)};

View File

@ -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 {

View File

@ -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");

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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; }

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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"

View File

@ -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);

View File

@ -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"

View File

@ -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)};

View File

@ -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)

View File

@ -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()
}; };

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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"

View File

@ -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
{ {

View File

@ -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;

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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()

View File

@ -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;

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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()

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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, &params, 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}};

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

View File

@ -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}};

View File

@ -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();
}; };

387
external/openal/alc/context.cpp vendored Normal file
View File

@ -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);
}
}

167
external/openal/alc/context.h vendored Normal file
View File

@ -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 */

76
external/openal/alc/device.cpp vendored Normal file
View File

@ -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);
}
}

110
external/openal/alc/device.h vendored Normal file
View File

@ -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

View File

@ -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)};

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;
} }

View File

@ -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);

View File

@ -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),

View File

@ -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);
} }

View File

@ -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;

View File

@ -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);

View File

@ -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}));

View File

@ -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*/)
{ {
} }

View File

@ -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};

View File

@ -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,

View File

@ -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}));

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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)

View File

@ -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.
*/ */

View File

@ -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)

70
external/openal/common/comptr.h vendored Normal file
View File

@ -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

347
external/openal/common/phase_shifter.h vendored Normal file
View File

@ -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