ppsspp/UWP/XAudioSoundStream.cpp

182 lines
4.3 KiB
C++
Raw Normal View History

#include "pch.h"
2020-09-14 22:34:45 +00:00
#include <XAudio2.h>
#include <algorithm>
#include <cstdint>
#include "Common/Log.h"
#include "Common/Thread/ThreadUtil.h"
#include "XAudioSoundStream.h"
#include <process.h>
const size_t BUFSIZE = 32 * 1024;
class XAudioBackend : public WindowsAudioBackend {
public:
XAudioBackend();
~XAudioBackend() override;
bool Init(HWND window, StreamCallback callback, int sampleRate) override; // If fails, can safely delete the object
int GetSampleRate() const override { return sampleRate_; }
private:
bool RunSound();
bool CreateBuffer();
void PollLoop();
StreamCallback callback_ = nullptr;
IXAudio2 *xaudioDevice = nullptr;
IXAudio2MasteringVoice *xaudioMaster = nullptr;
IXAudio2SourceVoice *xaudioVoice = nullptr;
int sampleRate_ = 0;
char realtimeBuffer_[BUFSIZE]{};
uint32_t cursor_ = 0;
HANDLE thread_ = 0;
HANDLE exitEvent_ = 0;
bool exit = false;
};
// TODO: Get rid of this
static XAudioBackend *g_dsound;
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;
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, 1.0, nullptr, nullptr, nullptr))
return false;
xaudioVoice->SetFrequencyRatio(1.0);
return true;
}
bool XAudioBackend::RunSound() {
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);
if (!CreateBuffer()) {
xaudioDevice->Release();
xaudioDevice = NULL;
return false;
}
cursor_ = 0;
if FAILED(xaudioVoice->Start(0, XAUDIO2_COMMIT_NOW)) {
xaudioDevice->Release();
xaudioDevice = NULL;
return false;
}
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);
return true;
}
XAudioBackend::~XAudioBackend() {
if (!xaudioDevice)
return;
if (!xaudioVoice)
return;
exit = true;
WaitForSingleObject(exitEvent_, INFINITE);
CloseHandle(exitEvent_);
xaudioDevice->Release();
}
bool XAudioBackend::Init(HWND window, StreamCallback _callback, int sampleRate) {
callback_ = _callback;
sampleRate_ = sampleRate;
return RunSound();
}
void XAudioBackend::PollLoop() {
ResetEvent(exitEvent_);
while (!exit) {
XAUDIO2_VOICE_STATE state;
xaudioVoice->GetState(&state);
// 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.
2023-03-24 14:32:48 +00:00
int stereoSamplesRendered = (*callback_)((short*)&realtimeBuffer_[cursor_], readCount / 4, sampleRate_);
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_);
}
WindowsAudioBackend *CreateAudioBackend(AudioBackendType type) {
// Only one type available on UWP.
return new XAudioBackend();
2020-09-14 22:34:45 +00:00
}