mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 21:39:52 +00:00
Merge pull request #12947 from unknownbrackets/wasapi-format
Windows: Avoid a WASAPI crash for a mono output format
This commit is contained in:
commit
c861bc0d7c
@ -35,8 +35,7 @@ public:
|
||||
}
|
||||
|
||||
~CMMNotificationClient() {
|
||||
if (currentDevice_)
|
||||
CoTaskMemFree(currentDevice_);
|
||||
CoTaskMemFree(currentDevice_);
|
||||
currentDevice_ = nullptr;
|
||||
SAFE_RELEASE(_pEnumerator)
|
||||
}
|
||||
@ -44,8 +43,8 @@ public:
|
||||
void SetCurrentDevice(IMMDevice *device) {
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
|
||||
if (currentDevice_)
|
||||
CoTaskMemFree(currentDevice_);
|
||||
CoTaskMemFree(currentDevice_);
|
||||
currentDevice_ = nullptr;
|
||||
if (!device || FAILED(device->GetId(¤tDevice_))) {
|
||||
currentDevice_ = nullptr;
|
||||
}
|
||||
@ -196,6 +195,8 @@ private:
|
||||
bool InitAudioDevice();
|
||||
void ShutdownAudioDevice();
|
||||
bool DetectFormat();
|
||||
bool ValidateFormat(const WAVEFORMATEXTENSIBLE *fmt);
|
||||
bool PrepareFormat();
|
||||
|
||||
std::atomic<int> &threadData_;
|
||||
int &sampleRate_;
|
||||
@ -252,6 +253,11 @@ bool WASAPIAudioThread::InitAudioDevice() {
|
||||
if (FAILED(hresult) || !deviceFormat_)
|
||||
return false;
|
||||
|
||||
if (!DetectFormat()) {
|
||||
// Format unsupported - let's not even try to initialize.
|
||||
return false;
|
||||
}
|
||||
|
||||
hresult = audioInterface_->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, hnsBufferDuration, 0, &deviceFormat_->Format, nullptr);
|
||||
if (FAILED(hresult))
|
||||
return false;
|
||||
@ -279,33 +285,84 @@ void WASAPIAudioThread::ShutdownAudioDevice() {
|
||||
}
|
||||
|
||||
bool WASAPIAudioThread::DetectFormat() {
|
||||
if (!ValidateFormat(deviceFormat_)) {
|
||||
// Last chance, let's try to ask for one we support instead.
|
||||
WAVEFORMATEXTENSIBLE fmt{};
|
||||
fmt.Format.cbSize = sizeof(fmt);
|
||||
fmt.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
fmt.Format.nChannels = 2;
|
||||
fmt.Format.nSamplesPerSec = 44100;
|
||||
if (deviceFormat_->Format.nSamplesPerSec >= 22050 && deviceFormat_->Format.nSamplesPerSec <= 192000)
|
||||
fmt.Format.nSamplesPerSec = deviceFormat_->Format.nSamplesPerSec;
|
||||
fmt.Format.nBlockAlign = 2 * sizeof(float);
|
||||
fmt.Format.nAvgBytesPerSec = fmt.Format.nSamplesPerSec * fmt.Format.nBlockAlign;
|
||||
fmt.Format.wBitsPerSample = sizeof(float) * 8;
|
||||
fmt.Samples.wReserved = 0;
|
||||
fmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||
fmt.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
|
||||
WAVEFORMATEXTENSIBLE *closest = nullptr;
|
||||
HRESULT hr = audioInterface_->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &fmt.Format, (WAVEFORMATEX **)&closest);
|
||||
if (hr == S_OK) {
|
||||
// Okay, great. Let's just use ours.
|
||||
CoTaskMemFree(closest);
|
||||
CoTaskMemFree(deviceFormat_);
|
||||
deviceFormat_ = (WAVEFORMATEXTENSIBLE *)CoTaskMemAlloc(sizeof(fmt));
|
||||
memcpy(deviceFormat_, &fmt, sizeof(fmt));
|
||||
|
||||
// In case something above gets out of date.
|
||||
return ValidateFormat(deviceFormat_);
|
||||
} else if (hr == S_FALSE && closest != nullptr) {
|
||||
// This means check closest. We'll allow it only if it's less specific on channels.
|
||||
if (ValidateFormat(closest)) {
|
||||
CoTaskMemFree(deviceFormat_);
|
||||
deviceFormat_ = closest;
|
||||
} else {
|
||||
ERROR_LOG_REPORT_ONCE(badfallbackclosest, SCEAUDIO, "WASAPI fallback and closest unsupported");
|
||||
CoTaskMemFree(closest);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
CoTaskMemFree(closest);
|
||||
ERROR_LOG_REPORT_ONCE(badfallback, SCEAUDIO, "WASAPI fallback format was unsupported");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WASAPIAudioThread::ValidateFormat(const WAVEFORMATEXTENSIBLE *fmt) {
|
||||
// Don't know if PCM16 ever shows up here, the documentation only talks about float... but let's blindly
|
||||
// try to support it :P
|
||||
format_ = Format::UNKNOWN;
|
||||
|
||||
if (deviceFormat_->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
if (!memcmp(&deviceFormat_->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(deviceFormat_->SubFormat))) {
|
||||
format_ = Format::IEEE_FLOAT;
|
||||
if (fmt->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
if (!memcmp(&fmt->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(fmt->SubFormat))) {
|
||||
if (fmt->Format.nChannels >= 2)
|
||||
format_ = Format::IEEE_FLOAT;
|
||||
} else {
|
||||
ERROR_LOG_REPORT_ONCE(unexpectedformat, SCEAUDIO, "Got unexpected WASAPI 0xFFFE stream format, expected float!");
|
||||
if (deviceFormat_->Format.wBitsPerSample == 16 && deviceFormat_->Format.nChannels == 2) {
|
||||
if (fmt->Format.wBitsPerSample == 16 && fmt->Format.nChannels == 2) {
|
||||
format_ = Format::PCM16;
|
||||
}
|
||||
}
|
||||
} else if (deviceFormat_->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
|
||||
format_ = Format::IEEE_FLOAT;
|
||||
} else if (fmt->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
|
||||
if (fmt->Format.nChannels >= 2)
|
||||
format_ = Format::IEEE_FLOAT;
|
||||
} else {
|
||||
ERROR_LOG_REPORT_ONCE(unexpectedformat2, SCEAUDIO, "Got unexpected non-extensible WASAPI stream format, expected extensible float!");
|
||||
if (deviceFormat_->Format.wBitsPerSample == 16 && deviceFormat_->Format.nChannels == 2) {
|
||||
if (fmt->Format.wBitsPerSample == 16 && fmt->Format.nChannels == 2) {
|
||||
format_ = Format::PCM16;
|
||||
}
|
||||
}
|
||||
|
||||
return format_ != Format::UNKNOWN;
|
||||
}
|
||||
|
||||
bool WASAPIAudioThread::PrepareFormat() {
|
||||
delete [] shortBuf_;
|
||||
shortBuf_ = nullptr;
|
||||
if (format_ == Format::UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BYTE *pData = nullptr;
|
||||
HRESULT hresult = renderClient_->GetBuffer(numBufferFrames, &pData);
|
||||
@ -356,7 +413,7 @@ void WASAPIAudioThread::Run() {
|
||||
ERROR_LOG(SCEAUDIO, "WASAPI: Could not init audio device");
|
||||
return;
|
||||
}
|
||||
if (!DetectFormat()) {
|
||||
if (!PrepareFormat()) {
|
||||
ERROR_LOG(SCEAUDIO, "WASAPI: Could not find a suitable audio output format");
|
||||
return;
|
||||
}
|
||||
@ -384,14 +441,14 @@ void WASAPIAudioThread::Run() {
|
||||
if (FAILED(hresult) || pData == nullptr) {
|
||||
// What to do?
|
||||
} else if (pNumAvFrames) {
|
||||
int chans = deviceFormat_->Format.nChannels;
|
||||
switch (format_) {
|
||||
case Format::IEEE_FLOAT:
|
||||
callback_(shortBuf_, pNumAvFrames, 16, sampleRate_, 2);
|
||||
if (deviceFormat_->Format.nChannels == 2) {
|
||||
ConvertS16ToF32((float *)pData, shortBuf_, pNumAvFrames * deviceFormat_->Format.nChannels);
|
||||
} else {
|
||||
callback_(shortBuf_, pNumAvFrames, 16, sampleRate_, chans);
|
||||
if (chans == 2) {
|
||||
ConvertS16ToF32((float *)pData, shortBuf_, pNumAvFrames * chans);
|
||||
} else if (chans > 2) {
|
||||
float *ptr = (float *)pData;
|
||||
int chans = deviceFormat_->Format.nChannels;
|
||||
memset(ptr, 0, pNumAvFrames * chans * sizeof(float));
|
||||
for (UINT32 i = 0; i < pNumAvFrames; i++) {
|
||||
ptr[i * chans + 0] = (float)shortBuf_[i * 2] * (1.0f / 32768.0f);
|
||||
@ -400,7 +457,7 @@ void WASAPIAudioThread::Run() {
|
||||
}
|
||||
break;
|
||||
case Format::PCM16:
|
||||
callback_((short *)pData, pNumAvFrames, 16, sampleRate_, 2);
|
||||
callback_((short *)pData, pNumAvFrames, 16, sampleRate_, chans);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -430,7 +487,7 @@ void WASAPIAudioThread::Run() {
|
||||
ERROR_LOG(SCEAUDIO, "WASAPI: Could not init audio device");
|
||||
return;
|
||||
}
|
||||
if (!DetectFormat()) {
|
||||
if (!PrepareFormat()) {
|
||||
ERROR_LOG(SCEAUDIO, "WASAPI: Could not find a suitable audio output format");
|
||||
return;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user