Merge pull request #13792 from hrydgard/uwp-audio-improvement

Fix UWP audio and a hang bug
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2020-12-19 13:38:17 +01:00
commit fd1b186e93
3 files changed
+134 -139

No files matched your search

+3 -2
View File
@@ -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<std::string> 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);
+6 -5
View File
@@ -20,12 +20,13 @@ StorageFileLoader::StorageFileLoader(Windows::Storage::StorageFile ^file) {
}
StorageFileLoader::~StorageFileLoader() {
initMutex.lock();
active_ = false;
operationRequested_ = false;
cond_.notify_all();
{
std::unique_lock<std::mutex> lock(mutex_);
active_ = false;
operationRequested_ = false;
cond_.notify_all();
}
thread_->join();
initMutex.unlock();
}
void StorageFileLoader::threadfunc() {
+125 -132
View File
@@ -1,192 +1,185 @@
#include "pch.h"
#include <algorithm>
#include <XAudio2.h>
#include <algorithm>
#include <cstdint>
#include "Common/Log.h"
#include "Common/Thread/ThreadUtil.h"
#include "XAudioSoundStream.h"
#include <process.h>
#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;
// 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;
uint32_t bytesLeftInBuffer = BUFSIZE - cursor_;
uint32_t readCount = std::min(bytesRequired, bytesLeftInBuffer);
// 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.
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) {
cursor_ = 0;
bytesLeftInBuffer = BUFSIZE;
}
}
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();
}