mirror of https://github.com/axmolengine/axmol.git
Update openal
This commit is contained in:
parent
529895baff
commit
02cb09c6e2
|
@ -138,7 +138,7 @@
|
|||
|
||||
## OpenAL Soft
|
||||
- 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
|
||||
|
||||
## OpenSSL
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Portions of this software are licensed under the BSD 3-Clause license.
|
||||
|
||||
Copyright (c) 2015, Archontis Politis
|
||||
Copyright (c) 2019, Anis A. Hireche
|
||||
Copyright (c) 2019, Christopher Robinson
|
||||
All rights reserved.
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
|
||||
if(APPLE)
|
||||
# The workaround for try_compile failing with code signing
|
||||
# since cmake-3.18.2, not required
|
||||
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
|
||||
|
@ -10,14 +11,15 @@ set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
|
|||
"CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO)
|
||||
endif()
|
||||
|
||||
# Fix compile failure with armv7 deployment target >= 11.0, xcode clang will
|
||||
# report:
|
||||
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_SYSTEM_NAME STREQUAL "iOS")
|
||||
if("${CMAKE_OSX_ARCHITECTURES}" MATCHES ".*armv7.*")
|
||||
if(NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET
|
||||
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_NEON 0)
|
||||
|
||||
# Check for SSE+SSE2 support
|
||||
# Check for SSE support
|
||||
option(ALSOFT_REQUIRE_SSE "Require SSE support" OFF)
|
||||
option(ALSOFT_REQUIRE_SSE2 "Require SSE2 support" OFF)
|
||||
if(HAVE_XMMINTRIN_H AND HAVE_EMMINTRIN_H)
|
||||
if(HAVE_XMMINTRIN_H)
|
||||
option(ALSOFT_CPUEXT_SSE "Enable SSE support" ON)
|
||||
option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON)
|
||||
if(ALSOFT_CPUEXT_SSE AND ALSOFT_CPUEXT_SSE2)
|
||||
if(ALSOFT_CPUEXT_SSE)
|
||||
set(HAVE_SSE 1)
|
||||
set(HAVE_SSE2 1)
|
||||
endif()
|
||||
endif()
|
||||
if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE)
|
||||
message(FATAL_ERROR "Failed to enabled required SSE CPU extensions")
|
||||
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)
|
||||
message(FATAL_ERROR "Failed to enable required SSE2 CPU extensions")
|
||||
endif()
|
||||
|
@ -622,11 +629,13 @@ set(COMMON_OBJS
|
|||
common/alstring.cpp
|
||||
common/alstring.h
|
||||
common/atomic.h
|
||||
common/comptr.h
|
||||
common/dynload.cpp
|
||||
common/dynload.h
|
||||
common/intrusive_ptr.h
|
||||
common/math_defs.h
|
||||
common/opthelpers.h
|
||||
common/phase_shifter.h
|
||||
common/polyphase_resampler.cpp
|
||||
common/polyphase_resampler.h
|
||||
common/pragmadefs.h
|
||||
|
@ -643,17 +652,29 @@ set(COMMON_OBJS
|
|||
set(CORE_OBJS
|
||||
core/ambdec.cpp
|
||||
core/ambdec.h
|
||||
core/ambidefs.cpp
|
||||
core/ambidefs.h
|
||||
core/async_event.h
|
||||
core/bformatdec.cpp
|
||||
core/bformatdec.h
|
||||
core/bs2b.cpp
|
||||
core/bs2b.h
|
||||
core/bsinc_defs.h
|
||||
core/bsinc_tables.cpp
|
||||
core/bsinc_tables.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.h
|
||||
core/devformat.cpp
|
||||
core/devformat.h
|
||||
core/device.cpp
|
||||
core/device.h
|
||||
core/except.cpp
|
||||
core/except.h
|
||||
core/filters/biquad.h
|
||||
|
@ -666,12 +687,47 @@ set(CORE_OBJS
|
|||
core/fmt_traits.h
|
||||
core/fpu_ctrl.cpp
|
||||
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.h
|
||||
core/mastering.cpp
|
||||
core/mastering.h
|
||||
core/mixer.cpp
|
||||
core/mixer.h
|
||||
core/resampler_limits.h
|
||||
core/uhjfilter.cpp
|
||||
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/hrtfbase.h
|
||||
core/mixer/hrtfdefs.h
|
||||
|
@ -715,20 +771,14 @@ set(OPENAL_OBJS
|
|||
# ALC and related routines
|
||||
set(ALC_OBJS
|
||||
alc/alc.cpp
|
||||
alc/alcmain.h
|
||||
alc/alu.cpp
|
||||
alc/alu.h
|
||||
alc/alconfig.cpp
|
||||
alc/alconfig.h
|
||||
alc/alcontext.h
|
||||
alc/async_event.h
|
||||
alc/bformatdec.cpp
|
||||
alc/bformatdec.h
|
||||
alc/buffer_storage.cpp
|
||||
alc/buffer_storage.h
|
||||
alc/compat.h
|
||||
alc/converter.cpp
|
||||
alc/converter.h
|
||||
alc/context.cpp
|
||||
alc/context.h
|
||||
alc/device.cpp
|
||||
alc/device.h
|
||||
alc/effectslot.cpp
|
||||
alc/effectslot.h
|
||||
alc/effects/base.h
|
||||
|
@ -746,22 +796,18 @@ set(ALC_OBJS
|
|||
alc/effects/pshifter.cpp
|
||||
alc/effects/reverb.cpp
|
||||
alc/effects/vmorpher.cpp
|
||||
alc/front_stablizer.h
|
||||
alc/helpers.cpp
|
||||
alc/hrtf.cpp
|
||||
alc/hrtf.h
|
||||
alc/inprogext.h
|
||||
alc/panning.cpp
|
||||
alc/uiddefs.cpp
|
||||
alc/voice.cpp
|
||||
alc/voice.h
|
||||
alc/voice_change.h)
|
||||
alc/panning.cpp)
|
||||
|
||||
# Include SIMD mixers
|
||||
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)
|
||||
set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse.cpp core/mixer/mixer_sse2.cpp)
|
||||
set(CPU_EXTS "${CPU_EXTS}, SSE, SSE2")
|
||||
set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse2.cpp)
|
||||
set(CPU_EXTS "${CPU_EXTS}, SSE2")
|
||||
endif()
|
||||
if(HAVE_SSE3)
|
||||
set(CORE_OBJS ${CORE_OBJS} core/mixer/mixer_sse3.cpp)
|
||||
|
@ -1149,10 +1195,13 @@ if(ALSOFT_EMBED_HRTF_DATA)
|
|||
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)
|
||||
endif()
|
||||
if(ALSOFT_EXAMPLES)
|
||||
endif()
|
||||
if(ALSOFT_UTILS OR ALSOFT_EXAMPLES)
|
||||
find_package(SndFile)
|
||||
find_package(SDL2)
|
||||
if(SDL2_FOUND)
|
||||
|
@ -1233,7 +1282,8 @@ else()
|
|||
router/al.cpp
|
||||
)
|
||||
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_link_libraries(OpenAL PRIVATE common ${LINKER_FLAGS})
|
||||
target_include_directories(OpenAL
|
||||
|
@ -1309,7 +1359,6 @@ target_include_directories(${IMPL_TARGET}
|
|||
${INC_PATHS}
|
||||
${OpenAL_BINARY_DIR}
|
||||
${OpenAL_SOURCE_DIR}
|
||||
${OpenAL_SOURCE_DIR}/alc
|
||||
${OpenAL_SOURCE_DIR}/common
|
||||
)
|
||||
|
||||
|
@ -1318,7 +1367,8 @@ set_target_properties(${IMPL_TARGET} PROPERTIES OUTPUT_NAME ${LIBNAME}
|
|||
SOVERSION ${LIB_MAJOR_VERSION}
|
||||
)
|
||||
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})
|
||||
|
||||
if(TARGET build_version)
|
||||
|
@ -1430,7 +1480,16 @@ if(ALSOFT_UTILS)
|
|||
set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info)
|
||||
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)
|
||||
set(SOFA_SUPPORT_SRCS
|
||||
utils/sofa-support.cpp
|
||||
|
@ -1476,7 +1535,7 @@ if(ALSOFT_UTILS)
|
|||
message(STATUS "")
|
||||
endif()
|
||||
|
||||
if(ALSOFT_EXAMPLES)
|
||||
|
||||
# Add a static library with common functions used by multiple example targets
|
||||
add_library(ex-common STATIC EXCLUDE_FROM_ALL
|
||||
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_compile_options(ex-common PUBLIC ${C_FLAGS})
|
||||
target_link_libraries(ex-common PUBLIC OpenAL PRIVATE ${RT_LIB})
|
||||
endif()
|
||||
|
||||
if(ALSOFT_EXAMPLES)
|
||||
add_executable(altonegen examples/altonegen.c)
|
||||
|
|
|
@ -36,18 +36,18 @@
|
|||
#include "AL/efx.h"
|
||||
|
||||
#include "albit.h"
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/alu.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "alu.h"
|
||||
#include "buffer.h"
|
||||
#include "core/except.h"
|
||||
#include "core/fpu_ctrl.h"
|
||||
#include "core/logging.h"
|
||||
#include "effect.h"
|
||||
#include "inprogext.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
|
@ -308,7 +308,7 @@ void FreeEffectSlot(ALCcontext *context, ALeffectslot *slot)
|
|||
&& slot->mState == SlotState::Playing) \
|
||||
slot->updateProps(context.get()); \
|
||||
else \
|
||||
slot->PropsClean.clear(std::memory_order_release); \
|
||||
slot->mPropsDirty.set(std::memory_order_release); \
|
||||
} while(0)
|
||||
|
||||
} // namespace
|
||||
|
@ -325,7 +325,7 @@ START_API_FUNC
|
|||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
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());
|
||||
|
||||
AddActiveEffectSlots({&slot, 1}, context.get());
|
||||
|
@ -491,7 +491,7 @@ START_API_FUNC
|
|||
|
||||
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());
|
||||
}
|
||||
slots[i] = slot;
|
||||
|
@ -571,7 +571,7 @@ START_API_FUNC
|
|||
switch(param)
|
||||
{
|
||||
case AL_EFFECTSLOT_EFFECT:
|
||||
device = context->mDevice.get();
|
||||
device = context->mALDevice.get();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> ___{device->EffectLock};
|
||||
|
@ -587,8 +587,12 @@ START_API_FUNC
|
|||
}
|
||||
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());
|
||||
slot->mState = SlotState::Playing;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -596,6 +600,8 @@ START_API_FUNC
|
|||
if(!(value == AL_TRUE || value == AL_FALSE))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,,
|
||||
"Effect slot auxiliary send auto out of range");
|
||||
if UNLIKELY(slot->AuxSendAuto == !!value)
|
||||
return;
|
||||
slot->AuxSendAuto = !!value;
|
||||
break;
|
||||
|
||||
|
@ -603,6 +609,8 @@ START_API_FUNC
|
|||
target = LookupEffectSlot(context.get(), static_cast<ALuint>(value));
|
||||
if(value && !target)
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid effect slot target ID");
|
||||
if UNLIKELY(slot->Target == target)
|
||||
return;
|
||||
if(target)
|
||||
{
|
||||
ALeffectslot *checker{target};
|
||||
|
@ -631,12 +639,20 @@ START_API_FUNC
|
|||
break;
|
||||
|
||||
case AL_BUFFER:
|
||||
device = context->mDevice.get();
|
||||
device = context->mALDevice.get();
|
||||
|
||||
if(slot->mState == SlotState::Playing)
|
||||
SETERR_RETURN(context, AL_INVALID_OPERATION,,
|
||||
"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};
|
||||
ALbuffer *buffer{};
|
||||
|
@ -720,6 +736,8 @@ START_API_FUNC
|
|||
case AL_EFFECTSLOT_GAIN:
|
||||
if(!(value >= 0.0f && value <= 1.0f))
|
||||
SETERR_RETURN(context, AL_INVALID_VALUE,, "Effect slot gain out of range");
|
||||
if UNLIKELY(slot->Gain == value)
|
||||
return;
|
||||
slot->Gain = value;
|
||||
break;
|
||||
|
||||
|
@ -884,7 +902,7 @@ END_API_FUNC
|
|||
|
||||
ALeffectslot::ALeffectslot()
|
||||
{
|
||||
PropsClean.test_and_set(std::memory_order_relaxed);
|
||||
mPropsDirty.test_and_clear(std::memory_order_relaxed);
|
||||
|
||||
EffectStateFactory *factory{getFactoryByType(EffectSlotType::None)};
|
||||
assert(factory != nullptr);
|
||||
|
@ -928,7 +946,7 @@ ALenum ALeffectslot::initEffect(ALeffect *effect, ALCcontext *context)
|
|||
}
|
||||
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};
|
||||
state->mOutTarget = device->Dry.Buffer;
|
||||
{
|
||||
|
@ -1004,7 +1022,7 @@ void UpdateAllEffectSlotProps(ALCcontext *context)
|
|||
usemask &= ~(1_u64 << idx);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
#include "AL/alc.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 "atomic.h"
|
||||
#include "effectslot.h"
|
||||
#include "effects/base.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "vector.h"
|
||||
|
||||
|
@ -40,7 +40,7 @@ struct ALeffectslot {
|
|||
al::intrusive_ptr<EffectState> State;
|
||||
} Effect;
|
||||
|
||||
std::atomic_flag PropsClean;
|
||||
al::atomic_invflag mPropsDirty;
|
||||
|
||||
SlotState mState{SlotState::Initial};
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <mutex>
|
||||
#include <new>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
|
@ -43,14 +44,16 @@
|
|||
|
||||
#include "albit.h"
|
||||
#include "albyte.h"
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "atomic.h"
|
||||
#include "core/except.h"
|
||||
#include "inprogext.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/voice.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
||||
|
@ -271,6 +274,9 @@ ALuint ChannelsFromUserFmt(UserFmtChannels chans, ALuint ambiorder) noexcept
|
|||
case UserFmtX71: return 8;
|
||||
case UserFmtBFormat2D: return (ambiorder*2) + 1;
|
||||
case UserFmtBFormat3D: return (ambiorder+1) * (ambiorder+1);
|
||||
case UserFmtUHJ2: return 2;
|
||||
case UserFmtUHJ3: return 3;
|
||||
case UserFmtUHJ4: return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -465,6 +471,9 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
|
|||
case UserFmtX71: DstChannels = FmtX71; break;
|
||||
case UserFmtBFormat2D: DstChannels = FmtBFormat2D; 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))
|
||||
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));
|
||||
|
||||
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))
|
||||
{
|
||||
|
@ -622,6 +633,9 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
|
|||
case UserFmtX71: DstChannels = FmtX71; break;
|
||||
case UserFmtBFormat2D: DstChannels = FmtBFormat2D; 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))
|
||||
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");
|
||||
|
||||
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) *
|
||||
size_t{BufferLineSize + (MaxResamplerPadding>>1)}).swap(ALBuf->mData);
|
||||
size_t{line_size}).swap(ALBuf->mData);
|
||||
|
||||
ALBuf->mCallback = callback;
|
||||
ALBuf->mUserData = userptr;
|
||||
|
@ -675,7 +692,7 @@ al::optional<DecompResult> DecomposeUserFormat(ALenum format)
|
|||
UserFmtChannels channels;
|
||||
UserFmtType type;
|
||||
};
|
||||
static const std::array<FormatMap,46> UserFmtList{{
|
||||
static const std::array<FormatMap,55> UserFmtList{{
|
||||
{ AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte },
|
||||
{ AL_FORMAT_MONO16, UserFmtMono, UserFmtShort },
|
||||
{ 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_FLOAT32, UserFmtBFormat3D, UserFmtFloat },
|
||||
{ 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)
|
||||
|
@ -754,7 +783,7 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Generating %d buffers", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if(!EnsureBuffers(device, static_cast<ALuint>(n)))
|
||||
{
|
||||
|
@ -794,7 +823,7 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Deleting %d buffers", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
/* First try to find any buffers that are invalid or in-use. */
|
||||
|
@ -834,7 +863,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if LIKELY(context)
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if(!buffer || LookupBuffer(device, buffer))
|
||||
return AL_TRUE;
|
||||
|
@ -855,7 +884,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
@ -889,7 +918,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return nullptr;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
@ -942,7 +971,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
@ -965,7 +994,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
@ -997,7 +1026,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
@ -1127,7 +1156,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
@ -1147,7 +1176,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
@ -1166,7 +1195,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
@ -1188,7 +1217,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
@ -1250,7 +1279,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
@ -1283,7 +1312,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
@ -1321,7 +1350,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
@ -1343,7 +1372,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
@ -1371,7 +1400,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
|
@ -1393,7 +1422,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
if UNLIKELY(!albuf)
|
||||
|
@ -1450,7 +1479,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
||||
|
@ -1488,7 +1517,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
if UNLIKELY(!albuf)
|
||||
|
@ -1516,7 +1545,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
|
@ -1546,7 +1575,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
ALbuffer *albuf = LookupBuffer(device, buffer);
|
||||
if UNLIKELY(!albuf)
|
||||
|
@ -1574,7 +1603,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
||||
|
@ -1602,7 +1631,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->BufferLock};
|
||||
if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
|
||||
context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
#include "AL/al.h"
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "buffer_storage.h"
|
||||
#include "inprogext.h"
|
||||
#include "core/buffer_storage.h"
|
||||
#include "vector.h"
|
||||
|
||||
|
||||
|
@ -35,6 +35,9 @@ enum UserFmtChannels : unsigned char {
|
|||
UserFmtX71 = FmtX71,
|
||||
UserFmtBFormat2D = FmtBFormat2D,
|
||||
UserFmtBFormat3D = FmtBFormat3D,
|
||||
UserFmtUHJ2 = FmtUHJ2,
|
||||
UserFmtUHJ3 = FmtUHJ3,
|
||||
UserFmtUHJ4 = FmtUHJ4,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -39,14 +39,15 @@
|
|||
#include "AL/efx.h"
|
||||
|
||||
#include "albit.h"
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alstring.h"
|
||||
#include "core/except.h"
|
||||
#include "core/logging.h"
|
||||
#include "effects/base.h"
|
||||
#include "opthelpers.h"
|
||||
#include "vector.h"
|
||||
|
||||
|
@ -233,7 +234,7 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Generating %d effects", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
if(!EnsureEffects(device, static_cast<ALuint>(n)))
|
||||
{
|
||||
|
@ -273,7 +274,7 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Deleting %d effects", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
/* First try to find any effects that are invalid. */
|
||||
|
@ -304,7 +305,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if LIKELY(context)
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
if(!effect || LookupEffect(device, effect))
|
||||
return AL_TRUE;
|
||||
|
@ -319,7 +320,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
@ -369,7 +370,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
@ -392,7 +393,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
@ -415,7 +416,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
@ -438,7 +439,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
@ -470,7 +471,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
@ -493,7 +494,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
@ -516,7 +517,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
|
||||
const ALeffect *aleffect{LookupEffect(device, effect)};
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "effects/base.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "core/logging.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
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_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "inprogext.h"
|
||||
#include "alc/inprogext.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
#include <cmath>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -12,7 +12,11 @@ class effect_exception final : public al::base_exception {
|
|||
ALenum mErrorCode;
|
||||
|
||||
public:
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
[[gnu::format(gnu_printf, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
effect_exception(ALenum code, const char *msg, ...);
|
||||
|
||||
ALenum errorCode() const noexcept { return mErrorCode; }
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alc/effects/base.h"
|
||||
#include "aloptional.h"
|
||||
#include "effects.h"
|
||||
#include "effects/base.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "almalloc.h"
|
||||
#include "core/except.h"
|
||||
#include "core/logging.h"
|
||||
|
|
|
@ -18,17 +18,17 @@
|
|||
#include "AL/alc.h"
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "async_event.h"
|
||||
#include "core/async_event.h"
|
||||
#include "core/except.h"
|
||||
#include "core/logging.h"
|
||||
#include "effects/base.h"
|
||||
#include "inprogext.h"
|
||||
#include "core/voice_change.h"
|
||||
#include "opthelpers.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "voice_change.h"
|
||||
|
||||
|
||||
static int EventThread(ALCcontext *context)
|
||||
|
@ -75,25 +75,22 @@ static int EventThread(ALCcontext *context)
|
|||
msg += " state has changed to ";
|
||||
switch(evt.u.srcstate.state)
|
||||
{
|
||||
case VChangeState::Reset:
|
||||
case AsyncEvent::SrcState::Reset:
|
||||
msg += "AL_INITIAL";
|
||||
state = AL_INITIAL;
|
||||
break;
|
||||
case VChangeState::Stop:
|
||||
case AsyncEvent::SrcState::Stop:
|
||||
msg += "AL_STOPPED";
|
||||
state = AL_STOPPED;
|
||||
break;
|
||||
case VChangeState::Play:
|
||||
case AsyncEvent::SrcState::Play:
|
||||
msg += "AL_PLAYING";
|
||||
state = AL_PLAYING;
|
||||
break;
|
||||
case VChangeState::Pause:
|
||||
case AsyncEvent::SrcState::Pause:
|
||||
msg += "AL_PAUSED";
|
||||
state = AL_PAUSED;
|
||||
break;
|
||||
/* Shouldn't happen */
|
||||
case VChangeState::Restart:
|
||||
break;
|
||||
}
|
||||
context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.u.srcstate.id,
|
||||
state, static_cast<ALsizei>(msg.length()), msg.c_str(), context->mEventParam);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "alstring.h"
|
||||
#include "core/except.h"
|
||||
#include "opthelpers.h"
|
||||
|
|
|
@ -37,8 +37,8 @@
|
|||
#include "AL/efx.h"
|
||||
|
||||
#include "albit.h"
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/except.h"
|
||||
|
@ -52,7 +52,11 @@ class filter_exception final : public al::base_exception {
|
|||
ALenum mErrorCode;
|
||||
|
||||
public:
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
[[gnu::format(gnu_printf, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
filter_exception(ALenum code, const char *msg, ...) : mErrorCode{code}
|
||||
{
|
||||
std::va_list args;
|
||||
|
@ -404,8 +408,8 @@ START_API_FUNC
|
|||
context->setError(AL_INVALID_VALUE, "Generating %d filters", n);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->EffectLock};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
if(!EnsureFilters(device, static_cast<ALuint>(n)))
|
||||
{
|
||||
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);
|
||||
if UNLIKELY(n <= 0) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
/* First try to find any filters that are invalid. */
|
||||
|
@ -475,7 +479,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if LIKELY(context)
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
if(!filter || LookupFilter(device, filter))
|
||||
return AL_TRUE;
|
||||
|
@ -491,7 +495,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
@ -532,7 +536,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
@ -555,7 +559,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
@ -578,7 +582,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
@ -601,7 +605,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
@ -636,7 +640,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
@ -659,7 +663,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
@ -682,7 +686,7 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
std::lock_guard<std::mutex> _{device->FilterLock};
|
||||
|
||||
const ALfilter *alfilt{LookupFilter(device, filter)};
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
#include "core/except.h"
|
||||
|
@ -40,7 +40,7 @@
|
|||
if(!context->mDeferUpdates.load(std::memory_order_acquire)) \
|
||||
UpdateListenerProps(context.get()); \
|
||||
else \
|
||||
listener.PropsClean.clear(std::memory_order_release); \
|
||||
listener.mPropsDirty.set(std::memory_order_release); \
|
||||
} while(0)
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "AL/efx.h"
|
||||
|
||||
#include "almalloc.h"
|
||||
#include "atomic.h"
|
||||
|
||||
|
||||
struct ALlistener {
|
||||
|
@ -19,9 +20,9 @@ struct ALlistener {
|
|||
float Gain{1.0f};
|
||||
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()
|
||||
};
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <mutex>
|
||||
#include <new>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
|
@ -46,31 +47,31 @@
|
|||
#include "AL/efx.h"
|
||||
|
||||
#include "albit.h"
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/alu.h"
|
||||
#include "alc/backends/base.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/device.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "alspan.h"
|
||||
#include "alu.h"
|
||||
#include "atomic.h"
|
||||
#include "auxeffectslot.h"
|
||||
#include "backends/base.h"
|
||||
#include "bformatdec.h"
|
||||
#include "buffer.h"
|
||||
#include "core/ambidefs.h"
|
||||
#include "core/bformatdec.h"
|
||||
#include "core/except.h"
|
||||
#include "core/filters/nfc.h"
|
||||
#include "core/filters/splitter.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/voice_change.h"
|
||||
#include "event.h"
|
||||
#include "filter.h"
|
||||
#include "inprogext.h"
|
||||
#include "math_defs.h"
|
||||
#include "opthelpers.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
#include "voice_change.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
@ -183,7 +184,7 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context
|
|||
*/
|
||||
int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
const VoiceBufferItem *Current{};
|
||||
uint64_t readPos{};
|
||||
ALuint refcount;
|
||||
|
@ -222,7 +223,7 @@ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds
|
|||
*/
|
||||
double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime)
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
const VoiceBufferItem *Current{};
|
||||
uint64_t readPos{};
|
||||
ALuint refcount;
|
||||
|
@ -271,7 +272,7 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl
|
|||
*/
|
||||
double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
const VoiceBufferItem *Current{};
|
||||
ALuint readPos{};
|
||||
ALuint readPosFrac{};
|
||||
|
@ -349,6 +350,57 @@ double GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
|
|||
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 {
|
||||
ALuint pos, frac;
|
||||
|
@ -439,72 +491,34 @@ void InitVoice(Voice *voice, ALsource *source, ALbufferQueueItem *BufferList, AL
|
|||
std::memory_order_relaxed);
|
||||
|
||||
ALbuffer *buffer{BufferList->mBuffer};
|
||||
ALuint num_channels{buffer->channelsFromFmt()};
|
||||
ALuint num_channels{(buffer->mChannels==FmtUHJ2) ? 3 : buffer->channelsFromFmt()};
|
||||
voice->mFrequency = buffer->mSampleRate;
|
||||
voice->mFmtChannels = buffer->mChannels;
|
||||
voice->mFmtType = buffer->mType;
|
||||
voice->mSampleSize = buffer->bytesFromFmt();
|
||||
voice->mAmbiLayout = buffer->mAmbiLayout;
|
||||
voice->mAmbiScaling = buffer->mAmbiScaling;
|
||||
voice->mFrameSize = buffer->frameSizeFromFmt();
|
||||
voice->mAmbiLayout = (buffer->mChannels == FmtUHJ2 || buffer->mChannels == FmtUHJ3
|
||||
|| 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;
|
||||
|
||||
if(buffer->mCallback) voice->mFlags |= VoiceIsCallback;
|
||||
else if(source->SourceType == AL_STATIC) voice->mFlags |= VoiceIsStatic;
|
||||
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())
|
||||
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.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
|
||||
* 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);
|
||||
voice->prepare(device);
|
||||
|
||||
const BandSplitter splitter{device->mXOverFreq / static_cast<float>(device->Frequency)};
|
||||
|
||||
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);
|
||||
source->mPropsDirty.test_and_clear(std::memory_order_acq_rel);
|
||||
UpdateSourceProps(source, voice, context);
|
||||
|
||||
voice->mSourceID.store(source->id, std::memory_order_release);
|
||||
|
@ -527,7 +541,7 @@ VoiceChange *GetVoiceChanger(ALCcontext *ctx)
|
|||
|
||||
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)};
|
||||
while(VoiceChange *next{oldhead->mNext.load(std::memory_order_relaxed)})
|
||||
|
@ -538,7 +552,11 @@ void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail)
|
|||
device->waitForMix();
|
||||
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)};
|
||||
while(VoiceChange *next{cur->mNext.load(std::memory_order_acquire)})
|
||||
{
|
||||
|
@ -549,6 +567,7 @@ void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail)
|
|||
ctx->mCurrentVoiceChange.store(cur, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALCcontext *context,
|
||||
|
@ -934,6 +953,11 @@ enum SourceProp : ALenum {
|
|||
/* AL_EXT_BFORMAT */
|
||||
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 */
|
||||
srcResampler = AL_SOURCE_RESAMPLER_SOFT,
|
||||
|
||||
|
@ -983,6 +1007,9 @@ ALuint FloatValsByProp(ALenum prop)
|
|||
case AL_SOURCE_RADIUS:
|
||||
case AL_SOURCE_RESAMPLER_SOFT:
|
||||
case AL_SOURCE_SPATIALIZE_SOFT:
|
||||
case AL_BYTE_LENGTH_SOFT:
|
||||
case AL_SAMPLE_LENGTH_SOFT:
|
||||
case AL_SEC_LENGTH_SOFT:
|
||||
return 1;
|
||||
|
||||
case AL_STEREO_ANGLES:
|
||||
|
@ -1045,6 +1072,9 @@ ALuint DoubleValsByProp(ALenum prop)
|
|||
case AL_SOURCE_RADIUS:
|
||||
case AL_SOURCE_RESAMPLER_SOFT:
|
||||
case AL_SOURCE_SPATIALIZE_SOFT:
|
||||
case AL_BYTE_LENGTH_SOFT:
|
||||
case AL_SAMPLE_LENGTH_SOFT:
|
||||
case AL_SEC_LENGTH_SOFT:
|
||||
return 1;
|
||||
|
||||
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)
|
||||
UpdateSourceProps(source, voice, context);
|
||||
else
|
||||
source->PropsClean.clear(std::memory_order_release);
|
||||
source->mPropsDirty.set(std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1105,6 +1135,7 @@ bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
|||
|
||||
switch(prop)
|
||||
{
|
||||
case AL_SEC_LENGTH_SOFT:
|
||||
case AL_SEC_OFFSET_LATENCY_SOFT:
|
||||
case AL_SEC_OFFSET_CLOCK_SOFT:
|
||||
/* Query only */
|
||||
|
@ -1223,7 +1254,7 @@ bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
|||
auto vpos = GetSampleOffset(Source->mQueue, prop, values[0]);
|
||||
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;
|
||||
}
|
||||
Source->OffsetType = prop;
|
||||
|
@ -1298,6 +1329,8 @@ bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
|||
case AL_DIRECT_CHANNELS_SOFT:
|
||||
case AL_SOURCE_RESAMPLER_SOFT:
|
||||
case AL_SOURCE_SPATIALIZE_SOFT:
|
||||
case AL_BYTE_LENGTH_SOFT:
|
||||
case AL_SAMPLE_LENGTH_SOFT:
|
||||
CHECKSIZE(values, 1);
|
||||
ival = static_cast<int>(values[0]);
|
||||
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)
|
||||
{
|
||||
ALCdevice *device{Context->mDevice.get()};
|
||||
ALCdevice *device{Context->mALDevice.get()};
|
||||
ALeffectslot *slot{nullptr};
|
||||
al::deque<ALbufferQueueItem> oldlist;
|
||||
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_BUFFERS_QUEUED:
|
||||
case AL_BUFFERS_PROCESSED:
|
||||
case AL_BYTE_LENGTH_SOFT:
|
||||
case AL_SAMPLE_LENGTH_SOFT:
|
||||
/* Query only */
|
||||
SETERR_RETURN(Context, AL_INVALID_OPERATION, false,
|
||||
"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)};
|
||||
if(voice) UpdateSourceProps(Source, voice, Context);
|
||||
else Source->PropsClean.clear(std::memory_order_release);
|
||||
else Source->mPropsDirty.set(std::memory_order_release);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1606,6 +1641,7 @@ bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
|||
case AL_AIR_ABSORPTION_FACTOR:
|
||||
case AL_ROOM_ROLLOFF_FACTOR:
|
||||
case AL_SOURCE_RADIUS:
|
||||
case AL_SEC_LENGTH_SOFT:
|
||||
CHECKSIZE(values, 1);
|
||||
fvals[0] = static_cast<float>(values[0]);
|
||||
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_PROCESSED:
|
||||
case AL_SOURCE_STATE:
|
||||
case AL_BYTE_LENGTH_SOFT:
|
||||
case AL_SAMPLE_LENGTH_SOFT:
|
||||
case AL_SAMPLE_OFFSET_LATENCY_SOFT:
|
||||
case AL_SAMPLE_OFFSET_CLOCK_SOFT:
|
||||
/* Query only */
|
||||
|
@ -1716,6 +1754,7 @@ bool SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const
|
|||
case AL_AIR_ABSORPTION_FACTOR:
|
||||
case AL_ROOM_ROLLOFF_FACTOR:
|
||||
case AL_SOURCE_RADIUS:
|
||||
case AL_SEC_LENGTH_SOFT:
|
||||
CHECKSIZE(values, 1);
|
||||
fvals[0] = static_cast<float>(values[0]);
|
||||
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)
|
||||
{
|
||||
ALCdevice *device{Context->mDevice.get()};
|
||||
ALCdevice *device{Context->mALDevice.get()};
|
||||
ClockLatency clocktime;
|
||||
nanoseconds srcclock;
|
||||
int ivals[MaxValues];
|
||||
|
@ -1851,6 +1890,13 @@ bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
|||
values[0] = Source->Radius;
|
||||
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:
|
||||
CHECKSIZE(values, 2);
|
||||
values[0] = Source->StereoPan[0];
|
||||
|
@ -2044,6 +2090,14 @@ bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a
|
|||
values[0] = ALenumFromDistanceModel(Source->mDistanceModel);
|
||||
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:
|
||||
CHECKSIZE(values, 1);
|
||||
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)
|
||||
{
|
||||
ALCdevice *device = Context->mDevice.get();
|
||||
ALCdevice *device{Context->mALDevice.get()};
|
||||
ClockLatency clocktime;
|
||||
nanoseconds srcclock;
|
||||
double dvals[MaxValues];
|
||||
|
@ -2135,6 +2189,13 @@ bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const
|
|||
|
||||
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:
|
||||
CHECKSIZE(values, 2);
|
||||
/* 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;
|
||||
|
||||
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)
|
||||
{
|
||||
context->setError(AL_OUT_OF_MEMORY, "Exceeding %u source limit (%u + %d)",
|
||||
|
@ -2869,19 +2930,24 @@ START_API_FUNC
|
|||
++sources;
|
||||
}
|
||||
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
/* If the device is disconnected, go right to stopped. */
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
/* If the device is disconnected, and voices stop on disconnect, go right
|
||||
* to stopped.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
/* TODO: Send state change event? */
|
||||
source->Offset = 0.0;
|
||||
source->OffsetType = AL_NONE;
|
||||
source->state = AL_STOPPED;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Count the number of reusable voices. */
|
||||
auto voicelist = context->getVoicesSpan();
|
||||
|
@ -3247,7 +3313,7 @@ START_API_FUNC
|
|||
SETERR_RETURN(context, AL_INVALID_OPERATION,, "Queueing onto static source %u", src);
|
||||
|
||||
/* Check for a valid Buffer, for its frequency and format */
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
ALCdevice *device{context->mALDevice.get()};
|
||||
ALbuffer *BufferFmt{nullptr};
|
||||
for(auto &item : source->mQueue)
|
||||
{
|
||||
|
@ -3396,6 +3462,17 @@ START_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()
|
||||
{
|
||||
Direct.Gain = 1.0f;
|
||||
|
@ -3413,7 +3490,7 @@ ALsource::ALsource()
|
|||
send.LFReference = HIGHPASSFREQREF;
|
||||
}
|
||||
|
||||
PropsClean.test_and_set(std::memory_order_relaxed);
|
||||
mPropsDirty.test_and_clear(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
ALsource::~ALsource()
|
||||
|
@ -3440,7 +3517,7 @@ void UpdateAllSourceProps(ALCcontext *context)
|
|||
ALsource *source = sid ? LookupSource(context, sid) : nullptr;
|
||||
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);
|
||||
}
|
||||
++vidx;
|
||||
|
|
|
@ -11,14 +11,15 @@
|
|||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
|
||||
#include "alcontext.h"
|
||||
#include "alc/alu.h"
|
||||
#include "alc/context.h"
|
||||
#include "aldeque.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alu.h"
|
||||
#include "atomic.h"
|
||||
#include "core/voice.h"
|
||||
#include "math_defs.h"
|
||||
#include "vector.h"
|
||||
#include "voice.h"
|
||||
|
||||
struct ALbuffer;
|
||||
struct ALeffectslot;
|
||||
|
@ -109,7 +110,7 @@ struct ALsource {
|
|||
/** Source Buffer Queue head. */
|
||||
al::deque<ALbufferQueueItem> mQueue;
|
||||
|
||||
std::atomic_flag PropsClean;
|
||||
al::atomic_invflag mPropsDirty;
|
||||
|
||||
/* Index into the context's Voices array. Lazily updated, only checked and
|
||||
* reset when looking up the voice.
|
||||
|
|
|
@ -24,26 +24,27 @@
|
|||
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "alcontext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alc/alu.h"
|
||||
#include "alc/context.h"
|
||||
#include "alc/inprogext.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "alu.h"
|
||||
#include "aloptional.h"
|
||||
#include "atomic.h"
|
||||
#include "core/context.h"
|
||||
#include "core/except.h"
|
||||
#include "event.h"
|
||||
#include "inprogext.h"
|
||||
#include "core/mixer/defs.h"
|
||||
#include "core/voice.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "opthelpers.h"
|
||||
#include "strutils.h"
|
||||
#include "voice.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
@ -142,7 +143,7 @@ END_API_FUNC
|
|||
if(!context->mDeferUpdates.load(std::memory_order_acquire)) \
|
||||
UpdateContextProps(context.get()); \
|
||||
else \
|
||||
context->mPropsClean.clear(std::memory_order_release); \
|
||||
context->mPropsDirty.set(std::memory_order_release); \
|
||||
} while(0)
|
||||
|
||||
|
||||
|
@ -152,12 +153,18 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
switch(capability)
|
||||
{
|
||||
case AL_SOURCE_DISTANCE_MODEL:
|
||||
{
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
context->mSourceDistanceModel = true;
|
||||
DO_UPDATEPROPS();
|
||||
}
|
||||
break;
|
||||
|
||||
case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
|
||||
context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -172,12 +179,18 @@ START_API_FUNC
|
|||
ContextRef context{GetContextRef()};
|
||||
if UNLIKELY(!context) return;
|
||||
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
switch(capability)
|
||||
{
|
||||
case AL_SOURCE_DISTANCE_MODEL:
|
||||
{
|
||||
std::lock_guard<std::mutex> _{context->mPropLock};
|
||||
context->mSourceDistanceModel = false;
|
||||
DO_UPDATEPROPS();
|
||||
}
|
||||
break;
|
||||
|
||||
case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
|
||||
context->mStopVoicesOnDisconnect = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -200,6 +213,10 @@ START_API_FUNC
|
|||
value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE;
|
||||
break;
|
||||
|
||||
case AL_STOP_SOURCES_ON_DISCONNECT_SOFT:
|
||||
value = context->mStopVoicesOnDisconnect ? AL_TRUE : AL_FALSE;
|
||||
break;
|
||||
|
||||
default:
|
||||
context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -40,7 +40,7 @@
|
|||
|
||||
#include "alfstream.h"
|
||||
#include "alstring.h"
|
||||
#include "compat.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "strutils.h"
|
||||
#include "vector.h"
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
@ -37,47 +36,48 @@
|
|||
#include <limits>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <numeric>
|
||||
#include <stdint.h>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "alstring.h"
|
||||
#include "async_event.h"
|
||||
#include "atomic.h"
|
||||
#include "bformatdec.h"
|
||||
#include "core/ambidefs.h"
|
||||
#include "core/async_event.h"
|
||||
#include "core/bformatdec.h"
|
||||
#include "core/bs2b.h"
|
||||
#include "core/bsinc_defs.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/devformat.h"
|
||||
#include "core/device.h"
|
||||
#include "core/filters/biquad.h"
|
||||
#include "core/filters/nfc.h"
|
||||
#include "core/filters/splitter.h"
|
||||
#include "core/fpu_ctrl.h"
|
||||
#include "core/hrtf.h"
|
||||
#include "core/mastering.h"
|
||||
#include "core/mixer.h"
|
||||
#include "core/mixer/defs.h"
|
||||
#include "core/mixer/hrtfdefs.h"
|
||||
#include "core/resampler_limits.h"
|
||||
#include "core/uhjfilter.h"
|
||||
#include "core/voice.h"
|
||||
#include "core/voice_change.h"
|
||||
#include "effects/base.h"
|
||||
#include "effectslot.h"
|
||||
#include "front_stablizer.h"
|
||||
#include "hrtf.h"
|
||||
#include "inprogext.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "math_defs.h"
|
||||
#include "opthelpers.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "strutils.h"
|
||||
#include "threads.h"
|
||||
#include "vecmat.h"
|
||||
#include "voice.h"
|
||||
#include "voice_change.h"
|
||||
#include "vector.h"
|
||||
|
||||
struct CTag;
|
||||
#ifdef HAVE_SSE
|
||||
|
@ -92,7 +92,6 @@ struct SSE4Tag;
|
|||
#ifdef HAVE_NEON
|
||||
struct NEONTag;
|
||||
#endif
|
||||
struct CopyTag;
|
||||
struct PointTag;
|
||||
struct LerpTag;
|
||||
struct CubicTag;
|
||||
|
@ -100,12 +99,13 @@ struct BSincTag;
|
|||
struct FastBSincTag;
|
||||
|
||||
|
||||
static_assert(MaxResamplerPadding >= BSincPointsMax, "MaxResamplerPadding is too small");
|
||||
static_assert(!(MaxResamplerPadding&1), "MaxResamplerPadding is not a multiple of two");
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
constexpr uint MaxPitch{10};
|
||||
|
||||
static_assert((BufferLineSize-1)/MaxPitch > 0, "MaxPitch is too large for BufferLineSize!");
|
||||
|
@ -154,9 +154,9 @@ struct ChanMap {
|
|||
float elevation;
|
||||
};
|
||||
|
||||
using HrtfDirectMixerFunc = void(*)(FloatBufferLine &LeftOut, FloatBufferLine &RightOut,
|
||||
const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples,
|
||||
float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize);
|
||||
using HrtfDirectMixerFunc = void(*)(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut,
|
||||
const al::span<const FloatBufferLine> InSamples, float2 *AccumSamples, float *TempBuf,
|
||||
HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize);
|
||||
|
||||
HrtfDirectMixerFunc MixDirectHrtf{MixDirectHrtf_<CTag>};
|
||||
|
||||
|
@ -182,8 +182,8 @@ inline void BsincPrepare(const uint increment, BsincState *state, const BSincTab
|
|||
|
||||
if(increment > MixerFracOne)
|
||||
{
|
||||
sf = MixerFracOne / static_cast<float>(increment);
|
||||
sf = maxf(0.0f, (BSincScaleCount-1) * (sf-table->scaleBase) * table->scaleRange);
|
||||
sf = MixerFracOne/static_cast<float>(increment) - table->scaleBase;
|
||||
sf = maxf(0.0f, BSincScaleCount*sf*table->scaleRange - 1.0f);
|
||||
si = float2uint(sf);
|
||||
/* The interpolation factor is fit to this diagonally-symmetric curve
|
||||
* 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. */
|
||||
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);
|
||||
}
|
||||
|
||||
void ALCdevice::ProcessAmbiDec(const size_t SamplesToDo)
|
||||
void DeviceBase::ProcessAmbiDec(const size_t 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. */
|
||||
const uint lidx{RealOut.ChannelIndex[FrontLeft]};
|
||||
|
@ -306,18 +306,18 @@ void ALCdevice::ProcessAmbiDecStablized(const size_t SamplesToDo)
|
|||
SamplesToDo);
|
||||
}
|
||||
|
||||
void ALCdevice::ProcessUhj(const size_t SamplesToDo)
|
||||
void DeviceBase::ProcessUhj(const size_t SamplesToDo)
|
||||
{
|
||||
/* UHJ is stereo output only. */
|
||||
const uint lidx{RealOut.ChannelIndex[FrontLeft]};
|
||||
const uint ridx{RealOut.ChannelIndex[FrontRight]};
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
void ALCdevice::ProcessBs2b(const size_t SamplesToDo)
|
||||
void DeviceBase::ProcessBs2b(const size_t SamplesToDo)
|
||||
{
|
||||
/* First, decode the ambisonic mix to the "real" output. */
|
||||
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)};
|
||||
if(!props) return false;
|
||||
|
@ -380,7 +380,7 @@ bool CalcContextParams(ALCcontext *ctx)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CalcListenerParams(ALCcontext *ctx)
|
||||
bool CalcListenerParams(ContextBase *ctx)
|
||||
{
|
||||
ListenerProps *props{ctx->mParams.ListenerUpdate.exchange(nullptr,
|
||||
std::memory_order_acq_rel)};
|
||||
|
@ -418,7 +418,7 @@ bool CalcListenerParams(ALCcontext *ctx)
|
|||
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)};
|
||||
if(!props) return false;
|
||||
|
@ -488,7 +488,7 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ALCcontex
|
|||
output = EffectTarget{&target->Wet, nullptr};
|
||||
else
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
DeviceBase *device{context->mDevice};
|
||||
output = EffectTarget{&device->Dry, &device->RealOut};
|
||||
}
|
||||
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,
|
||||
const float Distance, const float Spread, const GainTriplet &DryGain,
|
||||
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]{
|
||||
{ FrontCenter, 0.0f, 0.0f }
|
||||
|
@ -790,16 +790,21 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con
|
|||
|
||||
case FmtBFormat2D:
|
||||
case FmtBFormat3D:
|
||||
case FmtUHJ2:
|
||||
case FmtUHJ3:
|
||||
case FmtUHJ4:
|
||||
DirectChannels = DirectMode::Off;
|
||||
break;
|
||||
}
|
||||
|
||||
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. */
|
||||
|
||||
if(Device->AvgSpeakerDist > 0.0f)
|
||||
if(Device->AvgSpeakerDist > 0.0f && voice->mFmtChannels != FmtUHJ2)
|
||||
{
|
||||
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
|
||||
* 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() :
|
||||
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];
|
||||
|
||||
voice->mDirect.Buffer = Device->Dry.Buffer;
|
||||
|
@ -1242,9 +1249,9 @@ void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const ALCcon
|
|||
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};
|
||||
|
||||
/* Set mixing buffers and get send parameters. */
|
||||
|
@ -1542,7 +1549,7 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ALCcontex
|
|||
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)};
|
||||
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
|
||||
&& 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::Auto && voice->mFmtChannels != FmtMono))
|
||||
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()};
|
||||
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}};
|
||||
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);
|
||||
}
|
||||
|
||||
void ProcessVoiceChanges(ALCcontext *ctx)
|
||||
void ProcessVoiceChanges(ContextBase *ctx)
|
||||
{
|
||||
VoiceChange *cur{ctx->mCurrentVoiceChange.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);
|
||||
}
|
||||
|
||||
void ProcessParamUpdates(ALCcontext *ctx, const EffectSlotArray &slots,
|
||||
void ProcessParamUpdates(ContextBase *ctx, const EffectSlotArray &slots,
|
||||
const al::span<Voice*> voices)
|
||||
{
|
||||
ProcessVoiceChanges(ctx);
|
||||
|
@ -1696,11 +1722,11 @@ void ProcessParamUpdates(ALCcontext *ctx, const EffectSlotArray &slots,
|
|||
IncrementRef(ctx->mUpdateCount);
|
||||
}
|
||||
|
||||
void ProcessContexts(ALCdevice *device, const uint SamplesToDo)
|
||||
void ProcessContexts(DeviceBase *device, const uint SamplesToDo)
|
||||
{
|
||||
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 al::span<Voice*> voices{ctx->getVoicesSpanAcquired()};
|
||||
|
@ -1902,7 +1928,7 @@ void Write(const al::span<const FloatBufferLine> InBuffer, void *OutBuffer, cons
|
|||
|
||||
} // 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{};
|
||||
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))
|
||||
return;
|
||||
|
@ -1989,7 +2015,7 @@ void ALCdevice::handleDisconnect(const char *msg, ...)
|
|||
evt.u.disconnect.msg[sizeof(evt.u.disconnect.msg)-1] = 0;
|
||||
|
||||
IncrementRef(MixCount);
|
||||
for(ALCcontext *ctx : *mContexts.load())
|
||||
for(ContextBase *ctx : *mContexts.load())
|
||||
{
|
||||
const uint enabledevt{ctx->mEnabledEvts.load(std::memory_order_acquire)};
|
||||
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 stop_voice = [](Voice *voice) -> void
|
||||
{
|
||||
|
|
|
@ -1,40 +1,15 @@
|
|||
#ifndef 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 ALCdevice;
|
||||
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 SpeedOfSoundMetersPerSec{343.3f};
|
||||
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 {
|
||||
Hrtf_Default = 0,
|
||||
|
@ -44,8 +19,6 @@ enum HrtfRequestMode {
|
|||
|
||||
void aluInit(void);
|
||||
|
||||
void aluInitMixer(void);
|
||||
|
||||
/* aluInitRenderer
|
||||
*
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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 ZScale;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/alsa.h"
|
||||
#include "alsa.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
@ -36,12 +36,12 @@
|
|||
#include <utility>
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alcmain.h"
|
||||
#include "alconfig.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "alu.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
|
@ -407,7 +407,7 @@ int verify_state(snd_pcm_t *handle)
|
|||
|
||||
|
||||
struct AlsaPlayback final : public BackendBase {
|
||||
AlsaPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
AlsaPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~AlsaPlayback() override;
|
||||
|
||||
int mixerProc();
|
||||
|
@ -635,12 +635,16 @@ void AlsaPlayback::open(const char *name)
|
|||
name = alsaDevice;
|
||||
driver = GetConfigValue(nullptr, "alsa", "device", "default");
|
||||
}
|
||||
|
||||
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)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"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. */
|
||||
snd_config_update_free_global();
|
||||
|
@ -863,7 +867,7 @@ ClockLatency AlsaPlayback::getClockLatency()
|
|||
|
||||
|
||||
struct AlsaCapture final : public BackendBase {
|
||||
AlsaCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~AlsaCapture() override;
|
||||
|
||||
void open(const char *name) override;
|
||||
|
@ -1252,7 +1256,7 @@ std::string AlsaBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr AlsaBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr AlsaBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new AlsaPlayback{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_ALSA_H
|
||||
#define BACKENDS_ALSA_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct AlsaBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -3,21 +3,22 @@
|
|||
|
||||
#include "base.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <mmreg.h>
|
||||
#endif
|
||||
|
||||
#include "albit.h"
|
||||
#include "alcmain.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "atomic.h"
|
||||
#include "core/logging.h"
|
||||
#include "aloptional.h"
|
||||
#endif
|
||||
|
||||
#include "atomic.h"
|
||||
#include "core/devformat.h"
|
||||
|
||||
|
||||
bool BackendBase::reset()
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
#define ALC_BACKENDS_BASE_H
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdarg>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <ratio>
|
||||
#include <string>
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alcmain.h"
|
||||
#include "core/device.h"
|
||||
#include "core/except.h"
|
||||
|
||||
|
||||
|
@ -30,9 +31,9 @@ struct BackendBase {
|
|||
|
||||
virtual ClockLatency getClockLatency();
|
||||
|
||||
ALCdevice *const mDevice;
|
||||
DeviceBase *const mDevice;
|
||||
|
||||
BackendBase(ALCdevice *device) noexcept : mDevice{device} { }
|
||||
BackendBase(DeviceBase *device) noexcept : mDevice{device} { }
|
||||
virtual ~BackendBase() = default;
|
||||
|
||||
protected:
|
||||
|
@ -57,7 +58,7 @@ enum class BackendType {
|
|||
/* Helper to get the current clock time from the device's ClockBase, and
|
||||
* SamplesDone converted from the sample rate.
|
||||
*/
|
||||
inline std::chrono::nanoseconds GetDeviceClockTime(ALCdevice *device)
|
||||
inline std::chrono::nanoseconds GetDeviceClockTime(DeviceBase *device)
|
||||
{
|
||||
using std::chrono::seconds;
|
||||
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
|
||||
* latency from post-processing.
|
||||
*/
|
||||
inline ClockLatency GetClockLatency(ALCdevice *device)
|
||||
inline ClockLatency GetClockLatency(DeviceBase *device)
|
||||
{
|
||||
BackendBase *backend{device->Backend.get()};
|
||||
ClockLatency ret{backend->getClockLatency()};
|
||||
|
@ -85,7 +86,7 @@ struct BackendFactory {
|
|||
|
||||
virtual std::string probe(BackendType type) = 0;
|
||||
|
||||
virtual BackendPtr createBackend(ALCdevice *device, BackendType type) = 0;
|
||||
virtual BackendPtr createBackend(DeviceBase *device, BackendType type) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~BackendFactory() = default;
|
||||
|
@ -103,7 +104,11 @@ class backend_exception final : public base_exception {
|
|||
backend_error mErrorCode;
|
||||
|
||||
public:
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
[[gnu::format(gnu_printf, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
backend_exception(backend_error code, const char *msg, ...) : mErrorCode{code}
|
||||
{
|
||||
std::va_list args;
|
||||
|
|
|
@ -20,23 +20,23 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/coreaudio.h"
|
||||
#include "coreaudio.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "converter.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/converter.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
#include "backends/base.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
|
@ -47,7 +47,7 @@ static const char ca_device[] = "CoreAudio Default";
|
|||
|
||||
|
||||
struct CoreAudioPlayback final : public BackendBase {
|
||||
CoreAudioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
CoreAudioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~CoreAudioPlayback() override;
|
||||
|
||||
OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||
|
@ -105,7 +105,7 @@ void CoreAudioPlayback::open(const char *name)
|
|||
/* open the default output unit */
|
||||
AudioComponentDescription desc{};
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
#if TARGET_OS_IOS
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#else
|
||||
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
|
@ -118,17 +118,27 @@ void CoreAudioPlayback::open(const char *name)
|
|||
if(comp == nullptr)
|
||||
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)
|
||||
throw al::backend_exception{al::backend_error::NoDevice,
|
||||
"Could not create component instance: %u", err};
|
||||
|
||||
/* init and start the default audio unit... */
|
||||
err = AudioUnitInitialize(mAudioUnit);
|
||||
err = AudioUnitInitialize(audioUnit);
|
||||
if(err != noErr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"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;
|
||||
}
|
||||
|
||||
|
@ -294,7 +304,7 @@ void CoreAudioPlayback::stop()
|
|||
|
||||
|
||||
struct CoreAudioCapture final : public BackendBase {
|
||||
CoreAudioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
CoreAudioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~CoreAudioCapture() override;
|
||||
|
||||
OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags,
|
||||
|
@ -400,7 +410,7 @@ void CoreAudioCapture::open(const char *name)
|
|||
name};
|
||||
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
#if TARGET_OS_IOS
|
||||
#if TARGET_OS_IOS || TARGET_OS_TV
|
||||
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
||||
#else
|
||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
|
@ -436,7 +446,7 @@ void CoreAudioCapture::open(const char *name)
|
|||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"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
|
||||
AudioDeviceID inputDevice = kAudioDeviceUnknown;
|
||||
|
@ -676,7 +686,7 @@ std::string CoreAudioBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr CoreAudioBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr CoreAudioBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new CoreAudioPlayback{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_COREAUDIO_H
|
||||
#define BACKENDS_COREAUDIO_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct CoreAudioBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/dsound.h"
|
||||
#include "dsound.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
@ -44,9 +44,10 @@
|
|||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
#include "alnumeric.h"
|
||||
#include "comptr.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
|
@ -169,20 +170,20 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi
|
|||
|
||||
|
||||
struct DSoundPlayback final : public BackendBase {
|
||||
DSoundPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
DSoundPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~DSoundPlayback() override;
|
||||
|
||||
int mixerProc();
|
||||
|
||||
void open(const ALCchar *name) override;
|
||||
void open(const char *name) override;
|
||||
bool reset() override;
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
IDirectSound *mDS{nullptr};
|
||||
IDirectSoundBuffer *mPrimaryBuffer{nullptr};
|
||||
IDirectSoundBuffer *mBuffer{nullptr};
|
||||
IDirectSoundNotify *mNotifies{nullptr};
|
||||
ComPtr<IDirectSound> mDS;
|
||||
ComPtr<IDirectSoundBuffer> mPrimaryBuffer;
|
||||
ComPtr<IDirectSoundBuffer> mBuffer;
|
||||
ComPtr<IDirectSoundNotify> mNotifies;
|
||||
HANDLE mNotifyEvent{nullptr};
|
||||
|
||||
std::atomic<bool> mKillNow{true};
|
||||
|
@ -193,19 +194,11 @@ struct DSoundPlayback final : public BackendBase {
|
|||
|
||||
DSoundPlayback::~DSoundPlayback()
|
||||
{
|
||||
if(mNotifies)
|
||||
mNotifies->Release();
|
||||
mNotifies = nullptr;
|
||||
if(mBuffer)
|
||||
mBuffer->Release();
|
||||
mBuffer = nullptr;
|
||||
if(mPrimaryBuffer)
|
||||
mPrimaryBuffer->Release();
|
||||
mPrimaryBuffer = nullptr;
|
||||
|
||||
if(mDS)
|
||||
mDS->Release();
|
||||
mDS = nullptr;
|
||||
|
||||
if(mNotifyEvent)
|
||||
CloseHandle(mNotifyEvent);
|
||||
mNotifyEvent = nullptr;
|
||||
|
@ -234,8 +227,8 @@ FORCE_ALIGN int DSoundPlayback::mixerProc()
|
|||
bool Playing{false};
|
||||
DWORD LastCursor{0u};
|
||||
mBuffer->GetCurrentPosition(&LastCursor, nullptr);
|
||||
while(!mKillNow.load(std::memory_order_acquire) &&
|
||||
mDevice->Connected.load(std::memory_order_acquire))
|
||||
while(!mKillNow.load(std::memory_order_acquire)
|
||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||
{
|
||||
// Get current play cursor
|
||||
DWORD PlayCursor;
|
||||
|
@ -344,31 +337,34 @@ void DSoundPlayback::open(const char *name)
|
|||
}
|
||||
|
||||
hr = DS_OK;
|
||||
if(!mNotifyEvent)
|
||||
{
|
||||
mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||
if(!mNotifyEvent) hr = E_FAIL;
|
||||
}
|
||||
|
||||
//DirectSound Init code
|
||||
ComPtr<IDirectSound> ds;
|
||||
if(SUCCEEDED(hr))
|
||||
hr = DirectSoundCreate(guid, &mDS, nullptr);
|
||||
hr = DirectSoundCreate(guid, ds.getPtr(), nullptr);
|
||||
if(SUCCEEDED(hr))
|
||||
hr = mDS->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
|
||||
hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY);
|
||||
if(FAILED(hr))
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx",
|
||||
hr};
|
||||
|
||||
mNotifies = nullptr;
|
||||
mBuffer = nullptr;
|
||||
mPrimaryBuffer = nullptr;
|
||||
mDS = std::move(ds);
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
bool DSoundPlayback::reset()
|
||||
{
|
||||
if(mNotifies)
|
||||
mNotifies->Release();
|
||||
mNotifies = nullptr;
|
||||
if(mBuffer)
|
||||
mBuffer->Release();
|
||||
mBuffer = nullptr;
|
||||
if(mPrimaryBuffer)
|
||||
mPrimaryBuffer->Release();
|
||||
mPrimaryBuffer = nullptr;
|
||||
|
||||
switch(mDevice->FmtType)
|
||||
|
@ -465,7 +461,7 @@ retry_open:
|
|||
DSBUFFERDESC DSBDescription{};
|
||||
DSBDescription.dwSize = sizeof(DSBDescription);
|
||||
DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, &mPrimaryBuffer, nullptr);
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, mPrimaryBuffer.getPtr(), nullptr);
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
hr = mPrimaryBuffer->SetFormat(&OutputType.Format);
|
||||
|
@ -485,7 +481,7 @@ retry_open:
|
|||
DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign;
|
||||
DSBDescription.lpwfxFormat = &OutputType.Format;
|
||||
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, &mBuffer, nullptr);
|
||||
hr = mDS->CreateSoundBuffer(&DSBDescription, mBuffer.getPtr(), nullptr);
|
||||
if(FAILED(hr) && mDevice->FmtType == DevFmtFloat)
|
||||
{
|
||||
mDevice->FmtType = DevFmtShort;
|
||||
|
@ -499,7 +495,7 @@ retry_open:
|
|||
hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, &ptr);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
mNotifies = static_cast<IDirectSoundNotify*>(ptr);
|
||||
mNotifies = ComPtr<IDirectSoundNotify>{static_cast<IDirectSoundNotify*>(ptr)};
|
||||
|
||||
uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
|
||||
assert(num_updates <= MAX_UPDATES);
|
||||
|
@ -517,14 +513,8 @@ retry_open:
|
|||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
if(mNotifies)
|
||||
mNotifies->Release();
|
||||
mNotifies = nullptr;
|
||||
if(mBuffer)
|
||||
mBuffer->Release();
|
||||
mBuffer = nullptr;
|
||||
if(mPrimaryBuffer)
|
||||
mPrimaryBuffer->Release();
|
||||
mPrimaryBuffer = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
@ -558,7 +548,7 @@ void DSoundPlayback::stop()
|
|||
|
||||
|
||||
struct DSoundCapture final : public BackendBase {
|
||||
DSoundCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~DSoundCapture() override;
|
||||
|
||||
void open(const char *name) override;
|
||||
|
@ -567,8 +557,8 @@ struct DSoundCapture final : public BackendBase {
|
|||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
|
||||
IDirectSoundCapture *mDSC{nullptr};
|
||||
IDirectSoundCaptureBuffer *mDSCbuffer{nullptr};
|
||||
ComPtr<IDirectSoundCapture> mDSC;
|
||||
ComPtr<IDirectSoundCaptureBuffer> mDSCbuffer;
|
||||
DWORD mBufferBytes{0u};
|
||||
DWORD mCursor{0u};
|
||||
|
||||
|
@ -582,12 +572,8 @@ DSoundCapture::~DSoundCapture()
|
|||
if(mDSCbuffer)
|
||||
{
|
||||
mDSCbuffer->Stop();
|
||||
mDSCbuffer->Release();
|
||||
mDSCbuffer = nullptr;
|
||||
}
|
||||
|
||||
if(mDSC)
|
||||
mDSC->Release();
|
||||
mDSC = nullptr;
|
||||
}
|
||||
|
||||
|
@ -693,20 +679,16 @@ void DSoundCapture::open(const char *name)
|
|||
DSCBDescription.lpwfxFormat = &InputType.Format;
|
||||
|
||||
//DirectSoundCapture Init code
|
||||
hr = DirectSoundCaptureCreate(guid, &mDSC, nullptr);
|
||||
hr = DirectSoundCaptureCreate(guid, mDSC.getPtr(), nullptr);
|
||||
if(SUCCEEDED(hr))
|
||||
mDSC->CreateCaptureBuffer(&DSCBDescription, &mDSCbuffer, nullptr);
|
||||
mDSC->CreateCaptureBuffer(&DSCBDescription, mDSCbuffer.getPtr(), nullptr);
|
||||
if(SUCCEEDED(hr))
|
||||
mRing = RingBuffer::Create(mDevice->BufferSize, InputType.Format.nBlockAlign, false);
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
mRing = nullptr;
|
||||
if(mDSCbuffer)
|
||||
mDSCbuffer->Release();
|
||||
mDSCbuffer = nullptr;
|
||||
if(mDSC)
|
||||
mDSC->Release();
|
||||
mDSC = nullptr;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BackendPtr DSoundBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr DSoundBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new DSoundPlayback{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_DSOUND_H
|
||||
#define BACKENDS_DSOUND_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct DSoundBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/jack.h"
|
||||
#include "jack.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
@ -31,9 +31,10 @@
|
|||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
|
@ -45,9 +46,6 @@
|
|||
|
||||
namespace {
|
||||
|
||||
constexpr char jackDevice[] = "JACK Default";
|
||||
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
#define JACK_FUNCS(MAGIC) \
|
||||
MAGIC(jack_client_open); \
|
||||
|
@ -160,21 +158,55 @@ struct DeviceEntry {
|
|||
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)
|
||||
{
|
||||
for(size_t i{0};ports[i];++i)
|
||||
{
|
||||
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;
|
||||
|
||||
std::string customList{ConfigValueStr(nullptr, "jack", "custom-devices").value_or("")};
|
||||
size_t strpos{0};
|
||||
while(strpos < customList.size())
|
||||
while(strpos < listopt->size())
|
||||
{
|
||||
size_t nextpos{customList.find(';', strpos)};
|
||||
size_t seppos{customList.find('=', strpos)};
|
||||
size_t nextpos{listopt->find(';', strpos)};
|
||||
size_t seppos{listopt->find('=', 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());
|
||||
if(nextpos != std::string::npos) ++nextpos;
|
||||
strpos = nextpos;
|
||||
|
@ -182,18 +214,18 @@ void EnumerateDevices(al::vector<DeviceEntry> &list)
|
|||
}
|
||||
|
||||
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
|
||||
{ return entry.mName == name; };
|
||||
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 += std::to_string(++count);
|
||||
}
|
||||
|
||||
++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();
|
||||
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 {
|
||||
JackPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
JackPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~JackPlayback() override;
|
||||
|
||||
int process(jack_nframes_t numframes) noexcept;
|
||||
|
@ -352,31 +384,16 @@ int JackPlayback::mixerProc()
|
|||
|
||||
void JackPlayback::open(const char *name)
|
||||
{
|
||||
mPortPattern.clear();
|
||||
|
||||
if(!name)
|
||||
name = jackDevice;
|
||||
else if(strcmp(name, jackDevice) != 0)
|
||||
if(!mClient)
|
||||
{
|
||||
if(PlaybackList.empty())
|
||||
EnumerateDevices(PlaybackList);
|
||||
const PathNamePair &binname = GetProcBinary();
|
||||
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;
|
||||
mClient = jack_client_open(client_name, ClientOptions, &status, nullptr);
|
||||
if(mClient == nullptr)
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Failed to open client connection: 0x%02x", status};
|
||||
|
||||
if((status&JackServerStarted))
|
||||
TRACE("JACK server started\n");
|
||||
if((status&JackNameNotUnique))
|
||||
|
@ -384,6 +401,26 @@ void JackPlayback::open(const char *name)
|
|||
client_name = jack_get_client_name(mClient);
|
||||
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);
|
||||
|
||||
|
@ -453,8 +490,8 @@ void JackPlayback::start()
|
|||
const char *devname{mDevice->DeviceName.c_str()};
|
||||
if(ConfigValueBool(devname, "jack", "connect-ports").value_or(true))
|
||||
{
|
||||
const char **ports{jack_get_ports(mClient, mPortPattern.c_str(), nullptr,
|
||||
JackPortIsPhysical|JackPortIsInput)};
|
||||
const char **ports{jack_get_ports(mClient, mPortPattern.c_str(), JACK_DEFAULT_AUDIO_TYPE,
|
||||
JackPortIsInput)};
|
||||
if(ports == nullptr)
|
||||
{
|
||||
jack_deactivate(mClient);
|
||||
|
@ -547,10 +584,13 @@ bool JackBackendFactory::init()
|
|||
if(!GetConfigValueBool(nullptr, "jack", "spawn-server", 0))
|
||||
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};
|
||||
jack_set_error_function(jack_msg_handler);
|
||||
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);
|
||||
if(!client)
|
||||
{
|
||||
|
@ -575,10 +615,20 @@ std::string JackBackendFactory::probe(BackendType type)
|
|||
/* Includes null char. */
|
||||
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)
|
||||
{
|
||||
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);
|
||||
break;
|
||||
case BackendType::Capture:
|
||||
|
@ -587,7 +637,7 @@ std::string JackBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr JackBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr JackBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new JackPlayback{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_JACK_H
|
||||
#define BACKENDS_JACK_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct JackBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -20,18 +20,17 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/loopback.h"
|
||||
#include "loopback.h"
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "core/device.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
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;
|
||||
void start() 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;
|
||||
}
|
||||
|
@ -69,7 +68,7 @@ bool LoopbackBackendFactory::querySupport(BackendType)
|
|||
std::string LoopbackBackendFactory::probe(BackendType)
|
||||
{ return std::string{}; }
|
||||
|
||||
BackendPtr LoopbackBackendFactory::createBackend(ALCdevice *device, BackendType)
|
||||
BackendPtr LoopbackBackendFactory::createBackend(DeviceBase *device, BackendType)
|
||||
{ return BackendPtr{new LoopbackBackend{device}}; }
|
||||
|
||||
BackendFactory &LoopbackBackendFactory::getFactory()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_LOOPBACK_H
|
||||
#define BACKENDS_LOOPBACK_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct LoopbackBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/null.h"
|
||||
#include "null.h"
|
||||
|
||||
#include <exception>
|
||||
#include <atomic>
|
||||
|
@ -30,9 +30,9 @@
|
|||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "core/device.h"
|
||||
#include "almalloc.h"
|
||||
#include "alu.h"
|
||||
#include "core/helpers.h"
|
||||
#include "threads.h"
|
||||
|
||||
|
||||
|
@ -46,7 +46,7 @@ constexpr char nullDevice[] = "No Output";
|
|||
|
||||
|
||||
struct NullBackend final : public BackendBase {
|
||||
NullBackend(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
NullBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
|
||||
int mixerProc();
|
||||
|
||||
|
@ -165,7 +165,7 @@ std::string NullBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr NullBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr NullBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new NullBackend{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_NULL_H
|
||||
#define BACKENDS_NULL_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct NullBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "alu.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
#include "oboe/Oboe.h"
|
||||
|
@ -18,7 +20,7 @@ constexpr char device_name[] = "Oboe Default";
|
|||
|
||||
|
||||
struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback {
|
||||
OboePlayback(ALCdevice *device) : BackendBase{device} { }
|
||||
OboePlayback(DeviceBase *device) : BackendBase{device} { }
|
||||
|
||||
oboe::ManagedStream mStream;
|
||||
|
||||
|
@ -64,9 +66,10 @@ void OboePlayback::open(const char *name)
|
|||
name};
|
||||
|
||||
/* Open a basic output stream, just to ensure it can work. */
|
||||
oboe::ManagedStream stream;
|
||||
oboe::Result result{oboe::AudioStreamBuilder{}.setDirection(oboe::Direction::Output)
|
||||
->setPerformanceMode(oboe::PerformanceMode::LowLatency)
|
||||
->openManagedStream(mStream)};
|
||||
->openManagedStream(stream)};
|
||||
if(result != oboe::Result::OK)
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s",
|
||||
oboe::convertToText(result)};
|
||||
|
@ -220,7 +223,7 @@ void OboePlayback::stop()
|
|||
|
||||
|
||||
struct OboeCapture final : public BackendBase {
|
||||
OboeCapture(ALCdevice *device) : BackendBase{device} { }
|
||||
OboeCapture(DeviceBase *device) : BackendBase{device} { }
|
||||
|
||||
oboe::ManagedStream mStream;
|
||||
|
||||
|
@ -367,7 +370,7 @@ std::string OboeBackendFactory::probe(BackendType type)
|
|||
return std::string{};
|
||||
}
|
||||
|
||||
BackendPtr OboeBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr OboeBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new OboePlayback{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_OBOE_H
|
||||
#define BACKENDS_OBOE_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct OboeBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/opensl.h"
|
||||
#include "opensl.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <jni.h>
|
||||
|
@ -33,9 +33,9 @@
|
|||
#include <functional>
|
||||
|
||||
#include "albit.h"
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "opthelpers.h"
|
||||
#include "ringbuffer.h"
|
||||
|
@ -151,7 +151,7 @@ const char *res_str(SLresult result) noexcept
|
|||
|
||||
|
||||
struct OpenSLPlayback final : public BackendBase {
|
||||
OpenSLPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
OpenSLPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~OpenSLPlayback() override;
|
||||
|
||||
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",
|
||||
name};
|
||||
|
||||
/* There's only one device, so if it's already open, there's nothing to do. */
|
||||
if(mEngineObj) return;
|
||||
|
||||
// create engine
|
||||
SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
|
||||
PRINTERR(result, "slCreateEngine");
|
||||
|
@ -629,7 +632,7 @@ ClockLatency OpenSLPlayback::getClockLatency()
|
|||
|
||||
|
||||
struct OpenSLCapture final : public BackendBase {
|
||||
OpenSLCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
OpenSLCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~OpenSLCapture() override;
|
||||
|
||||
void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
|
||||
|
@ -959,7 +962,7 @@ std::string OSLBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr OSLBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr OSLBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new OpenSLPlayback{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_OSL_H
|
||||
#define BACKENDS_OSL_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct OSLBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/oss.h"
|
||||
#include "oss.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
|
@ -41,13 +41,13 @@
|
|||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alconfig.h"
|
||||
#include "albyte.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "alu.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
|
@ -226,7 +226,7 @@ uint log2i(uint x)
|
|||
|
||||
|
||||
struct OSSPlayback final : public BackendBase {
|
||||
OSSPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
OSSPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~OSSPlayback() override;
|
||||
|
||||
int mixerProc();
|
||||
|
@ -249,7 +249,7 @@ struct OSSPlayback final : public BackendBase {
|
|||
OSSPlayback::~OSSPlayback()
|
||||
{
|
||||
if(mFd != -1)
|
||||
close(mFd);
|
||||
::close(mFd);
|
||||
mFd = -1;
|
||||
}
|
||||
|
||||
|
@ -328,11 +328,15 @@ void OSSPlayback::open(const char *name)
|
|||
devname = iter->device_name.c_str();
|
||||
}
|
||||
|
||||
mFd = ::open(devname, O_WRONLY);
|
||||
if(mFd == -1)
|
||||
int fd{::open(devname, O_WRONLY)};
|
||||
if(fd == -1)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname,
|
||||
strerror(errno)};
|
||||
|
||||
if(mFd != -1)
|
||||
::close(mFd);
|
||||
mFd = fd;
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
|
@ -438,7 +442,7 @@ void OSSPlayback::stop()
|
|||
|
||||
|
||||
struct OSScapture final : public BackendBase {
|
||||
OSScapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
OSScapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~OSScapture() override;
|
||||
|
||||
int recordProc();
|
||||
|
@ -676,7 +680,7 @@ std::string OSSBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr OSSBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr OSSBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new OSSPlayback{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_OSS_H
|
||||
#define BACKENDS_OSS_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct OSSBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -20,15 +20,15 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/portaudio.h"
|
||||
#include "portaudio.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "ringbuffer.h"
|
||||
|
@ -72,7 +72,7 @@ MAKE_FUNC(Pa_GetStreamInfo);
|
|||
|
||||
|
||||
struct PortPlayback final : public BackendBase {
|
||||
PortPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~PortPlayback() override;
|
||||
|
||||
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",
|
||||
name};
|
||||
|
||||
mUpdateSize = mDevice->UpdateSize;
|
||||
|
||||
PaStreamParameters params{};
|
||||
auto devidopt = ConfigValueInt(nullptr, "port", "device");
|
||||
if(devidopt && *devidopt >= 0) mParams.device = *devidopt;
|
||||
else mParams.device = Pa_GetDefaultOutputDevice();
|
||||
mParams.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
|
||||
mParams.hostApiSpecificStreamInfo = nullptr;
|
||||
if(devidopt && *devidopt >= 0) params.device = *devidopt;
|
||||
else params.device = Pa_GetDefaultOutputDevice();
|
||||
params.suggestedLatency = mDevice->BufferSize / static_cast<double>(mDevice->Frequency);
|
||||
params.hostApiSpecificStreamInfo = nullptr;
|
||||
|
||||
mParams.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
params.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
|
||||
switch(mDevice->FmtType)
|
||||
{
|
||||
case DevFmtByte:
|
||||
mParams.sampleFormat = paInt8;
|
||||
params.sampleFormat = paInt8;
|
||||
break;
|
||||
case DevFmtUByte:
|
||||
mParams.sampleFormat = paUInt8;
|
||||
params.sampleFormat = paUInt8;
|
||||
break;
|
||||
case DevFmtUShort:
|
||||
/* fall-through */
|
||||
case DevFmtShort:
|
||||
mParams.sampleFormat = paInt16;
|
||||
params.sampleFormat = paInt16;
|
||||
break;
|
||||
case DevFmtUInt:
|
||||
/* fall-through */
|
||||
case DevFmtInt:
|
||||
mParams.sampleFormat = paInt32;
|
||||
params.sampleFormat = paInt32;
|
||||
break;
|
||||
case DevFmtFloat:
|
||||
mParams.sampleFormat = paFloat32;
|
||||
params.sampleFormat = paFloat32;
|
||||
break;
|
||||
}
|
||||
|
||||
retry_open:
|
||||
PaError err{Pa_OpenStream(&mStream, nullptr, &mParams, mDevice->Frequency, mDevice->UpdateSize,
|
||||
PaStream *stream{};
|
||||
PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency, mDevice->UpdateSize,
|
||||
paNoFlag, &PortPlayback::writeCallbackC, this)};
|
||||
if(err != paNoError)
|
||||
{
|
||||
if(mParams.sampleFormat == paFloat32)
|
||||
if(params.sampleFormat == paFloat32)
|
||||
{
|
||||
mParams.sampleFormat = paInt16;
|
||||
params.sampleFormat = paInt16;
|
||||
goto retry_open;
|
||||
}
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s",
|
||||
Pa_GetErrorText(err)};
|
||||
}
|
||||
|
||||
Pa_CloseStream(mStream);
|
||||
mStream = stream;
|
||||
mParams = params;
|
||||
mUpdateSize = mDevice->UpdateSize;
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
|
@ -226,7 +231,7 @@ void PortPlayback::stop()
|
|||
|
||||
|
||||
struct PortCapture final : public BackendBase {
|
||||
PortCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
PortCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~PortCapture() override;
|
||||
|
||||
int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer,
|
||||
|
@ -426,7 +431,7 @@ std::string PortBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr PortBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr PortBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new PortPlayback{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_PORTAUDIO_H
|
||||
#define BACKENDS_PORTAUDIO_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct PortBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -21,33 +21,49 @@
|
|||
|
||||
#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 <functional>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bitset>
|
||||
#include <chrono>
|
||||
#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 "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "compat.h"
|
||||
#include "albyte.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "alspan.h"
|
||||
#include "core/devformat.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "dynload.h"
|
||||
#include "opthelpers.h"
|
||||
#include "strutils.h"
|
||||
#include "vector.h"
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
#ifdef HAVE_DYNLOAD
|
||||
#define PULSE_FUNCS(MAGIC) \
|
||||
MAGIC(pa_mainloop_new); \
|
||||
|
@ -282,7 +298,7 @@ al::optional<Channel> ChannelFromPulse(pa_channel_position_t chan)
|
|||
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);
|
||||
for(uint i{0};i < chanmap.channels;++i)
|
||||
|
@ -497,19 +513,13 @@ public:
|
|||
|
||||
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)
|
||||
{
|
||||
mThread = std::thread{std::mem_fn(&PulseMainloop::mainloop_proc), this};
|
||||
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,
|
||||
"pa_context_new() failed"};
|
||||
|
||||
|
@ -661,7 +671,7 @@ PulseMainloop gGlobalMainloop;
|
|||
|
||||
|
||||
struct PulsePlayback final : public BackendBase {
|
||||
PulsePlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
PulsePlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~PulsePlayback() override;
|
||||
|
||||
void bufferAttrCallback(pa_stream *stream) noexcept;
|
||||
|
@ -846,6 +856,7 @@ void PulsePlayback::open(const char *name)
|
|||
}
|
||||
|
||||
auto plock = mMainloop.getUniqueLock();
|
||||
if(!mContext)
|
||||
mContext = mMainloop.connectContext(plock);
|
||||
|
||||
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();
|
||||
}
|
||||
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
|
||||
mStream = mMainloop.connectStream(pulse_name, plock, mContext, flags, nullptr, &spec, nullptr,
|
||||
BackendType::Playback);
|
||||
pa_stream *stream{mMainloop.connectStream(pulse_name, plock, mContext, flags, nullptr, &spec,
|
||||
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);
|
||||
mFrameSize = static_cast<uint>(pa_frame_size(pa_stream_get_sample_spec(mStream)));
|
||||
|
@ -1029,34 +1050,34 @@ void PulsePlayback::start()
|
|||
{
|
||||
auto plock = mMainloop.getUniqueLock();
|
||||
|
||||
pa_stream_set_write_callback(mStream, &PulsePlayback::streamWriteCallbackC, this);
|
||||
pa_operation *op{pa_stream_cork(mStream, 0, &PulseMainloop::streamSuccessCallbackC,
|
||||
&mMainloop)};
|
||||
|
||||
/* Write some (silent) samples to fill the prebuf amount if needed. */
|
||||
if(size_t prebuf{mAttr.prebuf})
|
||||
/* Write some (silent) samples to fill the buffer before we start feeding
|
||||
* it newly mixed samples.
|
||||
*/
|
||||
if(size_t todo{pa_stream_writable_size(mStream)})
|
||||
{
|
||||
prebuf = minz(prebuf, pa_stream_writable_size(mStream));
|
||||
|
||||
void *buf{pa_xmalloc(prebuf)};
|
||||
void *buf{pa_xmalloc(todo)};
|
||||
switch(mSpec.format)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
std::fill_n(static_cast<uint8_t*>(buf), prebuf, 0x00);
|
||||
std::fill_n(static_cast<uint8_t*>(buf), todo, 0x00);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1103,7 +1124,7 @@ ClockLatency PulsePlayback::getClockLatency()
|
|||
|
||||
|
||||
struct PulseCapture final : public BackendBase {
|
||||
PulseCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
PulseCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~PulseCapture() override;
|
||||
|
||||
void streamStateCallback(pa_stream *stream) noexcept;
|
||||
|
@ -1505,7 +1526,7 @@ std::string PulseBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr PulseBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr PulseBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new PulsePlayback{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_PULSEAUDIO_H
|
||||
#define BACKENDS_PULSEAUDIO_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
class PulseBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -20,16 +20,15 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/sdl2.h"
|
||||
#include "sdl2.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "almalloc.h"
|
||||
#include "alu.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
@ -46,7 +45,7 @@ namespace {
|
|||
constexpr char defaultDeviceName[] = DEVNAME_PREFIX "Default Device";
|
||||
|
||||
struct Sdl2Backend final : public BackendBase {
|
||||
Sdl2Backend(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~Sdl2Backend() override;
|
||||
|
||||
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
|
||||
* necessarily the first in the list.
|
||||
*/
|
||||
SDL_AudioDeviceID devid;
|
||||
if(!name || strcmp(name, defaultDeviceName) == 0)
|
||||
mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have,
|
||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
devid = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
else
|
||||
{
|
||||
const size_t prefix_len = strlen(DEVNAME_PREFIX);
|
||||
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);
|
||||
else
|
||||
mDeviceID = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have,
|
||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
devid = SDL_OpenAudioDevice(name, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||
}
|
||||
if(mDeviceID == 0)
|
||||
if(!devid)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()};
|
||||
|
||||
mDevice->Frequency = static_cast<uint>(have.freq);
|
||||
|
||||
DevFmtChannels devchans{};
|
||||
if(have.channels == 1)
|
||||
mDevice->FmtChans = DevFmtMono;
|
||||
devchans = DevFmtMono;
|
||||
else if(have.channels == 2)
|
||||
mDevice->FmtChans = DevFmtStereo;
|
||||
devchans = DevFmtStereo;
|
||||
else
|
||||
{
|
||||
SDL_CloseAudioDevice(devid);
|
||||
throw al::backend_exception{al::backend_error::DeviceError,
|
||||
"Unhandled SDL channel count: %d", int{have.channels}};
|
||||
}
|
||||
|
||||
DevFmtType devtype{};
|
||||
switch(have.format)
|
||||
{
|
||||
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_F32SYS: mDevice->FmtType = DevFmtFloat; break;
|
||||
default:
|
||||
SDL_CloseAudioDevice(devid);
|
||||
throw al::backend_exception{al::backend_error::DeviceError, "Unhandled SDL format: 0x%04x",
|
||||
have.format};
|
||||
}
|
||||
mDevice->UpdateSize = have.samples;
|
||||
mDevice->BufferSize = have.samples * 2; /* SDL always (tries to) use two periods. */
|
||||
|
||||
mFrameSize = mDevice->frameSizeFromFmt();
|
||||
mFrequency = mDevice->Frequency;
|
||||
mFmtChans = mDevice->FmtChans;
|
||||
mFmtType = mDevice->FmtType;
|
||||
mUpdateSize = mDevice->UpdateSize;
|
||||
if(mDeviceID)
|
||||
SDL_CloseAudioDevice(mDeviceID);
|
||||
mDeviceID = devid;
|
||||
|
||||
mFrameSize = FrameSizeFromDevFmt(devchans, devtype, 0);
|
||||
mFrequency = static_cast<uint>(have.freq);
|
||||
mFmtChans = devchans;
|
||||
mFmtType = devtype;
|
||||
mUpdateSize = have.samples;
|
||||
|
||||
mDevice->DeviceName = name ? name : defaultDeviceName;
|
||||
}
|
||||
|
@ -162,7 +166,7 @@ bool Sdl2Backend::reset()
|
|||
mDevice->FmtChans = mFmtChans;
|
||||
mDevice->FmtType = mFmtType;
|
||||
mDevice->UpdateSize = mUpdateSize;
|
||||
mDevice->BufferSize = mUpdateSize * 2;
|
||||
mDevice->BufferSize = mUpdateSize * 2; /* SDL always (tries to) use two periods. */
|
||||
setDefaultWFXChannelOrder();
|
||||
return true;
|
||||
}
|
||||
|
@ -208,7 +212,7 @@ std::string SDL2BackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr SDL2BackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr SDL2BackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new Sdl2Backend{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_SDL2_H
|
||||
#define BACKENDS_SDL2_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct SDL2BackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/sndio.h"
|
||||
#include "sndio.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -29,8 +29,9 @@
|
|||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "threads.h"
|
||||
|
@ -45,7 +46,7 @@ static const char sndio_device[] = "SndIO Default";
|
|||
|
||||
|
||||
struct SndioPlayback final : public BackendBase {
|
||||
SndioPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
SndioPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~SndioPlayback() override;
|
||||
|
||||
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",
|
||||
name};
|
||||
|
||||
mSndHandle = sio_open(nullptr, SIO_PLAY, 0);
|
||||
if(mSndHandle == nullptr)
|
||||
sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)};
|
||||
if(!sndHandle)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not open backend device"};
|
||||
|
||||
if(mSndHandle)
|
||||
sio_close(mSndHandle);
|
||||
mSndHandle = sndHandle;
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
|
@ -288,7 +293,7 @@ void SndioPlayback::stop()
|
|||
|
||||
|
||||
struct SndioCapture final : public BackendBase {
|
||||
SndioCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
SndioCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~SndioCapture() override;
|
||||
|
||||
int recordProc();
|
||||
|
@ -507,7 +512,7 @@ std::string SndIOBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr SndIOBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr SndIOBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new SndioPlayback{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_SNDIO_H
|
||||
#define BACKENDS_SNDIO_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct SndIOBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/solaris.h"
|
||||
#include "solaris.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -39,11 +39,10 @@
|
|||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "albyte.h"
|
||||
#include "alu.h"
|
||||
#include "alconfig.h"
|
||||
#include "compat.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "threads.h"
|
||||
#include "vector.h"
|
||||
|
@ -59,7 +58,7 @@ std::string solaris_driver{"/dev/audio"};
|
|||
|
||||
|
||||
struct SolarisBackend final : public BackendBase {
|
||||
SolarisBackend(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
SolarisBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~SolarisBackend() override;
|
||||
|
||||
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",
|
||||
name};
|
||||
|
||||
mFd = ::open(solaris_driver.c_str(), O_WRONLY);
|
||||
if(mFd == -1)
|
||||
int fd{::open(solaris_driver.c_str(), O_WRONLY)};
|
||||
if(fd == -1)
|
||||
throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s",
|
||||
solaris_driver.c_str(), strerror(errno)};
|
||||
|
||||
if(mFd != -1)
|
||||
::close(mFd);
|
||||
mFd = fd;
|
||||
|
||||
mDevice->DeviceName = name;
|
||||
}
|
||||
|
||||
|
@ -291,7 +294,7 @@ std::string SolarisBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr SolarisBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr SolarisBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new SolarisBackend{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_SOLARIS_H
|
||||
#define BACKENDS_SOLARIS_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct SolarisBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/wasapi.h"
|
||||
#include "wasapi.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
@ -56,10 +56,11 @@
|
|||
#include <vector>
|
||||
|
||||
#include "albit.h"
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
#include "converter.h"
|
||||
#include "alnumeric.h"
|
||||
#include "comptr.h"
|
||||
#include "core/converter.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "ringbuffer.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 {
|
||||
char mMsg[64];
|
||||
|
||||
|
@ -238,10 +176,9 @@ struct DevMap {
|
|||
|
||||
bool checkName(const al::vector<DevMap> &list, const std::string &name)
|
||||
{
|
||||
return std::find_if(list.cbegin(), list.cend(),
|
||||
[&name](const DevMap &entry) -> bool
|
||||
{ return entry.name == name; }
|
||||
) != list.cend();
|
||||
auto match_name = [&name](const DevMap &entry) -> bool
|
||||
{ return entry.name == name; };
|
||||
return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend();
|
||||
}
|
||||
|
||||
al::vector<DevMap> PlaybackDevices;
|
||||
|
@ -297,26 +234,26 @@ NameGUIDPair get_device_name_and_guid(IMMDevice *device)
|
|||
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;
|
||||
HRESULT hr = device->OpenPropertyStore(STGM_READ, ps.getPtr());
|
||||
HRESULT hr{device->OpenPropertyStore(STGM_READ, ps.getPtr())};
|
||||
if(FAILED(hr))
|
||||
{
|
||||
WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
|
||||
return;
|
||||
return UnknownFormFactor;
|
||||
}
|
||||
|
||||
EndpointFormFactor formfactor{UnknownFormFactor};
|
||||
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))
|
||||
WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
|
||||
else if(pvform->vt == VT_UI4)
|
||||
*formfactor = static_cast<EndpointFormFactor>(pvform->ulVal);
|
||||
else if(pvform->vt == VT_EMPTY)
|
||||
*formfactor = UnknownFormFactor;
|
||||
else
|
||||
formfactor = static_cast<EndpointFormFactor>(pvform->ulVal);
|
||||
else if(pvform->vt != VT_EMPTY)
|
||||
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 {
|
||||
OpenDevice,
|
||||
ReopenDevice,
|
||||
ResetDevice,
|
||||
StartDevice,
|
||||
StopDevice,
|
||||
CloseDevice,
|
||||
EnumeratePlayback,
|
||||
EnumerateCapture,
|
||||
QuitThread,
|
||||
|
||||
Count
|
||||
Count,
|
||||
QuitThread = Count
|
||||
};
|
||||
|
||||
constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{
|
||||
"Open Device",
|
||||
"Reopen Device",
|
||||
"Reset Device",
|
||||
"Start Device",
|
||||
"Stop Device",
|
||||
"Close Device",
|
||||
"Enumerate Playback",
|
||||
"Enumerate Capture",
|
||||
"Quit"
|
||||
"Enumerate Capture"
|
||||
};
|
||||
|
||||
|
||||
|
@ -511,7 +449,7 @@ constexpr char MessageStr[static_cast<size_t>(MsgType::Count)][20]{
|
|||
struct WasapiProxy {
|
||||
virtual ~WasapiProxy() = default;
|
||||
|
||||
virtual HRESULT openProxy() = 0;
|
||||
virtual HRESULT openProxy(const char *name) = 0;
|
||||
virtual void closeProxy() = 0;
|
||||
|
||||
virtual HRESULT resetProxy() = 0;
|
||||
|
@ -521,19 +459,22 @@ struct WasapiProxy {
|
|||
struct Msg {
|
||||
MsgType mType;
|
||||
WasapiProxy *mProxy;
|
||||
const char *mParam;
|
||||
std::promise<HRESULT> mPromise;
|
||||
|
||||
operator bool() const noexcept { return mType != MsgType::QuitThread; }
|
||||
};
|
||||
static std::deque<Msg> mMsgQueue;
|
||||
static std::mutex mMsgQueueLock;
|
||||
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::future<HRESULT> future{promise.get_future()};
|
||||
{
|
||||
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();
|
||||
return future;
|
||||
|
@ -545,19 +486,19 @@ struct WasapiProxy {
|
|||
std::future<HRESULT> future{promise.get_future()};
|
||||
{
|
||||
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();
|
||||
return future;
|
||||
}
|
||||
|
||||
static bool popMessage(Msg &msg)
|
||||
static Msg popMessage()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock{mMsgQueueLock};
|
||||
mMsgQueueCond.wait(lock, []{return !mMsgQueue.empty();});
|
||||
msg = std::move(mMsgQueue.front());
|
||||
Msg msg{std::move(mMsgQueue.front())};
|
||||
mMsgQueue.pop_front();
|
||||
return msg.mType != MsgType::QuitThread;
|
||||
return msg;
|
||||
}
|
||||
|
||||
static int messageHandler(std::promise<HRESULT> *promise);
|
||||
|
@ -597,12 +538,11 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
|
|||
|
||||
TRACE("Starting message loop\n");
|
||||
uint deviceCount{0};
|
||||
Msg msg;
|
||||
while(popMessage(msg))
|
||||
while(Msg msg{popMessage()})
|
||||
{
|
||||
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),
|
||||
decltype(std::declval<void*>()){msg.mProxy});
|
||||
static_cast<void*>(msg.mProxy), static_cast<const void*>(msg.mParam));
|
||||
|
||||
switch(msg.mType)
|
||||
{
|
||||
|
@ -611,7 +551,7 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
|
|||
if(++deviceCount == 1)
|
||||
hr = cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if(SUCCEEDED(hr))
|
||||
hr = msg.mProxy->openProxy();
|
||||
hr = msg.mProxy->openProxy(msg.mParam);
|
||||
msg.mPromise.set_value(hr);
|
||||
|
||||
if(FAILED(hr))
|
||||
|
@ -621,6 +561,11 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
|
|||
}
|
||||
continue;
|
||||
|
||||
case MsgType::ReopenDevice:
|
||||
hr = msg.mProxy->openProxy(msg.mParam);
|
||||
msg.mPromise.set_value(hr);
|
||||
continue;
|
||||
|
||||
case MsgType::ResetDevice:
|
||||
hr = msg.mProxy->resetProxy();
|
||||
msg.mPromise.set_value(hr);
|
||||
|
@ -669,11 +614,11 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
|
|||
CoUninitialize();
|
||||
continue;
|
||||
|
||||
default:
|
||||
case MsgType::QuitThread:
|
||||
break;
|
||||
}
|
||||
ERR("Unexpected message: %u\n", static_cast<uint>(msg.mType));
|
||||
msg.mPromise.set_value(E_FAIL);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
TRACE("Message loop finished\n");
|
||||
|
||||
|
@ -682,13 +627,13 @@ int WasapiProxy::messageHandler(std::promise<HRESULT> *promise)
|
|||
|
||||
|
||||
struct WasapiPlayback final : public BackendBase, WasapiProxy {
|
||||
WasapiPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
WasapiPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~WasapiPlayback() override;
|
||||
|
||||
int mixerProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
HRESULT openProxy() override;
|
||||
HRESULT openProxy(const char *name) override;
|
||||
void closeProxy() override;
|
||||
|
||||
bool reset() override;
|
||||
|
@ -700,8 +645,6 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy {
|
|||
|
||||
ClockLatency getClockLatency() override;
|
||||
|
||||
std::wstring mDevId;
|
||||
|
||||
HRESULT mOpenStatus{E_FAIL};
|
||||
ComPtr<IMMDevice> mMMDev{nullptr};
|
||||
ComPtr<IAudioClient> mClient{nullptr};
|
||||
|
@ -796,21 +739,40 @@ void WasapiPlayback::open(const char *name)
|
|||
{
|
||||
HRESULT hr{S_OK};
|
||||
|
||||
if(!mNotifyEvent)
|
||||
{
|
||||
mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||
if(mNotifyEvent == nullptr)
|
||||
{
|
||||
ERR("Failed to create notify events: %lu\n", GetLastError());
|
||||
hr = E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
if(name)
|
||||
{
|
||||
if(PlaybackDevices.empty())
|
||||
if(name && PlaybackDevices.empty())
|
||||
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(),
|
||||
[name](const DevMap &entry) -> bool
|
||||
{ return entry.name == name || entry.endpoint_guid == name; });
|
||||
|
@ -822,57 +784,36 @@ void WasapiPlayback::open(const char *name)
|
|||
{ return entry.devid == wname; });
|
||||
}
|
||||
if(iter == PlaybackDevices.cend())
|
||||
{
|
||||
WARN("Failed to find device name matching \"%s\"\n", name);
|
||||
else
|
||||
{
|
||||
mDevId = iter->devid;
|
||||
mDevice->DeviceName = iter->name;
|
||||
hr = S_OK;
|
||||
}
|
||||
return E_FAIL;
|
||||
}
|
||||
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;
|
||||
ComPtr<IMMDevice> mmdev;
|
||||
HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
|
||||
IID_IMMDeviceEnumerator, &ptr)};
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
|
||||
if(mDevId.empty())
|
||||
hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, mMMDev.getPtr());
|
||||
if(!devid)
|
||||
hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, mmdev.getPtr());
|
||||
else
|
||||
hr = enumerator->GetDevice(mDevId.c_str(), mMMDev.getPtr());
|
||||
hr = enumerator->GetDevice(devid, mmdev.getPtr());
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
|
||||
if(SUCCEEDED(hr))
|
||||
if(FAILED(hr))
|
||||
{
|
||||
mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)};
|
||||
if(mDevice->DeviceName.empty())
|
||||
mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first;
|
||||
WARN("Failed to open device \"%s\"\n", name?name:"(default)");
|
||||
return hr;
|
||||
}
|
||||
|
||||
if(FAILED(hr))
|
||||
mMMDev = nullptr;
|
||||
mClient = nullptr;
|
||||
mMMDev = std::move(mmdev);
|
||||
if(name) mDevice->DeviceName = name;
|
||||
else mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
@ -1105,8 +1046,7 @@ HRESULT WasapiPlayback::resetProxy()
|
|||
}
|
||||
mFrameStep = OutputType.Format.nChannels;
|
||||
|
||||
EndpointFormFactor formfactor{UnknownFormFactor};
|
||||
get_device_formfactor(mMMDev.get(), &formfactor);
|
||||
const EndpointFormFactor formfactor{get_device_formfactor(mMMDev.get())};
|
||||
mDevice->IsHeadphones = (mDevice->FmtChans == DevFmtStereo
|
||||
&& (formfactor == Headphones || formfactor == Headset));
|
||||
|
||||
|
@ -1220,13 +1160,13 @@ ClockLatency WasapiPlayback::getClockLatency()
|
|||
|
||||
|
||||
struct WasapiCapture final : public BackendBase, WasapiProxy {
|
||||
WasapiCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
WasapiCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~WasapiCapture() override;
|
||||
|
||||
int recordProc();
|
||||
|
||||
void open(const char *name) override;
|
||||
HRESULT openProxy() override;
|
||||
HRESULT openProxy(const char *name) override;
|
||||
void closeProxy() override;
|
||||
|
||||
HRESULT resetProxy() override;
|
||||
|
@ -1238,8 +1178,6 @@ struct WasapiCapture final : public BackendBase, WasapiProxy {
|
|||
void captureSamples(al::byte *buffer, uint samples) override;
|
||||
uint availableSamples() override;
|
||||
|
||||
std::wstring mDevId;
|
||||
|
||||
HRESULT mOpenStatus{E_FAIL};
|
||||
ComPtr<IMMDevice> mMMDev{nullptr};
|
||||
ComPtr<IAudioClient> mClient{nullptr};
|
||||
|
@ -1373,12 +1311,30 @@ void WasapiCapture::open(const char *name)
|
|||
|
||||
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(CaptureDevices.empty())
|
||||
pushMessage(MsgType::EnumerateCapture).wait();
|
||||
|
||||
hr = E_FAIL;
|
||||
auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(),
|
||||
[name](const DevMap &entry) -> bool
|
||||
{ return entry.name == name || entry.endpoint_guid == name; });
|
||||
|
@ -1390,65 +1346,34 @@ void WasapiCapture::open(const char *name)
|
|||
{ return entry.devid == wname; });
|
||||
}
|
||||
if(iter == CaptureDevices.cend())
|
||||
{
|
||||
WARN("Failed to find device name matching \"%s\"\n", name);
|
||||
else
|
||||
{
|
||||
mDevId = iter->devid;
|
||||
mDevice->DeviceName = iter->name;
|
||||
hr = S_OK;
|
||||
}
|
||||
return E_FAIL;
|
||||
}
|
||||
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;
|
||||
HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER,
|
||||
IID_IMMDeviceEnumerator, &ptr)};
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
ComPtr<IMMDeviceEnumerator> enumerator{static_cast<IMMDeviceEnumerator*>(ptr)};
|
||||
if(mDevId.empty())
|
||||
if(!devid)
|
||||
hr = enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, mMMDev.getPtr());
|
||||
else
|
||||
hr = enumerator->GetDevice(mDevId.c_str(), mMMDev.getPtr());
|
||||
hr = enumerator->GetDevice(devid, mMMDev.getPtr());
|
||||
}
|
||||
if(SUCCEEDED(hr))
|
||||
hr = mMMDev->Activate(IID_IAudioClient, CLSCTX_INPROC_SERVER, nullptr, &ptr);
|
||||
if(SUCCEEDED(hr))
|
||||
if(FAILED(hr))
|
||||
{
|
||||
mClient = ComPtr<IAudioClient>{static_cast<IAudioClient*>(ptr)};
|
||||
if(mDevice->DeviceName.empty())
|
||||
mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first;
|
||||
WARN("Failed to open device \"%s\"\n", name?name:"(default)");
|
||||
return hr;
|
||||
}
|
||||
|
||||
if(FAILED(hr))
|
||||
mMMDev = nullptr;
|
||||
mClient = nullptr;
|
||||
if(name) mDevice->DeviceName = name;
|
||||
else mDevice->DeviceName = get_device_name_and_guid(mMMDev.get()).first;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
@ -1567,7 +1492,7 @@ HRESULT WasapiCapture::resetProxy()
|
|||
CoTaskMemFree(wfx);
|
||||
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
|
||||
{
|
||||
switch(device->FmtChans)
|
||||
|
@ -1832,7 +1757,7 @@ std::string WasapiBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr WasapiBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr WasapiBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new WasapiPlayback{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_WASAPI_H
|
||||
#define BACKENDS_WASAPI_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct WasapiBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/wave.h"
|
||||
#include "wave.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
@ -35,12 +35,11 @@
|
|||
|
||||
#include "albit.h"
|
||||
#include "albyte.h"
|
||||
#include "alcmain.h"
|
||||
#include "alconfig.h"
|
||||
#include "alc/alconfig.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "opthelpers.h"
|
||||
#include "strutils.h"
|
||||
|
@ -93,7 +92,7 @@ void fwrite32le(uint val, FILE *f)
|
|||
|
||||
|
||||
struct WaveBackend final : public BackendBase {
|
||||
WaveBackend(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
WaveBackend(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~WaveBackend() override;
|
||||
|
||||
int mixerProc();
|
||||
|
@ -132,8 +131,8 @@ int WaveBackend::mixerProc()
|
|||
|
||||
int64_t done{0};
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while(!mKillNow.load(std::memory_order_acquire) &&
|
||||
mDevice->Connected.load(std::memory_order_acquire))
|
||||
while(!mKillNow.load(std::memory_order_acquire)
|
||||
&& mDevice->Connected.load(std::memory_order_acquire))
|
||||
{
|
||||
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",
|
||||
name};
|
||||
|
||||
/* There's only one "device", so if it's already open, we're done. */
|
||||
if(mFile) return;
|
||||
|
||||
#ifdef _WIN32
|
||||
{
|
||||
std::wstring wname = utf8_to_wstr(fname);
|
||||
std::wstring wname{utf8_to_wstr(fname)};
|
||||
mFile = _wfopen(wname.c_str(), L"wb");
|
||||
}
|
||||
#else
|
||||
|
@ -392,7 +394,7 @@ std::string WaveBackendFactory::probe(BackendType type)
|
|||
return outnames;
|
||||
}
|
||||
|
||||
BackendPtr WaveBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr WaveBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new WaveBackend{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_WAVE_H
|
||||
#define BACKENDS_WAVE_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct WaveBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "backends/winmm.h"
|
||||
#include "winmm.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
@ -38,9 +38,9 @@
|
|||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alu.h"
|
||||
#include "compat.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "core/helpers.h"
|
||||
#include "core/logging.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "strutils.h"
|
||||
|
@ -125,7 +125,7 @@ void ProbeCaptureDevices(void)
|
|||
|
||||
|
||||
struct WinMMPlayback final : public BackendBase {
|
||||
WinMMPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
WinMMPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~WinMMPlayback() override;
|
||||
|
||||
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));
|
||||
|
||||
retry_open:
|
||||
mFormat = WAVEFORMATEX{};
|
||||
WAVEFORMATEX format{};
|
||||
if(mDevice->FmtType == DevFmtFloat)
|
||||
{
|
||||
mFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
mFormat.wBitsPerSample = 32;
|
||||
format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
format.wBitsPerSample = 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
mFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
if(mDevice->FmtType == DevFmtUByte || mDevice->FmtType == DevFmtByte)
|
||||
mFormat.wBitsPerSample = 8;
|
||||
format.wBitsPerSample = 8;
|
||||
else
|
||||
mFormat.wBitsPerSample = 16;
|
||||
format.wBitsPerSample = 16;
|
||||
}
|
||||
mFormat.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
mFormat.nBlockAlign = static_cast<WORD>(mFormat.wBitsPerSample * mFormat.nChannels / 8);
|
||||
mFormat.nSamplesPerSec = mDevice->Frequency;
|
||||
mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign;
|
||||
mFormat.cbSize = 0;
|
||||
format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2);
|
||||
format.nBlockAlign = static_cast<WORD>(format.wBitsPerSample * format.nChannels / 8);
|
||||
format.nSamplesPerSec = mDevice->Frequency;
|
||||
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
||||
format.cbSize = 0;
|
||||
|
||||
MMRESULT res{waveOutOpen(&mOutHdl, DeviceID, &mFormat,
|
||||
HWAVEOUT outHandle{};
|
||||
MMRESULT res{waveOutOpen(&outHandle, DeviceID, &format,
|
||||
reinterpret_cast<DWORD_PTR>(&WinMMPlayback::waveOutProcC),
|
||||
reinterpret_cast<DWORD_PTR>(this), CALLBACK_FUNCTION)};
|
||||
if(res != MMSYSERR_NOERROR)
|
||||
|
@ -257,6 +258,11 @@ retry_open:
|
|||
throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: %u", res};
|
||||
}
|
||||
|
||||
if(mOutHdl)
|
||||
waveOutClose(mOutHdl);
|
||||
mOutHdl = outHandle;
|
||||
mFormat = format;
|
||||
|
||||
mDevice->DeviceName = PlaybackDevices[DeviceID];
|
||||
}
|
||||
|
||||
|
@ -364,7 +370,7 @@ void WinMMPlayback::stop()
|
|||
|
||||
|
||||
struct WinMMCapture final : public BackendBase {
|
||||
WinMMCapture(ALCdevice *device) noexcept : BackendBase{device} { }
|
||||
WinMMCapture(DeviceBase *device) noexcept : BackendBase{device} { }
|
||||
~WinMMCapture() override;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BackendPtr WinMMBackendFactory::createBackend(ALCdevice *device, BackendType type)
|
||||
BackendPtr WinMMBackendFactory::createBackend(DeviceBase *device, BackendType type)
|
||||
{
|
||||
if(type == BackendType::Playback)
|
||||
return BackendPtr{new WinMMPlayback{device}};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef BACKENDS_WINMM_H
|
||||
#define BACKENDS_WINMM_H
|
||||
|
||||
#include "backends/base.h"
|
||||
#include "base.h"
|
||||
|
||||
struct WinMMBackendFactory final : public BackendFactory {
|
||||
public:
|
||||
|
@ -11,7 +11,7 @@ public:
|
|||
|
||||
std::string probe(BackendType type) override;
|
||||
|
||||
BackendPtr createBackend(ALCdevice *device, BackendType type) override;
|
||||
BackendPtr createBackend(DeviceBase *device, BackendType type) override;
|
||||
|
||||
static BackendFactory &getFactory();
|
||||
};
|
||||
|
|
|
@ -0,0 +1,387 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "context.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <stddef.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "AL/efx.h"
|
||||
|
||||
#include "al/auxeffectslot.h"
|
||||
#include "al/source.h"
|
||||
#include "al/effect.h"
|
||||
#include "al/event.h"
|
||||
#include "al/listener.h"
|
||||
#include "albit.h"
|
||||
#include "alc/alu.h"
|
||||
#include "core/async_event.h"
|
||||
#include "core/device.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/voice.h"
|
||||
#include "core/voice_change.h"
|
||||
#include "device.h"
|
||||
#include "effectslot.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "vecmat.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
using voidp = void*;
|
||||
|
||||
/* Default context extensions */
|
||||
constexpr ALchar alExtList[] =
|
||||
"AL_EXT_ALAW "
|
||||
"AL_EXT_BFORMAT "
|
||||
"AL_EXT_DOUBLE "
|
||||
"AL_EXT_EXPONENT_DISTANCE "
|
||||
"AL_EXT_FLOAT32 "
|
||||
"AL_EXT_IMA4 "
|
||||
"AL_EXT_LINEAR_DISTANCE "
|
||||
"AL_EXT_MCFORMATS "
|
||||
"AL_EXT_MULAW "
|
||||
"AL_EXT_MULAW_BFORMAT "
|
||||
"AL_EXT_MULAW_MCFORMATS "
|
||||
"AL_EXT_OFFSET "
|
||||
"AL_EXT_source_distance_model "
|
||||
"AL_EXT_SOURCE_RADIUS "
|
||||
"AL_EXT_STEREO_ANGLES "
|
||||
"AL_LOKI_quadriphonic "
|
||||
"AL_SOFT_bformat_ex "
|
||||
"AL_SOFTX_bformat_hoa "
|
||||
"AL_SOFT_block_alignment "
|
||||
"AL_SOFTX_callback_buffer "
|
||||
"AL_SOFTX_convolution_reverb "
|
||||
"AL_SOFT_deferred_updates "
|
||||
"AL_SOFT_direct_channels "
|
||||
"AL_SOFT_direct_channels_remix "
|
||||
"AL_SOFT_effect_target "
|
||||
"AL_SOFT_events "
|
||||
"AL_SOFTX_filter_gain_ex "
|
||||
"AL_SOFT_gain_clamp_ex "
|
||||
"AL_SOFTX_hold_on_disconnect "
|
||||
"AL_SOFT_loop_points "
|
||||
"AL_SOFTX_map_buffer "
|
||||
"AL_SOFT_MSADPCM "
|
||||
"AL_SOFT_source_latency "
|
||||
"AL_SOFT_source_length "
|
||||
"AL_SOFT_source_resampler "
|
||||
"AL_SOFT_source_spatialize "
|
||||
"AL_SOFTX_UHJ";
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
|
||||
|
||||
thread_local ALCcontext *ALCcontext::sLocalContext{nullptr};
|
||||
ALCcontext::ThreadCtx::~ThreadCtx()
|
||||
{
|
||||
if(ALCcontext *ctx{ALCcontext::sLocalContext})
|
||||
{
|
||||
const bool result{ctx->releaseIfNoDelete()};
|
||||
ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx},
|
||||
result ? "" : ", leak detected");
|
||||
}
|
||||
}
|
||||
thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext;
|
||||
|
||||
ALeffect ALCcontext::sDefaultEffect;
|
||||
|
||||
|
||||
ContextBase::ContextBase(DeviceBase *device) : mDevice{device}
|
||||
{ }
|
||||
|
||||
ContextBase::~ContextBase()
|
||||
{
|
||||
size_t count{0};
|
||||
ContextProps *cprops{mParams.ContextUpdate.exchange(nullptr, std::memory_order_relaxed)};
|
||||
if(cprops)
|
||||
{
|
||||
++count;
|
||||
delete cprops;
|
||||
}
|
||||
cprops = mFreeContextProps.exchange(nullptr, std::memory_order_acquire);
|
||||
while(cprops)
|
||||
{
|
||||
std::unique_ptr<ContextProps> old{cprops};
|
||||
cprops = old->next.load(std::memory_order_relaxed);
|
||||
++count;
|
||||
}
|
||||
TRACE("Freed %zu context property object%s\n", count, (count==1)?"":"s");
|
||||
|
||||
count = 0;
|
||||
EffectSlotProps *eprops{mFreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)};
|
||||
while(eprops)
|
||||
{
|
||||
std::unique_ptr<EffectSlotProps> old{eprops};
|
||||
eprops = old->next.load(std::memory_order_relaxed);
|
||||
++count;
|
||||
}
|
||||
TRACE("Freed %zu AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
|
||||
|
||||
if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)})
|
||||
{
|
||||
al::destroy_n(curarray->end(), curarray->size());
|
||||
delete curarray;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
VoicePropsItem *vprops{mFreeVoiceProps.exchange(nullptr, std::memory_order_acquire)};
|
||||
while(vprops)
|
||||
{
|
||||
std::unique_ptr<VoicePropsItem> old{vprops};
|
||||
vprops = old->next.load(std::memory_order_relaxed);
|
||||
++count;
|
||||
}
|
||||
TRACE("Freed %zu voice property object%s\n", count, (count==1)?"":"s");
|
||||
|
||||
delete mVoices.exchange(nullptr, std::memory_order_relaxed);
|
||||
|
||||
count = 0;
|
||||
ListenerProps *lprops{mParams.ListenerUpdate.exchange(nullptr, std::memory_order_relaxed)};
|
||||
if(lprops)
|
||||
{
|
||||
++count;
|
||||
delete lprops;
|
||||
}
|
||||
lprops = mFreeListenerProps.exchange(nullptr, std::memory_order_acquire);
|
||||
while(lprops)
|
||||
{
|
||||
std::unique_ptr<ListenerProps> old{lprops};
|
||||
lprops = old->next.load(std::memory_order_relaxed);
|
||||
++count;
|
||||
}
|
||||
TRACE("Freed %zu listener property object%s\n", count, (count==1)?"":"s");
|
||||
|
||||
if(mAsyncEvents)
|
||||
{
|
||||
count = 0;
|
||||
auto evt_vec = mAsyncEvents->getReadVector();
|
||||
if(evt_vec.first.len > 0)
|
||||
{
|
||||
al::destroy_n(reinterpret_cast<AsyncEvent*>(evt_vec.first.buf), evt_vec.first.len);
|
||||
count += evt_vec.first.len;
|
||||
}
|
||||
if(evt_vec.second.len > 0)
|
||||
{
|
||||
al::destroy_n(reinterpret_cast<AsyncEvent*>(evt_vec.second.buf), evt_vec.second.len);
|
||||
count += evt_vec.second.len;
|
||||
}
|
||||
if(count > 0)
|
||||
TRACE("Destructed %zu orphaned event%s\n", count, (count==1)?"":"s");
|
||||
mAsyncEvents->readAdvance(count);
|
||||
}
|
||||
}
|
||||
|
||||
void ContextBase::allocVoiceChanges(size_t addcount)
|
||||
{
|
||||
constexpr size_t clustersize{128};
|
||||
/* Convert element count to cluster count. */
|
||||
addcount = (addcount+(clustersize-1)) / clustersize;
|
||||
while(addcount)
|
||||
{
|
||||
VoiceChangeCluster cluster{std::make_unique<VoiceChange[]>(clustersize)};
|
||||
for(size_t i{1};i < clustersize;++i)
|
||||
cluster[i-1].mNext.store(std::addressof(cluster[i]), std::memory_order_relaxed);
|
||||
cluster[clustersize-1].mNext.store(mVoiceChangeTail, std::memory_order_relaxed);
|
||||
mVoiceChangeClusters.emplace_back(std::move(cluster));
|
||||
mVoiceChangeTail = mVoiceChangeClusters.back().get();
|
||||
--addcount;
|
||||
}
|
||||
}
|
||||
|
||||
void ContextBase::allocVoices(size_t addcount)
|
||||
{
|
||||
constexpr size_t clustersize{32};
|
||||
/* Convert element count to cluster count. */
|
||||
addcount = (addcount+(clustersize-1)) / clustersize;
|
||||
|
||||
if(addcount >= std::numeric_limits<int>::max()/clustersize - mVoiceClusters.size())
|
||||
throw std::runtime_error{"Allocating too many voices"};
|
||||
const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize};
|
||||
TRACE("Increasing allocated voices to %zu\n", totalcount);
|
||||
|
||||
auto newarray = VoiceArray::Create(totalcount);
|
||||
while(addcount)
|
||||
{
|
||||
mVoiceClusters.emplace_back(std::make_unique<Voice[]>(clustersize));
|
||||
--addcount;
|
||||
}
|
||||
|
||||
auto voice_iter = newarray->begin();
|
||||
for(VoiceCluster &cluster : mVoiceClusters)
|
||||
{
|
||||
for(size_t i{0};i < clustersize;++i)
|
||||
*(voice_iter++) = &cluster[i];
|
||||
}
|
||||
|
||||
if(auto *oldvoices = mVoices.exchange(newarray.release(), std::memory_order_acq_rel))
|
||||
{
|
||||
mDevice->waitForMix();
|
||||
delete oldvoices;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device)
|
||||
: ContextBase{device.get()}, mALDevice{std::move(device)}
|
||||
{
|
||||
mPropsDirty.test_and_clear(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
ALCcontext::~ALCcontext()
|
||||
{
|
||||
TRACE("Freeing context %p\n", voidp{this});
|
||||
|
||||
size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), size_t{0u},
|
||||
[](size_t cur, const SourceSubList &sublist) noexcept -> size_t
|
||||
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
|
||||
if(count > 0)
|
||||
WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s");
|
||||
mSourceList.clear();
|
||||
mNumSources = 0;
|
||||
|
||||
mDefaultSlot = nullptr;
|
||||
count = std::accumulate(mEffectSlotList.cbegin(), mEffectSlotList.cend(), size_t{0u},
|
||||
[](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t
|
||||
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
|
||||
if(count > 0)
|
||||
WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s");
|
||||
mEffectSlotList.clear();
|
||||
mNumEffectSlots = 0;
|
||||
}
|
||||
|
||||
void ALCcontext::init()
|
||||
{
|
||||
if(sDefaultEffect.type != AL_EFFECT_NULL && mDevice->Type == DeviceType::Playback)
|
||||
{
|
||||
mDefaultSlot = std::make_unique<ALeffectslot>();
|
||||
aluInitEffectPanning(&mDefaultSlot->mSlot, this);
|
||||
}
|
||||
|
||||
EffectSlotArray *auxslots;
|
||||
if(!mDefaultSlot)
|
||||
auxslots = EffectSlot::CreatePtrArray(0);
|
||||
else
|
||||
{
|
||||
auxslots = EffectSlot::CreatePtrArray(1);
|
||||
(*auxslots)[0] = &mDefaultSlot->mSlot;
|
||||
mDefaultSlot->mState = SlotState::Playing;
|
||||
}
|
||||
mActiveAuxSlots.store(auxslots, std::memory_order_relaxed);
|
||||
|
||||
allocVoiceChanges(1);
|
||||
{
|
||||
VoiceChange *cur{mVoiceChangeTail};
|
||||
while(VoiceChange *next{cur->mNext.load(std::memory_order_relaxed)})
|
||||
cur = next;
|
||||
mCurrentVoiceChange.store(cur, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
mExtensionList = alExtList;
|
||||
|
||||
|
||||
mParams.Matrix = alu::Matrix::Identity();
|
||||
mParams.Velocity = alu::Vector{};
|
||||
mParams.Gain = mListener.Gain;
|
||||
mParams.MetersPerUnit = mListener.mMetersPerUnit;
|
||||
mParams.DopplerFactor = mDopplerFactor;
|
||||
mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity;
|
||||
mParams.SourceDistanceModel = mSourceDistanceModel;
|
||||
mParams.mDistanceModel = mDistanceModel;
|
||||
|
||||
|
||||
mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false);
|
||||
StartEventThrd(this);
|
||||
|
||||
|
||||
allocVoices(256);
|
||||
mActiveVoiceCount.store(64, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool ALCcontext::deinit()
|
||||
{
|
||||
if(sLocalContext == this)
|
||||
{
|
||||
WARN("%p released while current on thread\n", voidp{this});
|
||||
sThreadContext.set(nullptr);
|
||||
release();
|
||||
}
|
||||
|
||||
ALCcontext *origctx{this};
|
||||
if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
|
||||
release();
|
||||
|
||||
bool ret{};
|
||||
/* First make sure this context exists in the device's list. */
|
||||
auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire);
|
||||
if(auto toremove = static_cast<size_t>(std::count(oldarray->begin(), oldarray->end(), this)))
|
||||
{
|
||||
using ContextArray = al::FlexArray<ContextBase*>;
|
||||
auto alloc_ctx_array = [](const size_t count) -> ContextArray*
|
||||
{
|
||||
if(count == 0) return &DeviceBase::sEmptyContextArray;
|
||||
return ContextArray::Create(count).release();
|
||||
};
|
||||
auto *newarray = alloc_ctx_array(oldarray->size() - toremove);
|
||||
|
||||
/* Copy the current/old context handles to the new array, excluding the
|
||||
* given context.
|
||||
*/
|
||||
std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(),
|
||||
std::bind(std::not_equal_to<>{}, _1, this));
|
||||
|
||||
/* Store the new context array in the device. Wait for any current mix
|
||||
* to finish before deleting the old array.
|
||||
*/
|
||||
mDevice->mContexts.store(newarray);
|
||||
if(oldarray != &DeviceBase::sEmptyContextArray)
|
||||
{
|
||||
mDevice->waitForMix();
|
||||
delete oldarray;
|
||||
}
|
||||
|
||||
ret = !newarray->empty();
|
||||
}
|
||||
else
|
||||
ret = !oldarray->empty();
|
||||
|
||||
StopEventThrd(this);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ALCcontext::processUpdates()
|
||||
{
|
||||
std::lock_guard<std::mutex> _{mPropLock};
|
||||
if(mDeferUpdates.exchange(false, std::memory_order_acq_rel))
|
||||
{
|
||||
/* Tell the mixer to stop applying updates, then wait for any active
|
||||
* updating to finish, before providing updates.
|
||||
*/
|
||||
mHoldUpdates.store(true, std::memory_order_release);
|
||||
while((mUpdateCount.load(std::memory_order_acquire)&1) != 0) {
|
||||
/* busy-wait */
|
||||
}
|
||||
|
||||
if(mPropsDirty.test_and_clear(std::memory_order_acq_rel))
|
||||
UpdateContextProps(this);
|
||||
if(mListener.mPropsDirty.test_and_clear(std::memory_order_acq_rel))
|
||||
UpdateListenerProps(this);
|
||||
UpdateAllEffectSlotProps(this);
|
||||
UpdateAllSourceProps(this);
|
||||
|
||||
/* Now with all updates declared, let the mixer continue applying them
|
||||
* so they all happen at once.
|
||||
*/
|
||||
mHoldUpdates.store(false, std::memory_order_release);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
#ifndef ALC_CONTEXT_H
|
||||
#define ALC_CONTEXT_H
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdint.h>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "al/listener.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "atomic.h"
|
||||
#include "core/context.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "vector.h"
|
||||
|
||||
struct ALeffect;
|
||||
struct ALeffectslot;
|
||||
struct ALsource;
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
|
||||
struct SourceSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
ALsource *Sources{nullptr}; /* 64 */
|
||||
|
||||
SourceSubList() noexcept = default;
|
||||
SourceSubList(const SourceSubList&) = delete;
|
||||
SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; }
|
||||
~SourceSubList();
|
||||
|
||||
SourceSubList& operator=(const SourceSubList&) = delete;
|
||||
SourceSubList& operator=(SourceSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; }
|
||||
};
|
||||
|
||||
struct EffectSlotSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
ALeffectslot *EffectSlots{nullptr}; /* 64 */
|
||||
|
||||
EffectSlotSubList() noexcept = default;
|
||||
EffectSlotSubList(const EffectSlotSubList&) = delete;
|
||||
EffectSlotSubList(EffectSlotSubList&& rhs) noexcept
|
||||
: FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; }
|
||||
~EffectSlotSubList();
|
||||
|
||||
EffectSlotSubList& operator=(const EffectSlotSubList&) = delete;
|
||||
EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; }
|
||||
};
|
||||
|
||||
struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
|
||||
const al::intrusive_ptr<ALCdevice> mALDevice;
|
||||
|
||||
/* Wet buffers used by effect slots. */
|
||||
al::vector<WetBufferPtr> mWetBuffers;
|
||||
|
||||
|
||||
al::atomic_invflag mPropsDirty;
|
||||
std::atomic<bool> mDeferUpdates{false};
|
||||
|
||||
std::mutex mPropLock;
|
||||
|
||||
std::atomic<ALenum> mLastError{AL_NO_ERROR};
|
||||
|
||||
DistanceModel mDistanceModel{DistanceModel::Default};
|
||||
bool mSourceDistanceModel{false};
|
||||
|
||||
float mDopplerFactor{1.0f};
|
||||
float mDopplerVelocity{1.0f};
|
||||
float mSpeedOfSound{SpeedOfSoundMetersPerSec};
|
||||
|
||||
std::mutex mEventCbLock;
|
||||
ALEVENTPROCSOFT mEventCb{};
|
||||
void *mEventParam{nullptr};
|
||||
|
||||
ALlistener mListener{};
|
||||
|
||||
al::vector<SourceSubList> mSourceList;
|
||||
ALuint mNumSources{0};
|
||||
std::mutex mSourceLock;
|
||||
|
||||
al::vector<EffectSlotSubList> mEffectSlotList;
|
||||
ALuint mNumEffectSlots{0u};
|
||||
std::mutex mEffectSlotLock;
|
||||
|
||||
/* Default effect slot */
|
||||
std::unique_ptr<ALeffectslot> mDefaultSlot;
|
||||
|
||||
const char *mExtensionList{nullptr};
|
||||
|
||||
|
||||
ALCcontext(al::intrusive_ptr<ALCdevice> device);
|
||||
ALCcontext(const ALCcontext&) = delete;
|
||||
ALCcontext& operator=(const ALCcontext&) = delete;
|
||||
~ALCcontext();
|
||||
|
||||
void init();
|
||||
/**
|
||||
* Removes the context from its device and removes it from being current on
|
||||
* the running thread or globally. Returns true if other contexts still
|
||||
* exist on the device.
|
||||
*/
|
||||
bool deinit();
|
||||
|
||||
/**
|
||||
* Defers/suspends updates for the given context's listener and sources.
|
||||
* This does *NOT* stop mixing, but rather prevents certain property
|
||||
* changes from taking effect.
|
||||
*/
|
||||
void deferUpdates() noexcept { mDeferUpdates.exchange(true, std::memory_order_acq_rel); }
|
||||
|
||||
/** Resumes update processing after being deferred. */
|
||||
void processUpdates();
|
||||
|
||||
#ifdef __USE_MINGW_ANSI_STDIO
|
||||
[[gnu::format(gnu_printf, 3, 4)]]
|
||||
#else
|
||||
[[gnu::format(printf, 3, 4)]]
|
||||
#endif
|
||||
void setError(ALenum errorCode, const char *msg, ...);
|
||||
|
||||
/* Process-wide current context */
|
||||
static std::atomic<ALCcontext*> sGlobalContext;
|
||||
|
||||
/* Thread-local current context. */
|
||||
static thread_local ALCcontext *sLocalContext;
|
||||
/* Thread-local context handling. This handles attempting to release the
|
||||
* context which may have been left current when the thread is destroyed.
|
||||
*/
|
||||
class ThreadCtx {
|
||||
public:
|
||||
~ThreadCtx();
|
||||
void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; }
|
||||
};
|
||||
static thread_local ThreadCtx sThreadContext;
|
||||
|
||||
/* Default effect that applies to sources that don't have an effect on send 0. */
|
||||
static ALeffect sDefaultEffect;
|
||||
|
||||
DEF_NEWDEL(ALCcontext)
|
||||
};
|
||||
|
||||
#define SETERR_RETURN(ctx, err, retval, ...) do { \
|
||||
(ctx)->setError((err), __VA_ARGS__); \
|
||||
return retval; \
|
||||
} while(0)
|
||||
|
||||
|
||||
using ContextRef = al::intrusive_ptr<ALCcontext>;
|
||||
|
||||
ContextRef GetContextRef(void);
|
||||
|
||||
void UpdateContextProps(ALCcontext *context);
|
||||
|
||||
|
||||
extern bool TrapALError;
|
||||
|
||||
#endif /* ALC_CONTEXT_H */
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "device.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "albit.h"
|
||||
#include "alconfig.h"
|
||||
#include "backends/base.h"
|
||||
#include "core/bformatdec.h"
|
||||
#include "core/bs2b.h"
|
||||
#include "core/front_stablizer.h"
|
||||
#include "core/hrtf.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/mastering.h"
|
||||
#include "core/uhjfilter.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using voidp = void*;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
/* This should be in core/device.cpp. */
|
||||
DeviceBase::DeviceBase(DeviceType type) : Type{type}, mContexts{&sEmptyContextArray}
|
||||
{
|
||||
}
|
||||
|
||||
DeviceBase::~DeviceBase()
|
||||
{
|
||||
auto *oldarray = mContexts.exchange(nullptr, std::memory_order_relaxed);
|
||||
if(oldarray != &sEmptyContextArray) delete oldarray;
|
||||
}
|
||||
|
||||
|
||||
ALCdevice::~ALCdevice()
|
||||
{
|
||||
TRACE("Freeing device %p\n", voidp{this});
|
||||
|
||||
Backend = nullptr;
|
||||
|
||||
size_t count{std::accumulate(BufferList.cbegin(), BufferList.cend(), size_t{0u},
|
||||
[](size_t cur, const BufferSubList &sublist) noexcept -> size_t
|
||||
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); })};
|
||||
if(count > 0)
|
||||
WARN("%zu Buffer%s not deleted\n", count, (count==1)?"":"s");
|
||||
|
||||
count = std::accumulate(EffectList.cbegin(), EffectList.cend(), size_t{0u},
|
||||
[](size_t cur, const EffectSubList &sublist) noexcept -> size_t
|
||||
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
|
||||
if(count > 0)
|
||||
WARN("%zu Effect%s not deleted\n", count, (count==1)?"":"s");
|
||||
|
||||
count = std::accumulate(FilterList.cbegin(), FilterList.cend(), size_t{0u},
|
||||
[](size_t cur, const FilterSubList &sublist) noexcept -> size_t
|
||||
{ return cur + static_cast<uint>(al::popcount(~sublist.FreeMask)); });
|
||||
if(count > 0)
|
||||
WARN("%zu Filter%s not deleted\n", count, (count==1)?"":"s");
|
||||
}
|
||||
|
||||
void ALCdevice::enumerateHrtfs()
|
||||
{
|
||||
mHrtfList = EnumerateHrtf(ConfigValueStr(DeviceName.c_str(), nullptr, "hrtf-paths"));
|
||||
if(auto defhrtfopt = ConfigValueStr(DeviceName.c_str(), nullptr, "default-hrtf"))
|
||||
{
|
||||
auto iter = std::find(mHrtfList.begin(), mHrtfList.end(), *defhrtfopt);
|
||||
if(iter == mHrtfList.end())
|
||||
WARN("Failed to find default HRTF \"%s\"\n", defhrtfopt->c_str());
|
||||
else if(iter != mHrtfList.begin())
|
||||
std::rotate(mHrtfList.begin(), iter, iter+1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
#ifndef ALC_DEVICE_H
|
||||
#define ALC_DEVICE_H
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "AL/alc.h"
|
||||
#include "AL/alext.h"
|
||||
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "core/device.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "vector.h"
|
||||
|
||||
struct ALbuffer;
|
||||
struct ALeffect;
|
||||
struct ALfilter;
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
|
||||
struct BufferSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
ALbuffer *Buffers{nullptr}; /* 64 */
|
||||
|
||||
BufferSubList() noexcept = default;
|
||||
BufferSubList(const BufferSubList&) = delete;
|
||||
BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; }
|
||||
~BufferSubList();
|
||||
|
||||
BufferSubList& operator=(const BufferSubList&) = delete;
|
||||
BufferSubList& operator=(BufferSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; }
|
||||
};
|
||||
|
||||
struct EffectSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
ALeffect *Effects{nullptr}; /* 64 */
|
||||
|
||||
EffectSubList() noexcept = default;
|
||||
EffectSubList(const EffectSubList&) = delete;
|
||||
EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; }
|
||||
~EffectSubList();
|
||||
|
||||
EffectSubList& operator=(const EffectSubList&) = delete;
|
||||
EffectSubList& operator=(EffectSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; }
|
||||
};
|
||||
|
||||
struct FilterSubList {
|
||||
uint64_t FreeMask{~0_u64};
|
||||
ALfilter *Filters{nullptr}; /* 64 */
|
||||
|
||||
FilterSubList() noexcept = default;
|
||||
FilterSubList(const FilterSubList&) = delete;
|
||||
FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters}
|
||||
{ rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; }
|
||||
~FilterSubList();
|
||||
|
||||
FilterSubList& operator=(const FilterSubList&) = delete;
|
||||
FilterSubList& operator=(FilterSubList&& rhs) noexcept
|
||||
{ std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; }
|
||||
};
|
||||
|
||||
|
||||
struct ALCdevice : public al::intrusive_ref<ALCdevice>, DeviceBase {
|
||||
ALCuint NumMonoSources{};
|
||||
ALCuint NumStereoSources{};
|
||||
|
||||
// Maximum number of sources that can be created
|
||||
uint SourcesMax{};
|
||||
// Maximum number of slots that can be created
|
||||
uint AuxiliaryEffectSlotMax{};
|
||||
|
||||
std::string mHrtfName;
|
||||
al::vector<std::string> mHrtfList;
|
||||
ALCenum mHrtfStatus{ALC_FALSE};
|
||||
|
||||
ALCenum LimiterState{ALC_DONT_CARE_SOFT};
|
||||
|
||||
std::atomic<ALCenum> LastError{ALC_NO_ERROR};
|
||||
|
||||
// Map of Buffers for this device
|
||||
std::mutex BufferLock;
|
||||
al::vector<BufferSubList> BufferList;
|
||||
|
||||
// Map of Effects for this device
|
||||
std::mutex EffectLock;
|
||||
al::vector<EffectSubList> EffectList;
|
||||
|
||||
// Map of Filters for this device
|
||||
std::mutex FilterLock;
|
||||
al::vector<FilterSubList> FilterList;
|
||||
|
||||
|
||||
ALCdevice(DeviceType type) : DeviceBase{type} { }
|
||||
~ALCdevice();
|
||||
|
||||
void enumerateHrtfs();
|
||||
|
||||
DEF_NEWDEL(ALCdevice)
|
||||
};
|
||||
|
||||
#endif
|
|
@ -20,16 +20,26 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
#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 {
|
||||
|
||||
|
@ -69,8 +79,8 @@ struct AutowahState final : public EffectState {
|
|||
alignas(16) float mBufferOut[BufferLineSize];
|
||||
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
const al::span<FloatBufferLine> samplesOut) override;
|
||||
|
@ -78,7 +88,7 @@ struct AutowahState final : public EffectState {
|
|||
DEF_NEWDEL(AutowahState)
|
||||
};
|
||||
|
||||
void AutowahState::deviceUpdate(const ALCdevice*, const Buffer&)
|
||||
void AutowahState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||
{
|
||||
/* (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 ALCdevice *device{context->mDevice.get()};
|
||||
const DeviceBase *device{context->mDevice};
|
||||
const auto frequency = static_cast<float>(device->Frequency);
|
||||
|
||||
const float ReleaseTime{clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f)};
|
||||
|
|
|
@ -1,24 +1,37 @@
|
|||
#ifndef EFFECTS_BASE_H
|
||||
#define EFFECTS_BASE_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "albyte.h"
|
||||
#include "alcmain.h"
|
||||
#include "almalloc.h"
|
||||
#include "alspan.h"
|
||||
#include "atomic.h"
|
||||
#include "core/bufferline.h"
|
||||
#include "intrusive_ptr.h"
|
||||
|
||||
struct EffectSlot;
|
||||
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 {
|
||||
Sinusoid,
|
||||
Triangle
|
||||
};
|
||||
|
||||
constexpr float ChorusMaxDelay{0.016f};
|
||||
constexpr float FlangerMaxDelay{0.004f};
|
||||
|
||||
constexpr float EchoMaxDelay{0.207f};
|
||||
constexpr float EchoMaxLRDelay{0.404f};
|
||||
|
||||
|
@ -175,8 +188,8 @@ struct EffectState : public al::intrusive_ref<EffectState> {
|
|||
|
||||
virtual ~EffectState() = default;
|
||||
|
||||
virtual void deviceUpdate(const ALCdevice *device, const Buffer &buffer) = 0;
|
||||
virtual void update(const ALCcontext *context, const EffectSlot *slot,
|
||||
virtual void deviceUpdate(const DeviceBase *device, const Buffer &buffer) = 0;
|
||||
virtual void update(const ContextBase *context, const EffectSlot *slot,
|
||||
const EffectProps *props, const EffectTarget target) = 0;
|
||||
virtual void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
const al::span<FloatBufferLine> samplesOut) = 0;
|
||||
|
|
|
@ -21,20 +21,24 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/effectslot.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "alu.h"
|
||||
#include "core/ambidefs.h"
|
||||
#include "effects/base.h"
|
||||
#include "effectslot.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 "core/resampler_limits.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "math_defs.h"
|
||||
#include "opthelpers.h"
|
||||
#include "vector.h"
|
||||
|
@ -42,6 +46,8 @@
|
|||
|
||||
namespace {
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
#define MAX_UPDATE_SAMPLES 256
|
||||
|
||||
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 getSinusoidDelays(uint (*delays)[MAX_UPDATE_SAMPLES], const size_t todo);
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
const al::span<FloatBufferLine> samplesOut) override;
|
||||
|
@ -77,9 +83,9 @@ struct ChorusState final : public EffectState {
|
|||
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 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)
|
||||
{
|
||||
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
|
||||
* 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);
|
||||
|
||||
mWaveform = props->Chorus.Waveform;
|
||||
|
@ -247,7 +253,7 @@ void ChorusState::process(const size_t samplesToDo, const al::span<const FloatBu
|
|||
++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,
|
||||
samplesToDo-base, base);
|
||||
|
||||
|
|
|
@ -1,32 +1,56 @@
|
|||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* This file is part of the OpenAL Soft cross platform audio library
|
||||
*
|
||||
* Copyright (C) 2013 by Anis A. Hireche
|
||||
* 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,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* 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 <array>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alu.h"
|
||||
#include "effectslot.h"
|
||||
#include "vecmat.h"
|
||||
#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/devformat.h"
|
||||
#include "core/device.h"
|
||||
#include "core/mixer.h"
|
||||
#include "core/mixer/defs.h"
|
||||
#include "intrusive_ptr.h"
|
||||
|
||||
struct ContextBase;
|
||||
|
||||
|
||||
namespace {
|
||||
|
@ -49,8 +73,8 @@ struct CompressorState final : public EffectState {
|
|||
float mEnvFollower{1.0f};
|
||||
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
const al::span<FloatBufferLine> samplesOut) override;
|
||||
|
@ -58,7 +82,7 @@ struct CompressorState final : public EffectState {
|
|||
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
|
||||
* 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);
|
||||
}
|
||||
|
||||
void CompressorState::update(const ALCcontext*, const EffectSlot *slot,
|
||||
void CompressorState::update(const ContextBase*, const EffectSlot *slot,
|
||||
const EffectProps *props, const EffectTarget target)
|
||||
{
|
||||
mEnabled = props->Compressor.OnOff;
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <complex>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <utility>
|
||||
|
||||
#ifdef HAVE_SSE_INTRINSICS
|
||||
#include <xmmintrin.h>
|
||||
|
@ -9,21 +17,26 @@
|
|||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "albyte.h"
|
||||
#include "alcomplex.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/effectslot.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alspan.h"
|
||||
#include "bformatdec.h"
|
||||
#include "buffer_storage.h"
|
||||
#include "base.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/fmt_traits.h"
|
||||
#include "core/logging.h"
|
||||
#include "effects/base.h"
|
||||
#include "effectslot.h"
|
||||
#include "core/mixer.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "math_defs.h"
|
||||
#include "polyphase_resampler.h"
|
||||
#include "vector.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
@ -190,8 +203,8 @@ struct ConvolutionState final : public EffectState {
|
|||
void (ConvolutionState::*mMix)(const al::span<FloatBufferLine>,const size_t)
|
||||
{&ConvolutionState::NormalMix};
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
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};
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
/* 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)
|
||||
std::fill(std::begin(chan.Target), std::end(chan.Target), 0.0f);
|
||||
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)
|
||||
{
|
||||
mMix = &ConvolutionState::UpsampleMix;
|
||||
const auto scales = BFormatDec::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder);
|
||||
const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder);
|
||||
(*mChans)[0].mHfScale = scales[0];
|
||||
for(size_t i{1};i < mChans->size();++i)
|
||||
(*mChans)[i].mHfScale = scales[1];
|
||||
|
@ -403,7 +434,7 @@ void ConvolutionState::update(const ALCcontext *context, const EffectSlot *slot,
|
|||
}
|
||||
else
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
DeviceBase *device{context->mDevice};
|
||||
al::span<const ChanMap> chanmap{};
|
||||
switch(mChannels)
|
||||
{
|
||||
|
@ -416,6 +447,9 @@ void ConvolutionState::update(const ALCcontext *context, const EffectSlot *slot,
|
|||
case FmtX71: chanmap = X71Map; break;
|
||||
case FmtBFormat2D:
|
||||
case FmtBFormat3D:
|
||||
case FmtUHJ2:
|
||||
case FmtUHJ3:
|
||||
case FmtUHJ4:
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,25 +20,35 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alu.h"
|
||||
#include "effectslot.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/effectslot.h"
|
||||
#include "almalloc.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 {
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
struct DedicatedState final : public EffectState {
|
||||
float mCurrentGains[MAX_OUTPUT_CHANNELS];
|
||||
float mTargetGains[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
const al::span<FloatBufferLine> samplesOut) override;
|
||||
|
@ -46,12 +56,12 @@ struct DedicatedState final : public EffectState {
|
|||
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);
|
||||
}
|
||||
|
||||
void DedicatedState::update(const ALCcontext*, const EffectSlot *slot,
|
||||
void DedicatedState::update(const ContextBase*, const EffectSlot *slot,
|
||||
const EffectProps *props, const EffectTarget target)
|
||||
{
|
||||
std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f);
|
||||
|
|
|
@ -21,13 +21,24 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/effects/base.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 "effectslot.h"
|
||||
#include "core/mixer.h"
|
||||
#include "core/mixer/defs.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "math_defs.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
@ -45,8 +56,8 @@ struct DistortionState final : public EffectState {
|
|||
float mBuffer[2][BufferLineSize]{};
|
||||
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
const al::span<FloatBufferLine> samplesOut) override;
|
||||
|
@ -54,16 +65,16 @@ struct DistortionState final : public EffectState {
|
|||
DEF_NEWDEL(DistortionState)
|
||||
};
|
||||
|
||||
void DistortionState::deviceUpdate(const ALCdevice*, const Buffer&)
|
||||
void DistortionState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||
{
|
||||
mLowpass.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 ALCdevice *device{context->mDevice.get()};
|
||||
const DeviceBase *device{context->mDevice};
|
||||
|
||||
/* Store waveshaper edge settings. */
|
||||
const float edge{minf(std::sin(al::MathDefs<float>::Pi()*0.5f * props->Distortion.Edge),
|
||||
|
|
|
@ -20,20 +20,32 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/effects/base.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 "effectslot.h"
|
||||
#include "core/mixer.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "opthelpers.h"
|
||||
#include "vector.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
constexpr float LowpassFreqRef{5000.0f};
|
||||
|
||||
struct EchoState final : public EffectState {
|
||||
|
@ -57,8 +69,8 @@ struct EchoState final : public EffectState {
|
|||
|
||||
alignas(16) float mTempBuffer[2][BufferLineSize];
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
const al::span<FloatBufferLine> samplesOut) override;
|
||||
|
@ -66,7 +78,7 @@ struct EchoState final : public EffectState {
|
|||
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);
|
||||
|
||||
|
@ -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 ALCdevice *device{context->mDevice.get()};
|
||||
const DeviceBase *device{context->mDevice};
|
||||
const auto frequency = static_cast<float>(device->Frequency);
|
||||
|
||||
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);
|
||||
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,
|
||||
samplesToDo, 0);
|
||||
}
|
||||
|
|
|
@ -20,17 +20,25 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/effects/base.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 "effectslot.h"
|
||||
#include "vecmat.h"
|
||||
#include "core/mixer.h"
|
||||
#include "intrusive_ptr.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
@ -90,8 +98,8 @@ struct EqualizerState final : public EffectState {
|
|||
FloatBufferLine mSampleBuffer{};
|
||||
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
const al::span<FloatBufferLine> samplesOut) override;
|
||||
|
@ -99,7 +107,7 @@ struct EqualizerState final : public EffectState {
|
|||
DEF_NEWDEL(EqualizerState)
|
||||
};
|
||||
|
||||
void EqualizerState::deviceUpdate(const ALCdevice*, const Buffer&)
|
||||
void EqualizerState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||
{
|
||||
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 ALCdevice *device{context->mDevice.get()};
|
||||
const DeviceBase *device{context->mDevice};
|
||||
auto frequency = static_cast<float>(device->Frequency);
|
||||
float gain, f0norm;
|
||||
|
||||
|
|
|
@ -20,22 +20,32 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <array>
|
||||
#include <complex>
|
||||
#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 "alcontext.h"
|
||||
#include "alu.h"
|
||||
#include "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/mixer.h"
|
||||
#include "core/mixer/defs.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "math_defs.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using uint = unsigned int;
|
||||
using complex_d = std::complex<double>;
|
||||
|
||||
#define HIL_SIZE 1024
|
||||
|
@ -84,8 +94,8 @@ struct FshifterState final : public EffectState {
|
|||
} mGains[2];
|
||||
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
const al::span<FloatBufferLine> samplesOut) override;
|
||||
|
@ -93,7 +103,7 @@ struct FshifterState final : public EffectState {
|
|||
DEF_NEWDEL(FshifterState)
|
||||
};
|
||||
|
||||
void FshifterState::deviceUpdate(const ALCdevice*, const Buffer&)
|
||||
void FshifterState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||
{
|
||||
/* (Re-)initializing parameters and clear the buffers. */
|
||||
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 ALCdevice *device{context->mDevice.get()};
|
||||
const DeviceBase *device{context->mDevice};
|
||||
|
||||
const float step{props->Fshifter.Frequency / static_cast<float>(device->Frequency)};
|
||||
mPhaseStep[0] = mPhaseStep[1] = fastf2u(minf(step, 1.0f) * MixerFracOne);
|
||||
|
|
|
@ -20,21 +20,31 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#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/filters/biquad.h"
|
||||
#include "effectslot.h"
|
||||
#include "vecmat.h"
|
||||
#include "core/mixer.h"
|
||||
#include "intrusive_ptr.h"
|
||||
#include "math_defs.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
#define MAX_UPDATE_SAMPLES 128
|
||||
|
||||
#define WAVEFORM_FRACBITS 24
|
||||
|
@ -81,8 +91,8 @@ struct ModulatorState final : public EffectState {
|
|||
} mChans[MaxAmbiChannels];
|
||||
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
const al::span<FloatBufferLine> samplesOut) override;
|
||||
|
@ -90,7 +100,7 @@ struct ModulatorState final : public EffectState {
|
|||
DEF_NEWDEL(ModulatorState)
|
||||
};
|
||||
|
||||
void ModulatorState::deviceUpdate(const ALCdevice*, const Buffer&)
|
||||
void ModulatorState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||
{
|
||||
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 ALCdevice *device{context->mDevice.get()};
|
||||
const DeviceBase *device{context->mDevice};
|
||||
|
||||
const float step{props->Modulator.Frequency / static_cast<float>(device->Frequency)};
|
||||
mStep = fastf2u(clampf(step*WAVEFORM_FRACONE, 0.0f, float{WAVEFORM_FRACONE-1}));
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#include "almalloc.h"
|
||||
#include "alspan.h"
|
||||
#include "effects/base.h"
|
||||
#include "effectslot.h"
|
||||
#include "base.h"
|
||||
#include "core/bufferline.h"
|
||||
#include "intrusive_ptr.h"
|
||||
|
||||
struct ContextBase;
|
||||
struct DeviceBase;
|
||||
struct EffectSlot;
|
||||
|
||||
|
||||
namespace {
|
||||
|
@ -15,8 +20,8 @@ struct NullState final : public EffectState {
|
|||
NullState();
|
||||
~NullState() override;
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
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
|
||||
* 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
|
||||
* 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*/)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -20,23 +20,33 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <array>
|
||||
#include <complex>
|
||||
#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 "alcontext.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "alu.h"
|
||||
#include "effectslot.h"
|
||||
#include "alspan.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"
|
||||
|
||||
struct ContextBase;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using uint = unsigned int;
|
||||
using complex_d = std::complex<double>;
|
||||
|
||||
#define STFT_SIZE 1024
|
||||
|
@ -93,8 +103,8 @@ struct PshifterState final : public EffectState {
|
|||
float mTargetGains[MAX_OUTPUT_CHANNELS];
|
||||
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
const al::span<FloatBufferLine> samplesOut) override;
|
||||
|
@ -102,7 +112,7 @@ struct PshifterState final : public EffectState {
|
|||
DEF_NEWDEL(PshifterState)
|
||||
};
|
||||
|
||||
void PshifterState::deviceUpdate(const ALCdevice*, const Buffer&)
|
||||
void PshifterState::deviceUpdate(const DeviceBase*, const Buffer&)
|
||||
{
|
||||
/* (Re-)initializing parameters and clear the buffers. */
|
||||
mCount = 0;
|
||||
|
@ -122,7 +132,7 @@ void PshifterState::deviceUpdate(const ALCdevice*, const Buffer&)
|
|||
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 int tune{props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune};
|
||||
|
|
|
@ -20,23 +20,33 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
|
||||
#include <array>
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/effects/base.h"
|
||||
#include "alc/effectslot.h"
|
||||
#include "almalloc.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 "effectslot.h"
|
||||
#include "vector.h"
|
||||
#include "core/filters/splitter.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 "vector.h"
|
||||
|
||||
/* This is a user config option for modifying the overall output of the reverb
|
||||
* effect.
|
||||
|
@ -45,6 +55,11 @@ float ReverbBoost = 1.0f;
|
|||
|
||||
namespace {
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
constexpr float MaxModulationTime{4.0f};
|
||||
constexpr float DefaultModulationTime{0.25f};
|
||||
|
||||
#define MOD_FRACBITS 24
|
||||
#define MOD_FRACONE (1<<MOD_FRACBITS)
|
||||
#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
|
||||
* an update.
|
||||
*/
|
||||
float Density{AL_EAXREVERB_DEFAULT_DENSITY};
|
||||
float Diffusion{AL_EAXREVERB_DEFAULT_DIFFUSION};
|
||||
float DecayTime{AL_EAXREVERB_DEFAULT_DECAY_TIME};
|
||||
float HFDecayTime{AL_EAXREVERB_DEFAULT_DECAY_HFRATIO * AL_EAXREVERB_DEFAULT_DECAY_TIME};
|
||||
float LFDecayTime{AL_EAXREVERB_DEFAULT_DECAY_LFRATIO * AL_EAXREVERB_DEFAULT_DECAY_TIME};
|
||||
float ModulationTime{AL_EAXREVERB_DEFAULT_MODULATION_TIME};
|
||||
float ModulationDepth{AL_EAXREVERB_DEFAULT_MODULATION_DEPTH};
|
||||
float HFReference{AL_EAXREVERB_DEFAULT_HFREFERENCE};
|
||||
float LFReference{AL_EAXREVERB_DEFAULT_LFREFERENCE};
|
||||
float Density{1.0f};
|
||||
float Diffusion{1.0f};
|
||||
float DecayTime{1.49f};
|
||||
float HFDecayTime{0.83f * 1.49f};
|
||||
float LFDecayTime{1.0f * 1.49f};
|
||||
float ModulationTime{0.25f};
|
||||
float ModulationDepth{0.0f};
|
||||
float HFReference{5000.0f};
|
||||
float LFReference{250.0f};
|
||||
} mParams;
|
||||
|
||||
/* 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,
|
||||
const float fadeStep);
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
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
|
||||
* 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
|
||||
* largest early tap width, the maximum late reverb delay, and the
|
||||
* largest late tap width. Finally, it must also be extended by the
|
||||
* update size (BufferLineSize) for block processing.
|
||||
*/
|
||||
float length{AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS.back()*multiplier +
|
||||
AL_EAXREVERB_MAX_LATE_REVERB_DELAY +
|
||||
(LATE_LINE_LENGTHS.back() - LATE_LINE_LENGTHS.front())/float{NUM_LINES}*multiplier};
|
||||
constexpr float LateLineDiffAvg{(LATE_LINE_LENGTHS.back()-LATE_LINE_LENGTHS.front()) /
|
||||
float{NUM_LINES}};
|
||||
float length{ReverbMaxReflectionsDelay + EARLY_TAP_LENGTHS.back()*multiplier +
|
||||
ReverbMaxLateReverbDelay + LateLineDiffAvg*multiplier};
|
||||
totalSamples += mDelay.calcLineLength(length, totalSamples, frequency, BufferLineSize);
|
||||
|
||||
/* 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
|
||||
* 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
|
||||
* 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());
|
||||
}
|
||||
|
||||
void ReverbState::deviceUpdate(const ALCdevice *device, const Buffer&)
|
||||
void ReverbState::deviceUpdate(const DeviceBase *device, const Buffer&)
|
||||
{
|
||||
const auto frequency = static_cast<float>(device->Frequency);
|
||||
|
||||
/* Allocate the delay lines. */
|
||||
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. */
|
||||
mLateFeedTap = float2uint(
|
||||
(AL_EAXREVERB_MAX_REFLECTIONS_DELAY + EARLY_TAP_LENGTHS.back()*multiplier) * frequency);
|
||||
mLateFeedTap = float2uint((ReverbMaxReflectionsDelay + EARLY_TAP_LENGTHS.back()*multiplier) *
|
||||
frequency);
|
||||
|
||||
/* Clear filters and gain coefficients since the delay lines were all just
|
||||
* cleared (if not reallocated).
|
||||
|
@ -665,7 +681,7 @@ void ReverbState::deviceUpdate(const ALCdevice *device, const Buffer&)
|
|||
if(device->mAmbiOrder > 1)
|
||||
{
|
||||
mMixOut = &ReverbState::MixOutAmbiUp;
|
||||
mOrderScales = BFormatDec::GetHFOrderScales(1, device->mAmbiOrder);
|
||||
mOrderScales = AmbiScale::GetHFOrderScales(1, device->mAmbiOrder);
|
||||
}
|
||||
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
|
||||
* it).
|
||||
*/
|
||||
if(modTime >= AL_EAXREVERB_DEFAULT_MODULATION_TIME)
|
||||
if(modTime >= DefaultModulationTime)
|
||||
{
|
||||
/* To cancel the effects of a long period modulation on the late
|
||||
* reverberation, the amount of pitch should be varied (decreased)
|
||||
* according to the modulation time. The natural form is varying
|
||||
* inversely, in fact resulting in an invariant.
|
||||
*/
|
||||
Depth[1] = MODULATION_DEPTH_COEFF / 4.0f * AL_EAXREVERB_DEFAULT_MODULATION_TIME *
|
||||
modDepth * frequency;
|
||||
Depth[1] = MODULATION_DEPTH_COEFF / 4.0f * DefaultModulationTime * modDepth * frequency;
|
||||
}
|
||||
else
|
||||
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
|
||||
* 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{
|
||||
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 ALCdevice *Device{Context->mDevice.get()};
|
||||
const DeviceBase *Device{Context->mDevice};
|
||||
const auto frequency = static_cast<float>(Device->Frequency);
|
||||
|
||||
/* Calculate the master filters */
|
||||
|
@ -1024,10 +1040,10 @@ void ReverbState::update(const ALCcontext *Context, const EffectSlot *Slot,
|
|||
props->Reverb.DecayTime);
|
||||
|
||||
/* Calculate the LF/HF decay times. */
|
||||
constexpr float MinDecayTime{0.1f}, MaxDecayTime{20.0f};
|
||||
const float lfDecayTime{clampf(props->Reverb.DecayTime*props->Reverb.DecayLFRatio,
|
||||
AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME)};
|
||||
const float hfDecayTime{clampf(props->Reverb.DecayTime * hfRatio,
|
||||
AL_EAXREVERB_MIN_DECAY_TIME, AL_EAXREVERB_MAX_DECAY_TIME)};
|
||||
MinDecayTime, MaxDecayTime)};
|
||||
const float hfDecayTime{clampf(props->Reverb.DecayTime*hfRatio, MinDecayTime, MaxDecayTime)};
|
||||
|
||||
/* Update the modulator rate and depth. */
|
||||
mLate.Mod.updateModulator(props->Reverb.ModulationTime, props->Reverb.ModulationDepth,
|
||||
|
|
|
@ -1,39 +1,62 @@
|
|||
/**
|
||||
* OpenAL cross platform audio library
|
||||
* This file is part of the OpenAL Soft cross platform audio library
|
||||
*
|
||||
* Copyright (C) 2019 by Anis A. Hireche
|
||||
* 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,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* 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 <cmath>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
|
||||
#include "alcmain.h"
|
||||
#include "alcontext.h"
|
||||
#include "alu.h"
|
||||
#include "effectslot.h"
|
||||
#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"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using uint = unsigned int;
|
||||
|
||||
#define MAX_UPDATE_SAMPLES 256
|
||||
#define NUM_FORMANTS 4
|
||||
#define NUM_FILTERS 2
|
||||
|
@ -138,8 +161,8 @@ struct VmorpherState final : public EffectState {
|
|||
alignas(16) float mSampleBufferB[MAX_UPDATE_SAMPLES]{};
|
||||
alignas(16) float mLfo[MAX_UPDATE_SAMPLES]{};
|
||||
|
||||
void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
|
||||
void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
|
||||
void deviceUpdate(const DeviceBase *device, const Buffer &buffer) override;
|
||||
void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props,
|
||||
const EffectTarget target) override;
|
||||
void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
|
||||
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)
|
||||
{
|
||||
|
@ -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 ALCdevice *device{context->mDevice.get()};
|
||||
const DeviceBase *device{context->mDevice};
|
||||
const float frequency{static_cast<float>(device->Frequency)};
|
||||
const float step{props->Vmorpher.Rate / frequency};
|
||||
mStep = fastf2u(clampf(step*WAVEFORM_FRACONE, 0.0f, float{WAVEFORM_FRACONE-1}));
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "alcontext.h"
|
||||
#include "almalloc.h"
|
||||
#include "context.h"
|
||||
|
||||
|
||||
EffectSlotArray *EffectSlot::CreatePtrArray(size_t count) noexcept
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
#ifndef EFFECTSLOT_H
|
||||
#define EFFECTSLOT_H
|
||||
|
||||
#include <atomic.h>
|
||||
#include <atomic>
|
||||
|
||||
#include "almalloc.h"
|
||||
#include "alcmain.h"
|
||||
#include "core/device.h"
|
||||
#include "effects/base.h"
|
||||
#include "intrusive_ptr.h"
|
||||
|
||||
|
||||
struct EffectSlot;
|
||||
struct WetBuffer;
|
||||
|
||||
|
|
|
@ -66,6 +66,34 @@ AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *
|
|||
#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
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
|
|
@ -38,24 +38,24 @@
|
|||
#include "AL/alext.h"
|
||||
|
||||
#include "al/auxeffectslot.h"
|
||||
#include "alcmain.h"
|
||||
#include "alconfig.h"
|
||||
#include "alcontext.h"
|
||||
#include "alc/context.h"
|
||||
#include "almalloc.h"
|
||||
#include "alnumeric.h"
|
||||
#include "aloptional.h"
|
||||
#include "alspan.h"
|
||||
#include "alstring.h"
|
||||
#include "alu.h"
|
||||
#include "bformatdec.h"
|
||||
#include "core/ambdec.h"
|
||||
#include "core/ambidefs.h"
|
||||
#include "core/bformatdec.h"
|
||||
#include "core/bs2b.h"
|
||||
#include "core/devformat.h"
|
||||
#include "core/front_stablizer.h"
|
||||
#include "core/hrtf.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/uhjfilter.h"
|
||||
#include "front_stablizer.h"
|
||||
#include "hrtf.h"
|
||||
#include "device.h"
|
||||
#include "math_defs.h"
|
||||
#include "opthelpers.h"
|
||||
|
||||
|
@ -486,11 +486,6 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
|
|||
decoder = X71Config;
|
||||
break;
|
||||
case DevFmtAmbi3D:
|
||||
break;
|
||||
}
|
||||
|
||||
if(device->FmtChans == DevFmtAmbi3D)
|
||||
{
|
||||
const char *devname{device->DeviceName.c_str()};
|
||||
auto&& acnmap = GetAmbiLayout(device->mAmbiLayout);
|
||||
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)};
|
||||
std::transform(acnmap.begin(), acnmap.begin()+count, std::begin(device->Dry.AmbiMap),
|
||||
[&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);
|
||||
|
||||
float nfc_delay{ConfigValueFloat(devname, "decoder", "nfc-ref-delay").value_or(0.0f)};
|
||||
if(nfc_delay > 0.0f)
|
||||
InitNearFieldCtrl(device, nfc_delay * SpeedOfSoundMetersPerSec, device->mAmbiOrder,
|
||||
true);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
const bool dual_band{hqdec && !decoder.mCoeffsLF.empty()};
|
||||
al::vector<ChannelDec> chancoeffs, chancoeffslf;
|
||||
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{});
|
||||
al::span<float,MaxAmbiChannels> coeffs{chancoeffs[idx]};
|
||||
size_t start{0};
|
||||
for(uint o{0};o <= decoder.mOrder;++o)
|
||||
size_t ambichan{0};
|
||||
for(uint o{0};o < decoder.mOrder+1;++o)
|
||||
{
|
||||
size_t count{o ? 2u : 1u};
|
||||
do {
|
||||
coeffs[start] = decoder.mCoeffs[i][start] * decoder.mOrderGain[o];
|
||||
++start;
|
||||
} while(--count);
|
||||
const float order_gain{decoder.mOrderGain[o]};
|
||||
const size_t order_max{Ambi2DChannelsFromOrder(o)};
|
||||
for(;ambichan < order_max;++ambichan)
|
||||
coeffs[ambichan] = decoder.mCoeffs[i][ambichan] * order_gain;
|
||||
}
|
||||
if(!dual_band)
|
||||
continue;
|
||||
|
||||
chancoeffslf.resize(maxz(chancoeffslf.size(), idx+1u), ChannelDec{});
|
||||
coeffs = chancoeffslf[idx];
|
||||
start = 0;
|
||||
for(uint o{0};o <= decoder.mOrder;++o)
|
||||
ambichan = 0;
|
||||
for(uint o{0};o < decoder.mOrder+1;++o)
|
||||
{
|
||||
size_t count{o ? 2u : 1u};
|
||||
do {
|
||||
coeffs[start] = decoder.mCoeffsLF[i][start] * decoder.mOrderGainLF[o];
|
||||
++start;
|
||||
} while(--count);
|
||||
const float order_gain{decoder.mOrderGainLF[o]};
|
||||
const size_t order_max{Ambi2DChannelsFromOrder(o)};
|
||||
for(;ambichan < order_max;++ambichan)
|
||||
coeffs[ambichan] = decoder.mCoeffsLF[i][ambichan] * order_gain;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -556,8 +548,7 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
|
|||
const size_t ambicount{Ambi2DChannelsFromOrder(decoder.mOrder)};
|
||||
std::transform(AmbiIndex::FromACN2D().begin(), AmbiIndex::FromACN2D().begin()+ambicount,
|
||||
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());
|
||||
|
||||
std::unique_ptr<FrontStablizer> stablizer;
|
||||
|
@ -593,7 +584,6 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=
|
|||
device->AmbiDecoder = BFormatDec::Create(ambicount, chancoeffs, chancoeffslf,
|
||||
std::move(stablizer));
|
||||
}
|
||||
}
|
||||
|
||||
void InitCustomPanning(ALCdevice *device, const bool hqdec, const bool stablize,
|
||||
const AmbDecConf *conf, const uint (&speakermap)[MAX_OUTPUT_CHANNELS])
|
||||
|
@ -750,7 +740,9 @@ void InitHrtfPanning(ALCdevice *device)
|
|||
static const float AmbiOrderHFGain1O[MaxAmbiOrder+1]{
|
||||
2.000000000e+00f, 1.154700538e+00f
|
||||
}, 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");
|
||||
|
@ -798,7 +790,7 @@ void InitHrtfPanning(ALCdevice *device)
|
|||
((ambi_order%10) == 2) ? "nd" :
|
||||
((ambi_order%10) == 3) ? "rd" : "th",
|
||||
(device->mRenderMode == RenderMode::Hrtf) ? "+ Full " : "",
|
||||
device->HrtfName.c_str());
|
||||
device->mHrtfName.c_str());
|
||||
|
||||
al::span<const AngularPoint> AmbiPoints{AmbiPoints1O};
|
||||
const float (*AmbiMatrix)[MaxAmbiChannels]{AmbiMatrix1O};
|
||||
|
@ -854,7 +846,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, HrtfRequestMode hrtf_appreq
|
|||
device->mHrtfState = nullptr;
|
||||
device->mHrtf = nullptr;
|
||||
device->mIrSize = 0;
|
||||
device->HrtfName.clear();
|
||||
device->mHrtfName.clear();
|
||||
device->mXOverFreq = 400.0f;
|
||||
device->mRenderMode = RenderMode::Normal;
|
||||
|
||||
|
@ -862,7 +854,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, HrtfRequestMode hrtf_appreq
|
|||
{
|
||||
old_hrtf = nullptr;
|
||||
if(hrtf_appreq == Hrtf_Enable)
|
||||
device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
|
||||
device->mHrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
|
||||
|
||||
const char *layout{nullptr};
|
||||
switch(device->FmtChans)
|
||||
|
@ -943,42 +935,42 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, HrtfRequestMode hrtf_appreq
|
|||
(hrtf_appreq == Hrtf_Enable);
|
||||
if(!usehrtf) goto no_hrtf;
|
||||
|
||||
device->HrtfStatus = ALC_HRTF_ENABLED_SOFT;
|
||||
device->mHrtfStatus = ALC_HRTF_ENABLED_SOFT;
|
||||
if(headphones && hrtf_appreq != Hrtf_Disable)
|
||||
device->HrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
|
||||
device->mHrtfStatus = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(hrtf_userreq != Hrtf_Enable)
|
||||
{
|
||||
if(hrtf_appreq == Hrtf_Enable)
|
||||
device->HrtfStatus = ALC_HRTF_DENIED_SOFT;
|
||||
device->mHrtfStatus = ALC_HRTF_DENIED_SOFT;
|
||||
goto no_hrtf;
|
||||
}
|
||||
device->HrtfStatus = ALC_HRTF_REQUIRED_SOFT;
|
||||
device->mHrtfStatus = ALC_HRTF_REQUIRED_SOFT;
|
||||
}
|
||||
|
||||
if(device->HrtfList.empty())
|
||||
device->HrtfList = EnumerateHrtf(device->DeviceName.c_str());
|
||||
if(device->mHrtfList.empty())
|
||||
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)})
|
||||
{
|
||||
device->mHrtf = std::move(hrtf);
|
||||
device->HrtfName = hrtfname;
|
||||
device->mHrtfName = hrtfname;
|
||||
}
|
||||
}
|
||||
|
||||
if(!device->mHrtf)
|
||||
{
|
||||
for(const auto &hrtfname : device->HrtfList)
|
||||
for(const auto &hrtfname : device->mHrtfList)
|
||||
{
|
||||
if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->Frequency)})
|
||||
{
|
||||
device->mHrtf = std::move(hrtf);
|
||||
device->HrtfName = hrtfname;
|
||||
device->mHrtfName = hrtfname;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1000,7 +992,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, HrtfRequestMode hrtf_appreq
|
|||
device->PostProcess = &ALCdevice::ProcessHrtf;
|
||||
return;
|
||||
}
|
||||
device->HrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
|
||||
device->mHrtfStatus = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;
|
||||
|
||||
no_hrtf:
|
||||
old_hrtf = nullptr;
|
||||
|
@ -1034,7 +1026,7 @@ no_hrtf:
|
|||
}
|
||||
if(device->mRenderMode == RenderMode::Normal)
|
||||
{
|
||||
device->Uhj_Encoder = std::make_unique<Uhj2Encoder>();
|
||||
device->mUhjEncoder = std::make_unique<UhjEncoder>();
|
||||
TRACE("UHJ enabled\n");
|
||||
InitUhjPanning(device);
|
||||
device->PostProcess = &ALCdevice::ProcessUhj;
|
||||
|
@ -1049,7 +1041,7 @@ no_hrtf:
|
|||
|
||||
void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context)
|
||||
{
|
||||
ALCdevice *device{context->mDevice.get()};
|
||||
DeviceBase *device{context->mDevice};
|
||||
const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)};
|
||||
|
||||
auto wetbuffer_iter = context->mWetBuffers.end();
|
||||
|
@ -1098,113 +1090,3 @@ void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context)
|
|||
std::fill(iter, slot->Wet.AmbiMap.end(), BFChannelConfig{});
|
||||
slot->Wet.Buffer = wetbuffer->mBuffer;
|
||||
}
|
||||
|
||||
|
||||
std::array<float,MaxAmbiChannels> CalcAmbiCoeffs(const float y, const float z, const float x,
|
||||
const float spread)
|
||||
{
|
||||
std::array<float,MaxAmbiChannels> coeffs;
|
||||
|
||||
/* Zeroth-order */
|
||||
coeffs[0] = 1.0f; /* ACN 0 = 1 */
|
||||
/* First-order */
|
||||
coeffs[1] = 1.732050808f * y; /* ACN 1 = sqrt(3) * Y */
|
||||
coeffs[2] = 1.732050808f * z; /* ACN 2 = sqrt(3) * Z */
|
||||
coeffs[3] = 1.732050808f * x; /* ACN 3 = sqrt(3) * X */
|
||||
/* Second-order */
|
||||
const float xx{x*x}, yy{y*y}, zz{z*z}, xy{x*y}, yz{y*z}, xz{x*z};
|
||||
coeffs[4] = 3.872983346f * xy; /* ACN 4 = sqrt(15) * X * Y */
|
||||
coeffs[5] = 3.872983346f * yz; /* ACN 5 = sqrt(15) * Y * Z */
|
||||
coeffs[6] = 1.118033989f * (3.0f*zz - 1.0f); /* ACN 6 = sqrt(5)/2 * (3*Z*Z - 1) */
|
||||
coeffs[7] = 3.872983346f * xz; /* ACN 7 = sqrt(15) * X * Z */
|
||||
coeffs[8] = 1.936491673f * (xx - yy); /* ACN 8 = sqrt(15)/2 * (X*X - Y*Y) */
|
||||
/* Third-order */
|
||||
coeffs[9] = 2.091650066f * (y*(3.0f*xx - yy)); /* ACN 9 = sqrt(35/8) * Y * (3*X*X - Y*Y) */
|
||||
coeffs[10] = 10.246950766f * (z*xy); /* ACN 10 = sqrt(105) * Z * X * Y */
|
||||
coeffs[11] = 1.620185175f * (y*(5.0f*zz - 1.0f)); /* ACN 11 = sqrt(21/8) * Y * (5*Z*Z - 1) */
|
||||
coeffs[12] = 1.322875656f * (z*(5.0f*zz - 3.0f)); /* ACN 12 = sqrt(7)/2 * Z * (5*Z*Z - 3) */
|
||||
coeffs[13] = 1.620185175f * (x*(5.0f*zz - 1.0f)); /* ACN 13 = sqrt(21/8) * X * (5*Z*Z - 1) */
|
||||
coeffs[14] = 5.123475383f * (z*(xx - yy)); /* ACN 14 = sqrt(105)/2 * Z * (X*X - Y*Y) */
|
||||
coeffs[15] = 2.091650066f * (x*(xx - 3.0f*yy)); /* ACN 15 = sqrt(35/8) * X * (X*X - 3*Y*Y) */
|
||||
/* Fourth-order */
|
||||
/* ACN 16 = sqrt(35)*3/2 * X * Y * (X*X - Y*Y) */
|
||||
/* ACN 17 = sqrt(35/2)*3/2 * (3*X*X - Y*Y) * Y * Z */
|
||||
/* ACN 18 = sqrt(5)*3/2 * X * Y * (7*Z*Z - 1) */
|
||||
/* ACN 19 = sqrt(5/2)*3/2 * Y * Z * (7*Z*Z - 3) */
|
||||
/* ACN 20 = 3/8 * (35*Z*Z*Z*Z - 30*Z*Z + 3) */
|
||||
/* ACN 21 = sqrt(5/2)*3/2 * X * Z * (7*Z*Z - 3) */
|
||||
/* ACN 22 = sqrt(5)*3/4 * (X*X - Y*Y) * (7*Z*Z - 1) */
|
||||
/* ACN 23 = sqrt(35/2)*3/2 * (X*X - 3*Y*Y) * X * Z */
|
||||
/* ACN 24 = sqrt(35)*3/8 * (X*X*X*X - 6*X*X*Y*Y + Y*Y*Y*Y) */
|
||||
|
||||
if(spread > 0.0f)
|
||||
{
|
||||
/* Implement the spread by using a spherical source that subtends the
|
||||
* angle spread. See:
|
||||
* http://www.ppsloan.org/publications/StupidSH36.pdf - Appendix A3
|
||||
*
|
||||
* When adjusted for N3D normalization instead of SN3D, these
|
||||
* calculations are:
|
||||
*
|
||||
* ZH0 = -sqrt(pi) * (-1+ca);
|
||||
* ZH1 = 0.5*sqrt(pi) * sa*sa;
|
||||
* ZH2 = -0.5*sqrt(pi) * ca*(-1+ca)*(ca+1);
|
||||
* ZH3 = -0.125*sqrt(pi) * (-1+ca)*(ca+1)*(5*ca*ca - 1);
|
||||
* ZH4 = -0.125*sqrt(pi) * ca*(-1+ca)*(ca+1)*(7*ca*ca - 3);
|
||||
* ZH5 = -0.0625*sqrt(pi) * (-1+ca)*(ca+1)*(21*ca*ca*ca*ca - 14*ca*ca + 1);
|
||||
*
|
||||
* The gain of the source is compensated for size, so that the
|
||||
* loudness doesn't depend on the spread. Thus:
|
||||
*
|
||||
* ZH0 = 1.0f;
|
||||
* ZH1 = 0.5f * (ca+1.0f);
|
||||
* ZH2 = 0.5f * (ca+1.0f)*ca;
|
||||
* ZH3 = 0.125f * (ca+1.0f)*(5.0f*ca*ca - 1.0f);
|
||||
* ZH4 = 0.125f * (ca+1.0f)*(7.0f*ca*ca - 3.0f)*ca;
|
||||
* ZH5 = 0.0625f * (ca+1.0f)*(21.0f*ca*ca*ca*ca - 14.0f*ca*ca + 1.0f);
|
||||
*/
|
||||
const float ca{std::cos(spread * 0.5f)};
|
||||
/* Increase the source volume by up to +3dB for a full spread. */
|
||||
const float scale{std::sqrt(1.0f + spread/al::MathDefs<float>::Tau())};
|
||||
|
||||
const float ZH0_norm{scale};
|
||||
const float ZH1_norm{scale * 0.5f * (ca+1.f)};
|
||||
const float ZH2_norm{scale * 0.5f * (ca+1.f)*ca};
|
||||
const float ZH3_norm{scale * 0.125f * (ca+1.f)*(5.f*ca*ca-1.f)};
|
||||
|
||||
/* Zeroth-order */
|
||||
coeffs[0] *= ZH0_norm;
|
||||
/* First-order */
|
||||
coeffs[1] *= ZH1_norm;
|
||||
coeffs[2] *= ZH1_norm;
|
||||
coeffs[3] *= ZH1_norm;
|
||||
/* Second-order */
|
||||
coeffs[4] *= ZH2_norm;
|
||||
coeffs[5] *= ZH2_norm;
|
||||
coeffs[6] *= ZH2_norm;
|
||||
coeffs[7] *= ZH2_norm;
|
||||
coeffs[8] *= ZH2_norm;
|
||||
/* Third-order */
|
||||
coeffs[9] *= ZH3_norm;
|
||||
coeffs[10] *= ZH3_norm;
|
||||
coeffs[11] *= ZH3_norm;
|
||||
coeffs[12] *= ZH3_norm;
|
||||
coeffs[13] *= ZH3_norm;
|
||||
coeffs[14] *= ZH3_norm;
|
||||
coeffs[15] *= ZH3_norm;
|
||||
}
|
||||
|
||||
return coeffs;
|
||||
}
|
||||
|
||||
void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain,
|
||||
const al::span<float,MAX_OUTPUT_CHANNELS> gains)
|
||||
{
|
||||
auto ambimap = mix->AmbiMap.cbegin();
|
||||
|
||||
auto iter = std::transform(ambimap, ambimap+mix->Buffer.size(), gains.begin(),
|
||||
[coeffs,ingain](const BFChannelConfig &chanmap) noexcept -> float
|
||||
{ return chanmap.Scale * coeffs[chanmap.Index] * ingain; }
|
||||
);
|
||||
std::fill(iter, gains.end(), 0.0f);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef AL_BIT_H
|
||||
#define AL_BIT_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#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.
|
||||
*/
|
||||
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>
|
||||
constexpr T repbits(unsigned char bits) noexcept
|
||||
{
|
||||
|
@ -85,17 +97,18 @@ namespace detail_ {
|
|||
|
||||
template<typename T>
|
||||
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)};
|
||||
constexpr T m33{detail_::repbits<T>(0x33)};
|
||||
constexpr T m0f{detail_::repbits<T>(0x0f)};
|
||||
constexpr T m01{detail_::repbits<T>(0x01)};
|
||||
using fast_type = typename detail_::fast_utype<T>::type;
|
||||
constexpr fast_type m55{detail_::repbits<fast_type>(0x55)};
|
||||
constexpr fast_type m33{detail_::repbits<fast_type>(0x33)};
|
||||
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 + (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)
|
||||
|
|
|
@ -2,23 +2,10 @@
|
|||
#define AL_STRING_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "almalloc.h"
|
||||
|
||||
|
||||
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
|
||||
* case-insensitive char traits.
|
||||
*/
|
||||
|
|
|
@ -2,8 +2,29 @@
|
|||
#define AL_ATOMIC_H
|
||||
|
||||
#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>;
|
||||
|
||||
inline void InitRef(RefCount &ref, unsigned int value)
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
#ifndef COMMON_COMPTR_H
|
||||
#define COMMON_COMPTR_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
|
||||
template<typename T>
|
||||
class ComPtr {
|
||||
T *mPtr{nullptr};
|
||||
|
||||
public:
|
||||
ComPtr() noexcept = default;
|
||||
ComPtr(const ComPtr &rhs) : mPtr{rhs.mPtr} { if(mPtr) mPtr->AddRef(); }
|
||||
ComPtr(ComPtr&& rhs) noexcept : mPtr{rhs.mPtr} { rhs.mPtr = nullptr; }
|
||||
ComPtr(std::nullptr_t) noexcept { }
|
||||
explicit ComPtr(T *ptr) noexcept : mPtr{ptr} { }
|
||||
~ComPtr() { if(mPtr) mPtr->Release(); }
|
||||
|
||||
ComPtr& operator=(const ComPtr &rhs)
|
||||
{
|
||||
if(!rhs.mPtr)
|
||||
{
|
||||
if(mPtr)
|
||||
mPtr->Release();
|
||||
mPtr = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs.mPtr->AddRef();
|
||||
try {
|
||||
if(mPtr)
|
||||
mPtr->Release();
|
||||
mPtr = rhs.mPtr;
|
||||
}
|
||||
catch(...) {
|
||||
rhs.mPtr->Release();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
ComPtr& operator=(ComPtr&& rhs)
|
||||
{
|
||||
if(mPtr)
|
||||
mPtr->Release();
|
||||
mPtr = rhs.mPtr;
|
||||
rhs.mPtr = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const noexcept { return mPtr != nullptr; }
|
||||
|
||||
T& operator*() const noexcept { return *mPtr; }
|
||||
T* operator->() const noexcept { return mPtr; }
|
||||
T* get() const noexcept { return mPtr; }
|
||||
T** getPtr() noexcept { return &mPtr; }
|
||||
|
||||
T* release() noexcept
|
||||
{
|
||||
T *ret{mPtr};
|
||||
mPtr = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void swap(ComPtr &rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
|
||||
void swap(ComPtr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); }
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,347 @@
|
|||
#ifndef PHASE_SHIFTER_H
|
||||
#define PHASE_SHIFTER_H
|
||||
|
||||
#ifdef HAVE_SSE_INTRINSICS
|
||||
#include <xmmintrin.h>
|
||||
#elif defined(HAVE_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "alcomplex.h"
|
||||
#include "alspan.h"
|
||||
|
||||
|
||||
/* Implements a wide-band +90 degree phase-shift. Note that this should be
|
||||
* given one sample less of a delay (FilterSize/2 - 1) compared to the direct
|
||||
* signal delay (FilterSize/2) to properly align.
|
||||
*/
|
||||
template<size_t FilterSize>
|
||||
struct PhaseShifterT {
|
||||
static_assert(FilterSize >= 16, "FilterSize needs to be at least 16");
|
||||
static_assert((FilterSize&(FilterSize-1)) == 0, "FilterSize needs to be power-of-two");
|
||||
|
||||
alignas(16) std::array<float,FilterSize/2> mCoeffs{};
|
||||
|
||||
/* Some notes on this filter construction.
|
||||
*
|
||||
* A wide-band phase-shift filter needs a delay to maintain linearity. A
|
||||
* dirac impulse in the center of a time-domain buffer represents a filter
|
||||
* passing all frequencies through as-is with a pure delay. Converting that
|
||||
* to the frequency domain, adjusting the phase of each frequency bin by
|
||||
* +90 degrees, then converting back to the time domain, results in a FIR
|
||||
* filter that applies a +90 degree wide-band phase-shift.
|
||||
*
|
||||
* A particularly notable aspect of the time-domain filter response is that
|
||||
* every other coefficient is 0. This allows doubling the effective size of
|
||||
* the filter, by storing only the non-0 coefficients and double-stepping
|
||||
* over the input to apply it.
|
||||
*
|
||||
* Additionally, the resulting filter is independent of the sample rate.
|
||||
* The same filter can be applied regardless of the device's sample rate
|
||||
* and achieve the same effect.
|
||||
*/
|
||||
PhaseShifterT()
|
||||
{
|
||||
using complex_d = std::complex<double>;
|
||||
constexpr size_t fft_size{FilterSize};
|
||||
constexpr size_t half_size{fft_size / 2};
|
||||
|
||||
auto fftBuffer = std::make_unique<complex_d[]>(fft_size);
|
||||
std::fill_n(fftBuffer.get(), fft_size, complex_d{});
|
||||
fftBuffer[half_size] = 1.0;
|
||||
|
||||
forward_fft({fftBuffer.get(), fft_size});
|
||||
for(size_t i{0};i < half_size+1;++i)
|
||||
fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()};
|
||||
for(size_t i{half_size+1};i < fft_size;++i)
|
||||
fftBuffer[i] = std::conj(fftBuffer[fft_size - i]);
|
||||
inverse_fft({fftBuffer.get(), fft_size});
|
||||
|
||||
auto fftiter = fftBuffer.get() + half_size + (FilterSize/2 - 1);
|
||||
for(float &coeff : mCoeffs)
|
||||
{
|
||||
coeff = static_cast<float>(fftiter->real() / double{fft_size});
|
||||
fftiter -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void process(al::span<float> dst, const float *RESTRICT src) const;
|
||||
void processAccum(al::span<float> dst, const float *RESTRICT src) const;
|
||||
};
|
||||
|
||||
template<size_t S>
|
||||
inline void PhaseShifterT<S>::process(al::span<float> dst, const float *RESTRICT src) const
|
||||
{
|
||||
#ifdef HAVE_SSE_INTRINSICS
|
||||
if(size_t todo{dst.size()>>1})
|
||||
{
|
||||
auto *out = reinterpret_cast<__m64*>(dst.data());
|
||||
do {
|
||||
__m128 r04{_mm_setzero_ps()};
|
||||
__m128 r14{_mm_setzero_ps()};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||
const __m128 s0{_mm_loadu_ps(&src[j*2])};
|
||||
const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])};
|
||||
|
||||
__m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))};
|
||||
r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs));
|
||||
|
||||
s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1));
|
||||
r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs));
|
||||
}
|
||||
src += 2;
|
||||
|
||||
__m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))};
|
||||
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||
|
||||
_mm_storel_pi(out, r4);
|
||||
++out;
|
||||
} while(--todo);
|
||||
}
|
||||
if((dst.size()&1))
|
||||
{
|
||||
__m128 r4{_mm_setzero_ps()};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||
const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||
r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs));
|
||||
}
|
||||
r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
|
||||
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||
|
||||
dst.back() = _mm_cvtss_f32(r4);
|
||||
}
|
||||
|
||||
#elif defined(HAVE_NEON)
|
||||
|
||||
size_t pos{0};
|
||||
if(size_t todo{dst.size()>>1})
|
||||
{
|
||||
/* There doesn't seem to be NEON intrinsics to do this kind of stipple
|
||||
* shuffling, so there's two custom methods for it.
|
||||
*/
|
||||
auto shuffle_2020 = [](float32x4_t a, float32x4_t b)
|
||||
{
|
||||
float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 0))};
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(a, 2), ret, 1);
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(b, 0), ret, 2);
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(b, 2), ret, 3);
|
||||
return ret;
|
||||
};
|
||||
auto shuffle_3131 = [](float32x4_t a, float32x4_t b)
|
||||
{
|
||||
float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 1))};
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(a, 3), ret, 1);
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(b, 1), ret, 2);
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(b, 3), ret, 3);
|
||||
return ret;
|
||||
};
|
||||
auto unpacklo = [](float32x4_t a, float32x4_t b)
|
||||
{
|
||||
float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))};
|
||||
return vcombine_f32(result.val[0], result.val[1]);
|
||||
};
|
||||
auto unpackhi = [](float32x4_t a, float32x4_t b)
|
||||
{
|
||||
float32x2x2_t result{vzip_f32(vget_high_f32(a), vget_high_f32(b))};
|
||||
return vcombine_f32(result.val[0], result.val[1]);
|
||||
};
|
||||
do {
|
||||
float32x4_t r04{vdupq_n_f32(0.0f)};
|
||||
float32x4_t r14{vdupq_n_f32(0.0f)};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
|
||||
const float32x4_t s0{vld1q_f32(&src[j*2])};
|
||||
const float32x4_t s1{vld1q_f32(&src[j*2 + 4])};
|
||||
|
||||
r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs);
|
||||
r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs);
|
||||
}
|
||||
src += 2;
|
||||
|
||||
float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))};
|
||||
float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))};
|
||||
|
||||
vst1_f32(&dst[pos], r2);
|
||||
pos += 2;
|
||||
} while(--todo);
|
||||
}
|
||||
if((dst.size()&1))
|
||||
{
|
||||
auto load4 = [](float32_t a, float32_t b, float32_t c, float32_t d)
|
||||
{
|
||||
float32x4_t ret{vmovq_n_f32(a)};
|
||||
ret = vsetq_lane_f32(b, ret, 1);
|
||||
ret = vsetq_lane_f32(c, ret, 2);
|
||||
ret = vsetq_lane_f32(d, ret, 3);
|
||||
return ret;
|
||||
};
|
||||
float32x4_t r4{vdupq_n_f32(0.0f)};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
|
||||
const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||
r4 = vmlaq_f32(r4, s, coeffs);
|
||||
}
|
||||
r4 = vaddq_f32(r4, vrev64q_f32(r4));
|
||||
dst[pos] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
for(float &output : dst)
|
||||
{
|
||||
float ret{0.0f};
|
||||
for(size_t j{0};j < mCoeffs.size();++j)
|
||||
ret += src[j*2] * mCoeffs[j];
|
||||
|
||||
output = ret;
|
||||
++src;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template<size_t S>
|
||||
inline void PhaseShifterT<S>::processAccum(al::span<float> dst, const float *RESTRICT src) const
|
||||
{
|
||||
#ifdef HAVE_SSE_INTRINSICS
|
||||
if(size_t todo{dst.size()>>1})
|
||||
{
|
||||
auto *out = reinterpret_cast<__m64*>(dst.data());
|
||||
do {
|
||||
__m128 r04{_mm_setzero_ps()};
|
||||
__m128 r14{_mm_setzero_ps()};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||
const __m128 s0{_mm_loadu_ps(&src[j*2])};
|
||||
const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])};
|
||||
|
||||
__m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))};
|
||||
r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs));
|
||||
|
||||
s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1));
|
||||
r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs));
|
||||
}
|
||||
src += 2;
|
||||
|
||||
__m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))};
|
||||
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||
|
||||
_mm_storel_pi(out, _mm_add_ps(_mm_loadl_pi(_mm_undefined_ps(), out), r4));
|
||||
++out;
|
||||
} while(--todo);
|
||||
}
|
||||
if((dst.size()&1))
|
||||
{
|
||||
__m128 r4{_mm_setzero_ps()};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const __m128 coeffs{_mm_load_ps(&mCoeffs[j])};
|
||||
/* NOTE: This could alternatively be done with two unaligned loads
|
||||
* and a shuffle. Which would be better?
|
||||
*/
|
||||
const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||
r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs));
|
||||
}
|
||||
r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3)));
|
||||
r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4));
|
||||
|
||||
dst.back() += _mm_cvtss_f32(r4);
|
||||
}
|
||||
|
||||
#elif defined(HAVE_NEON)
|
||||
|
||||
size_t pos{0};
|
||||
if(size_t todo{dst.size()>>1})
|
||||
{
|
||||
auto shuffle_2020 = [](float32x4_t a, float32x4_t b)
|
||||
{
|
||||
float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 0))};
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(a, 2), ret, 1);
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(b, 0), ret, 2);
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(b, 2), ret, 3);
|
||||
return ret;
|
||||
};
|
||||
auto shuffle_3131 = [](float32x4_t a, float32x4_t b)
|
||||
{
|
||||
float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 1))};
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(a, 3), ret, 1);
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(b, 1), ret, 2);
|
||||
ret = vsetq_lane_f32(vgetq_lane_f32(b, 3), ret, 3);
|
||||
return ret;
|
||||
};
|
||||
auto unpacklo = [](float32x4_t a, float32x4_t b)
|
||||
{
|
||||
float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))};
|
||||
return vcombine_f32(result.val[0], result.val[1]);
|
||||
};
|
||||
auto unpackhi = [](float32x4_t a, float32x4_t b)
|
||||
{
|
||||
float32x2x2_t result{vzip_f32(vget_high_f32(a), vget_high_f32(b))};
|
||||
return vcombine_f32(result.val[0], result.val[1]);
|
||||
};
|
||||
do {
|
||||
float32x4_t r04{vdupq_n_f32(0.0f)};
|
||||
float32x4_t r14{vdupq_n_f32(0.0f)};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
|
||||
const float32x4_t s0{vld1q_f32(&src[j*2])};
|
||||
const float32x4_t s1{vld1q_f32(&src[j*2 + 4])};
|
||||
|
||||
r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs);
|
||||
r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs);
|
||||
}
|
||||
src += 2;
|
||||
|
||||
float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))};
|
||||
float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))};
|
||||
|
||||
vst1_f32(&dst[pos], vadd_f32(vld1_f32(&dst[pos]), r2));
|
||||
pos += 2;
|
||||
} while(--todo);
|
||||
}
|
||||
if((dst.size()&1))
|
||||
{
|
||||
auto load4 = [](float32_t a, float32_t b, float32_t c, float32_t d)
|
||||
{
|
||||
float32x4_t ret{vmovq_n_f32(a)};
|
||||
ret = vsetq_lane_f32(b, ret, 1);
|
||||
ret = vsetq_lane_f32(c, ret, 2);
|
||||
ret = vsetq_lane_f32(d, ret, 3);
|
||||
return ret;
|
||||
};
|
||||
float32x4_t r4{vdupq_n_f32(0.0f)};
|
||||
for(size_t j{0};j < mCoeffs.size();j+=4)
|
||||
{
|
||||
const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])};
|
||||
const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])};
|
||||
r4 = vmlaq_f32(r4, s, coeffs);
|
||||
}
|
||||
r4 = vaddq_f32(r4, vrev64q_f32(r4));
|
||||
dst[pos] += vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
for(float &output : dst)
|
||||
{
|
||||
float ret{0.0f};
|
||||
for(size_t j{0};j < mCoeffs.size();++j)
|
||||
ret += src[j*2] * mCoeffs[j];
|
||||
|
||||
output += ret;
|
||||
++src;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* PHASE_SHIFTER_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue