mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-23 00:34:03 +02:00
More work on the audio thread's new main loop.
Automatic device switching has not yet been tested with this.
This commit is contained in:
@@ -1941,8 +1941,10 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device
|
|||||||
mal_IMMNotificationClient notificationClient;
|
mal_IMMNotificationClient notificationClient;
|
||||||
/*HANDLE*/ mal_handle hEventPlayback; /* Used with the blocking API. Manual reset. Initialized to signaled. */
|
/*HANDLE*/ mal_handle hEventPlayback; /* Used with the blocking API. Manual reset. Initialized to signaled. */
|
||||||
/*HANDLE*/ mal_handle hEventCapture; /* Used with the blocking API. Manual reset. Initialized to unsignaled. */
|
/*HANDLE*/ mal_handle hEventCapture; /* Used with the blocking API. Manual reset. Initialized to unsignaled. */
|
||||||
|
#if 0
|
||||||
/*HANDLE*/ mal_handle hEvent;
|
/*HANDLE*/ mal_handle hEvent;
|
||||||
/*HANDLE*/ mal_handle hBreakEvent; /* <-- Used to break from WaitForMultipleObjects() in the main loop. */
|
/*HANDLE*/ mal_handle hBreakEvent; /* <-- Used to break from WaitForMultipleObjects() in the main loop. */
|
||||||
|
#endif
|
||||||
void* pDeviceBufferPlayback;
|
void* pDeviceBufferPlayback;
|
||||||
void* pDeviceBufferCapture;
|
void* pDeviceBufferCapture;
|
||||||
mal_uint32 deviceBufferFramesRemainingPlayback;
|
mal_uint32 deviceBufferFramesRemainingPlayback;
|
||||||
@@ -2294,14 +2296,6 @@ mal_result mal_device_init_ex(const mal_backend backends[], mal_uint32 backendCo
|
|||||||
// try using the device at the same time as uninitializing it.
|
// try using the device at the same time as uninitializing it.
|
||||||
void mal_device_uninit(mal_device* pDevice);
|
void mal_device_uninit(mal_device* pDevice);
|
||||||
|
|
||||||
|
|
||||||
// Writes PCM frames to the device.
|
|
||||||
mal_result mal_device_write(mal_device* pDevice, mal_uint32 pcmFrameCount, const void* pPCMFrames, mal_uint32* pPCMFramesWritten);
|
|
||||||
|
|
||||||
// Reads PCM frames from the device.
|
|
||||||
mal_result mal_device_read(mal_device* pDevice, mal_uint32 pcmFrameCount, void* pPCMFrames, mal_uint32* pPCMFramesRead);
|
|
||||||
|
|
||||||
|
|
||||||
// Sets the callback to use when the device has stopped, either explicitly or as a result of an error.
|
// Sets the callback to use when the device has stopped, either explicitly or as a result of an error.
|
||||||
//
|
//
|
||||||
// Thread Safety: SAFE
|
// Thread Safety: SAFE
|
||||||
@@ -4576,6 +4570,12 @@ static MAL_INLINE mal_uint32 mal_device__get_state(mal_device* pDevice)
|
|||||||
return pDevice->state;
|
return pDevice->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* A helper for determining whether or not the device is running in async mode. */
|
||||||
|
static MAL_INLINE mal_bool32 mal_device__is_async(mal_device* pDevice)
|
||||||
|
{
|
||||||
|
return pDevice->onRecv != NULL || pDevice->onSend != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef MAL_WIN32
|
#ifdef MAL_WIN32
|
||||||
GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
GUID MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||||
@@ -5928,8 +5928,10 @@ HRESULT STDMETHODCALLTYPE mal_IMMNotificationClient_OnDefaultDeviceChanged(mal_I
|
|||||||
// We don't change the device here - we change it in the worker thread to keep synchronization simple. To this I'm just setting a flag to
|
// We don't change the device here - we change it in the worker thread to keep synchronization simple. To this I'm just setting a flag to
|
||||||
// indicate that the default device has changed.
|
// indicate that the default device has changed.
|
||||||
mal_atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultDeviceChanged, MAL_TRUE);
|
mal_atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultDeviceChanged, MAL_TRUE);
|
||||||
SetEvent(pThis->pDevice->wasapi.hBreakEvent); // <-- The main loop will be waiting on some events. We want to break from this wait ASAP so we can change the device as quickly as possible.
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
SetEvent(pThis->pDevice->wasapi.hBreakEvent); // <-- The main loop will be waiting on some events. We want to break from this wait ASAP so we can change the device as quickly as possible.
|
||||||
|
#endif
|
||||||
|
|
||||||
(void)pDefaultDeviceID;
|
(void)pDefaultDeviceID;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
@@ -6419,12 +6421,14 @@ void mal_device_uninit__wasapi(mal_device* pDevice)
|
|||||||
CloseHandle(pDevice->wasapi.hEventCapture);
|
CloseHandle(pDevice->wasapi.hEventCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
if (pDevice->wasapi.hEvent) {
|
if (pDevice->wasapi.hEvent) {
|
||||||
CloseHandle(pDevice->wasapi.hEvent);
|
CloseHandle(pDevice->wasapi.hEvent);
|
||||||
}
|
}
|
||||||
if (pDevice->wasapi.hBreakEvent) {
|
if (pDevice->wasapi.hBreakEvent) {
|
||||||
CloseHandle(pDevice->wasapi.hBreakEvent);
|
CloseHandle(pDevice->wasapi.hBreakEvent);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -6813,7 +6817,9 @@ mal_result mal_device_reinit__wasapi(mal_device* pDevice)
|
|||||||
pDevice->periods = data.periodsOut;
|
pDevice->periods = data.periodsOut;
|
||||||
mal_strcpy_s(pDevice->name, sizeof(pDevice->name), data.deviceName);
|
mal_strcpy_s(pDevice->name, sizeof(pDevice->name), data.deviceName);
|
||||||
|
|
||||||
|
#if 0
|
||||||
mal_IAudioClient_SetEventHandle((mal_IAudioClient*)pDevice->wasapi.pAudioClient, pDevice->wasapi.hEvent);
|
mal_IAudioClient_SetEventHandle((mal_IAudioClient*)pDevice->wasapi.pAudioClient, pDevice->wasapi.hEvent);
|
||||||
|
#endif
|
||||||
|
|
||||||
return MAL_SUCCESS;
|
return MAL_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -6884,9 +6890,7 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Events. */
|
/* Events. */
|
||||||
mal_bool32 isSynchronous = (pConfig->onRecvCallback == NULL && pConfig->onSendCallback == NULL);
|
|
||||||
|
|
||||||
if (isSynchronous) {
|
|
||||||
/*
|
/*
|
||||||
The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
|
The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
|
||||||
only after the whole available space has been filled, never before.
|
only after the whole available space has been filled, never before.
|
||||||
@@ -6917,7 +6921,8 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type,
|
|||||||
|
|
||||||
mal_IAudioClient_SetEventHandle((mal_IAudioClient*)pDevice->wasapi.pAudioClient, pDevice->wasapi.hEventCapture);
|
mal_IAudioClient_SetEventHandle((mal_IAudioClient*)pDevice->wasapi.pAudioClient, pDevice->wasapi.hEventCapture);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
#if 0
|
||||||
// We need to create and set the event for event-driven mode. This event is signaled whenever a new chunk of audio
|
// We need to create and set the event for event-driven mode. This event is signaled whenever a new chunk of audio
|
||||||
// data needs to be written or read from the device.
|
// data needs to be written or read from the device.
|
||||||
pDevice->wasapi.hEvent = CreateEventA(NULL, FALSE, TRUE, NULL);
|
pDevice->wasapi.hEvent = CreateEventA(NULL, FALSE, TRUE, NULL);
|
||||||
@@ -6927,11 +6932,6 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
mal_IAudioClient_SetEventHandle((mal_IAudioClient*)pDevice->wasapi.pAudioClient, pDevice->wasapi.hEvent);
|
mal_IAudioClient_SetEventHandle((mal_IAudioClient*)pDevice->wasapi.pAudioClient, pDevice->wasapi.hEvent);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// When the device is playing the worker thread will be waiting on a bunch of notification events. To return from
|
// When the device is playing the worker thread will be waiting on a bunch of notification events. To return from
|
||||||
// this wait state we need to signal a special event.
|
// this wait state we need to signal a special event.
|
||||||
@@ -6940,6 +6940,7 @@ mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type type,
|
|||||||
errorMsg = "[WASAPI] Failed to create break event for main loop break notification.", result = MAL_FAILED_TO_CREATE_EVENT;
|
errorMsg = "[WASAPI] Failed to create break event for main loop break notification.", result = MAL_FAILED_TO_CREATE_EVENT;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
result = MAL_SUCCESS;
|
result = MAL_SUCCESS;
|
||||||
|
|
||||||
@@ -7203,7 +7204,7 @@ mal_result mal_device_read__wasapi(mal_device* pDevice, mal_uint32 pcmFrameCount
|
|||||||
return MAL_SUCCESS;
|
return MAL_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
mal_result mal_device_break_main_loop__wasapi(mal_device* pDevice)
|
mal_result mal_device_break_main_loop__wasapi(mal_device* pDevice)
|
||||||
{
|
{
|
||||||
mal_assert(pDevice != NULL);
|
mal_assert(pDevice != NULL);
|
||||||
@@ -7367,6 +7368,7 @@ mal_result mal_device_main_loop__wasapi(mal_device* pDevice)
|
|||||||
|
|
||||||
return MAL_SUCCESS;
|
return MAL_SUCCESS;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
mal_result mal_context_uninit__wasapi(mal_context* pContext)
|
mal_result mal_context_uninit__wasapi(mal_context* pContext)
|
||||||
{
|
{
|
||||||
@@ -7416,8 +7418,10 @@ mal_result mal_context_init__wasapi(mal_context* pContext)
|
|||||||
pContext->onDeviceWrite = mal_device_write__wasapi;
|
pContext->onDeviceWrite = mal_device_write__wasapi;
|
||||||
pContext->onDeviceRead = mal_device_read__wasapi;
|
pContext->onDeviceRead = mal_device_read__wasapi;
|
||||||
|
|
||||||
|
#if 0
|
||||||
pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__wasapi;
|
pContext->onDeviceBreakMainLoop = mal_device_break_main_loop__wasapi;
|
||||||
pContext->onDeviceMainLoop = mal_device_main_loop__wasapi;
|
pContext->onDeviceMainLoop = mal_device_main_loop__wasapi;
|
||||||
|
#endif
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -19895,7 +19899,7 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData)
|
|||||||
mal_device__set_state(pDevice, MAL_STATE_STOPPED);
|
mal_device__set_state(pDevice, MAL_STATE_STOPPED);
|
||||||
mal_event_signal(&pDevice->stopEvent);
|
mal_event_signal(&pDevice->stopEvent);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
|
||||||
// We wait on an event to know when something has requested that the device be started and the main loop entered.
|
// We wait on an event to know when something has requested that the device be started and the main loop entered.
|
||||||
mal_event_wait(&pDevice->wakeupEvent);
|
mal_event_wait(&pDevice->wakeupEvent);
|
||||||
|
|
||||||
@@ -19912,6 +19916,87 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData)
|
|||||||
// in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
|
// in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
|
||||||
mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STARTING);
|
mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STARTING);
|
||||||
|
|
||||||
|
/*
|
||||||
|
The old main loop is getting replaced with an improved implementation that's based on the blocking read/write API.
|
||||||
|
*/
|
||||||
|
if (pDevice->pContext->onDeviceRead || pDevice->pContext->onDeviceWrite) { /* <-- TODO: Get rid of this check once the old implementation has been entirely replaced. */
|
||||||
|
mal_uint32 periodSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods;
|
||||||
|
|
||||||
|
/*
|
||||||
|
With the blocking API, the device is started automatically in read()/write(). All we need to do is enter the loop and just keep reading
|
||||||
|
or writing based on the period size.
|
||||||
|
*/
|
||||||
|
mal_assert(pDevice->periods >= 2);
|
||||||
|
mal_assert(pDevice->bufferSizeInFrames >= pDevice->periods);
|
||||||
|
|
||||||
|
/* Make sure the state is set appropriately. */
|
||||||
|
mal_device__set_state(pDevice, MAL_STATE_STARTED);
|
||||||
|
mal_event_signal(&pDevice->startEvent);
|
||||||
|
|
||||||
|
/* Main Loop */
|
||||||
|
mal_assert(periodSizeInFrames >= 1);
|
||||||
|
while (mal_device__get_state(pDevice) == MAL_STATE_STARTED) {
|
||||||
|
mal_result result = MAL_SUCCESS;
|
||||||
|
mal_uint32 totalFramesProcessed = 0;
|
||||||
|
mal_uint8 buffer[4096];
|
||||||
|
mal_uint32 bufferSizeInFrames = sizeof(buffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
|
||||||
|
|
||||||
|
while (totalFramesProcessed < periodSizeInFrames) {
|
||||||
|
mal_uint32 framesProcessed;
|
||||||
|
mal_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed;
|
||||||
|
mal_uint32 framesToProcess = framesRemaining;
|
||||||
|
if (framesToProcess > bufferSizeInFrames) {
|
||||||
|
framesToProcess = bufferSizeInFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
framesProcessed = 0;
|
||||||
|
if (pDevice->type == mal_device_type_playback) {
|
||||||
|
mal_device__read_frames_from_client(pDevice, framesToProcess, buffer);
|
||||||
|
result = pDevice->pContext->onDeviceWrite(pDevice, framesToProcess, buffer, &framesProcessed);
|
||||||
|
} else {
|
||||||
|
result = pDevice->pContext->onDeviceRead(pDevice, framesToProcess, buffer, &framesProcessed);
|
||||||
|
mal_device__send_frames_to_client(pDevice, framesProcessed, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
totalFramesProcessed += framesProcessed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If something has gone wrong while writing or reading, we can try switching devices. */
|
||||||
|
if (result != MAL_SUCCESS && pDevice->isDefaultDevice && mal_device__get_state(pDevice) == MAL_STATE_STARTED && pDevice->initConfig.shareMode != mal_share_mode_exclusive) {
|
||||||
|
/* Here is where we try switching to the new default device. */
|
||||||
|
mal_result reinitResult = MAL_ERROR;
|
||||||
|
if (pDevice->pContext->onDeviceReinit) {
|
||||||
|
reinitResult = pDevice->pContext->onDeviceReinit(pDevice);
|
||||||
|
} else {
|
||||||
|
pDevice->pContext->onDeviceStop(pDevice);
|
||||||
|
mal_device__set_state(pDevice, MAL_STATE_STOPPED);
|
||||||
|
|
||||||
|
pDevice->pContext->onDeviceUninit(pDevice);
|
||||||
|
mal_device__set_state(pDevice, MAL_STATE_UNINITIALIZED);
|
||||||
|
|
||||||
|
reinitResult = pDevice->pContext->onDeviceInit(pDevice->pContext, pDevice->type, NULL, &pDevice->initConfig, pDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform the post initialization setup just in case the data conversion pipeline needs to be reinitialized. */
|
||||||
|
if (reinitResult == MAL_SUCCESS) {
|
||||||
|
mal_device__post_init_setup(pDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If reinitialization was successful, loop back to the start. */
|
||||||
|
if (reinitResult == MAL_SUCCESS) {
|
||||||
|
mal_device__set_state(pDevice, MAL_STATE_STARTING); /* <-- The device is restarting. */
|
||||||
|
mal_event_signal(&pDevice->wakeupEvent);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get out of the loop if read()/write() returned an error. It probably means the device has been stopped. */
|
||||||
|
if (result != MAL_SUCCESS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Old loop. This will be removed later. */
|
||||||
pDevice->workResult = pDevice->pContext->onDeviceStart(pDevice);
|
pDevice->workResult = pDevice->pContext->onDeviceStart(pDevice);
|
||||||
if (pDevice->workResult != MAL_SUCCESS) {
|
if (pDevice->workResult != MAL_SUCCESS) {
|
||||||
mal_device__set_state(pDevice, MAL_STATE_STOPPED);
|
mal_device__set_state(pDevice, MAL_STATE_STOPPED);
|
||||||
@@ -19955,7 +20040,7 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Getting here means we have broken from the main loop which happens the application has requested that device be stopped. Note that this
|
// Getting here means we have broken from the main loop which happens the application has requested that device be stopped. Note that this
|
||||||
// may have actually already happened above if the device was lost and mini_al has attempted to re-initialize the device. In this case we
|
// may have actually already happened above if the device was lost and mini_al has attempted to re-initialize the device. In this case we
|
||||||
@@ -20732,6 +20817,9 @@ void mal_device_uninit(mal_device* pDevice)
|
|||||||
mal_zero_object(pDevice);
|
mal_zero_object(pDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Writes PCM frames to the device.
|
||||||
|
*/
|
||||||
mal_result mal_device_write(mal_device* pDevice, mal_uint32 pcmFrameCount, const void* pPCMFrames, mal_uint32* pPCMFramesWritten)
|
mal_result mal_device_write(mal_device* pDevice, mal_uint32 pcmFrameCount, const void* pPCMFrames, mal_uint32* pPCMFramesWritten)
|
||||||
{
|
{
|
||||||
mal_result result;
|
mal_result result;
|
||||||
@@ -20741,7 +20829,7 @@ mal_result mal_device_write(mal_device* pDevice, mal_uint32 pcmFrameCount, const
|
|||||||
*pPCMFramesWritten = 0; /* Safety. */
|
*pPCMFramesWritten = 0; /* Safety. */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pDevice == NULL || pPCMFrames == NULL) {
|
if (mal_device__is_async(pDevice)) {
|
||||||
return MAL_INVALID_ARGS;
|
return MAL_INVALID_ARGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20761,28 +20849,9 @@ mal_result mal_device_write(mal_device* pDevice, mal_uint32 pcmFrameCount, const
|
|||||||
result = pDevice->pContext->onDeviceWrite(pDevice, pcmFrameCount, pPCMFrames, &totalPCMFramesWritten);
|
result = pDevice->pContext->onDeviceWrite(pDevice, pcmFrameCount, pPCMFrames, &totalPCMFramesWritten);
|
||||||
} else {
|
} else {
|
||||||
/* Slow path. Perform a data conversion. */
|
/* Slow path. Perform a data conversion. */
|
||||||
#if 0
|
|
||||||
mal_uint8 buffer[4096];
|
|
||||||
while (totalPCMFramesWritten < pcmFrameCount) {
|
|
||||||
mal_uint32 framesJustWritten = 0;
|
|
||||||
mal_uint32 framesRemaining = (pcmFrameCount - totalPCMFramesWritten);
|
|
||||||
mal_uint32 framesToWrite = framesRemaining;
|
|
||||||
if (framesToWrite > (sizeof(buffer)/mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels))) {
|
|
||||||
framesToWrite = (sizeof(buffer)/mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Convert the pPCMFrames to the device's internal format. */
|
|
||||||
|
|
||||||
result = pDevice->pContext->onDeviceWrite(pDevice, framesToWrite, buffer, &framesJustWritten);
|
|
||||||
totalPCMFramesWritten += framesJustWritten;
|
|
||||||
if (result != MAL_SUCCESS) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
/* TODO: Implement me. */
|
||||||
result = MAL_INVALID_OPERATION;
|
result = MAL_INVALID_OPERATION;
|
||||||
/*result = MAL_SUCCESS;*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -20793,6 +20862,9 @@ mal_result mal_device_write(mal_device* pDevice, mal_uint32 pcmFrameCount, const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reads PCM frames from the device.
|
||||||
|
*/
|
||||||
mal_result mal_device_read(mal_device* pDevice, mal_uint32 pcmFrameCount, void* pPCMFrames, mal_uint32* pPCMFramesRead)
|
mal_result mal_device_read(mal_device* pDevice, mal_uint32 pcmFrameCount, void* pPCMFrames, mal_uint32* pPCMFramesRead)
|
||||||
{
|
{
|
||||||
mal_result result;
|
mal_result result;
|
||||||
@@ -20807,7 +20879,7 @@ mal_result mal_device_read(mal_device* pDevice, mal_uint32 pcmFrameCount, void*
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Not allowed to call this in asynchronous mode. */
|
/* Not allowed to call this in asynchronous mode. */
|
||||||
if (pDevice->onRecv != NULL || pDevice->onSend != NULL) {
|
if (mal_device__is_async(pDevice)) {
|
||||||
return MAL_INVALID_OPERATION;
|
return MAL_INVALID_OPERATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20816,7 +20888,6 @@ mal_result mal_device_read(mal_device* pDevice, mal_uint32 pcmFrameCount, void*
|
|||||||
return MAL_INVALID_OPERATION;
|
return MAL_INVALID_OPERATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* If it's a passthrough we can call the backend directly, otherwise we need a data conversion into an intermediary buffer. */
|
/* If it's a passthrough we can call the backend directly, otherwise we need a data conversion into an intermediary buffer. */
|
||||||
if (pDevice->dsp.isPassthrough) {
|
if (pDevice->dsp.isPassthrough) {
|
||||||
/* Fast path. Write directly to the device. */
|
/* Fast path. Write directly to the device. */
|
||||||
@@ -20824,7 +20895,6 @@ mal_result mal_device_read(mal_device* pDevice, mal_uint32 pcmFrameCount, void*
|
|||||||
} else {
|
} else {
|
||||||
/* Slow path. Perform a data conversion. */
|
/* Slow path. Perform a data conversion. */
|
||||||
|
|
||||||
|
|
||||||
/* TODO: Implement me. */
|
/* TODO: Implement me. */
|
||||||
result = MAL_INVALID_OPERATION;
|
result = MAL_INVALID_OPERATION;
|
||||||
}
|
}
|
||||||
@@ -20853,11 +20923,20 @@ mal_result mal_device_start(mal_device* pDevice)
|
|||||||
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED);
|
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Starting the device doesn't do anything in synchronous mode because in that case it's started automatically with
|
||||||
|
mal_device_write() and mal_device_read(). It's best to return an error so that the application can be aware that
|
||||||
|
it's not doing it right.
|
||||||
|
*/
|
||||||
|
if (!mal_device__is_async(pDevice)) {
|
||||||
|
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_start() called in synchronous mode. This should only be used in asynchronous/callback mode.", MAL_DEVICE_NOT_INITIALIZED);
|
||||||
|
}
|
||||||
|
|
||||||
mal_result result = MAL_ERROR;
|
mal_result result = MAL_ERROR;
|
||||||
mal_mutex_lock(&pDevice->lock);
|
mal_mutex_lock(&pDevice->lock);
|
||||||
{
|
{
|
||||||
// Starting, stopping and pausing are wrapped in a mutex which means we can assert that the device is in a stopped or paused state.
|
// Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state.
|
||||||
mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STOPPED /*|| mal_device__get_state(pDevice) == MAL_STATE_PAUSED*/);
|
mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STOPPED);
|
||||||
|
|
||||||
mal_device__set_state(pDevice, MAL_STATE_STARTING);
|
mal_device__set_state(pDevice, MAL_STATE_STARTING);
|
||||||
|
|
||||||
@@ -20893,11 +20972,19 @@ mal_result mal_device_stop(mal_device* pDevice)
|
|||||||
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED);
|
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "mal_device_stop() called for an uninitialized device.", MAL_DEVICE_NOT_INITIALIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Stopping is slightly different for synchronous mode. In this case it just tells the driver to stop the internal processing of the device. Also,
|
||||||
|
stopping in synchronous mode does not require state checking.
|
||||||
|
*/
|
||||||
|
if (!mal_device__is_async(pDevice)) {
|
||||||
|
return pDevice->pContext->onDeviceStop(pDevice);
|
||||||
|
}
|
||||||
|
|
||||||
mal_result result = MAL_ERROR;
|
mal_result result = MAL_ERROR;
|
||||||
mal_mutex_lock(&pDevice->lock);
|
mal_mutex_lock(&pDevice->lock);
|
||||||
{
|
{
|
||||||
// Starting, stopping and pausing are wrapped in a mutex which means we can assert that the device is in a started or paused state.
|
// Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state.
|
||||||
mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STARTED /*|| mal_device__get_state(pDevice) == MAL_STATE_PAUSED*/);
|
mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STARTED);
|
||||||
|
|
||||||
mal_device__set_state(pDevice, MAL_STATE_STOPPING);
|
mal_device__set_state(pDevice, MAL_STATE_STOPPING);
|
||||||
|
|
||||||
@@ -20912,7 +20999,9 @@ mal_result mal_device_stop(mal_device* pDevice)
|
|||||||
|
|
||||||
// When we get here the worker thread is likely in a wait state while waiting for the backend device to deliver or request
|
// When we get here the worker thread is likely in a wait state while waiting for the backend device to deliver or request
|
||||||
// audio data. We need to force these to return as quickly as possible.
|
// audio data. We need to force these to return as quickly as possible.
|
||||||
|
if (pDevice->pContext->onDeviceBreakMainLoop) {
|
||||||
pDevice->pContext->onDeviceBreakMainLoop(pDevice);
|
pDevice->pContext->onDeviceBreakMainLoop(pDevice);
|
||||||
|
}
|
||||||
|
|
||||||
// We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
|
// 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 mal_device__set_state() here.
|
// the one who puts the device into the stopped state. Don't call mal_device__set_state() here.
|
||||||
|
|||||||
Reference in New Issue
Block a user