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.
This commit is contained in:
Chris Robinson 2021-04-26 20:25:24 -07:00
parent 22a8ebff80
commit 26c8c50c26
7 changed files with 69 additions and 21 deletions

View File

@ -551,7 +551,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)})
{
@ -562,6 +566,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,
@ -2925,18 +2930,23 @@ 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? */
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();

View File

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

View File

@ -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<ALCcontext*>(ctxbase);
if(!ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire))
continue;
/* Clear any pending voice changes and reallocate voices to get a
* clean restart.

View File

@ -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<bool> mHoldUpdates{false};
std::atomic<bool> mStopVoicesOnDisconnect{true};
float mGainBoost{1.0f};

View File

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

View File

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

View File

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