mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-22 00:06:59 +02:00
WASAPI: Don't stop the device while in the middle of rerouting.
Public issue https://github.com/mackron/miniaudio/issues/582
This commit is contained in:
+41
-36
@@ -41247,54 +41247,59 @@ MA_API ma_result ma_device_stop(ma_device* pDevice)
|
||||
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_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started);
|
||||
ma_mutex_lock(&pDevice->startStopLock);
|
||||
{
|
||||
/* 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);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
ma_device__set_state(pDevice, ma_device_state_stopped);
|
||||
} 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);
|
||||
} else {
|
||||
/*
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
|
||||
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.
|
||||
*/
|
||||
ma_event_wait(&pDevice->stopEvent);
|
||||
result = MA_SUCCESS;
|
||||
pDevice->playback.intermediaryBufferLen = 0;
|
||||
pDevice->playback.inputCacheConsumed = 0;
|
||||
pDevice->playback.inputCacheRemaining = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
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->startStopLock);
|
||||
ma_mutex_unlock(&pDevice->wasapi.rerouteLock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user