Commit ff5d4e61 authored by David Reid's avatar David Reid

WASAPI: Fix errors with the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.

AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM is incompatible in exclusive mode
and low-latency shared mode.
parent 3f5aebfa
......@@ -282,6 +282,14 @@ NOTES
BACKEND NUANCES
===============
WASAPI
------
- Low-latency shared mode will be disabled when using an application-defined sample rate which is different to the
device's native sample rate. To work around this, set wasapi.noAutoConvertSRC to true in the device config. This
is due to IAudioClient3_InitializeSharedAudioStream() failing when the AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM flag is
specified. Setting wasapi.noAutoConvertSRC will result in miniaudio's lower quality internal resampler being used
instead which will in turn enable the use of low-latency shared mode.
PulseAudio
----------
- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
......@@ -8246,7 +8254,7 @@ ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type d
DWORD streamFlags = 0;
MA_REFERENCE_TIME bufferDurationInMicroseconds;
ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;
WAVEFORMATEXTENSIBLE wf;
WAVEFORMATEXTENSIBLE wf = {0};
ma_WASAPIDeviceInterface* pDeviceInterface = NULL;
ma_IAudioClient2* pAudioClient2;
......@@ -8263,10 +8271,10 @@ ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type d
pData->pCaptureClient = NULL;
streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
if (!pData->noAutoConvertSRC && !pData->usingDefaultSampleRate) {
if (!pData->noAutoConvertSRC && !pData->usingDefaultSampleRate && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */
streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
}
if (!pData->noDefaultQualitySRC && !pData->noAutoConvertSRC && !pData->usingDefaultSampleRate) {
if (!pData->noDefaultQualitySRC && !pData->usingDefaultSampleRate && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) {
streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
}
if (deviceType == ma_device_type_loopback) {
......@@ -8296,7 +8304,6 @@ ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type d
pAudioClient2->lpVtbl->Release(pAudioClient2);
}
/* Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. */
result = MA_FORMAT_NOT_SUPPORTED;
if (pData->shareMode == ma_share_mode_exclusive) {
......@@ -8365,6 +8372,7 @@ ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type d
*/
if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) {
wf.Format.nSamplesPerSec = pData->sampleRateIn;
wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign;
}
pData->formatOut = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf);
......@@ -8378,10 +8386,10 @@ ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type d
pData->periodsOut = pData->periodsIn;
pData->bufferSizeInFramesOut = pData->bufferSizeInFramesIn;
if (pData->bufferSizeInFramesOut == 0) {
pData->bufferSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->bufferSizeInMillisecondsIn, pData->sampleRateOut);
pData->bufferSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->bufferSizeInMillisecondsIn, wf.Format.nSamplesPerSec);
}
bufferDurationInMicroseconds = ((ma_uint64)pData->bufferSizeInFramesOut * 1000 * 1000) / pData->sampleRateOut;
bufferDurationInMicroseconds = ((ma_uint64)pData->bufferSizeInFramesOut * 1000 * 1000) / wf.Format.nSamplesPerSec;
/* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */
......@@ -8446,8 +8454,18 @@ ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type d
}
if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
/* Low latency shared mode via IAudioClient3. */
/*
Low latency shared mode via IAudioClient3.
NOTE
====
Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the
use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using
any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to
that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.
*/
#ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0) {
ma_IAudioClient3* pAudioClient3 = NULL;
hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
if (SUCCEEDED(hr)) {
......@@ -8506,6 +8524,7 @@ ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type d
ma_IAudioClient3_Release(pAudioClient3);
pAudioClient3 = NULL;
}
}
#else
#if defined(MA_DEBUG_OUTPUT)
printf("[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n");
......@@ -9034,12 +9053,14 @@ ma_result ma_device_main_loop__wasapi(ma_device* pDevice)
ma_uint32 inputDataInExternalFormatCap = sizeof(inputDataInExternalFormat) / bpfCapture;
ma_uint8 outputDataInExternalFormat[4096];
ma_uint32 outputDataInExternalFormatCap = sizeof(outputDataInExternalFormat) / bpfPlayback;
ma_uint32 periodSizeInFramesCapture = (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods);
ma_uint32 periodSizeInFramesCapture = 0;
ma_assert(pDevice != NULL);
/* The capture device needs to be started immediately. */
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
periodSizeInFramesCapture = (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods);
hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
if (FAILED(hr)) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment