diff options
-rw-r--r-- | alc/alc.cpp | 127 | ||||
-rw-r--r-- | alc/inprogext.h | 10 |
2 files changed, 112 insertions, 25 deletions
diff --git a/alc/alc.cpp b/alc/alc.cpp index a755e675..a41aa944 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -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); + return ResetDeviceParams(dev.get(), attribs) ? ALC_TRUE : ALC_FALSE; +} +END_API_FUNC - ctx->mVoiceClusters.clear(); - ctx->allocVoices(std::max<size_t>(256, - ctx->mActiveVoiceCount.load(std::memory_order_relaxed))); - } - dev->Connected.store(true); +/************************************************ + * 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; } - ALCenum err{UpdateDeviceParams(dev.get(), attribs)}; - if LIKELY(err == ALC_NO_ERROR) return ALC_TRUE; + 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(); - alcSetError(dev.get(), err); - return ALC_FALSE; + /* 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 diff --git a/alc/inprogext.h b/alc/inprogext.h index ea27a531..bb16531a 100644 --- a/alc/inprogext.h +++ b/alc/inprogext.h @@ -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 |