mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 02:32:36 +01:00
Windows/Audio: add listener for device change
For some reason XAudio2 doesn't automatically change the device anymore. So let's just listen for the OnDefaultDeviceChanged event and update the cell audio thread if necessary.
This commit is contained in:
parent
4e139ee080
commit
72f0637efe
@ -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);
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <string_view>
|
||||
|
||||
#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
|
||||
|
@ -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<u32>(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<u32>(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;
|
||||
|
107
rpcs3/Emu/Audio/audio_device_listener.cpp
Normal file
107
rpcs3/Emu/Audio/audio_device_listener.cpp
Normal file
@ -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<u32>(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<ERole>::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<EDataFlow>::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<cell_audio>(); g_fxo->is_init<cell_audio>())
|
||||
{
|
||||
g_audio.m_update_configuration = true;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
#endif
|
31
rpcs3/Emu/Audio/audio_device_listener.h
Normal file
31
rpcs3/Emu/Audio/audio_device_listener.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <MMDeviceAPI.h>
|
||||
#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
|
||||
};
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<audio_ringbuffer> ringbuffer;
|
||||
audio_device_listener listener;
|
||||
|
||||
void reset_ports(s32 offset = 0);
|
||||
void advance(u64 timestamp, bool reset = true);
|
||||
|
@ -53,6 +53,7 @@
|
||||
<ClCompile Include="Emu\Audio\ALSA\ALSABackend.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Audio\audio_device_listener.cpp" />
|
||||
<ClCompile Include="Emu\Audio\FAudio\FAudioBackend.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
@ -428,6 +429,7 @@
|
||||
<ClInclude Include="Emu\Audio\ALSA\ALSABackend.h">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Audio\audio_device_listener.h" />
|
||||
<ClInclude Include="Emu\Audio\FAudio\FAudioBackend.h">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
|
@ -999,6 +999,9 @@
|
||||
<ClCompile Include="Emu\Cell\Modules\libfs_utility_init.cpp">
|
||||
<Filter>Emu\Cell\Modules</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Audio\audio_device_listener.cpp">
|
||||
<Filter>Emu\Audio</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crypto\aes.h">
|
||||
@ -1974,6 +1977,9 @@
|
||||
<ClInclude Include="Emu\Cell\Modules\libfs_utility_init.h">
|
||||
<Filter>Emu\Cell\Modules</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Audio\audio_device_listener.h">
|
||||
<Filter>Emu\Audio</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">
|
||||
|
Loading…
Reference in New Issue
Block a user