Add a function/extension to reopen a playback device

This commit is contained in:
Chris Robinson 2021-03-09 05:52:40 -08:00
parent e6c7cdc1ba
commit e824c808a0
2 changed files with 117 additions and 30 deletions

View File

@ -269,6 +269,8 @@ const struct {
DECL(alcGetInteger64vSOFT),
DECL(alcReopenDeviceSOFT),
DECL(alEnable),
DECL(alDisable),
DECL(alIsEnabled),
@ -942,7 +944,8 @@ constexpr ALCchar alcNoDeviceExtList[] =
"ALC_EXT_EFX "
"ALC_EXT_thread_local_context "
"ALC_SOFT_loopback "
"ALC_SOFT_loopback_bformat";
"ALC_SOFT_loopback_bformat "
"ALC_SOFTX_reopen_device";
constexpr ALCchar alcExtensionList[] =
"ALC_ENUMERATE_ALL_EXT "
"ALC_ENUMERATION_EXT "
@ -956,7 +959,8 @@ constexpr ALCchar alcExtensionList[] =
"ALC_SOFT_loopback "
"ALC_SOFT_loopback_bformat "
"ALC_SOFT_output_limiter "
"ALC_SOFT_pause_device";
"ALC_SOFT_pause_device "
"ALC_SOFTX_reopen_device";
constexpr int alcMajorVersion{1};
constexpr int alcMinorVersion{1};
@ -2286,6 +2290,44 @@ static ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList)
return ALC_NO_ERROR;
}
/**
* Updates device parameters as above, and also first clears the disconnected
* status, if set.
*/
static bool ResetDeviceParams(ALCdevice *device, const int *attrList)
{
/* If the device was disconnected, reset it since we're opened anew. */
if UNLIKELY(!device->Connected.load(std::memory_order_relaxed))
{
/* Make sure disconnection is finished before continuing on. */
device->waitForMix();
for(ALCcontext *ctx : *device->mContexts.load(std::memory_order_acquire))
{
/* Clear any pending voice changes and reallocate voices to get a
* clean restart.
*/
std::lock_guard<std::mutex> __{ctx->mSourceLock};
auto *vchg = ctx->mCurrentVoiceChange.load(std::memory_order_acquire);
while(auto *next = vchg->mNext.load(std::memory_order_acquire))
vchg = next;
ctx->mCurrentVoiceChange.store(vchg, std::memory_order_release);
ctx->mVoiceClusters.clear();
ctx->allocVoices(std::max<size_t>(256,
ctx->mActiveVoiceCount.load(std::memory_order_relaxed)));
}
device->Connected.store(true);
}
ALCenum err{UpdateDeviceParams(device, attrList)};
if LIKELY(err == ALC_NO_ERROR) return ALC_TRUE;
alcSetError(device, err);
return ALC_FALSE;
}
ALCdevice::ALCdevice(DeviceType type) : Type{type}, mContexts{&EmptyContextArray}
{
@ -2654,7 +2696,10 @@ START_API_FUNC
case ALC_ALL_DEVICES_SPECIFIER:
if(DeviceRef dev{VerifyDevice(Device)})
{
std::lock_guard<std::mutex> _{dev->StateLock};
value = dev->DeviceName.c_str();
}
else
{
ProbeAllDevicesList();
@ -4119,34 +4164,66 @@ START_API_FUNC
if(dev->Flags.test(DeviceRunning))
dev->Backend->stop();
dev->Flags.reset(DeviceRunning);
if(!dev->Connected.load(std::memory_order_relaxed))
{
/* Make sure disconnection is finished before continuing on. */
dev->waitForMix();
for(ALCcontext *ctx : *dev->mContexts.load(std::memory_order_acquire))
{
/* Clear any pending voice changes and reallocate voices to get a
* clean restart.
*/
std::lock_guard<std::mutex> __{ctx->mSourceLock};
auto *vchg = ctx->mCurrentVoiceChange.load(std::memory_order_acquire);
while(auto *next = vchg->mNext.load(std::memory_order_acquire))
vchg = next;
ctx->mCurrentVoiceChange.store(vchg, std::memory_order_release);
ctx->mVoiceClusters.clear();
ctx->allocVoices(std::max<size_t>(256,
ctx->mActiveVoiceCount.load(std::memory_order_relaxed)));
}
dev->Connected.store(true);
}
ALCenum err{UpdateDeviceParams(dev.get(), attribs)};
if LIKELY(err == ALC_NO_ERROR) return ALC_TRUE;
alcSetError(dev.get(), err);
return ALC_FALSE;
return ResetDeviceParams(dev.get(), attribs) ? ALC_TRUE : ALC_FALSE;
}
END_API_FUNC
/************************************************
* ALC device reopen functions
************************************************/
/** Reopens the given device output, using the specified name and attribute list. */
FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device,
const ALCchar *deviceName, const ALCint *attribs)
START_API_FUNC
{
if(deviceName)
{
if(!deviceName[0] || al::strcasecmp(deviceName, alcDefaultName) == 0)
deviceName = nullptr;
}
std::unique_lock<std::recursive_mutex> listlock{ListLock};
DeviceRef dev{VerifyDevice(device)};
if(!dev || dev->Type != DeviceType::Playback)
{
listlock.unlock();
alcSetError(dev.get(), ALC_INVALID_DEVICE);
return ALC_FALSE;
}
std::lock_guard<std::mutex> _{dev->StateLock};
auto backend = dev->Backend.get();
/* Force the backend to stop mixing first since we're reopening. */
if(dev->Flags.test(DeviceRunning))
backend->stop();
dev->Flags.reset(DeviceRunning);
try {
backend->open(deviceName);
}
catch(al::backend_exception &e) {
WARN("Failed to reopen playback device: %s\n", e.what());
alcSetError(dev.get(), (e.errorCode() == al::backend_error::OutOfMemory)
? ALC_OUT_OF_MEMORY : ALC_INVALID_VALUE);
if(dev->Connected.load(std::memory_order_relaxed) && !dev->Flags.test(DevicePaused)
&& !dev->mContexts.load(std::memory_order_relaxed)->empty())
{
try {
backend->start();
dev->Flags.set(DeviceRunning);
}
catch(al::backend_exception& e) {
dev->handleDisconnect("%s", e.what());
}
}
return ALC_FALSE;
}
listlock.unlock();
TRACE("Reopened device %p, \"%s\"\n", voidp{dev.get()}, dev->DeviceName.c_str());
return ResetDeviceParams(dev.get(), attribs) ? ALC_TRUE : ALC_FALSE;
}
END_API_FUNC

View File

@ -66,6 +66,16 @@ 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
#ifdef __cplusplus
} /* extern "C" */
#endif