From e085bf14f99c52eff514e2317af740869691ba82 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Sat, 18 Jun 2022 05:31:14 +0200 Subject: Core: Use extra thread for Cubeb on Windows to not disturb the CoInitialize state of whatever thread happens to call a Cubeb function. --- Source/Core/AudioCommon/CubebStream.cpp | 136 ++++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 31 deletions(-) (limited to 'Source/Core/AudioCommon/CubebStream.cpp') diff --git a/Source/Core/AudioCommon/CubebStream.cpp b/Source/Core/AudioCommon/CubebStream.cpp index afef4f467e..5d3d70d9cb 100644 --- a/Source/Core/AudioCommon/CubebStream.cpp +++ b/Source/Core/AudioCommon/CubebStream.cpp @@ -7,10 +7,16 @@ #include "AudioCommon/CubebUtils.h" #include "Common/CommonTypes.h" +#include "Common/Event.h" #include "Common/Logging/Log.h" +#include "Common/ScopeGuard.h" #include "Common/Thread.h" #include "Core/Config/MainSettings.h" +#ifdef _WIN32 +#include +#endif + // ~10 ms - needs to be at least 240 for surround constexpr u32 BUFFER_SAMPLES = 512; @@ -31,61 +37,129 @@ void CubebStream::StateCallback(cubeb_stream* stream, void* user_data, cubeb_sta { } +CubebStream::CubebStream() +#ifdef _WIN32 + : m_work_queue([](const std::function& func) { func(); }) +{ + Common::Event sync_event; + m_work_queue.EmplaceItem([this, &sync_event] { + Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); }); + auto result = ::CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); + m_coinit_success = result == S_OK; + }); + sync_event.Wait(); +} +#else + = default; +#endif + bool CubebStream::Init() { - m_ctx = CubebUtils::GetContext(); - if (!m_ctx) - return false; + bool return_value = false; - m_stereo = !Config::ShouldUseDPL2Decoder(); +#ifdef _WIN32 + if (!m_coinit_success) + return false; + Common::Event sync_event; + m_work_queue.EmplaceItem([this, &return_value, &sync_event] { + Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); }); +#endif + + m_ctx = CubebUtils::GetContext(); + if (m_ctx) + { + m_stereo = !Config::ShouldUseDPL2Decoder(); + + cubeb_stream_params params{}; + params.rate = m_mixer->GetSampleRate(); + if (m_stereo) + { + params.channels = 2; + params.format = CUBEB_SAMPLE_S16NE; + params.layout = CUBEB_LAYOUT_STEREO; + } + else + { + params.channels = 6; + params.format = CUBEB_SAMPLE_FLOAT32NE; + params.layout = CUBEB_LAYOUT_3F2_LFE; + } + + u32 minimum_latency = 0; + if (cubeb_get_min_latency(m_ctx.get(), ¶ms, &minimum_latency) != CUBEB_OK) + ERROR_LOG_FMT(AUDIO, "Error getting minimum latency"); + INFO_LOG_FMT(AUDIO, "Minimum latency: {} frames", minimum_latency); + + return_value = + cubeb_stream_init(m_ctx.get(), &m_stream, "Dolphin Audio Output", nullptr, nullptr, + nullptr, ¶ms, std::max(BUFFER_SAMPLES, minimum_latency), + DataCallback, StateCallback, this) == CUBEB_OK; + } + +#ifdef _WIN32 + }); + sync_event.Wait(); +#endif - cubeb_stream_params params{}; - params.rate = m_mixer->GetSampleRate(); - if (m_stereo) - { - params.channels = 2; - params.format = CUBEB_SAMPLE_S16NE; - params.layout = CUBEB_LAYOUT_STEREO; - } - else - { - params.channels = 6; - params.format = CUBEB_SAMPLE_FLOAT32NE; - params.layout = CUBEB_LAYOUT_3F2_LFE; - } - - u32 minimum_latency = 0; - if (cubeb_get_min_latency(m_ctx.get(), ¶ms, &minimum_latency) != CUBEB_OK) - ERROR_LOG_FMT(AUDIO, "Error getting minimum latency"); - INFO_LOG_FMT(AUDIO, "Minimum latency: {} frames", minimum_latency); - - return cubeb_stream_init(m_ctx.get(), &m_stream, "Dolphin Audio Output", nullptr, nullptr, - nullptr, ¶ms, std::max(BUFFER_SAMPLES, minimum_latency), - DataCallback, StateCallback, this) == CUBEB_OK; + return return_value; } bool CubebStream::SetRunning(bool running) { bool return_value = false; - CubebUtils::RunInCubebContext([&] { + +#ifdef _WIN32 + if (!m_coinit_success) + return false; + Common::Event sync_event; + m_work_queue.EmplaceItem([this, running, &return_value, &sync_event] { + Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); }); +#endif if (running) return_value = cubeb_stream_start(m_stream) == CUBEB_OK; else return_value = cubeb_stream_stop(m_stream) == CUBEB_OK; +#ifdef _WIN32 }); + sync_event.Wait(); +#endif + return return_value; } CubebStream::~CubebStream() { - CubebUtils::RunInCubebContext([&] { - SetRunning(false); +#ifdef _WIN32 + Common::Event sync_event; + m_work_queue.EmplaceItem([this, &sync_event] { + Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); }); +#endif + cubeb_stream_stop(m_stream); cubeb_stream_destroy(m_stream); +#ifdef _WIN32 + if (m_coinit_success) + { + m_coinit_success = false; + CoUninitialize(); + } }); + sync_event.Wait(); +#endif m_ctx.reset(); } void CubebStream::SetVolume(int volume) { - CubebUtils::RunInCubebContext([&] { cubeb_stream_set_volume(m_stream, volume / 100.0f); }); +#ifdef _WIN32 + if (!m_coinit_success) + return; + Common::Event sync_event; + m_work_queue.EmplaceItem([this, volume, &sync_event] { + Common::ScopeGuard sync_event_guard([&sync_event] { sync_event.Set(); }); +#endif + cubeb_stream_set_volume(m_stream, volume / 100.0f); +#ifdef _WIN32 + }); + sync_event.Wait(); +#endif } -- cgit v1.2.3