From 99d1d85e4bd64e6f6fcd637d79543e1f0f67c54a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 19 Dec 2020 12:57:05 +0100 Subject: [PATCH 1/3] Reformat and do minor cleanups in UWP/XAudioSoundStream --- UI/GameSettingsScreen.cpp | 5 +- UWP/XAudioSoundStream.cpp | 258 +++++++++++++++++++------------------- 2 files changed, 129 insertions(+), 134 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 27f7b16d16..417d17da45 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -616,7 +616,8 @@ void GameSettingsScreen::CreateViews() { altVolume->SetZeroLabel(a->T("Mute")); altVolume->SetNegativeDisable(a->T("Use global volume")); -#ifdef _WIN32 + // Hide the backend selector in UWP builds (we only support XAudio2 there). +#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) if (IsVistaOrHigher()) { static const char *backend[] = { "Auto", "DSound (compatible)", "WASAPI (fast)" }; PopupMultiChoice *audioBackend = audioSettings->Add(new PopupMultiChoice(&g_Config.iAudioBackend, a->T("Audio backend", "Audio backend (restart req.)"), backend, 0, ARRAY_SIZE(backend), a->GetName(), screenManager())); @@ -625,7 +626,7 @@ void GameSettingsScreen::CreateViews() { #endif std::vector micList = Microphone::getDeviceList(); - if (micList.size() >= 1) { + if (!micList.empty()) { audioSettings->Add(new ItemHeader(a->T("Microphone"))); PopupMultiChoiceDynamic *MicChoice = audioSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sMicDevice, a->T("Microphone Device"), micList, nullptr, screenManager())); MicChoice->OnChoice.Handle(this, &GameSettingsScreen::OnMicDeviceChange); diff --git a/UWP/XAudioSoundStream.cpp b/UWP/XAudioSoundStream.cpp index fa4bc4dd29..97c8dee2cf 100644 --- a/UWP/XAudioSoundStream.cpp +++ b/UWP/XAudioSoundStream.cpp @@ -1,192 +1,186 @@ #include "pch.h" -#include #include +#include +#include + +#include "Common/Log.h" #include "Common/Thread/ThreadUtil.h" #include "XAudioSoundStream.h" #include -#define BUFSIZE 0x80000U +const size_t BUFSIZE = 32 * 1024; class XAudioBackend : public WindowsAudioBackend { public: - XAudioBackend(); - ~XAudioBackend() override; + XAudioBackend(); + ~XAudioBackend() override; - bool Init(HWND window, StreamCallback callback, int sampleRate ) override; // If fails, can safely delete the object - void Update() override; - int GetSampleRate() override { return sampleRate_; } + bool Init(HWND window, StreamCallback callback, int sampleRate) override; // If fails, can safely delete the object + void Update() override; + int GetSampleRate() override { return sampleRate_; } private: - inline int ModBufferSize( int x ) { return ( x + BUFSIZE ) % BUFSIZE; } - bool RunSound(); - bool CreateBuffer(); - bool WriteDataToBuffer( char* soundData, DWORD soundBytes ); - void PollLoop(); + bool RunSound(); + bool CreateBuffer(); + void PollLoop(); - StreamCallback callback_; + StreamCallback callback_ = nullptr; - IXAudio2* xaudioDevice; - IXAudio2MasteringVoice* xaudioMaster; - IXAudio2SourceVoice* xaudioVoice; + IXAudio2 *xaudioDevice = nullptr; + IXAudio2MasteringVoice *xaudioMaster = nullptr; + IXAudio2SourceVoice *xaudioVoice = nullptr; - int sampleRate_; + int sampleRate_ = 0; - char realtimeBuffer_[ BUFSIZE ]; - unsigned cursor_; + char realtimeBuffer_[BUFSIZE]{}; + uint32_t cursor_ = 0; - HANDLE thread_; - HANDLE exitEvent_; + HANDLE thread_ = 0; + HANDLE exitEvent_ = 0; - bool exit = false; + bool exit = false; }; // TODO: Get rid of this static XAudioBackend *g_dsound; -inline int RoundDown128( int x ) { - return x & ( ~127 ); +XAudioBackend::XAudioBackend() { + exitEvent_ = CreateEvent(nullptr, true, true, L""); +} + +inline int RoundDown128(int x) { + return x & (~127); } bool XAudioBackend::CreateBuffer() { - if FAILED( xaudioDevice->CreateMasteringVoice( &xaudioMaster, 2, sampleRate_, 0, 0, NULL ) ) - return false; + if FAILED(xaudioDevice->CreateMasteringVoice(&xaudioMaster, 2, sampleRate_, 0, 0, NULL)) + return false; - WAVEFORMATEX waveFormat; - waveFormat.cbSize = sizeof( waveFormat ); - waveFormat.nAvgBytesPerSec = sampleRate_ * 4; - waveFormat.nBlockAlign = 4; - waveFormat.nChannels = 2; - waveFormat.nSamplesPerSec = sampleRate_; - waveFormat.wBitsPerSample = 16; - waveFormat.wFormatTag = WAVE_FORMAT_PCM; + WAVEFORMATEX waveFormat; + waveFormat.cbSize = sizeof(waveFormat); + waveFormat.nAvgBytesPerSec = sampleRate_ * 4; + waveFormat.nBlockAlign = 4; + waveFormat.nChannels = 2; + waveFormat.nSamplesPerSec = sampleRate_; + waveFormat.wBitsPerSample = 16; + waveFormat.wFormatTag = WAVE_FORMAT_PCM; - if FAILED( xaudioDevice->CreateSourceVoice( &xaudioVoice, &waveFormat, 0, XAUDIO2_DEFAULT_FREQ_RATIO, nullptr, nullptr, nullptr ) ) - return false; + if FAILED(xaudioDevice->CreateSourceVoice(&xaudioVoice, &waveFormat, 0, 1.0, nullptr, nullptr, nullptr)) + return false; - return true; + xaudioVoice->SetFrequencyRatio(1.0); + return true; } bool XAudioBackend::RunSound() { - if FAILED( XAudio2Create( &xaudioDevice, 0, XAUDIO2_DEFAULT_PROCESSOR ) ) { - xaudioDevice = NULL; - return false; - } + if FAILED(XAudio2Create(&xaudioDevice, 0, XAUDIO2_DEFAULT_PROCESSOR)) { + xaudioDevice = NULL; + return false; + } - XAUDIO2_DEBUG_CONFIGURATION dbgCfg; - ZeroMemory( &dbgCfg, sizeof( dbgCfg ) ); - dbgCfg.TraceMask = XAUDIO2_LOG_WARNINGS | XAUDIO2_LOG_DETAIL; - //dbgCfg.BreakMask = XAUDIO2_LOG_ERRORS; - xaudioDevice->SetDebugConfiguration( &dbgCfg ); + XAUDIO2_DEBUG_CONFIGURATION dbgCfg; + ZeroMemory(&dbgCfg, sizeof(dbgCfg)); + dbgCfg.TraceMask = XAUDIO2_LOG_WARNINGS | XAUDIO2_LOG_DETAIL; + //dbgCfg.BreakMask = XAUDIO2_LOG_ERRORS; + xaudioDevice->SetDebugConfiguration(&dbgCfg); - if ( !CreateBuffer() ) { - xaudioDevice->Release(); - xaudioDevice = NULL; - return false; - } + if (!CreateBuffer()) { + xaudioDevice->Release(); + xaudioDevice = NULL; + return false; + } - cursor_ = 0; + cursor_ = 0; - if FAILED( xaudioVoice->Start( 0, XAUDIO2_COMMIT_NOW ) ) { - xaudioDevice->Release(); - xaudioDevice = NULL; - return false; - } + if FAILED(xaudioVoice->Start(0, XAUDIO2_COMMIT_NOW)) { + xaudioDevice->Release(); + xaudioDevice = NULL; + return false; + } - thread_ = (HANDLE)_beginthreadex( 0, 0, []( void* param ) - { + thread_ = (HANDLE)_beginthreadex(0, 0, [](void* param) + { setCurrentThreadName("XAudio2"); - XAudioBackend *backend = (XAudioBackend *)param; - backend->PollLoop(); - return 0U; - }, ( void * )this, 0, 0 ); - SetThreadPriority( thread_, THREAD_PRIORITY_ABOVE_NORMAL ); + XAudioBackend *backend = (XAudioBackend *)param; + backend->PollLoop(); + return 0U; + }, (void *)this, 0, 0); + SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL); - return true; -} - -XAudioBackend::XAudioBackend() : xaudioDevice( nullptr ) { - exitEvent_ = CreateEvent( nullptr, true, true, L"" ); + return true; } XAudioBackend::~XAudioBackend() { - if ( !xaudioDevice ) - return; + if (!xaudioDevice) + return; - if ( !xaudioVoice ) - return; + if (!xaudioVoice) + return; - exit = true; - WaitForSingleObject( exitEvent_, INFINITE ); - CloseHandle( exitEvent_ ); + exit = true; + WaitForSingleObject(exitEvent_, INFINITE); + CloseHandle(exitEvent_); - xaudioDevice->Release(); + xaudioDevice->Release(); } -bool XAudioBackend::Init(HWND window, StreamCallback _callback, int sampleRate ) { - callback_ = _callback; - sampleRate_ = sampleRate; - return RunSound(); +bool XAudioBackend::Init(HWND window, StreamCallback _callback, int sampleRate) { + callback_ = _callback; + sampleRate_ = sampleRate; + return RunSound(); } void XAudioBackend::Update() { } -bool XAudioBackend::WriteDataToBuffer( char* soundData, DWORD soundBytes ) { - XAUDIO2_BUFFER xaudioBuffer; - ZeroMemory( &xaudioBuffer, sizeof( xaudioBuffer ) ); - xaudioBuffer.pAudioData = (const BYTE*)soundData; - xaudioBuffer.AudioBytes = soundBytes; +void XAudioBackend::PollLoop() { + ResetEvent(exitEvent_); - if FAILED( xaudioVoice->SubmitSourceBuffer( &xaudioBuffer, NULL ) ) - return false; + while (!exit) { + XAUDIO2_VOICE_STATE state; + xaudioVoice->GetState(&state); - return true; + if (state.BuffersQueued < 1) { + int a = 0; + a++; + } + + uint32_t bytesRequired = (sampleRate_ * 4) / 100; + + while (bytesRequired) { + uint32_t bytesLeftInBuffer = BUFSIZE - cursor_; + uint32_t readCount = std::min(bytesRequired, bytesLeftInBuffer); + + int stereoSamplesRendered = (*callback_)((short*)&realtimeBuffer_[cursor_], readCount / 4, 16, sampleRate_, 2); + int numBytesRendered = 2 * sizeof(short) * stereoSamplesRendered; + + XAUDIO2_BUFFER xaudioBuffer{}; + xaudioBuffer.pAudioData = (const BYTE*)&realtimeBuffer_[cursor_]; + xaudioBuffer.AudioBytes = numBytesRendered; + + if FAILED(xaudioVoice->SubmitSourceBuffer(&xaudioBuffer, NULL)) { + WARN_LOG(AUDIO, "XAudioBackend: Failed writing bytes"); + } + cursor_ += numBytesRendered; + + if (cursor_ >= BUFSIZE) { + // This can't happen, right? + cursor_ = 0; + bytesLeftInBuffer = BUFSIZE; + } + bytesRequired -= numBytesRendered; + } + + Sleep(2); + } + + SetEvent(exitEvent_); } -void XAudioBackend::PollLoop() -{ - ResetEvent( exitEvent_ ); - - while ( !exit ) - { - XAUDIO2_VOICE_STATE state; - xaudioVoice->GetState( &state ); - - if ( state.BuffersQueued < 1 ) - { - int a = 0; - a++; - } - - unsigned bytesRequired = ( sampleRate_ * 4 ) / 100; - - while ( bytesRequired ) - { - unsigned bytesLeftInBuffer = BUFSIZE - cursor_; - unsigned readCount = std::min( bytesRequired, bytesLeftInBuffer ); - - int numBytesRendered = 4 * ( *callback_ )( (short*)&realtimeBuffer_[ cursor_ ], readCount / 4, 16, sampleRate_, 2 ); - - WriteDataToBuffer( &realtimeBuffer_[ cursor_ ], numBytesRendered ); - cursor_ += numBytesRendered; - if ( cursor_ >= BUFSIZE ) - { - cursor_ = 0; - bytesLeftInBuffer = BUFSIZE; - } - - bytesRequired -= numBytesRendered; - } - - Sleep( 2 ); - } - - SetEvent( exitEvent_ ); -} - -WindowsAudioBackend *CreateAudioBackend( AudioBackendType type ) { - return new XAudioBackend(); +WindowsAudioBackend *CreateAudioBackend(AudioBackendType type) { + // Only one type available on UWP. + return new XAudioBackend(); } From 222bf5c02cc5b1a203f67accc0a5150db0ce83f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 19 Dec 2020 12:59:28 +0100 Subject: [PATCH 2/3] XAudioBackend: Limit the number of queued buffers properly. This is probably not the optimal way to write this loop but it works a hell of a lot better than the old one.. --- UWP/XAudioSoundStream.cpp | 45 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/UWP/XAudioSoundStream.cpp b/UWP/XAudioSoundStream.cpp index 97c8dee2cf..ec343dd664 100644 --- a/UWP/XAudioSoundStream.cpp +++ b/UWP/XAudioSoundStream.cpp @@ -143,38 +143,37 @@ void XAudioBackend::PollLoop() { XAUDIO2_VOICE_STATE state; xaudioVoice->GetState(&state); - if (state.BuffersQueued < 1) { - int a = 0; - a++; + // TODO: Still plenty of tuning to do here. + // 4 seems to work fine. + if (state.BuffersQueued > 4) { + Sleep(1); + continue; } uint32_t bytesRequired = (sampleRate_ * 4) / 100; - while (bytesRequired) { - uint32_t bytesLeftInBuffer = BUFSIZE - cursor_; - uint32_t readCount = std::min(bytesRequired, bytesLeftInBuffer); + uint32_t bytesLeftInBuffer = BUFSIZE - cursor_; + uint32_t readCount = std::min(bytesRequired, bytesLeftInBuffer); - int stereoSamplesRendered = (*callback_)((short*)&realtimeBuffer_[cursor_], readCount / 4, 16, sampleRate_, 2); - int numBytesRendered = 2 * sizeof(short) * stereoSamplesRendered; + // realtimeBuffer_ is just used as a ring of scratch space to be submitted, since SubmitSourceBuffer doesn't + // take ownership of the data. It needs to be big enough to fit the max number of buffers we check for + // above, which it is, easily. - XAUDIO2_BUFFER xaudioBuffer{}; - xaudioBuffer.pAudioData = (const BYTE*)&realtimeBuffer_[cursor_]; - xaudioBuffer.AudioBytes = numBytesRendered; + int stereoSamplesRendered = (*callback_)((short*)&realtimeBuffer_[cursor_], readCount / 4, 16, sampleRate_, 2); + int numBytesRendered = 2 * sizeof(short) * stereoSamplesRendered; - if FAILED(xaudioVoice->SubmitSourceBuffer(&xaudioBuffer, NULL)) { - WARN_LOG(AUDIO, "XAudioBackend: Failed writing bytes"); - } - cursor_ += numBytesRendered; + XAUDIO2_BUFFER xaudioBuffer{}; + xaudioBuffer.pAudioData = (const BYTE*)&realtimeBuffer_[cursor_]; + xaudioBuffer.AudioBytes = numBytesRendered; - if (cursor_ >= BUFSIZE) { - // This can't happen, right? - cursor_ = 0; - bytesLeftInBuffer = BUFSIZE; - } - bytesRequired -= numBytesRendered; + if FAILED(xaudioVoice->SubmitSourceBuffer(&xaudioBuffer, NULL)) { + WARN_LOG(AUDIO, "XAudioBackend: Failed writing bytes"); + } + cursor_ += numBytesRendered; + if (cursor_ >= BUFSIZE) { + cursor_ = 0; + bytesLeftInBuffer = BUFSIZE; } - - Sleep(2); } SetEvent(exitEvent_); From 97e75bb563d347eb3f0958590a9b003a5ff7e732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 19 Dec 2020 13:17:53 +0100 Subject: [PATCH 3/3] Fix synchronization bug in StorageFileLoader.cpp (wrong mutex) --- UWP/StorageFileLoader.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/UWP/StorageFileLoader.cpp b/UWP/StorageFileLoader.cpp index 3dfced89de..81c6dde3ae 100644 --- a/UWP/StorageFileLoader.cpp +++ b/UWP/StorageFileLoader.cpp @@ -20,12 +20,13 @@ StorageFileLoader::StorageFileLoader(Windows::Storage::StorageFile ^file) { } StorageFileLoader::~StorageFileLoader() { - initMutex.lock(); - active_ = false; - operationRequested_ = false; - cond_.notify_all(); + { + std::unique_lock lock(mutex_); + active_ = false; + operationRequested_ = false; + cond_.notify_all(); + } thread_->join(); - initMutex.unlock(); } void StorageFileLoader::threadfunc() {