mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
WASAPI: Rework the data discontinuity recovery logic.
Public issue https://github.com/mackron/miniaudio/issues/572
This commit is contained in:
+46
-21
@@ -22223,12 +22223,17 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui
|
|||||||
} else {
|
} else {
|
||||||
/* We don't have any cached data pointer, so grab another one. */
|
/* We don't have any cached data pointer, so grab another one. */
|
||||||
HRESULT hr;
|
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. */
|
/* 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);
|
hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
|
||||||
if (hr == S_OK) {
|
if (hr == S_OK) {
|
||||||
/* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */
|
/* 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. */
|
/* Overrun detection. */
|
||||||
if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
|
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);
|
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
|
If we got an overrun it probably means we're straddling the end of the buffer. In normal capture
|
||||||
a never-ending sequence of glitches we're going to recover by completely clearing out the capture
|
mode this is the fault of the client application because they're responsible for ensuring data is
|
||||||
buffer.
|
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.
|
||||||
*/
|
*/
|
||||||
{
|
if (pDevice->type == ma_device_type_duplex) {
|
||||||
ma_uint32 iterationCount = 4; /* Safety to prevent an infinite loop. */
|
/*
|
||||||
|
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 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) {
|
for (i = 0; i < iterationCount; i += 1) {
|
||||||
hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flags = 0;
|
||||||
hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
|
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)) {
|
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) {
|
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)) {
|
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);
|
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) {
|
/* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */
|
||||||
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Iteration limit reached.\n");
|
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;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user