Commit d2bd8ffc authored by David Reid's avatar David Reid

WASAPI: Rework the data discontinuity recovery logic.

Public issue https://github.com/mackron/miniaudio/issues/572
parent b72567d8
......@@ -22223,12 +22223,17 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui
} else {
/* We don't have any cached data pointer, so grab another one. */
HRESULT hr;
DWORD flags;
DWORD flags = 0;
/* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */
hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
if (hr == S_OK) {
/* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */
pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;
if (flags != 0) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags);
}
/* Overrun detection. */
if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
......@@ -22236,13 +22241,29 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap);
/*
If we got an overrun it probably means we're straddling the end of the buffer. In order to prevent
a never-ending sequence of glitches we're going to recover by completely clearing out the capture
buffer.
If we got an overrun it probably means we're straddling the end of the buffer. In normal capture
mode this is the fault of the client application because they're responsible for ensuring data is
processed fast enough. In duplex mode, however, the processing of audio is tied to the playback
device, so this can possibly be the result of a timing de-sync.
In capture mode we're not going to do any kind of recovery because the real fix is for the client
application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers
to prevent a never-ending sequence of glitches due to straddling the end of the buffer.
*/
{
ma_uint32 iterationCount = 4; /* Safety to prevent an infinite loop. */
if (pDevice->type == ma_device_type_duplex) {
/*
Experiment:
If we empty out the *entire* buffer we may end up putting ourselves into an underrun position
which isn't really any better than the overrun we're probably in right now. Instead we'll just
empty out about half.
*/
ma_uint32 i;
ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture);
ma_uint32 iterationCount = periodCount / 2;
if ((periodCount % 2) > 0) {
iterationCount += 1;
}
for (i = 0; i < iterationCount; i += 1) {
hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
......@@ -22251,11 +22272,26 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui
break;
}
flags = 0;
hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) {
/*
The buffer has been completely emptied or an error occurred. In this case we'll need
to reset the state of the mapped buffer which will trigger the next iteration to get
a fresh buffer from WASAPI.
*/
pDevice->wasapi.pMappedBufferCapture = NULL;
pDevice->wasapi.mappedBufferCaptureCap = 0;
pDevice->wasapi.mappedBufferCaptureLen = 0;
if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n");
if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n");
} else {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n");
}
}
if (FAILED(hr)) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %d.\n", hr);
}
......@@ -22264,22 +22300,11 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui
}
}
if (i == iterationCount) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Iteration limit reached.\n");
/* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */
if (pDevice->wasapi.pMappedBufferCapture != NULL) {
pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;
}
}
/* We should not have a valid buffer at this point so make sure everything is empty. */
pDevice->wasapi.pMappedBufferCapture = NULL;
pDevice->wasapi.mappedBufferCaptureCap = 0;
pDevice->wasapi.mappedBufferCaptureLen = 0;
} else {
/* The data is clean. */
pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;
if (flags != 0) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags);
}
}
continue;
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