mirror of
https://github.com/holub/mame
synced 2025-06-25 22:04:15 +03:00
-sound/wasapi_sound.cpp: Added preliminary WASAPI sound module (not enabled yet).
-sound/xaudio2_sound.cpp: More fixes: * Fixed a couple more potential deadlocks when handling critical engine errors. * Better tracking of the default device when devices are reconfigured.
This commit is contained in:
parent
282aa822c6
commit
72de682bbf
@ -141,6 +141,7 @@ function osdmodulesbuild()
|
|||||||
MAME_DIR .. "src/osd/modules/sound/sdl_sound.cpp",
|
MAME_DIR .. "src/osd/modules/sound/sdl_sound.cpp",
|
||||||
MAME_DIR .. "src/osd/modules/sound/sound_module.cpp",
|
MAME_DIR .. "src/osd/modules/sound/sound_module.cpp",
|
||||||
MAME_DIR .. "src/osd/modules/sound/sound_module.h",
|
MAME_DIR .. "src/osd/modules/sound/sound_module.h",
|
||||||
|
MAME_DIR .. "src/osd/modules/sound/wasapi_sound.cpp",
|
||||||
MAME_DIR .. "src/osd/modules/sound/xaudio2_sound.cpp",
|
MAME_DIR .. "src/osd/modules/sound/xaudio2_sound.cpp",
|
||||||
}
|
}
|
||||||
includedirs {
|
includedirs {
|
||||||
|
@ -86,9 +86,7 @@ HRESULT populate_audio_node_info(
|
|||||||
result = device.GetId(&id_raw);
|
result = device.GetId(&id_raw);
|
||||||
if (FAILED(result) || !id_raw)
|
if (FAILED(result) || !id_raw)
|
||||||
{
|
{
|
||||||
osd_printf_error(
|
osd_printf_error("Error getting ID for audio device. Error: 0x%X\n", result);
|
||||||
"Error getting ID for audio device. Error: 0x%X\n",
|
|
||||||
static_cast<unsigned int>(result));
|
|
||||||
return FAILED(result) ? result : E_POINTER;
|
return FAILED(result) ? result : E_POINTER;
|
||||||
}
|
}
|
||||||
device_id_w.reset(std::exchange(id_raw, nullptr));
|
device_id_w.reset(std::exchange(id_raw, nullptr));
|
||||||
@ -110,7 +108,7 @@ HRESULT populate_audio_node_info(
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Error opening property store for audio device %s. Error: 0x%X\n",
|
"Error opening property store for audio device %s. Error: 0x%X\n",
|
||||||
id_string,
|
id_string,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return FAILED(result) ? result : E_POINTER;
|
return FAILED(result) ? result : E_POINTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +123,7 @@ HRESULT populate_audio_node_info(
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Error getting display name for audio device %s. Error: 0x%X\n",
|
"Error getting display name for audio device %s. Error: 0x%X\n",
|
||||||
id_string,
|
id_string,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
device_name = id_string;
|
device_name = id_string;
|
||||||
@ -151,7 +149,7 @@ HRESULT populate_audio_node_info(
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Error getting endpoint information for audio device %s. Error: 0x%X\n",
|
"Error getting endpoint information for audio device %s. Error: 0x%X\n",
|
||||||
device_name,
|
device_name,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return FAILED(result) ? result : E_POINTER;
|
return FAILED(result) ? result : E_POINTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +159,7 @@ HRESULT populate_audio_node_info(
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Error getting data flow direction for audio device %s. Error: 0x%X\n",
|
"Error getting data flow direction for audio device %s. Error: 0x%X\n",
|
||||||
device_name,
|
device_name,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +181,7 @@ HRESULT populate_audio_node_info(
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Error getting stream format for audio device %s. Error: 0x%X\n",
|
"Error getting stream format for audio device %s. Error: 0x%X\n",
|
||||||
device_name,
|
device_name,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else if (VT_BLOB != format_property.value.vt)
|
else if (VT_BLOB != format_property.value.vt)
|
||||||
@ -210,7 +208,7 @@ HRESULT populate_audio_node_info(
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Error getting speaker arrangement for audio device %s. Error: 0x%X\n",
|
"Error getting speaker arrangement for audio device %s. Error: 0x%X\n",
|
||||||
device_name,
|
device_name,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
}
|
}
|
||||||
else switch (speakers_property.value.vt)
|
else switch (speakers_property.value.vt)
|
||||||
{
|
{
|
||||||
@ -263,7 +261,7 @@ HRESULT populate_audio_node_info(
|
|||||||
channel_names.emplace_back(util::string_format("Channel %u", i + 1));
|
channel_names.emplace_back(util::string_format("Channel %u", i + 1));
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
channel_positions.resize(format->nChannels, std::array<double, 3>{ 0.0, 0.0, 0.0 });
|
channel_positions.resize(format->nChannels, std::array<double, 3>{ 0.0, 0.0, 1.0 });
|
||||||
}
|
}
|
||||||
catch (std::bad_alloc const &)
|
catch (std::bad_alloc const &)
|
||||||
{
|
{
|
||||||
|
1440
src/osd/modules/sound/wasapi_sound.cpp
Normal file
1440
src/osd/modules/sound/wasapi_sound.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// standard windows headers
|
// standard windows headers
|
||||||
@ -45,12 +46,11 @@
|
|||||||
|
|
||||||
#include <wrl/client.h>
|
#include <wrl/client.h>
|
||||||
|
|
||||||
// XAudio2 include
|
// sound API headers
|
||||||
#include <xaudio2.h>
|
|
||||||
|
|
||||||
// MMDevice API headers
|
|
||||||
#include <mmdeviceapi.h>
|
|
||||||
#include <functiondiscoverykeys_devpkey.h>
|
#include <functiondiscoverykeys_devpkey.h>
|
||||||
|
#include <mmdeviceapi.h>
|
||||||
|
#include <mmreg.h>
|
||||||
|
#include <xaudio2.h>
|
||||||
|
|
||||||
|
|
||||||
namespace osd {
|
namespace osd {
|
||||||
@ -196,6 +196,7 @@ public:
|
|||||||
m_exiting(false),
|
m_exiting(false),
|
||||||
m_audio_latency(0.0F),
|
m_audio_latency(0.0F),
|
||||||
m_hEventNeedUpdate(nullptr),
|
m_hEventNeedUpdate(nullptr),
|
||||||
|
m_hEventEngineError(nullptr),
|
||||||
m_hEventExiting(nullptr),
|
m_hEventExiting(nullptr),
|
||||||
m_overflows(0),
|
m_overflows(0),
|
||||||
m_underflows(0)
|
m_underflows(0)
|
||||||
@ -221,7 +222,7 @@ private:
|
|||||||
class device_info final : public IXAudio2EngineCallback
|
class device_info final : public IXAudio2EngineCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
device_info(sound_xaudio2 &host) : m_host(host) { }
|
device_info(sound_xaudio2 &host) : engine_error(false), m_host(host) { }
|
||||||
~device_info();
|
~device_info();
|
||||||
device_info(device_info const &) = delete;
|
device_info(device_info const &) = delete;
|
||||||
device_info(device_info &&) = delete;
|
device_info(device_info &&) = delete;
|
||||||
@ -231,6 +232,7 @@ private:
|
|||||||
x_audio_2_ptr engine;
|
x_audio_2_ptr engine;
|
||||||
mastering_voice_ptr mastering_voice;
|
mastering_voice_ptr mastering_voice;
|
||||||
audio_info::node_info info;
|
audio_info::node_info info;
|
||||||
|
bool engine_error;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void STDMETHODCALLTYPE OnProcessingPassStart() override { }
|
virtual void STDMETHODCALLTYPE OnProcessingPassStart() override { }
|
||||||
@ -291,7 +293,7 @@ private:
|
|||||||
virtual HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) override;
|
virtual HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) override;
|
||||||
virtual HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) override;
|
virtual HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) override;
|
||||||
virtual HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId) override;
|
virtual HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId) override;
|
||||||
virtual HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDefaultDeviceId, PROPERTYKEY const key) override;
|
virtual HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, PROPERTYKEY const key) override;
|
||||||
|
|
||||||
// stub IUnknown implementation
|
// stub IUnknown implementation
|
||||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) noexcept override;
|
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) noexcept override;
|
||||||
@ -309,6 +311,7 @@ private:
|
|||||||
|
|
||||||
// tracking audio endpoint devices
|
// tracking audio endpoint devices
|
||||||
mm_device_enumerator_ptr m_device_enum;
|
mm_device_enumerator_ptr m_device_enum;
|
||||||
|
std::wstring m_default_sink_id;
|
||||||
device_info_vector m_device_info;
|
device_info_vector m_device_info;
|
||||||
std::mutex m_device_mutex;
|
std::mutex m_device_mutex;
|
||||||
uint32_t m_next_node_id;
|
uint32_t m_next_node_id;
|
||||||
@ -322,6 +325,7 @@ private:
|
|||||||
std::thread m_cleanup_thread;
|
std::thread m_cleanup_thread;
|
||||||
std::mutex m_cleanup_mutex;
|
std::mutex m_cleanup_mutex;
|
||||||
|
|
||||||
|
// tracking streams
|
||||||
voice_info_vector m_voice_info;
|
voice_info_vector m_voice_info;
|
||||||
std::mutex m_voice_mutex;
|
std::mutex m_voice_mutex;
|
||||||
uint32_t m_next_voice_id;
|
uint32_t m_next_voice_id;
|
||||||
@ -331,6 +335,7 @@ private:
|
|||||||
|
|
||||||
float m_audio_latency;
|
float m_audio_latency;
|
||||||
HANDLE m_hEventNeedUpdate;
|
HANDLE m_hEventNeedUpdate;
|
||||||
|
HANDLE m_hEventEngineError;
|
||||||
HANDLE m_hEventExiting;
|
HANDLE m_hEventExiting;
|
||||||
std::thread m_audioThread;
|
std::thread m_audioThread;
|
||||||
std::atomic<uint32_t> m_overflows;
|
std::atomic<uint32_t> m_overflows;
|
||||||
@ -372,23 +377,38 @@ int sound_xaudio2::init(osd_interface &osd, osd_options const &options)
|
|||||||
// create a multimedia device enumerator and enumerate devices
|
// create a multimedia device enumerator and enumerate devices
|
||||||
HR_GOERR(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&m_device_enum)));
|
HR_GOERR(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, IID_PPV_ARGS(&m_device_enum)));
|
||||||
{
|
{
|
||||||
co_task_wstr_ptr default_id;
|
|
||||||
std::lock_guard device_lock(m_device_mutex);
|
std::lock_guard device_lock(m_device_mutex);
|
||||||
|
|
||||||
HR_GOERR(m_device_enum->RegisterEndpointNotificationCallback(this));
|
HR_GOERR(m_device_enum->RegisterEndpointNotificationCallback(this));
|
||||||
m_endpoint_notifications = true;
|
m_endpoint_notifications = true;
|
||||||
|
|
||||||
HR_GOERR(get_default_audio_device_id(*m_device_enum.Get(), eRender, eMultimedia, default_id));
|
{
|
||||||
|
co_task_wstr_ptr default_id;
|
||||||
|
result = get_default_audio_device_id(*m_device_enum.Get(), eRender, eMultimedia, default_id);
|
||||||
|
if (FAILED(result) || !default_id)
|
||||||
|
{
|
||||||
|
// this is non-fatal, they just might get the wrong default output device
|
||||||
|
osd_printf_error("Sound: Error getting default audio output device ID. Error: 0x%X\n", result);
|
||||||
|
m_default_sink_id.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// reserve double the space to avoid allocations when the default device changes
|
||||||
|
std::wstring_view default_id_str(default_id.get());
|
||||||
|
m_default_sink_id.reserve(default_id_str.length() * 2);
|
||||||
|
m_default_sink_id = default_id_str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result = enumerate_audio_endpoints(
|
result = enumerate_audio_endpoints(
|
||||||
*m_device_enum.Get(),
|
*m_device_enum.Get(),
|
||||||
eRender,
|
eRender,
|
||||||
DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED,
|
DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED,
|
||||||
[this, &default_id] (HRESULT hr, mm_device_ptr &dev) -> bool
|
[this] (HRESULT hr, mm_device_ptr &dev) -> bool
|
||||||
{
|
{
|
||||||
if (FAILED(hr) || !dev)
|
if (FAILED(hr) || !dev)
|
||||||
{
|
{
|
||||||
osd_printf_error("Sound: Error getting audio device. Error: 0x%X\n", static_cast<unsigned int>(hr));
|
osd_printf_error("Sound: Error getting audio device. Error: 0x%X\n", hr);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,7 +417,7 @@ int sound_xaudio2::init(osd_interface &osd, osd_options const &options)
|
|||||||
hr = dev->GetState(&state);
|
hr = dev->GetState(&state);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
osd_printf_error("Sound: Error getting audio device state. Error: 0x%X\n", static_cast<unsigned int>(hr));
|
osd_printf_error("Sound: Error getting audio device state. Error: 0x%X\n", hr);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ((DEVICE_STATE_ACTIVE != state) && (DEVICE_STATE_UNPLUGGED != state))
|
if ((DEVICE_STATE_ACTIVE != state) && (DEVICE_STATE_UNPLUGGED != state))
|
||||||
@ -428,7 +448,7 @@ int sound_xaudio2::init(osd_interface &osd, osd_options const &options)
|
|||||||
devinfo->info.m_name,
|
devinfo->info.m_name,
|
||||||
osd::text::from_wstring(devinfo->device_id),
|
osd::text::from_wstring(devinfo->device_id),
|
||||||
devinfo->info.m_id);
|
devinfo->info.m_id);
|
||||||
if (!m_default_sink && default_id && (devinfo->device_id == default_id.get()))
|
if (!m_default_sink && (devinfo->device_id == m_default_sink_id))
|
||||||
m_default_sink = devinfo->info.m_id;
|
m_default_sink = devinfo->info.m_id;
|
||||||
m_device_info.emplace(pos, std::move(devinfo));
|
m_device_info.emplace(pos, std::move(devinfo));
|
||||||
|
|
||||||
@ -439,15 +459,16 @@ int sound_xaudio2::init(osd_interface &osd, osd_options const &options)
|
|||||||
}
|
}
|
||||||
if (FAILED(result))
|
if (FAILED(result))
|
||||||
{
|
{
|
||||||
osd_printf_error("Sound: Error enumerating audio endpoints. Error: 0x%X\n", static_cast<unsigned int>(result));
|
osd_printf_error("Sound: Error enumerating audio endpoints. Error: 0x%X\n", result);
|
||||||
goto Error;
|
goto Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// start a thread to clean up removed devices
|
// start a thread to clean up removed voices and devices
|
||||||
m_cleanup_thread = std::thread([] (sound_xaudio2 *self) { self->cleanup_task(); }, this);
|
m_cleanup_thread = std::thread([] (sound_xaudio2 *self) { self->cleanup_task(); }, this);
|
||||||
|
|
||||||
// Initialize our events
|
// Initialize our events
|
||||||
m_hEventNeedUpdate = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
m_hEventNeedUpdate = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||||
|
m_hEventEngineError = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||||
m_hEventExiting = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
m_hEventExiting = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||||
|
|
||||||
// Start the thread listening
|
// Start the thread listening
|
||||||
@ -514,6 +535,12 @@ void sound_xaudio2::exit()
|
|||||||
m_hEventNeedUpdate = nullptr;
|
m_hEventNeedUpdate = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_hEventEngineError)
|
||||||
|
{
|
||||||
|
CloseHandle(m_hEventEngineError);
|
||||||
|
m_hEventEngineError = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_hEventExiting)
|
if (m_hEventExiting)
|
||||||
{
|
{
|
||||||
CloseHandle(m_hEventExiting);
|
CloseHandle(m_hEventExiting);
|
||||||
@ -604,7 +631,7 @@ uint32_t sound_xaudio2::stream_sink_open(uint32_t node, std::string name, uint32
|
|||||||
// always check for stale argument values
|
// always check for stale argument values
|
||||||
if (m_device_info.end() == device)
|
if (m_device_info.end() == device)
|
||||||
{
|
{
|
||||||
osd_printf_verbose("Sound: Attempt to open audio stream %s for unknown node %u.\n", name, node);
|
osd_printf_verbose("Sound: Attempt to open audio output stream %s on unknown node %u.\n", name, node);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -618,7 +645,7 @@ uint32_t sound_xaudio2::stream_sink_open(uint32_t node, std::string name, uint32
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Sound: Error creating XAudio2 engine for audio device %s. Error: 0x%X\n",
|
"Sound: Error creating XAudio2 engine for audio device %s. Error: 0x%X\n",
|
||||||
(*device)->info.m_name,
|
(*device)->info.m_name,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,7 +656,7 @@ uint32_t sound_xaudio2::stream_sink_open(uint32_t node, std::string name, uint32
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Sound: Error registering to receive XAudio2 engine callbacks for audio device %s. Error: 0x%X\n",
|
"Sound: Error registering to receive XAudio2 engine callbacks for audio device %s. Error: 0x%X\n",
|
||||||
(*device)->info.m_name,
|
(*device)->info.m_name,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -657,7 +684,7 @@ uint32_t sound_xaudio2::stream_sink_open(uint32_t node, std::string name, uint32
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Sound: Error creating mastering voice for audio device %s. Error: 0x%X\n",
|
"Sound: Error creating mastering voice for audio device %s. Error: 0x%X\n",
|
||||||
(*device)->info.m_name,
|
(*device)->info.m_name,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -705,7 +732,7 @@ uint32_t sound_xaudio2::stream_sink_open(uint32_t node, std::string name, uint32
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Sound: Error creating source voice for audio device %s. Error: 0x%X\n",
|
"Sound: Error creating source voice for audio device %s. Error: 0x%X\n",
|
||||||
(*device)->info.m_name,
|
(*device)->info.m_name,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
osd_printf_verbose(
|
osd_printf_verbose(
|
||||||
@ -725,7 +752,7 @@ uint32_t sound_xaudio2::stream_sink_open(uint32_t node, std::string name, uint32
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Sound: Error setting source voice output matrix for audio device %s. Error: 0x%X\n",
|
"Sound: Error setting source voice output matrix for audio device %s. Error: 0x%X\n",
|
||||||
(*device)->info.m_name,
|
(*device)->info.m_name,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,7 +763,7 @@ uint32_t sound_xaudio2::stream_sink_open(uint32_t node, std::string name, uint32
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Sound: Error starting source voice for audio device %s. Error: 0x%X\n",
|
"Sound: Error starting source voice for audio device %s. Error: 0x%X\n",
|
||||||
(*device)->info.m_name,
|
(*device)->info.m_name,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -791,7 +818,7 @@ void sound_xaudio2::stream_set_volumes(uint32_t id, const std::vector<float> &db
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Sound: Error setting source voice output matrix for audio stream %u. Error: 0x%X\n",
|
"Sound: Error setting source voice output matrix for audio stream %u. Error: 0x%X\n",
|
||||||
(*pos)->info.m_id,
|
(*pos)->info.m_id,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -801,19 +828,34 @@ void sound_xaudio2::stream_set_volumes(uint32_t id, const std::vector<float> &db
|
|||||||
|
|
||||||
void sound_xaudio2::stream_close(uint32_t id)
|
void sound_xaudio2::stream_close(uint32_t id)
|
||||||
{
|
{
|
||||||
std::lock_guard voice_lock(m_voice_mutex);
|
voice_info_ptr voice;
|
||||||
auto const pos = std::lower_bound(
|
|
||||||
m_voice_info.begin(),
|
|
||||||
m_voice_info.end(),
|
|
||||||
id,
|
|
||||||
[] (voice_info_ptr const &a, uint32_t b) { return a->info.m_id < b; });
|
|
||||||
if ((m_voice_info.end() == pos) || ((*pos)->info.m_id != id))
|
|
||||||
{
|
{
|
||||||
// the sound manager tries to close streams that have disappeared due to device disconnection
|
std::lock_guard voice_lock(m_voice_mutex);
|
||||||
return;
|
auto const pos = std::lower_bound(
|
||||||
|
m_voice_info.begin(),
|
||||||
|
m_voice_info.end(),
|
||||||
|
id,
|
||||||
|
[] (voice_info_ptr const &a, uint32_t b) { return a->info.m_id < b; });
|
||||||
|
if ((m_voice_info.end() == pos) || ((*pos)->info.m_id != id))
|
||||||
|
{
|
||||||
|
// the sound manager tries to close streams that have disappeared due to device disconnection
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
voice = std::move(*pos);
|
||||||
|
m_voice_info.erase(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_voice_info.erase(pos);
|
// try to clean up on a separate thread to avoid potentially blocking here
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::lock_guard cleanup_lock(m_cleanup_mutex);
|
||||||
|
m_zombie_voices.emplace_back(std::move(voice));
|
||||||
|
}
|
||||||
|
catch (std::bad_alloc const &)
|
||||||
|
{
|
||||||
|
// voice will be destroyed when it falls out of scope
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//============================================================
|
//============================================================
|
||||||
@ -858,54 +900,27 @@ sound_xaudio2::device_info::~device_info()
|
|||||||
|
|
||||||
void sound_xaudio2::device_info::OnCriticalError(HRESULT error)
|
void sound_xaudio2::device_info::OnCriticalError(HRESULT error)
|
||||||
{
|
{
|
||||||
x_audio_2_ptr old_engine;
|
std::string name;
|
||||||
mastering_voice_ptr old_mastering_voice;
|
|
||||||
{
|
{
|
||||||
std::lock_guard device_lock(m_host.m_device_mutex);
|
std::lock_guard device_lock(m_host.m_device_mutex);
|
||||||
|
engine_error = true;
|
||||||
|
SetEvent(m_host.m_hEventEngineError);
|
||||||
|
|
||||||
// clean up any voices associated with this device
|
try
|
||||||
m_host.purge_voices(info.m_id);
|
{
|
||||||
|
name = info.m_name;
|
||||||
// tear down the XAudio2 engine - it will be recreated next time a stream is opened
|
}
|
||||||
old_engine = std::move(engine);
|
catch (std::bad_alloc const &)
|
||||||
old_mastering_voice = std::move(mastering_voice);
|
{
|
||||||
mastering_voice.reset();
|
}
|
||||||
engine = nullptr;
|
|
||||||
|
|
||||||
// bump this so the sound manager recreates its streams
|
|
||||||
//info.m_id = m_host.m_next_node_id++; FIXME: causes crash in sound_manager::update_osd_streams()
|
|
||||||
|
|
||||||
++m_host.m_generation;
|
|
||||||
|
|
||||||
osd_printf_verbose(
|
|
||||||
"Sound: XAudio2 critical error for device %s. Error: 0x%X\n",
|
|
||||||
info.m_name,
|
|
||||||
static_cast<unsigned int>(error));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to get another thread to do the cleanup
|
|
||||||
device_info_ptr zombie;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
zombie = std::make_unique<device_info>(m_host);
|
osd_printf_verbose("Sound: XAudio2 critical error for device %s. Error: 0x%X\n", name, error);
|
||||||
zombie->engine = std::move(old_engine);
|
|
||||||
zombie->mastering_voice = std::move(old_mastering_voice);
|
|
||||||
old_mastering_voice.reset();
|
|
||||||
old_engine = nullptr;
|
|
||||||
{
|
|
||||||
std::lock_guard cleanup_lock(m_host.m_cleanup_mutex);
|
|
||||||
m_host.m_zombie_devices.emplace_back(std::move(zombie));
|
|
||||||
m_host.m_cleanup_condition.notify_one();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (std::bad_alloc const &)
|
catch (std::bad_alloc const &)
|
||||||
{
|
{
|
||||||
// make sure the cleanup thread wakes up
|
|
||||||
std::lock_guard cleanup_lock(m_host.m_cleanup_mutex);
|
|
||||||
m_host.m_cleanup_condition.notify_one();
|
|
||||||
|
|
||||||
// old engine and mastering voice will fall out of scope and be destroyed
|
|
||||||
// this could block things it shouldn't, but leaking would be worse
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1096,7 +1111,7 @@ void sound_xaudio2::voice_info::submit_buffer(std::unique_ptr<BYTE []> &&data, D
|
|||||||
HRESULT result;
|
HRESULT result;
|
||||||
if (FAILED(result = voice->SubmitSourceBuffer(&buf)))
|
if (FAILED(result = voice->SubmitSourceBuffer(&buf)))
|
||||||
{
|
{
|
||||||
osd_printf_verbose("Sound: XAudio2 failed to submit source buffer (non-fatal). Error: 0x%X\n", static_cast<unsigned int>(result));
|
osd_printf_verbose("Sound: XAudio2 failed to submit source buffer (non-fatal). Error: 0x%X\n", result);
|
||||||
m_buffer_pool->return_to_pool(std::move(data));
|
m_buffer_pool->return_to_pool(std::move(data));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1152,10 +1167,7 @@ void sound_xaudio2::voice_info::OnVoiceError(void *pBufferContext, HRESULT error
|
|||||||
std::lock_guard device_lock(m_host.m_device_mutex);
|
std::lock_guard device_lock(m_host.m_device_mutex);
|
||||||
std::lock_guard voice_lock(m_host.m_voice_mutex);
|
std::lock_guard voice_lock(m_host.m_voice_mutex);
|
||||||
|
|
||||||
osd_printf_verbose(
|
osd_printf_verbose("Sound: XAudio2 voice error for stream %u. Error: 0x%X\n", info.m_id, error);
|
||||||
"Sound: XAudio2 voice error for stream %u. Error: 0x%X\n",
|
|
||||||
info.m_id,
|
|
||||||
static_cast<unsigned int>(error));
|
|
||||||
|
|
||||||
if (completed_buffer)
|
if (completed_buffer)
|
||||||
m_buffer_pool->return_to_pool(std::unique_ptr<BYTE []>(completed_buffer));
|
m_buffer_pool->return_to_pool(std::unique_ptr<BYTE []>(completed_buffer));
|
||||||
@ -1204,7 +1216,7 @@ HRESULT sound_xaudio2::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewSt
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Sound: Error getting audio device %s. Error: 0x%X\n",
|
"Sound: Error getting audio device %s. Error: 0x%X\n",
|
||||||
osd::text::from_wstring(device_id),
|
osd::text::from_wstring(device_id),
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1302,9 +1314,9 @@ HRESULT sound_xaudio2::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWST
|
|||||||
if ((eRender == flow) && (eMultimedia == role))
|
if ((eRender == flow) && (eMultimedia == role))
|
||||||
{
|
{
|
||||||
std::lock_guard device_lock(m_device_mutex);
|
std::lock_guard device_lock(m_device_mutex);
|
||||||
std::wstring_view device_id(pwstrDefaultDeviceId);
|
m_default_sink_id = pwstrDefaultDeviceId;
|
||||||
auto const pos = find_device(device_id);
|
auto const pos = find_device(m_default_sink_id);
|
||||||
if ((m_device_info.end() != pos) && ((*pos)->device_id == device_id) && ((*pos)->info.m_id != m_default_sink))
|
if ((m_device_info.end() != pos) && ((*pos)->device_id == m_default_sink_id) && ((*pos)->info.m_id != m_default_sink))
|
||||||
{
|
{
|
||||||
m_default_sink = (*pos)->info.m_id;
|
m_default_sink = (*pos)->info.m_id;
|
||||||
|
|
||||||
@ -1344,7 +1356,7 @@ HRESULT sound_xaudio2::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, PROPERTYKEY
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Sound: Error opening property store for audio device %s. Error: 0x%X\n",
|
"Sound: Error opening property store for audio device %s. Error: 0x%X\n",
|
||||||
(*pos)->info.m_name,
|
(*pos)->info.m_name,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1355,7 +1367,7 @@ HRESULT sound_xaudio2::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, PROPERTYKEY
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Sound: Error getting updated display name for audio device %s. Error: 0x%X\n",
|
"Sound: Error getting updated display name for audio device %s. Error: 0x%X\n",
|
||||||
(*pos)->info.m_name,
|
(*pos)->info.m_name,
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1432,7 +1444,7 @@ HRESULT sound_xaudio2::add_device(device_info_vector_iterator pos, LPCWSTR devic
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Sound: Error getting audio device %s. Error: 0x%X\n",
|
"Sound: Error getting audio device %s. Error: 0x%X\n",
|
||||||
osd::text::from_wstring(device_id),
|
osd::text::from_wstring(device_id),
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1444,7 +1456,7 @@ HRESULT sound_xaudio2::add_device(device_info_vector_iterator pos, LPCWSTR devic
|
|||||||
osd_printf_error(
|
osd_printf_error(
|
||||||
"Sound: Error getting audio device %s state. Error: 0x%X\n",
|
"Sound: Error getting audio device %s state. Error: 0x%X\n",
|
||||||
osd::text::from_wstring(device_id),
|
osd::text::from_wstring(device_id),
|
||||||
static_cast<unsigned int>(result));
|
result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if ((DEVICE_STATE_ACTIVE != state) && (DEVICE_STATE_UNPLUGGED != state))
|
if ((DEVICE_STATE_ACTIVE != state) && (DEVICE_STATE_UNPLUGGED != state))
|
||||||
@ -1476,7 +1488,7 @@ HRESULT sound_xaudio2::add_device(device_info_vector_iterator pos, std::wstring_
|
|||||||
devinfo->device_id = std::move(device_id_str);
|
devinfo->device_id = std::move(device_id_str);
|
||||||
devinfo->device = std::move(device);
|
devinfo->device = std::move(device);
|
||||||
devinfo->info = std::move(info);
|
devinfo->info = std::move(info);
|
||||||
if (!m_default_sink)
|
if (!m_default_sink || (devinfo->device_id == m_default_sink_id))
|
||||||
m_default_sink = devinfo->info.m_id;
|
m_default_sink = devinfo->info.m_id;
|
||||||
m_device_info.emplace(pos, std::move(devinfo));
|
m_device_info.emplace(pos, std::move(devinfo));
|
||||||
|
|
||||||
@ -1536,7 +1548,6 @@ void sound_xaudio2::purge_voices(uint32_t node)
|
|||||||
{
|
{
|
||||||
voice_info_ptr voice = std::move(*it);
|
voice_info_ptr voice = std::move(*it);
|
||||||
it = m_voice_info.erase(it);
|
it = m_voice_info.erase(it);
|
||||||
voice->stop();
|
|
||||||
{
|
{
|
||||||
std::lock_guard cleanup_lock(m_cleanup_mutex);
|
std::lock_guard cleanup_lock(m_cleanup_mutex);
|
||||||
m_zombie_voices.emplace_back(std::move(voice));
|
m_zombie_voices.emplace_back(std::move(voice));
|
||||||
@ -1552,7 +1563,7 @@ void sound_xaudio2::purge_voices(uint32_t node)
|
|||||||
void sound_xaudio2::audio_task()
|
void sound_xaudio2::audio_task()
|
||||||
{
|
{
|
||||||
bool exiting = FALSE;
|
bool exiting = FALSE;
|
||||||
HANDLE hEvents[] = { m_hEventNeedUpdate, m_hEventExiting };
|
HANDLE hEvents[] = { m_hEventNeedUpdate, m_hEventEngineError, m_hEventExiting };
|
||||||
while (!exiting)
|
while (!exiting)
|
||||||
{
|
{
|
||||||
DWORD wait_result = WaitForMultipleObjects(std::size(hEvents), hEvents, FALSE, INFINITE);
|
DWORD wait_result = WaitForMultipleObjects(std::size(hEvents), hEvents, FALSE, INFINITE);
|
||||||
@ -1565,7 +1576,62 @@ void sound_xaudio2::audio_task()
|
|||||||
voice->submit_if_needed();
|
voice->submit_if_needed();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1: // exiting
|
case 1: // engine error
|
||||||
|
{
|
||||||
|
std::lock_guard device_lock(m_device_mutex);
|
||||||
|
auto it = m_device_info.begin();
|
||||||
|
bool errors = false;
|
||||||
|
for (auto const &device : m_device_info)
|
||||||
|
{
|
||||||
|
if (!device->engine_error)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// clean up any voices associated with this device
|
||||||
|
purge_voices((*it)->info.m_id);
|
||||||
|
|
||||||
|
// tear down the XAudio2 engine - it will be recreated next time a stream is opened
|
||||||
|
device->engine->UnregisterForCallbacks(device.get());
|
||||||
|
x_audio_2_ptr old_engine = std::move(device->engine);
|
||||||
|
mastering_voice_ptr old_mastering_voice = std::move(device->mastering_voice);
|
||||||
|
device->mastering_voice.reset();
|
||||||
|
device->engine = nullptr;
|
||||||
|
device->engine_error = false;
|
||||||
|
|
||||||
|
// bump this so the sound manager recreates its streams
|
||||||
|
//device->info.m_id = m_next_node_id++; FIXME: causes crash in sound_manager::update_osd_streams()
|
||||||
|
|
||||||
|
// try to get another thread to do the cleanup
|
||||||
|
device_info_ptr zombie;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
zombie = std::make_unique<device_info>(*this);
|
||||||
|
zombie->engine = std::move(old_engine);
|
||||||
|
zombie->mastering_voice = std::move(old_mastering_voice);
|
||||||
|
old_mastering_voice.reset();
|
||||||
|
old_engine = nullptr;
|
||||||
|
{
|
||||||
|
std::lock_guard cleanup_lock(m_cleanup_mutex);
|
||||||
|
m_zombie_devices.emplace_back(std::move(zombie));
|
||||||
|
m_cleanup_condition.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::bad_alloc const &)
|
||||||
|
{
|
||||||
|
// make sure the cleanup thread wakes up
|
||||||
|
std::lock_guard cleanup_lock(m_cleanup_mutex);
|
||||||
|
m_cleanup_condition.notify_one();
|
||||||
|
|
||||||
|
// old engine and mastering voice will fall out of scope and be destroyed
|
||||||
|
// this could block things it shouldn't, but leaking would be worse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bump generation if we actually changed something
|
||||||
|
if (errors)
|
||||||
|
++m_generation;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: // exiting
|
||||||
exiting = true;
|
exiting = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user