Skip to content

Commit

Permalink
Windows: Add safety checks to WASAPI code.
Browse files Browse the repository at this point in the history
Seems to be crashing for some users.
  • Loading branch information
unknownbrackets committed May 8, 2020
1 parent b5a0af6 commit b3b86bc
Showing 1 changed file with 53 additions and 17 deletions.
70 changes: 53 additions & 17 deletions Windows/WASAPIStream.cpp
Expand Up @@ -46,7 +46,9 @@ class CMMNotificationClient : public IMMNotificationClient {

if (currentDevice_)
CoTaskMemFree(currentDevice_);
device->GetId(&currentDevice_);
if (!device || FAILED(device->GetId(&currentDevice_))) {
currentDevice_ = nullptr;
}
deviceChanged_ = false;
}

Expand Down Expand Up @@ -75,7 +77,7 @@ class CMMNotificationClient : public IMMNotificationClient {
AddRef();
*ppvInterface = (IMMNotificationClient*)this;
} else {
*ppvInterface = NULL;
*ppvInterface = nullptr;
return E_NOINTERFACE;
}
return S_OK;
Expand All @@ -92,7 +94,11 @@ class CMMNotificationClient : public IMMNotificationClient {
}

// pwstrDeviceId can be null. We consider that a new device, I think?
if (pwstrDeviceId && !wcscmp(currentDevice_, pwstrDeviceId)) {
bool same = currentDevice_ == pwstrDeviceId;
if (!same && currentDevice_ && pwstrDeviceId) {
same = !wcscmp(currentDevice_, pwstrDeviceId);
}
if (same) {
// Already the current device, nothing to do.
return S_OK;
}
Expand Down Expand Up @@ -226,27 +232,37 @@ WASAPIAudioThread::~WASAPIAudioThread() {
}

bool WASAPIAudioThread::ActivateDefaultDevice() {
assert(device_ == nullptr);
HRESULT hresult = deviceEnumerator_->GetDefaultAudioEndpoint(eRender, eMultimedia, &device_);
if (FAILED(hresult))
if (FAILED(hresult) || device_ == nullptr)
return false;

hresult = device_->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&audioInterface_);
if (FAILED(hresult))
assert(audioInterface_ == nullptr);
hresult = device_->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&audioInterface_);
if (FAILED(hresult) || audioInterface_ == nullptr)
return false;

return true;
}

bool WASAPIAudioThread::InitAudioDevice() {
REFERENCE_TIME hnsBufferDuration = REFTIMES_PER_SEC;
assert(deviceFormat_ == nullptr);
HRESULT hresult = audioInterface_->GetMixFormat((WAVEFORMATEX **)&deviceFormat_);
if (FAILED(hresult) || !deviceFormat_)
return false;

hresult = audioInterface_->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, hnsBufferDuration, 0, &deviceFormat_->Format, nullptr);
hresult = audioInterface_->GetService(IID_IAudioRenderClient, (void **)&renderClient_);
if (FAILED(hresult))
return false;
assert(renderClient_ == nullptr);
hresult = audioInterface_->GetService(IID_IAudioRenderClient, (void **)&renderClient_);
if (FAILED(hresult) || !renderClient_)
return false;

numBufferFrames = 0;
hresult = audioInterface_->GetBufferSize(&numBufferFrames);
if (FAILED(hresult))
if (FAILED(hresult) || numBufferFrames == 0)
return false;

sampleRate_ = deviceFormat_->Format.nSamplesPerSec;
Expand Down Expand Up @@ -291,28 +307,35 @@ bool WASAPIAudioThread::DetectFormat() {
return false;
}

BYTE *pData;
BYTE *pData = nullptr;
HRESULT hresult = renderClient_->GetBuffer(numBufferFrames, &pData);
if (FAILED(hresult) || !pData)
return false;

const int numSamples = numBufferFrames * deviceFormat_->Format.nChannels;
if (format_ == Format::IEEE_FLOAT) {
memset(pData, 0, sizeof(float) * numSamples);
shortBuf_ = new short[numBufferFrames * deviceFormat_->Format.nChannels];
shortBuf_ = new short[numSamples];
} else if (format_ == Format::PCM16) {
memset(pData, 0, sizeof(short) * numSamples);
}

hresult = renderClient_->ReleaseBuffer(numBufferFrames, 0);
if (FAILED(hresult))
return false;

actualDuration_ = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * numBufferFrames / deviceFormat_->Format.nSamplesPerSec);
return true;
}

void WASAPIAudioThread::Run() {
// Adapted from http://msdn.microsoft.com/en-us/library/windows/desktop/dd316756(v=vs.85).aspx

assert(deviceEnumerator_ == nullptr);
HRESULT hresult = CoCreateInstance(CLSID_MMDeviceEnumerator,
nullptr, /* Object is not created as the part of the aggregate */
CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&deviceEnumerator_);
if (FAILED(hresult))
if (FAILED(hresult) || deviceEnumerator_ == nullptr)
return;

if (!ActivateDefaultDevice()) {
Expand All @@ -339,22 +362,26 @@ void WASAPIAudioThread::Run() {
}

hresult = audioInterface_->Start();
if (FAILED(hresult)) {
ERROR_LOG(SCEAUDIO, "WASAPI: Failed to start audio stream");
return;
}

DWORD flags = 0;
while (flags != AUDCLNT_BUFFERFLAGS_SILENT) {
Sleep((DWORD)(actualDuration_ / REFTIMES_PER_MILLISEC / 2));

uint32_t pNumPaddingFrames;
uint32_t pNumPaddingFrames = 0;
hresult = audioInterface_->GetCurrentPadding(&pNumPaddingFrames);
if (FAILED(hresult)) {
// What to do?
pNumPaddingFrames = 0;
}
uint32_t pNumAvFrames = numBufferFrames - pNumPaddingFrames;

BYTE *pData;
BYTE *pData = nullptr;
hresult = renderClient_->GetBuffer(pNumAvFrames, &pData);
if (FAILED(hresult)) {
if (FAILED(hresult) || pData == nullptr) {
// What to do?
} else if (pNumAvFrames) {
switch (format_) {
Expand Down Expand Up @@ -382,9 +409,11 @@ void WASAPIAudioThread::Run() {
flags = AUDCLNT_BUFFERFLAGS_SILENT;
}

hresult = renderClient_->ReleaseBuffer(pNumAvFrames, flags);
if (FAILED(hresult)) {
// Not much to do here either...
if (!FAILED(hresult) && pData) {
hresult = renderClient_->ReleaseBuffer(pNumAvFrames, flags);
if (FAILED(hresult)) {
// Not much to do here either...
}
}

// Check if we should use a new device.
Expand All @@ -407,13 +436,20 @@ void WASAPIAudioThread::Run() {
}

hresult = audioInterface_->Start();
if (FAILED(hresult)) {
ERROR_LOG(SCEAUDIO, "WASAPI: Failed to start audio stream");
return;
}
}
}

// Wait for last data in buffer to play before stopping.
Sleep((DWORD)(actualDuration_ / REFTIMES_PER_MILLISEC / 2));

hresult = audioInterface_->Stop();
if (FAILED(hresult)) {
ERROR_LOG(SCEAUDIO, "WASAPI: Failed to stop audio stream");
}
}

int WASAPIAudioBackend::RunThread() {
Expand Down

0 comments on commit b3b86bc

Please sign in to comment.