summaryrefslogtreecommitdiff
path: root/Source/Core/AudioCommon/CubebUtils.cpp
diff options
context:
space:
mode:
authorAdmiral H. Curtiss <pikachu025@gmail.com>2022-06-18 00:39:41 +0200
committerAdmiral H. Curtiss <pikachu025@gmail.com>2022-11-26 05:05:56 +0100
commitca10e92ab92afeb4cb6d3ac935cf86767ab469eb (patch)
tree4830494d6220750b7c0bf1187a22fdf2d58e6d4f /Source/Core/AudioCommon/CubebUtils.cpp
parent59b1419a8ef2912f349c1bd3984bc85cc21c6ff1 (diff)
Core: Abide by COM MTA requirement for cubeb on Windows. Partially based on https://github.com/dolphin-emu/dolphin/pull/8920#discussion_r459746604
Co-authored-by: Michael M <mchtly@gmail.com>
Diffstat (limited to 'Source/Core/AudioCommon/CubebUtils.cpp')
-rw-r--r--Source/Core/AudioCommon/CubebUtils.cpp75
1 files changed, 75 insertions, 0 deletions
diff --git a/Source/Core/AudioCommon/CubebUtils.cpp b/Source/Core/AudioCommon/CubebUtils.cpp
index e4050a8bf5..3aff62cecf 100644
--- a/Source/Core/AudioCommon/CubebUtils.cpp
+++ b/Source/Core/AudioCommon/CubebUtils.cpp
@@ -6,7 +6,9 @@
#include <cstdarg>
#include <cstddef>
#include <cstring>
+#include <thread>
+#include "Common/Assert.h"
#include "Common/CommonPaths.h"
#include "Common/Logging/Log.h"
#include "Common/Logging/LogManager.h"
@@ -14,6 +16,49 @@
#include <cubeb/cubeb.h>
+#ifdef _WIN32
+#include <Objbase.h>
+#endif
+
+// On Windows, we must manually ensure that COM is initialized in MTA mode on every thread that
+// accesses the cubeb API. See the comment on cubeb_init in cubeb.h
+// We do this with a thread-local variable that keeps track of whether COM is initialized or not,
+// and initialize it if it isn't. When the thread ends COM is uninitialized again.
+#ifdef _WIN32
+namespace
+{
+class auto_com
+{
+public:
+ auto_com() = default;
+ auto_com(const auto_com&) = delete;
+ auto_com(auto_com&&) = delete;
+ auto_com& operator=(const auto_com&) = delete;
+ auto_com& operator=(auto_com&&) = delete;
+ ~auto_com()
+ {
+ if (m_initialized)
+ {
+ CoUninitialize();
+ }
+ }
+ bool initialize()
+ {
+ if (!m_initialized)
+ {
+ HRESULT result = CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
+ m_initialized = SUCCEEDED(result);
+ }
+ return m_initialized;
+ }
+
+private:
+ bool m_initialized = false;
+};
+} // namespace
+static thread_local auto_com tls_com_context;
+#endif
+
static ptrdiff_t s_path_cutoff_point = 0;
static void LogCallback(const char* format, ...)
@@ -47,8 +92,38 @@ static void DestroyContext(cubeb* ctx)
}
}
+static bool EnsureCubebCallable()
+{
+#ifdef _WIN32
+ if (!tls_com_context.initialize())
+ return false;
+#endif
+ return true;
+}
+
+void CubebUtils::RunInCubebContext(const std::function<void()>& func)
+{
+ // Cubeb is documented to require MTA COM mode, so if the current thread was initialized in STA
+ // mode, we make a temporary thread to execute the cubeb call.
+ if (EnsureCubebCallable())
+ {
+ func();
+ }
+ else
+ {
+ std::thread([&] {
+ // this should never fail, so yell loudly if it does
+ ASSERT(EnsureCubebCallable());
+ func();
+ }).join();
+ }
+}
+
std::shared_ptr<cubeb> CubebUtils::GetContext()
{
+ if (!EnsureCubebCallable())
+ return nullptr;
+
static std::weak_ptr<cubeb> weak;
std::shared_ptr<cubeb> shared = weak.lock();