From 26c8c50c2605e377f74d7a73bae3bbbf4f7bad61 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Mon, 26 Apr 2021 20:25:24 -0700 Subject: [PATCH] Partially implement an extension to hold sources on disconnect Rather than stopping voices/sources when the device becomes disconnected, the context can be set to leave them alone. As a consequence, their state will remain as playing and they'll keep their last known sample offset indefinately. For applications mindful of this behavior, it will allow resetting or reopening the device to reconnect and automatically resume where it left off. --- al/source.cpp | 38 ++++++++++++++++++++++++-------------- al/state.cpp | 29 +++++++++++++++++++++++------ alc/alc.cpp | 5 +++++ alc/alcontext.h | 3 +++ alc/alu.cpp | 6 ++++++ alc/backends/oboe.cpp | 4 +++- alc/inprogext.h | 5 +++++ 7 files changed, 69 insertions(+), 21 deletions(-) diff --git a/al/source.cpp b/al/source.cpp index 4855b72d..27eeff9c 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -551,15 +551,20 @@ void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail) device->waitForMix(); if UNLIKELY(!connected) { - /* If the device is disconnected, just ignore all pending changes. */ - VoiceChange *cur{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)}; - while(VoiceChange *next{cur->mNext.load(std::memory_order_acquire)}) + if(ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire)) { - cur = next; - if(Voice *voice{cur->mVoice}) - voice->mSourceID.store(0, std::memory_order_relaxed); + /* 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)}) + { + cur = next; + if(Voice *voice{cur->mVoice}) + voice->mSourceID.store(0, std::memory_order_relaxed); + } + ctx->mCurrentVoiceChange.store(cur, std::memory_order_release); } - ctx->mCurrentVoiceChange.store(cur, std::memory_order_release); } } @@ -2925,17 +2930,22 @@ START_API_FUNC } ALCdevice *device{context->mALDevice.get()}; - /* If the device is disconnected, go right to stopped. */ + /* If the device is disconnected, and voices stop on disconnect, go right + * to stopped. + */ if UNLIKELY(!device->Connected.load(std::memory_order_acquire)) { - /* TODO: Send state change event? */ - for(ALsource *source : srchandles) + if(context->mStopVoicesOnDisconnect.load(std::memory_order_acquire)) { - source->Offset = 0.0; - source->OffsetType = AL_NONE; - source->state = AL_STOPPED; + for(ALsource *source : srchandles) + { + /* TODO: Send state change event? */ + source->Offset = 0.0; + source->OffsetType = AL_NONE; + source->state = AL_STOPPED; + } + return; } - return; } /* Count the number of reusable voices. */ diff --git a/al/state.cpp b/al/state.cpp index e2f164ca..86a85b4d 100644 --- a/al/state.cpp +++ b/al/state.cpp @@ -32,6 +32,7 @@ #include "AL/alc.h" #include "AL/alext.h" +#include "alcmain.h" #include "alcontext.h" #include "almalloc.h" #include "alnumeric.h" @@ -152,12 +153,18 @@ START_API_FUNC ContextRef context{GetContextRef()}; if UNLIKELY(!context) return; - std::lock_guard _{context->mPropLock}; switch(capability) { case AL_SOURCE_DISTANCE_MODEL: - context->mSourceDistanceModel = true; - DO_UPDATEPROPS(); + { + std::lock_guard _{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 _{context->mPropLock}; switch(capability) { case AL_SOURCE_DISTANCE_MODEL: - context->mSourceDistanceModel = false; - DO_UPDATEPROPS(); + { + std::lock_guard _{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); } diff --git a/alc/alc.cpp b/alc/alc.cpp index d06ca067..47b77758 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -834,6 +834,8 @@ constexpr struct { DECL(AL_FORMAT_UHJ4CHN8_SOFT), DECL(AL_FORMAT_UHJ4CHN16_SOFT), DECL(AL_FORMAT_UHJ4CHN_FLOAT32_SOFT), + + DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT), }; #undef DECL @@ -889,6 +891,7 @@ constexpr ALchar alExtList[] = "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 " @@ -2269,6 +2272,8 @@ static bool ResetDeviceParams(ALCdevice *device, const int *attrList) for(ContextBase *ctxbase : *device->mContexts.load(std::memory_order_acquire)) { auto *ctx = static_cast(ctxbase); + if(!ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire)) + continue; /* Clear any pending voice changes and reallocate voices to get a * clean restart. diff --git a/alc/alcontext.h b/alc/alcontext.h index b1b41e76..075e6a55 100644 --- a/alc/alcontext.h +++ b/alc/alcontext.h @@ -35,6 +35,8 @@ struct Voice; struct VoiceChange; struct VoicePropsItem; +using uint = unsigned int; + enum class DistanceModel : unsigned char { Disable, @@ -108,6 +110,7 @@ struct ContextBase { */ RefCount mUpdateCount{0u}; std::atomic mHoldUpdates{false}; + std::atomic mStopVoicesOnDisconnect{true}; float mGainBoost{1.0f}; diff --git a/alc/alu.cpp b/alc/alu.cpp index 168f0112..d7f4410f 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -2030,6 +2030,12 @@ void DeviceBase::handleDisconnect(const char *msg, ...) } } + if(!ctx->mStopVoicesOnDisconnect) + { + ProcessVoiceChanges(ctx); + continue; + } + auto voicelist = ctx->getVoicesSpanAcquired(); auto stop_voice = [](Voice *voice) -> void { diff --git a/alc/backends/oboe.cpp b/alc/backends/oboe.cpp index d82db834..28ade849 100644 --- a/alc/backends/oboe.cpp +++ b/alc/backends/oboe.cpp @@ -5,8 +5,10 @@ #include #include +#include -#include "alu.h" +#include "alnumeric.h" +#include "core/device.h" #include "core/logging.h" #include "oboe/Oboe.h" diff --git a/alc/inprogext.h b/alc/inprogext.h index 091703e2..781ccfd3 100644 --- a/alc/inprogext.h +++ b/alc/inprogext.h @@ -89,6 +89,11 @@ ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *de #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