diff --git a/Utilities/StrFmt.cpp b/Utilities/StrFmt.cpp index 689b71de7b..63eccf8883 100644 --- a/Utilities/StrFmt.cpp +++ b/Utilities/StrFmt.cpp @@ -16,7 +16,7 @@ #endif #ifdef _WIN32 -std::string wchar_to_utf8(wchar_t *src) +std::string wchar_to_utf8(const wchar_t *src) { std::string utf8_string; const auto tmp_size = WideCharToMultiByte(CP_UTF8, 0, src, -1, nullptr, 0, nullptr, nullptr); diff --git a/Utilities/StrUtil.h b/Utilities/StrUtil.h index 922b0fba16..f305fecf1d 100644 --- a/Utilities/StrUtil.h +++ b/Utilities/StrUtil.h @@ -7,7 +7,7 @@ #include #ifdef _WIN32 -std::string wchar_to_utf8(wchar_t *src); +std::string wchar_to_utf8(const wchar_t *src); std::string wchar_path_to_ansi_path(const std::wstring& src); std::string utf8_path_to_ansi_path(const std::string& src); #endif diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp index 2ecba851f5..defd8a3cc3 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp @@ -21,9 +21,14 @@ XAudio2Backend::XAudio2Backend() // In order to prevent errors on CreateMasteringVoice, apparently we need CoInitializeEx according to: // https://docs.microsoft.com/en-us/windows/win32/api/xaudio2fx/nf-xaudio2fx-xaudio2createvolumemeter - CoInitializeEx(nullptr, COINIT_MULTITHREADED); + HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) + { + XAudio.error("CoInitializeEx() failed: %s (0x%08x)", std::system_category().message(hr), static_cast(hr)); + return; + } - HRESULT hr = XAudio2Create(instance.GetAddressOf(), 0, XAUDIO2_DEFAULT_PROCESSOR); + hr = XAudio2Create(instance.GetAddressOf(), 0, XAUDIO2_DEFAULT_PROCESSOR); if (FAILED(hr)) { XAudio.error("XAudio2Create() failed: %s (0x%08x)", std::system_category().message(hr), static_cast(hr)); @@ -90,7 +95,7 @@ void XAudio2Backend::Pause() void XAudio2Backend::Open(u32 /* num_buffers */) { - WAVEFORMATEX waveformatex; + WAVEFORMATEX waveformatex{}; waveformatex.wFormatTag = m_convert_to_u16 ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT; waveformatex.nChannels = m_channels; waveformatex.nSamplesPerSec = m_sampling_rate; @@ -133,8 +138,7 @@ bool XAudio2Backend::AddData(const void* src, u32 num_samples) return false; } - XAUDIO2_BUFFER buffer; - + XAUDIO2_BUFFER buffer{}; buffer.AudioBytes = num_samples * m_sample_size; buffer.Flags = 0; buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION; diff --git a/rpcs3/Emu/Audio/audio_device_listener.cpp b/rpcs3/Emu/Audio/audio_device_listener.cpp new file mode 100644 index 0000000000..03f86d620c --- /dev/null +++ b/rpcs3/Emu/Audio/audio_device_listener.cpp @@ -0,0 +1,107 @@ +#include "stdafx.h" +#include "audio_device_listener.h" +#include "util/logs.hpp" +#include "Utilities/StrUtil.h" +#include "Emu/Cell/Modules/cellAudio.h" +#include "Emu/IdManager.h" + +LOG_CHANNEL(IO); + +audio_device_listener::audio_device_listener() +{ +#ifdef _WIN32 + // Try to register a listener for device changes + HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_device_enumerator)); + if (hr != S_OK) + { + IO.error("CoCreateInstance() failed: %s (0x%08x)", std::system_category().message(hr), static_cast(hr)); + } + else if (m_device_enumerator) + { + m_device_enumerator->RegisterEndpointNotificationCallback(&m_listener); + } + else + { + IO.error("Device enumerator invalid"); + } +#endif +} + +audio_device_listener::~audio_device_listener() +{ +#ifdef _WIN32 + if (m_device_enumerator != nullptr) + { + m_device_enumerator->Release(); + } +#endif +} + +#ifdef _WIN32 +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](auto value) + { + switch (value) + { + case eConsole: return "eConsole"; + case eMultimedia: return "eMultimedia"; + case eCommunications: return "eCommunications"; + } + + return unknown; + }); +} + +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](auto value) + { + switch (value) + { + case eRender: return "eRender"; + case eCapture: return "eCapture"; + case eAll: return "eAll"; + } + + return unknown; + }); +} + +HRESULT audio_device_listener::listener::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id) +{ + IO.notice("OnDefaultDeviceChanged(flow=%s, role=%s, new_default_device_id=0x%x)", flow, role, new_default_device_id); + + if (!new_default_device_id) + { + IO.notice("OnDefaultDeviceChanged(): new_default_device_id empty"); + return S_OK; + } + + // Only listen for console and communication device changes. + if ((role != eConsole && role != eCommunications) || (flow != eRender && flow != eCapture)) + { + IO.notice("OnDefaultDeviceChanged(): we don't care about this device"); + return S_OK; + } + + const std::wstring tmp(new_default_device_id); + const std::string new_device_id = wchar_to_utf8(tmp.c_str()); + + if (device_id != new_device_id) + { + device_id = new_device_id; + + IO.warning("Default device changed: new device = '%s'", device_id); + + if (auto& g_audio = g_fxo->get(); g_fxo->is_init()) + { + g_audio.m_update_configuration = true; + } + } + + return S_OK; +} +#endif diff --git a/rpcs3/Emu/Audio/audio_device_listener.h b/rpcs3/Emu/Audio/audio_device_listener.h new file mode 100644 index 0000000000..4d5e51fe01 --- /dev/null +++ b/rpcs3/Emu/Audio/audio_device_listener.h @@ -0,0 +1,31 @@ +#pragma once + +#ifdef _WIN32 +#include +#endif + +class audio_device_listener +{ +public: + audio_device_listener(); + ~audio_device_listener(); + +private: +#ifdef _WIN32 + struct listener : public IMMNotificationClient + { + std::string device_id; + + IFACEMETHODIMP_(ULONG) AddRef() override { return 1; }; + IFACEMETHODIMP_(ULONG) Release() override { return 1; }; + IFACEMETHODIMP QueryInterface(REFIID iid, void** object) override { return S_OK; }; + IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key) override { return S_OK; }; + IFACEMETHODIMP OnDeviceAdded(LPCWSTR device_id) override { return S_OK; }; + IFACEMETHODIMP OnDeviceRemoved(LPCWSTR device_id) override { return S_OK; }; + IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state) override { return S_OK; }; + IFACEMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id) override; + } m_listener; + + IMMDeviceEnumerator* m_device_enumerator = nullptr; +#endif +}; diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 457a7ccf75..38c8f29a53 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -106,6 +106,7 @@ target_sources(rpcs3_emu PRIVATE # Audio target_sources(rpcs3_emu PRIVATE + Audio/audio_device_listener.cpp Audio/AudioDumper.cpp Audio/AudioBackend.cpp Audio/AL/OpenALBackend.cpp diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.cpp b/rpcs3/Emu/Cell/Modules/cellAudio.cpp index a596e6db4f..670d40f41c 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAudio.cpp @@ -305,7 +305,6 @@ u64 audio_ringbuffer::update() //cellAudio.error("play_delta=%llu delta_samples=%llu", play_delta, delta_samples); if (delta_samples > 0) { - if (enqueued_samples < delta_samples) { enqueued_samples = 0; @@ -618,6 +617,7 @@ void cell_audio_thread::operator()() { if (m_update_configuration) { + cellAudio.warning("Updating cell_audio_thread configuration"); update_config(); m_update_configuration = false; } diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.h b/rpcs3/Emu/Cell/Modules/cellAudio.h index d18f3094af..c3636939d2 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.h +++ b/rpcs3/Emu/Cell/Modules/cellAudio.h @@ -3,6 +3,7 @@ #include "Emu/Memory/vm_ptr.h" #include "Utilities/Thread.h" #include "Emu/Memory/vm.h" +#include "Emu/Audio/audio_device_listener.h" #include "Emu/Audio/AudioBackend.h" #include "Emu/Audio/AudioDumper.h" #include "Emu/system_config_types.h" @@ -353,6 +354,7 @@ class cell_audio_thread { private: std::unique_ptr ringbuffer; + audio_device_listener listener; void reset_ports(s32 offset = 0); void advance(u64 timestamp, bool reset = true); diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index e45bd20c48..54c12d901b 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -53,6 +53,7 @@ true + true @@ -428,6 +429,7 @@ true + true diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index a5bef0104d..6f766a03b8 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -999,6 +999,9 @@ Emu\Cell\Modules + + Emu\Audio + @@ -1974,6 +1977,9 @@ Emu\Cell\Modules + + Emu\Audio +