mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-14 01:59:34 +01:00
Merge pull request #13792 from hrydgard/uwp-audio-improvement
Fix UWP audio and a hang bug
This commit is contained in:
3 files changed
+134
-139
No files matched your search
@@ -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);
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
Reference in new issue
Block a user