Commit a6ec466e authored by David Reid's avatar David Reid

WASAPI: Don't stop the device while in the middle of rerouting.

Public issue https://github.com/mackron/miniaudio/issues/582
parent 0ef02e01
...@@ -41247,54 +41247,59 @@ MA_API ma_result ma_device_stop(ma_device* pDevice) ...@@ -41247,54 +41247,59 @@ MA_API ma_result ma_device_stop(ma_device* pDevice)
return MA_SUCCESS; /* Already stopped. */ return MA_SUCCESS; /* Already stopped. */
} }
ma_mutex_lock(&pDevice->startStopLock); /* Wait for any rerouting to finish before attempting to stop the device. */
ma_mutex_lock(&pDevice->wasapi.rerouteLock);
{ {
/* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ ma_mutex_lock(&pDevice->startStopLock);
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); {
/* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started);
ma_device__set_state(pDevice, ma_device_state_stopping);
ma_device__set_state(pDevice, ma_device_state_stopping); /* Asynchronous backends need to be handled differently. */
if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
/* Asynchronous backends must have a stop operation. */
if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
result = pDevice->pContext->callbacks.onDeviceStop(pDevice);
} else {
result = MA_INVALID_OPERATION;
}
/* Asynchronous backends need to be handled differently. */ ma_device__set_state(pDevice, ma_device_state_stopped);
if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
/* Asynchronous backends must have a stop operation. */
if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
result = pDevice->pContext->callbacks.onDeviceStop(pDevice);
} else { } else {
result = MA_INVALID_OPERATION; /*
} Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If
the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make
sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super
important though, so I'm asserting it here as well for extra safety in case we accidentally change something later.
*/
MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started);
ma_device__set_state(pDevice, ma_device_state_stopped); if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) {
} else { pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice);
/* }
Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If
the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make
sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super
important though, so I'm asserting it here as well for extra safety in case we accidentally change something later.
*/
MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started);
if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) { /*
pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice); We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
*/
ma_event_wait(&pDevice->stopEvent);
result = MA_SUCCESS;
} }
/* /*
We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be This is a safety measure to ensure the internal buffer has been cleared so any leftover
the one who puts the device into the stopped state. Don't call ma_device__set_state() here. does not get played the next time the device starts. Ideally this should be drained by
the backend first.
*/ */
ma_event_wait(&pDevice->stopEvent); pDevice->playback.intermediaryBufferLen = 0;
result = MA_SUCCESS; pDevice->playback.inputCacheConsumed = 0;
pDevice->playback.inputCacheRemaining = 0;
} }
ma_mutex_unlock(&pDevice->startStopLock);
/*
This is a safety measure to ensure the internal buffer has been cleared so any leftover
does not get played the next time the device starts. Ideally this should be drained by
the backend first.
*/
pDevice->playback.intermediaryBufferLen = 0;
pDevice->playback.inputCacheConsumed = 0;
pDevice->playback.inputCacheRemaining = 0;
} }
ma_mutex_unlock(&pDevice->startStopLock); ma_mutex_unlock(&pDevice->wasapi.rerouteLock);
return result; return result;
} }
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