aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2023-12-12 20:10:47 -0800
committerChris Robinson <[email protected]>2023-12-12 20:10:47 -0800
commitc7774aa488961e0af2895008fb0063fb790b0a58 (patch)
tree8579a6813db24e03a49e89e21f7e373874006868
parentbdd54018f3dd63a9591fae8b7a21a71e8adc3ddb (diff)
Track the device state for being properly configured
And don't allow resuming if the backend device isn't properly set up.
-rw-r--r--alc/alc.cpp92
-rw-r--r--alc/context.cpp15
-rw-r--r--alc/context.h6
-rw-r--r--core/device.h9
4 files changed, 81 insertions, 41 deletions
diff --git a/alc/alc.cpp b/alc/alc.cpp
index a0c4f409..3ab9325a 100644
--- a/alc/alc.cpp
+++ b/alc/alc.cpp
@@ -1340,16 +1340,19 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList)
/* If a context is already running on the device, stop playback so the
* device attributes can be updated.
*/
- if(device->Flags.test(DeviceRunning))
+ if(device->mDeviceState == DeviceState::Playing)
+ {
device->Backend->stop();
- device->Flags.reset(DeviceRunning);
+ device->mDeviceState = DeviceState::Unprepared;
+ }
UpdateClockBase(device);
}
- if(device->Flags.test(DeviceRunning))
+ if(device->mDeviceState == DeviceState::Playing)
return ALC_NO_ERROR;
+ device->mDeviceState = DeviceState::Unprepared;
device->AvgSpeakerDist = 0.0f;
device->mNFCtrlFilter = NfcFilter{};
device->mUhjEncoder = nullptr;
@@ -1759,12 +1762,13 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList)
}
mixer_mode.leave();
+ device->mDeviceState = DeviceState::Configured;
if(!device->Flags.test(DevicePaused))
{
try {
auto backend = device->Backend.get();
backend->start();
- device->Flags.set(DeviceRunning);
+ device->mDeviceState = DeviceState::Playing;
}
catch(al::backend_exception& e) {
ERR("%s\n", e.what());
@@ -2774,11 +2778,7 @@ ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) noexcept
ALCdevice *Device{ctx->mALDevice.get()};
std::lock_guard<std::mutex> _{Device->StateLock};
- if(!ctx->deinit() && Device->Flags.test(DeviceRunning))
- {
- Device->Backend->stop();
- Device->Flags.reset(DeviceRunning);
- }
+ ctx->deinit();
}
@@ -2999,9 +2999,11 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept
}
orphanctxs.clear();
- if(dev->Flags.test(DeviceRunning))
+ if(dev->mDeviceState == DeviceState::Playing)
+ {
dev->Backend->stop();
- dev->Flags.reset(DeviceRunning);
+ dev->mDeviceState = DeviceState::Configured;
+ }
return ALC_TRUE;
}
@@ -3083,6 +3085,7 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName,
auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get());
DeviceList.emplace(iter, device.get());
}
+ device->mDeviceState = DeviceState::Configured;
TRACE("Created capture device %p, \"%s\"\n", voidp{device.get()}, device->DeviceName.c_str());
return device.release();
@@ -3108,9 +3111,11 @@ ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) noexcep
listlock.unlock();
std::lock_guard<std::mutex> _{dev->StateLock};
- if(dev->Flags.test(DeviceRunning))
+ if(dev->mDeviceState == DeviceState::Playing)
+ {
dev->Backend->stop();
- dev->Flags.reset(DeviceRunning);
+ dev->mDeviceState = DeviceState::Configured;
+ }
return ALC_TRUE;
}
@@ -3125,14 +3130,15 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) noexcept
}
std::lock_guard<std::mutex> _{dev->StateLock};
- if(!dev->Connected.load(std::memory_order_acquire))
+ if(!dev->Connected.load(std::memory_order_acquire)
+ || dev->mDeviceState < DeviceState::Configured)
alcSetError(dev.get(), ALC_INVALID_DEVICE);
- else if(!dev->Flags.test(DeviceRunning))
+ else if(dev->mDeviceState != DeviceState::Playing)
{
try {
auto backend = dev->Backend.get();
backend->start();
- dev->Flags.set(DeviceRunning);
+ dev->mDeviceState = DeviceState::Playing;
}
catch(al::backend_exception& e) {
ERR("%s\n", e.what());
@@ -3150,9 +3156,11 @@ ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) noexcept
else
{
std::lock_guard<std::mutex> _{dev->StateLock};
- if(dev->Flags.test(DeviceRunning))
+ if(dev->mDeviceState == DeviceState::Playing)
+ {
dev->Backend->stop();
- dev->Flags.reset(DeviceRunning);
+ dev->mDeviceState = DeviceState::Configured;
+ }
}
}
@@ -3304,9 +3312,17 @@ ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) noexcept
else
{
std::lock_guard<std::mutex> _{dev->StateLock};
- if(dev->Flags.test(DeviceRunning))
+ if(!dev->Connected.load())
+ {
+ WARN("Cannot pause a disconnected device\n");
+ alcSetError(dev.get(), ALC_INVALID_DEVICE);
+ return;
+ }
+ if(dev->mDeviceState == DeviceState::Playing)
+ {
dev->Backend->stop();
- dev->Flags.reset(DeviceRunning);
+ dev->mDeviceState = DeviceState::Configured;
+ }
dev->Flags.set(DevicePaused);
}
}
@@ -3324,6 +3340,18 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept
std::lock_guard<std::mutex> _{dev->StateLock};
if(!dev->Flags.test(DevicePaused))
return;
+ if(dev->mDeviceState < DeviceState::Configured)
+ {
+ WARN("Cannot resume unconfigured device\n");
+ alcSetError(dev.get(), ALC_INVALID_DEVICE);
+ return;
+ }
+ if(!dev->Connected.load())
+ {
+ WARN("Cannot resume a disconnected device\n");
+ alcSetError(dev.get(), ALC_INVALID_DEVICE);
+ return;
+ }
dev->Flags.reset(DevicePaused);
if(dev->mContexts.load()->empty())
return;
@@ -3331,7 +3359,7 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept
try {
auto backend = dev->Backend.get();
backend->start();
- dev->Flags.set(DeviceRunning);
+ dev->mDeviceState = DeviceState::Playing;
}
catch(al::backend_exception& e) {
ERR("%s\n", e.what());
@@ -3340,8 +3368,8 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept
return;
}
TRACE("Post-resume: %s, %s, %uhz, %u / %u buffer\n",
- DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType),
- device->Frequency, device->UpdateSize, device->BufferSize);
+ DevFmtChannelsString(dev->FmtChans), DevFmtTypeString(dev->FmtType),
+ dev->Frequency, dev->UpdateSize, dev->BufferSize);
}
@@ -3388,9 +3416,11 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi
/* Force the backend to stop mixing first since we're resetting. Also reset
* the connected state so lost devices can attempt recover.
*/
- if(dev->Flags.test(DeviceRunning))
+ if(dev->mDeviceState == DeviceState::Playing)
+ {
dev->Backend->stop();
- dev->Flags.reset(DeviceRunning);
+ dev->mDeviceState = DeviceState::Configured;
+ }
return ResetDeviceParams(dev.get(), attribs) ? ALC_TRUE : ALC_FALSE;
}
@@ -3421,11 +3451,10 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device,
std::lock_guard<std::mutex> _{dev->StateLock};
/* Force the backend to stop mixing first since we're reopening. */
- if(dev->Flags.test(DeviceRunning))
+ if(dev->mDeviceState == DeviceState::Playing)
{
- auto backend = dev->Backend.get();
- backend->stop();
- dev->Flags.reset(DeviceRunning);
+ dev->Backend->stop();
+ dev->mDeviceState = DeviceState::Configured;
}
BackendPtr newbackend;
@@ -3451,12 +3480,12 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device,
* continues playing.
*/
if(dev->Connected.load(std::memory_order_relaxed) && !dev->Flags.test(DevicePaused)
- && !dev->mContexts.load(std::memory_order_relaxed)->empty())
+ && dev->mDeviceState == DeviceState::Configured)
{
try {
auto backend = dev->Backend.get();
backend->start();
- dev->Flags.set(DeviceRunning);
+ dev->mDeviceState = DeviceState::Playing;
}
catch(al::backend_exception &be) {
ERR("%s\n", be.what());
@@ -3467,6 +3496,7 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device,
}
listlock.unlock();
dev->Backend = std::move(newbackend);
+ dev->mDeviceState = DeviceState::Unprepared;
TRACE("Reopened device %p, \"%s\"\n", voidp{dev.get()}, dev->DeviceName.c_str());
/* Always return true even if resetting fails. It shouldn't fail, but this
diff --git a/alc/context.cpp b/alc/context.cpp
index 2def58ba..ff22acdf 100644
--- a/alc/context.cpp
+++ b/alc/context.cpp
@@ -24,6 +24,7 @@
#include "al/listener.h"
#include "albit.h"
#include "alc/alu.h"
+#include "alc/backends/base.h"
#include "alspan.h"
#include "core/async_event.h"
#include "core/device.h"
@@ -236,7 +237,7 @@ void ALCcontext::init()
mActiveVoiceCount.store(64, std::memory_order_relaxed);
}
-bool ALCcontext::deinit()
+void ALCcontext::deinit()
{
if(sLocalContext == this)
{
@@ -256,7 +257,7 @@ bool ALCcontext::deinit()
dec_ref();
}
- bool ret{};
+ bool stopPlayback{};
/* 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)))
@@ -285,14 +286,18 @@ bool ALCcontext::deinit()
delete oldarray;
}
- ret = !newarray->empty();
+ stopPlayback = newarray->empty();
}
else
- ret = !oldarray->empty();
+ stopPlayback = oldarray->empty();
StopEventThrd(this);
- return ret;
+ if(stopPlayback && mALDevice->mDeviceState == DeviceState::Playing)
+ {
+ mALDevice->Backend->stop();
+ mALDevice->mDeviceState = DeviceState::Configured;
+ }
}
void ALCcontext::applyAllUpdates()
diff --git a/alc/context.h b/alc/context.h
index 32db76c7..d923e46e 100644
--- a/alc/context.h
+++ b/alc/context.h
@@ -158,10 +158,10 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
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.
+ * the running thread or globally. Stops device playback if this was the
+ * last context on its device.
*/
- bool deinit();
+ void deinit();
/**
* Defers/suspends updates for the given context's listener and sources.
diff --git a/core/device.h b/core/device.h
index e813b182..668779fa 100644
--- a/core/device.h
+++ b/core/device.h
@@ -161,8 +161,6 @@ enum {
// Specifies if the DSP is paused at user request
DevicePaused,
- // Specifies if the device is currently running
- DeviceRunning,
// Specifies if the output plays directly on/in ears (headphones, headset,
// ear buds, etc).
@@ -176,6 +174,12 @@ enum {
DeviceFlagsCount
};
+enum class DeviceState : uint8_t {
+ Unprepared,
+ Configured,
+ Playing
+};
+
struct DeviceBase {
/* To avoid extraneous allocations, a 0-sized FlexArray<ContextBase*> is
* defined globally as a sharable object.
@@ -205,6 +209,7 @@ struct DeviceBase {
// Device flags
std::bitset<DeviceFlagsCount> Flags{};
+ DeviceState mDeviceState{DeviceState::Unprepared};
uint NumAuxSends{};