From 5159d3559e25c1defcb13d5035be2f363c0b11d5 Mon Sep 17 00:00:00 2001 From: Rui Pinheiro Date: Sun, 16 Dec 2018 17:40:50 +0000 Subject: [PATCH] Implement Audio Backend Capabilities querying Also renames "AudioThread" to "AudioBackend". The new name is more descriptive of what the class really is responsible for, since the backends are not responsible for managing the audio thread. NOTE: Right now only XAudio2 is supported --- rpcs3/Emu/Audio/AL/OpenALThread.h | 4 +- rpcs3/Emu/Audio/ALSA/ALSAThread.h | 2 +- .../Audio/{AudioThread.h => AudioBackend.h} | 26 +++- rpcs3/Emu/Audio/Null/NullAudioBackend.h | 24 ++++ rpcs3/Emu/Audio/Null/NullAudioThread.h | 20 --- rpcs3/Emu/Audio/Pulse/PulseThread.h | 2 +- ...XAudio27Thread.cpp => XAudio27Backend.cpp} | 46 +++--- ...XAudio28Thread.cpp => XAudio28Backend.cpp} | 46 +++--- .../{XAudio2Thread.cpp => XAudio2Backend.cpp} | 65 +++++---- .../{XAudio2Thread.h => XAudio2Backend.h} | 18 ++- rpcs3/Emu/Cell/Modules/cellAudio.cpp | 135 ++++++++++-------- rpcs3/Emu/Cell/Modules/cellAudio.h | 69 +++++---- rpcs3/Emu/Cell/Modules/libmixer.cpp | 2 +- rpcs3/Emu/System.h | 2 +- rpcs3/XAudio.vcxproj | 8 +- rpcs3/XAudio.vcxproj.filters | 8 +- rpcs3/emucore.vcxproj | 4 +- rpcs3/emucore.vcxproj.filters | 8 +- rpcs3/rpcs3_app.cpp | 10 +- rpcs3/rpcs3_app.h | 2 +- 20 files changed, 297 insertions(+), 204 deletions(-) rename rpcs3/Emu/Audio/{AudioThread.h => AudioBackend.h} (69%) create mode 100644 rpcs3/Emu/Audio/Null/NullAudioBackend.h delete mode 100644 rpcs3/Emu/Audio/Null/NullAudioThread.h rename rpcs3/Emu/Audio/XAudio2/{XAudio27Thread.cpp => XAudio27Backend.cpp} (69%) rename rpcs3/Emu/Audio/XAudio2/{XAudio28Thread.cpp => XAudio28Backend.cpp} (70%) rename rpcs3/Emu/Audio/XAudio2/{XAudio2Thread.cpp => XAudio2Backend.cpp} (75%) rename rpcs3/Emu/Audio/XAudio2/{XAudio2Thread.h => XAudio2Backend.h} (64%) diff --git a/rpcs3/Emu/Audio/AL/OpenALThread.h b/rpcs3/Emu/Audio/AL/OpenALThread.h index 86504d9e4a..b3868c2948 100644 --- a/rpcs3/Emu/Audio/AL/OpenALThread.h +++ b/rpcs3/Emu/Audio/AL/OpenALThread.h @@ -1,10 +1,10 @@ #pragma once -/*#include "Emu/Audio/AudioThread.h" +/*#include "Emu/Audio/AudioBackend.h" #include "3rdparty/OpenAL/include/alext.h" #include -class OpenALThread : public AudioThread +class OpenALThread : public AudioBackend { private: ALint m_format; diff --git a/rpcs3/Emu/Audio/ALSA/ALSAThread.h b/rpcs3/Emu/Audio/ALSA/ALSAThread.h index 6b729db2e5..14f4ce40d5 100644 --- a/rpcs3/Emu/Audio/ALSA/ALSAThread.h +++ b/rpcs3/Emu/Audio/ALSA/ALSAThread.h @@ -4,7 +4,7 @@ #include "Emu/Audio/AudioThread.h" -class ALSAThread : public AudioThread +class ALSAThread : public AudioBackend { public: ALSAThread(); diff --git a/rpcs3/Emu/Audio/AudioThread.h b/rpcs3/Emu/Audio/AudioBackend.h similarity index 69% rename from rpcs3/Emu/Audio/AudioThread.h rename to rpcs3/Emu/Audio/AudioBackend.h index 805ca86eba..ffca646b9a 100644 --- a/rpcs3/Emu/Audio/AudioThread.h +++ b/rpcs3/Emu/Audio/AudioBackend.h @@ -10,22 +10,42 @@ enum : u32 AUDIO_BUFFER_SAMPLES = 256 }; -class AudioThread +class AudioBackend { public: - virtual ~AudioThread() = default; + enum Capabilities : u32 + { + NON_BLOCKING = 0x1, + IS_PLAYING = 0x2, + GET_NUM_ENQUEUED_SAMPLES = 0x4, + }; + + virtual ~AudioBackend() = default; // Callbacks + virtual const char* GetName() const = 0; + virtual u32 GetCapabilities() const = 0; + virtual void Open() = 0; virtual void Close() = 0; virtual void Play() = 0; virtual void Pause() = 0; - virtual bool IsPlaying() = 0; + + virtual bool IsPlaying() + { + fmt::throw_exception("IsPlaying() not implemented"); + }; virtual bool AddData(const void* src, int size) = 0; virtual void Flush() = 0; + virtual u64 GetNumEnqueuedSamples() + { + fmt::throw_exception("GetNumEnqueuedSamples() not implemented"); + return 0; + } + // Helper methods static u32 get_sampling_rate() { diff --git a/rpcs3/Emu/Audio/Null/NullAudioBackend.h b/rpcs3/Emu/Audio/Null/NullAudioBackend.h new file mode 100644 index 0000000000..6ef96629ee --- /dev/null +++ b/rpcs3/Emu/Audio/Null/NullAudioBackend.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Emu/Audio/AudioBackend.h" + +class NullAudioBackend : public AudioBackend +{ +public: + NullAudioBackend() {} + virtual ~NullAudioBackend() {} + + virtual const char* GetName() const override { return "NullAudioBackend"; } + + static const u32 capabilities = NON_BLOCKING; + virtual u32 GetCapabilities() const override { return capabilities; }; + + virtual void Open() override {}; + virtual void Close() override {}; + + virtual void Play() override {}; + virtual void Pause() override {}; + + virtual bool AddData(const void* src, int size) override { return true; }; + virtual void Flush() override {}; +}; diff --git a/rpcs3/Emu/Audio/Null/NullAudioThread.h b/rpcs3/Emu/Audio/Null/NullAudioThread.h deleted file mode 100644 index a1ccf99d84..0000000000 --- a/rpcs3/Emu/Audio/Null/NullAudioThread.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "Emu/Audio/AudioThread.h" - -class NullAudioThread : public AudioThread -{ -public: - NullAudioThread() {} - virtual ~NullAudioThread() {} - - virtual void Open() {}; - virtual void Close() {}; - - virtual void Play() {}; - virtual void Pause() {}; - virtual bool IsPlaying() { return true; }; - - virtual bool AddData(const void* src, int size) { return true; }; - virtual void Flush() {}; -}; diff --git a/rpcs3/Emu/Audio/Pulse/PulseThread.h b/rpcs3/Emu/Audio/Pulse/PulseThread.h index 657722f622..94dda9e8e2 100644 --- a/rpcs3/Emu/Audio/Pulse/PulseThread.h +++ b/rpcs3/Emu/Audio/Pulse/PulseThread.h @@ -4,7 +4,7 @@ #include #include "Emu/Audio/AudioThread.h" -class PulseThread : public AudioThread +class PulseThread : public AudioBackend { public: PulseThread(); diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio27Thread.cpp b/rpcs3/Emu/Audio/XAudio2/XAudio27Backend.cpp similarity index 69% rename from rpcs3/Emu/Audio/XAudio2/XAudio27Thread.cpp rename to rpcs3/Emu/Audio/XAudio2/XAudio27Backend.cpp index 62a20245ba..fe8d822c76 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio27Thread.cpp +++ b/rpcs3/Emu/Audio/XAudio2/XAudio27Backend.cpp @@ -4,7 +4,7 @@ #include "Utilities/StrFmt.h" #include "Emu/System.h" -#include "XAudio2Thread.h" +#include "XAudio2Backend.h" #include "3rdparty/XAudio2_7/XAudio2.h" static thread_local HMODULE s_tls_xaudio2_lib{}; @@ -12,7 +12,7 @@ static thread_local IXAudio2* s_tls_xaudio2_instance{}; static thread_local IXAudio2MasteringVoice* s_tls_master_voice{}; static thread_local IXAudio2SourceVoice* s_tls_source_voice{}; -void XAudio2Thread::xa27_init(void* lib2_7) +void XAudio2Backend::xa27_init(void* lib2_7) { s_tls_xaudio2_lib = (HMODULE)lib2_7; @@ -21,7 +21,7 @@ void XAudio2Thread::xa27_init(void* lib2_7) hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : CoInitializeEx() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : CoInitializeEx() failed(0x%08x)", (u32)hr); Emu.Pause(); return; } @@ -29,7 +29,7 @@ void XAudio2Thread::xa27_init(void* lib2_7) hr = XAudio2Create(&s_tls_xaudio2_instance, 0, XAUDIO2_DEFAULT_PROCESSOR); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : XAudio2Create() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : XAudio2Create() failed(0x%08x)", (u32)hr); Emu.Pause(); return; } @@ -37,13 +37,13 @@ void XAudio2Thread::xa27_init(void* lib2_7) hr = s_tls_xaudio2_instance->CreateMasteringVoice(&s_tls_master_voice, g_cfg.audio.downmix_to_2ch ? 2 : 8, 48000); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : CreateMasteringVoice() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : CreateMasteringVoice() failed(0x%08x)", (u32)hr); s_tls_xaudio2_instance->Release(); Emu.Pause(); } } -void XAudio2Thread::xa27_destroy() +void XAudio2Backend::xa27_destroy() { if (s_tls_source_voice != nullptr) { @@ -67,37 +67,37 @@ void XAudio2Thread::xa27_destroy() FreeLibrary(s_tls_xaudio2_lib); } -void XAudio2Thread::xa27_play() +void XAudio2Backend::xa27_play() { HRESULT hr = s_tls_source_voice->Start(); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : Start() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : Start() failed(0x%08x)", (u32)hr); Emu.Pause(); } } -void XAudio2Thread::xa27_flush() +void XAudio2Backend::xa27_flush() { HRESULT hr = s_tls_source_voice->FlushSourceBuffers(); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : FlushSourceBuffers() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : FlushSourceBuffers() failed(0x%08x)", (u32)hr); Emu.Pause(); } } -void XAudio2Thread::xa27_stop() +void XAudio2Backend::xa27_stop() { HRESULT hr = s_tls_source_voice->Stop(); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : Stop() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : Stop() failed(0x%08x)", (u32)hr); Emu.Pause(); } } -bool XAudio2Thread::xa27_is_playing() +bool XAudio2Backend::xa27_is_playing() { XAUDIO2_VOICE_STATE state; s_tls_source_voice->GetState(&state); @@ -105,7 +105,7 @@ bool XAudio2Thread::xa27_is_playing() return state.BuffersQueued > 0 || state.pCurrentBufferContext != nullptr; } -void XAudio2Thread::xa27_open() +void XAudio2Backend::xa27_open() { HRESULT hr; @@ -125,7 +125,7 @@ void XAudio2Thread::xa27_open() hr = s_tls_xaudio2_instance->CreateSourceVoice(&s_tls_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : CreateSourceVoice() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : CreateSourceVoice() failed(0x%08x)", (u32)hr); Emu.Pause(); return; } @@ -133,7 +133,7 @@ void XAudio2Thread::xa27_open() s_tls_source_voice->SetVolume(channels == 2 ? 1.0f : 4.0f); } -bool XAudio2Thread::xa27_add(const void* src, int size) +bool XAudio2Backend::xa27_add(const void* src, int size) { XAUDIO2_VOICE_STATE state; s_tls_source_voice->GetState(&state); @@ -141,7 +141,7 @@ bool XAudio2Thread::xa27_add(const void* src, int size) // XAudio 2.7 bug workaround, when it says "SimpList: non-growable list ran out of room for new elements" and hits int 3 if (state.BuffersQueued >= MAX_AUDIO_BUFFERS) { - LOG_WARNING(GENERAL, "XAudio2Thread : too many buffers enqueued (%d, pos=%u)", state.BuffersQueued, state.SamplesPlayed); + LOG_WARNING(GENERAL, "XAudio2Backend : too many buffers enqueued (%d, pos=%u)", state.BuffersQueued, state.SamplesPlayed); return false; } @@ -160,11 +160,21 @@ bool XAudio2Thread::xa27_add(const void* src, int size) HRESULT hr = s_tls_source_voice->SubmitSourceBuffer(&buffer); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : AddData() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : AddData() failed(0x%08x)", (u32)hr); Emu.Pause(); + return false; } return true; } +u64 XAudio2Backend::xa27_enqueued_samples() +{ + XAUDIO2_VOICE_STATE state; + s_tls_source_voice->GetState(&state); + + // all buffers contain AUDIO_BUFFER_SAMPLES, so we can easily calculate how many samples there are remaining + return (AUDIO_BUFFER_SAMPLES - state.SamplesPlayed % AUDIO_BUFFER_SAMPLES) + (state.BuffersQueued * AUDIO_BUFFER_SAMPLES); +} + #endif diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio28Thread.cpp b/rpcs3/Emu/Audio/XAudio2/XAudio28Backend.cpp similarity index 70% rename from rpcs3/Emu/Audio/XAudio2/XAudio28Thread.cpp rename to rpcs3/Emu/Audio/XAudio2/XAudio28Backend.cpp index d6953fc818..add61ee7a1 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio28Thread.cpp +++ b/rpcs3/Emu/Audio/XAudio2/XAudio28Backend.cpp @@ -4,7 +4,7 @@ #include "Utilities/StrFmt.h" #include "Emu/System.h" -#include "XAudio2Thread.h" +#include "XAudio2Backend.h" #include "3rdparty/minidx12/Include/xaudio2.h" static thread_local HMODULE s_tls_xaudio2_lib{}; @@ -12,7 +12,7 @@ static thread_local IXAudio2* s_tls_xaudio2_instance{}; static thread_local IXAudio2MasteringVoice* s_tls_master_voice{}; static thread_local IXAudio2SourceVoice* s_tls_source_voice{}; -void XAudio2Thread::xa28_init(void* lib) +void XAudio2Backend::xa28_init(void* lib) { s_tls_xaudio2_lib = (HMODULE)lib; @@ -23,7 +23,7 @@ void XAudio2Thread::xa28_init(void* lib) hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : CoInitializeEx() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : CoInitializeEx() failed(0x%08x)", (u32)hr); Emu.Pause(); return; } @@ -31,7 +31,7 @@ void XAudio2Thread::xa28_init(void* lib) hr = create(&s_tls_xaudio2_instance, 0, XAUDIO2_DEFAULT_PROCESSOR); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : XAudio2Create() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : XAudio2Create() failed(0x%08x)", (u32)hr); Emu.Pause(); return; } @@ -39,13 +39,13 @@ void XAudio2Thread::xa28_init(void* lib) hr = s_tls_xaudio2_instance->CreateMasteringVoice(&s_tls_master_voice, g_cfg.audio.downmix_to_2ch ? 2 : 8, 48000); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : CreateMasteringVoice() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : CreateMasteringVoice() failed(0x%08x)", (u32)hr); s_tls_xaudio2_instance->Release(); Emu.Pause(); } } -void XAudio2Thread::xa28_destroy() +void XAudio2Backend::xa28_destroy() { if (s_tls_source_voice != nullptr) { @@ -69,43 +69,43 @@ void XAudio2Thread::xa28_destroy() FreeLibrary(s_tls_xaudio2_lib); } -void XAudio2Thread::xa28_play() +void XAudio2Backend::xa28_play() { AUDIT(s_tls_source_voice != nullptr); HRESULT hr = s_tls_source_voice->Start(); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : Start() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : Start() failed(0x%08x)", (u32)hr); Emu.Pause(); } } -void XAudio2Thread::xa28_flush() +void XAudio2Backend::xa28_flush() { AUDIT(s_tls_source_voice != nullptr); HRESULT hr = s_tls_source_voice->FlushSourceBuffers(); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : FlushSourceBuffers() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : FlushSourceBuffers() failed(0x%08x)", (u32)hr); Emu.Pause(); } } -void XAudio2Thread::xa28_stop() +void XAudio2Backend::xa28_stop() { AUDIT(s_tls_source_voice != nullptr); HRESULT hr = s_tls_source_voice->Stop(); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : Stop() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : Stop() failed(0x%08x)", (u32)hr); Emu.Pause(); } } -bool XAudio2Thread::xa28_is_playing() +bool XAudio2Backend::xa28_is_playing() { AUDIT(s_tls_source_voice != nullptr); @@ -115,7 +115,7 @@ bool XAudio2Thread::xa28_is_playing() return state.BuffersQueued > 0 || state.pCurrentBufferContext != nullptr; } -void XAudio2Thread::xa28_open() +void XAudio2Backend::xa28_open() { HRESULT hr; @@ -135,7 +135,7 @@ void XAudio2Thread::xa28_open() hr = s_tls_xaudio2_instance->CreateSourceVoice(&s_tls_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : CreateSourceVoice() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : CreateSourceVoice() failed(0x%08x)", (u32)hr); Emu.Pause(); return; } @@ -144,7 +144,7 @@ void XAudio2Thread::xa28_open() s_tls_source_voice->SetVolume(channels == 2 ? 1.0 : 4.0); } -bool XAudio2Thread::xa28_add(const void* src, int size) +bool XAudio2Backend::xa28_add(const void* src, int size) { AUDIT(s_tls_source_voice != nullptr); @@ -153,7 +153,7 @@ bool XAudio2Thread::xa28_add(const void* src, int size) if (state.BuffersQueued >= MAX_AUDIO_BUFFERS) { - LOG_WARNING(GENERAL, "XAudio2Thread : too many buffers enqueued (%d, pos=%u)", state.BuffersQueued, state.SamplesPlayed); + LOG_WARNING(GENERAL, "XAudio2Backend : too many buffers enqueued (%d, pos=%u)", state.BuffersQueued, state.SamplesPlayed); return false; } @@ -172,11 +172,21 @@ bool XAudio2Thread::xa28_add(const void* src, int size) HRESULT hr = s_tls_source_voice->SubmitSourceBuffer(&buffer); if (FAILED(hr)) { - LOG_ERROR(GENERAL, "XAudio2Thread : AddData() failed(0x%08x)", (u32)hr); + LOG_ERROR(GENERAL, "XAudio2Backend : AddData() failed(0x%08x)", (u32)hr); Emu.Pause(); + return false; } return true; } +u64 XAudio2Backend::xa28_enqueued_samples() +{ + XAUDIO2_VOICE_STATE state; + s_tls_source_voice->GetState(&state); + + // all buffers contain AUDIO_BUFFER_SAMPLES, so we can easily calculate how many samples there are remaining + return (AUDIO_BUFFER_SAMPLES - state.SamplesPlayed % AUDIO_BUFFER_SAMPLES) + (state.BuffersQueued * AUDIO_BUFFER_SAMPLES); +} + #endif diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio2Thread.cpp b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp similarity index 75% rename from rpcs3/Emu/Audio/XAudio2/XAudio2Thread.cpp rename to rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp index 5bd13d4d3c..8af0304ac7 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Thread.cpp +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp @@ -3,16 +3,11 @@ #include "Utilities/Log.h" #include "Utilities/StrFmt.h" -#include "XAudio2Thread.h" +#include "XAudio2Backend.h" #include -XAudio2Thread::XAudio2Thread() +XAudio2Backend::XAudio2Backend() { - if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) - { - LOG_ERROR(GENERAL, "XAudio: failed to increase thread priority"); - } - if (auto lib2_9 = LoadLibraryExW(L"XAudio2_9.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32)) { // xa28* implementation is fully compatible with library 2.9 @@ -25,11 +20,29 @@ XAudio2Thread::XAudio2Thread() m_funcs.open = &xa28_open; m_funcs.is_playing = &xa28_is_playing; m_funcs.add = &xa28_add; + m_funcs.enqueued_samples = &xa28_enqueued_samples; LOG_SUCCESS(GENERAL, "XAudio 2.9 initialized"); return; } + if (auto lib2_8 = LoadLibraryExW(L"XAudio2_8.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32)) + { + xa28_init(lib2_8); + + m_funcs.destroy = &xa28_destroy; + m_funcs.play = &xa28_play; + m_funcs.flush = &xa28_flush; + m_funcs.stop = &xa28_stop; + m_funcs.open = &xa28_open; + m_funcs.is_playing = &xa28_is_playing; + m_funcs.add = &xa28_add; + m_funcs.enqueued_samples = &xa28_enqueued_samples; + + LOG_SUCCESS(GENERAL, "XAudio 2.8 initialized"); + return; + } + if (auto lib2_7 = LoadLibraryExW(L"XAudio2_7.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32)) { xa27_init(lib2_7); @@ -41,69 +54,59 @@ XAudio2Thread::XAudio2Thread() m_funcs.open = &xa27_open; m_funcs.is_playing = &xa27_is_playing; m_funcs.add = &xa27_add; + m_funcs.enqueued_samples = &xa27_enqueued_samples; LOG_SUCCESS(GENERAL, "XAudio 2.7 initialized"); return; } - - if (auto lib2_8 = LoadLibraryExW(L"XAudio2_8.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32)) - { - xa28_init(lib2_8); - - m_funcs.destroy = &xa28_destroy; - m_funcs.play = &xa28_play; - m_funcs.flush = &xa28_flush; - m_funcs.stop = &xa28_stop; - m_funcs.open = &xa28_open; - m_funcs.is_playing = &xa28_is_playing; - m_funcs.add = &xa28_add; - - LOG_SUCCESS(GENERAL, "XAudio 2.8 initialized"); - return; - } fmt::throw_exception("No supported XAudio2 library found"); } -XAudio2Thread::~XAudio2Thread() +XAudio2Backend::~XAudio2Backend() { m_funcs.destroy(); } -void XAudio2Thread::Play() +void XAudio2Backend::Play() { m_funcs.play(); } -void XAudio2Thread::Close() +void XAudio2Backend::Close() { m_funcs.stop(); m_funcs.flush(); } -void XAudio2Thread::Pause() +void XAudio2Backend::Pause() { m_funcs.stop(); } -void XAudio2Thread::Open() +void XAudio2Backend::Open() { m_funcs.open(); } -bool XAudio2Thread::IsPlaying() +bool XAudio2Backend::IsPlaying() { return m_funcs.is_playing(); } -bool XAudio2Thread::AddData(const void* src, int size) +bool XAudio2Backend::AddData(const void* src, int size) { return m_funcs.add(src, size); } -void XAudio2Thread::Flush() +void XAudio2Backend::Flush() { m_funcs.flush(); } +u64 XAudio2Backend::GetNumEnqueuedSamples() +{ + return m_funcs.enqueued_samples(); +} + #endif diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio2Thread.h b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h similarity index 64% rename from rpcs3/Emu/Audio/XAudio2/XAudio2Thread.h rename to rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h index 9a77d442f2..d79c6fbda6 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Thread.h +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h @@ -2,9 +2,9 @@ #ifdef _WIN32 -#include "Emu/Audio/AudioThread.h" +#include "Emu/Audio/AudioBackend.h" -class XAudio2Thread : public AudioThread +class XAudio2Backend : public AudioBackend { struct vtable { @@ -15,6 +15,7 @@ class XAudio2Thread : public AudioThread void(*open)(); bool(*is_playing)(); bool(*add)(const void*, int); + u64(*enqueued_samples)(); }; vtable m_funcs; @@ -27,6 +28,7 @@ class XAudio2Thread : public AudioThread static void xa27_open(); static bool xa27_is_playing(); static bool xa27_add(const void*, int); + static u64 xa27_enqueued_samples(); static void xa28_init(void*); static void xa28_destroy(); @@ -36,10 +38,16 @@ class XAudio2Thread : public AudioThread static void xa28_open(); static bool xa28_is_playing(); static bool xa28_add(const void*, int); + static u64 xa28_enqueued_samples(); public: - XAudio2Thread(); - virtual ~XAudio2Thread() override; + XAudio2Backend(); + virtual ~XAudio2Backend() override; + + virtual const char* GetName() const override { return "XAudio2Backend"; }; + + static const u32 capabilities = NON_BLOCKING | IS_PLAYING | GET_NUM_ENQUEUED_SAMPLES; + virtual u32 GetCapabilities() const override { return capabilities; }; virtual void Open() override; virtual void Close() override; @@ -50,6 +58,8 @@ public: virtual bool AddData(const void* src, int size) override; virtual void Flush() override; + + virtual u64 GetNumEnqueuedSamples() override; }; #endif diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.cpp b/rpcs3/Emu/Cell/Modules/cellAudio.cpp index 21ad34278b..6db3b2a593 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAudio.cpp @@ -37,21 +37,19 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } -audio_ringbuffer::audio_ringbuffer(u32 num_buffers, u32 audio_sampling_rate, u32 channels) - : num_allocated_buffers(num_buffers) - , audio_sampling_rate(audio_sampling_rate) +audio_ringbuffer::audio_ringbuffer(cell_audio_config& _cfg) + : cfg(_cfg) , backend(Emu.GetCallbacks().get_audio()) - , channels(channels) - , buf_sz(AUDIO_BUFFER_SAMPLES * channels) + , buf_sz(AUDIO_BUFFER_SAMPLES * cfg.audio_channels) , emu_paused(Emu.IsPaused()) { // Initialize buffers - if (num_allocated_buffers >= MAX_AUDIO_BUFFERS) + if (cfg.num_allocated_buffers >= MAX_AUDIO_BUFFERS) { fmt::throw_exception("MAX_AUDIO_BUFFERS is too small"); } - for (u32 i = 0; i < num_allocated_buffers; i++) + for (u32 i = 0; i < cfg.num_allocated_buffers; i++) { buffer[i].reset(new float[buf_sz]{}); } @@ -59,37 +57,53 @@ audio_ringbuffer::audio_ringbuffer(u32 num_buffers, u32 audio_sampling_rate, u32 // Init audio dumper if enabled if (g_cfg.audio.dump_to_file) { - m_dump.reset(new AudioDumper(channels)); + m_dump.reset(new AudioDumper(cfg.audio_channels)); + } + + // Sanity check configuration vs. capabilities + backend_capabilities = backend->GetCapabilities(); + if (cfg.buffering_enabled) + { + if (!(backend_capabilities & AudioBackend::NON_BLOCKING) || !(backend_capabilities & AudioBackend::IS_PLAYING)) + { + // We need a non-blocking backend to be able to do buffering correctly + fmt::throw_exception("Audio backend %s does not support buffering.", backend->GetName()); + } } // Initialize backend backend->Open(); backend_open = true; - ASSERT(!backend->IsPlaying()); + + ASSERT(!backend_is_playing()); } audio_ringbuffer::~audio_ringbuffer() { if (!backend_open) + { return; + } - if (backend->IsPlaying()) + if (backend_is_playing()) + { backend->Pause(); + } backend->Close(); } void audio_ringbuffer::enqueue(const float* in_buffer) { - AUDIT(next_buf < num_allocated_buffers); + AUDIT(cur_pos < cfg.num_allocated_buffers); // Prepare buffer const void* buf = in_buffer; if (buf == nullptr) { - buf = buffer[next_buf].get(); - next_buf = (next_buf + 1) % num_allocated_buffers; + buf = buffer[cur_pos].get(); + cur_pos = (cur_pos + 1) % cfg.num_allocated_buffers; } // Dump audio if enabled @@ -165,35 +179,42 @@ u64 audio_ringbuffer::update() } const u64 timestamp = get_timestamp(); - const bool new_playing = !emu_paused && backend->IsPlaying(); + const bool new_playing = !emu_paused && backend_is_playing(); // Calculate how many audio samples have played since last time - // TODO: Natively query backend for remaining samples - if (playing || new_playing) + if (cfg.buffering_enabled && (playing || new_playing)) { - const u64 play_delta = timestamp - (play_timestamp > update_timestamp ? play_timestamp : update_timestamp); - - // NOTE: Only works with a fixed sampling rate - const u64 delta_samples_tmp = (play_delta * audio_sampling_rate) + last_remainder; - last_remainder = delta_samples_tmp % 1'000'000; - const u64 delta_samples = delta_samples_tmp / 1'000'000; - - //cellAudio.error("play_delta=%llu delta_samples=%llu", play_delta, delta_samples); - if (delta_samples > 0) + if (backend_capabilities & AudioBackend::GET_NUM_ENQUEUED_SAMPLES) { + // Backend supports querying for the remaining playtime, so just ask it + enqueued_samples = backend->GetNumEnqueuedSamples(); + } + else + { + const u64 play_delta = timestamp - (play_timestamp > update_timestamp ? play_timestamp : update_timestamp); - if (enqueued_samples < delta_samples) - { - enqueued_samples = 0; - } - else - { - enqueued_samples -= delta_samples; - } + // NOTE: Only works with a fixed sampling rate + const u64 delta_samples_tmp = (play_delta * cfg.audio_sampling_rate) + last_remainder; + last_remainder = delta_samples_tmp % 1'000'000; + const u64 delta_samples = delta_samples_tmp / 1'000'000; - if (enqueued_samples == 0) + //cellAudio.error("play_delta=%llu delta_samples=%llu", play_delta, delta_samples); + if (delta_samples > 0) { - cellAudio.warning("Audio buffer about to underrun!"); + + if (enqueued_samples < delta_samples) + { + enqueued_samples = 0; + } + else + { + enqueued_samples -= delta_samples; + } + + if (enqueued_samples == 0) + { + cellAudio.warning("Audio buffer about to underrun!"); + } } } } @@ -261,7 +282,7 @@ void audio_port::apply_tag_backups(s32 offset) std::tuple cell_audio_thread::count_port_buffer_tags() { - AUDIT(buffering_enabled); + AUDIT(cfg.buffering_enabled); u32 active = 0; u32 in_progress = 0; @@ -339,7 +360,7 @@ void cell_audio_thread::reset_ports(s32 offset) memset(port.get_vm_ptr(offset), 0, port.block_size() * sizeof(float)); - if (buffering_enabled) + if (cfg.buffering_enabled) { //port.reset_tag_backups(offset); port.tag(offset); @@ -398,7 +419,7 @@ void cell_audio_thread::operator()() thread_ctrl::set_native_priority(1); // Allocate ringbuffer - ringbuffer.reset(new audio_ringbuffer(num_allocated_buffers, audio_sampling_rate, audio_channels)); + ringbuffer.reset(new audio_ringbuffer(cfg)); // Initialize loop variables m_counter = 0; @@ -425,12 +446,12 @@ void cell_audio_thread::operator()() const u64 time_since_last_period = timestamp - m_last_period_end; const bool playing = !ringbuffer->is_playing(); - if (!buffering_enabled) + if (!cfg.buffering_enabled) { - const u64 period_end = (m_counter * audio_block_period) + m_start_time; + const u64 period_end = (m_counter * cfg.audio_block_period) + m_start_time; const s64 time_left = period_end - timestamp; - if (time_left > period_comparison_margin) + if (time_left > cfg.period_comparison_margin) { thread_ctrl::wait_for(get_thread_wait_delay(time_left)); continue; @@ -438,8 +459,8 @@ void cell_audio_thread::operator()() } else { - const u64 enqueued_playtime = ringbuffer->get_enqueued_samples() * 1'000'000 / audio_sampling_rate; - const u64 enqueued_buffers = (enqueued_playtime) / audio_block_period; + const u64 enqueued_playtime = ringbuffer->get_enqueued_samples() * 1'000'000 / cfg.audio_sampling_rate; + const u64 enqueued_buffers = (enqueued_playtime) / cfg.audio_block_period; const bool playing = ringbuffer->is_playing(); @@ -455,32 +476,32 @@ void cell_audio_thread::operator()() if (!playing) { // When the buffer is empty, always use the correct block period - m_dynamic_period = audio_block_period; + m_dynamic_period = cfg.audio_block_period; } else { // 1.0 means exactly as desired // <1.0 means not as full as desired // >1.0 means more full than desired - const f32 desired_duration_rate = (enqueued_playtime) / static_cast(desired_buffer_duration); + const f32 desired_duration_rate = (enqueued_playtime) / static_cast(cfg.desired_buffer_duration); if (desired_duration_rate >= 1.0f) { // more full than desired const f32 multiplier = 1.0f / desired_duration_rate; - m_dynamic_period = maximum_block_period - static_cast((maximum_block_period - audio_block_period) * multiplier); + m_dynamic_period = cfg.maximum_block_period - static_cast((cfg.maximum_block_period - cfg.audio_block_period) * multiplier); } else { // not as full as desired const f32 multiplier = desired_duration_rate; - m_dynamic_period = minimum_block_period + static_cast((audio_block_period - minimum_block_period) * multiplier); + m_dynamic_period = cfg.minimum_block_period + static_cast((cfg.audio_block_period - cfg.minimum_block_period) * multiplier); } } } s64 time_left = m_dynamic_period - time_since_last_period; - if (time_left > period_comparison_margin) + if (time_left > cfg.period_comparison_margin) { thread_ctrl::wait_for(get_thread_wait_delay(time_left)); continue; @@ -549,33 +570,33 @@ void cell_audio_thread::operator()() { // We are not playing (likely buffer underrun) // align to 5.(3)ms on global clock - const s64 audio_period_alignment_delta = (timestamp - m_start_time) % audio_block_period; - if (audio_period_alignment_delta > period_comparison_margin) + const s64 audio_period_alignment_delta = (timestamp - m_start_time) % cfg.audio_block_period; + if (audio_period_alignment_delta > cfg.period_comparison_margin) { - thread_ctrl::wait_for(audio_period_alignment_delta - period_comparison_margin); + thread_ctrl::wait_for(audio_period_alignment_delta - cfg.period_comparison_margin); } // Flush, add silence, restart algorithm cellAudio.error("play/resume audio: received first audio buffer"); ringbuffer->flush(); - ringbuffer->enqueue_silence(desired_full_buffers); + ringbuffer->enqueue_silence(cfg.desired_full_buffers); finish_port_volume_stepping(); } } // Mix float *buf = ringbuffer->get_current_buffer(); - if (audio_channels == 2) + if (cfg.audio_channels == 2) { mix(buf); } - else if (audio_channels == 8) + else if (cfg.audio_channels == 8) { mix(buf); } else { - fmt::throw_exception("Unsupported number of audio channels: %u", audio_channels); + fmt::throw_exception("Unsupported number of audio channels: %u", cfg.audio_channels); } // Enqueue @@ -604,7 +625,7 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset) { if (port.state != audio_port_state::started) continue; - if (buffering_enabled) + if (cfg.buffering_enabled) { port.apply_tag_backups(offset); } @@ -1094,7 +1115,7 @@ error_code cellAudioGetPortTimestamp(u32 portNum, u64 tag, vm::ptr stamp) } u64 delta_tag = port.global_counter - tag; - u64 delta_tag_stamp = delta_tag * g_audio->audio_block_period; + u64 delta_tag_stamp = delta_tag * g_audio->cfg.audio_block_period; *stamp = port.timestamp - delta_tag_stamp; return CELL_OK; diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.h b/rpcs3/Emu/Cell/Modules/cellAudio.h index 25f14eb982..1288509714 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.h +++ b/rpcs3/Emu/Cell/Modules/cellAudio.h @@ -2,7 +2,7 @@ #include "Utilities/Thread.h" #include "Emu/Memory/vm.h" -#include "Emu/Audio/AudioThread.h" +#include "Emu/Audio/AudioBackend.h" #include "Emu/Audio/AudioDumper.h" // Error codes @@ -170,15 +170,34 @@ struct audio_port void apply_tag_backups(s32 offset = 0); }; +struct cell_audio_config +{ + const s64 period_comparison_margin = 100; // When comparing the current period time with the desired period, if it is below this number of usecs we do not wait any longer + + const u32 audio_channels = AudioBackend::get_channels(); + const u32 audio_sampling_rate = AudioBackend::get_sampling_rate(); + const u64 audio_block_period = AUDIO_BUFFER_SAMPLES * 1000000 / audio_sampling_rate; + const u64 desired_buffer_duration = g_cfg.audio.enable_buffering ? g_cfg.audio.desired_buffer_duration : 0; + + const u32 audio_buffer_length = AUDIO_BUFFER_SAMPLES * audio_channels; + const u32 audio_buffer_size = audio_buffer_length * sizeof(f32); + const bool buffering_enabled = g_cfg.audio.enable_buffering && (desired_buffer_duration >= audio_block_period); + + const u64 minimum_block_period = audio_block_period / 2; // the block period will not be dynamically lowered below this value (usecs) + const u64 maximum_block_period = audio_block_period + (audio_block_period - minimum_block_period); // the block period will not be dynamically increased above this value (usecs) + + const u32 desired_full_buffers = buffering_enabled ? static_cast(desired_buffer_duration / audio_block_period) + 1 : 1; + const u32 num_allocated_buffers = desired_full_buffers + EXTRA_AUDIO_BUFFERS; // number of ringbuffer buffers +}; + class audio_ringbuffer { private: - const std::shared_ptr backend; + const std::shared_ptr backend; + + const cell_audio_config& cfg; - const u32 num_allocated_buffers; const u32 buf_sz; - const u32 audio_sampling_rate; - const u32 channels; std::unique_ptr m_dump; @@ -189,16 +208,23 @@ private: bool playing = false; bool emu_paused = false; + u32 backend_capabilities; + u64 update_timestamp = 0; u64 play_timestamp = 0; u64 last_remainder = 0; u64 enqueued_samples = 0; - u32 next_buf = 0; + u32 cur_pos = 0; + + bool backend_is_playing() const + { + return (backend_capabilities & AudioBackend::IS_PLAYING) ? backend->IsPlaying() : playing; + } public: - audio_ringbuffer(u32 num_buffers, u32 audio_sampling_rate, u32 channels); + audio_ringbuffer(cell_audio_config &cfg); ~audio_ringbuffer(); void play(); @@ -209,16 +235,11 @@ public: float* get_buffer(u32 num) const { - AUDIT(num < num_allocated_buffers); + AUDIT(num < cfg.num_allocated_buffers); AUDIT(buffer[num].get() != nullptr); return buffer[num].get(); } - u32 get_buf_sz() const - { - return buf_sz; - } - u64 get_timestamp() const { return get_system_time() - Emu.GetPauseTime(); @@ -226,11 +247,12 @@ public: float* get_current_buffer() const { - return get_buffer(next_buf); + return get_buffer(cur_pos); } u64 get_enqueued_samples() const { + AUDIT(cfg.buffering_enabled); return enqueued_samples; } @@ -238,6 +260,11 @@ public: { return playing; } + + u32 capabilities() const + { + return backend_capabilities; + } }; @@ -260,19 +287,7 @@ class cell_audio_thread } public: - const s64 period_comparison_margin = 100; // When comparing the current period time with the desired period, if it is below this number of usecs we do not wait any longer - - const u32 audio_channels = AudioThread::get_channels(); - const u32 audio_sampling_rate = AudioThread::get_sampling_rate(); - const u64 audio_block_period = AUDIO_BUFFER_SAMPLES * 1000000 / audio_sampling_rate; - const u64 desired_buffer_duration = g_cfg.audio.enable_buffering ? g_cfg.audio.desired_buffer_duration : 0; - const bool buffering_enabled = g_cfg.audio.enable_buffering && (desired_buffer_duration >= audio_block_period); - - const u64 minimum_block_period = audio_block_period / 2; // the block period will not be dynamically lowered below this value (usecs) - const u64 maximum_block_period = audio_block_period + (audio_block_period - minimum_block_period); // the block period will not be dynamically increased above this value (usecs) - - const u32 desired_full_buffers = buffering_enabled ? static_cast(desired_buffer_duration / audio_block_period) + 1 : 1; - const u32 num_allocated_buffers = desired_full_buffers + EXTRA_AUDIO_BUFFERS; // number of ringbuffer buffers + cell_audio_config cfg; std::vector keys; std::array ports; diff --git a/rpcs3/Emu/Cell/Modules/libmixer.cpp b/rpcs3/Emu/Cell/Modules/libmixer.cpp index f07053ffe5..61f8fa06c7 100644 --- a/rpcs3/Emu/Cell/Modules/libmixer.cpp +++ b/rpcs3/Emu/Cell/Modules/libmixer.cpp @@ -633,7 +633,7 @@ s32 cellSurMixerGetTimestamp(u64 tag, vm::ptr stamp) const auto g_audio = fxm::get(); - *stamp = g_audio->m_start_time + tag * AUDIO_BUFFER_SAMPLES * 1'000'000 / g_audio->audio_sampling_rate; + *stamp = g_audio->m_start_time + tag * AUDIO_BUFFER_SAMPLES * 1'000'000 / g_audio->cfg.audio_sampling_rate; return CELL_OK; } diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index b59767772d..c59e1c4602 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -196,7 +196,7 @@ struct EmuCallbacks std::function()> get_pad_handler; std::function()> get_gs_frame; std::function()> get_gs_render; - std::function()> get_audio; + std::function()> get_audio; std::function()> get_msg_dialog; std::function()> get_osk_dialog; std::function()> get_save_dialog; diff --git a/rpcs3/XAudio.vcxproj b/rpcs3/XAudio.vcxproj index 921afab18f..ea1ab488a1 100644 --- a/rpcs3/XAudio.vcxproj +++ b/rpcs3/XAudio.vcxproj @@ -72,12 +72,12 @@ - + - - - + + + diff --git a/rpcs3/XAudio.vcxproj.filters b/rpcs3/XAudio.vcxproj.filters index 84d38c3e6a..f882c3bc6c 100644 --- a/rpcs3/XAudio.vcxproj.filters +++ b/rpcs3/XAudio.vcxproj.filters @@ -7,18 +7,18 @@ - + Source Files - + Source Files - + Source Files - + Source Files diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index dd25ea47d1..5e58a1a0bd 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -406,8 +406,8 @@ - - + + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 3a06f8ffb5..d64cefec91 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -925,9 +925,6 @@ Utilities - - Emu\Audio\Null - Utilities @@ -1456,8 +1453,11 @@ Emu\GPU\RSX\Common - + Emu\Audio + + Emu\Audio\Null + \ No newline at end of file diff --git a/rpcs3/rpcs3_app.cpp b/rpcs3/rpcs3_app.cpp index 6076bc089b..346e191803 100644 --- a/rpcs3/rpcs3_app.cpp +++ b/rpcs3/rpcs3_app.cpp @@ -36,7 +36,7 @@ #include "Emu/RSX/Null/NullGSRender.h" #include "Emu/RSX/GL/GLGSRender.h" -#include "Emu/Audio/Null/NullAudioThread.h" +#include "Emu/Audio/Null/NullAudioBackend.h" //#include "Emu/Audio/AL/OpenALThread.h" #ifdef _MSC_VER #include "Emu/RSX/D3D12/D3D12GSRender.h" @@ -45,7 +45,7 @@ #include "Emu/RSX/VK/VKGSRender.h" #endif #ifdef _WIN32 -#include "Emu/Audio/XAudio2/XAudio2Thread.h" +#include "Emu/Audio/XAudio2/XAudio2Backend.h" #endif #ifdef HAVE_ALSA #include "Emu/Audio/ALSA/ALSAThread.h" @@ -254,13 +254,13 @@ void rpcs3_app::InitializeCallbacks() } }; - callbacks.get_audio = []() -> std::shared_ptr + callbacks.get_audio = []() -> std::shared_ptr { switch (audio_renderer type = g_cfg.audio.renderer) { - case audio_renderer::null: return std::make_shared(); + case audio_renderer::null: return std::make_shared(); #ifdef _WIN32 - case audio_renderer::xaudio: return std::make_shared(); + case audio_renderer::xaudio: return std::make_shared(); #endif #ifdef HAVE_ALSA case audio_renderer::alsa: return std::make_shared(); diff --git a/rpcs3/rpcs3_app.h b/rpcs3/rpcs3_app.h index 70d4e17f79..7605404679 100644 --- a/rpcs3/rpcs3_app.h +++ b/rpcs3/rpcs3_app.h @@ -12,7 +12,7 @@ #include "Emu/Io/KeyboardHandler.h" #include "Emu/Io/PadHandler.h" #include "Emu/Io/MouseHandler.h" -#include "Emu/Audio/AudioThread.h" +#include "Emu/Audio/AudioBackend.h" #include "rpcs3qt/msg_dialog_frame.h" #include "rpcs3qt/osk_dialog_frame.h"