mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-24 01:04:02 +02:00
Set up some infrastructure for transitioning to the new callbacks.
With the introduction of custom backends, it became necessary to simplify the backend callbacks. For backwards compatibility, all built-in backends are still using the old callbacks. Future commits are going to be transitioning built-in backends to the new system. This commit is designed to help with this migration so that we can migrate backends one at a time while still keeping compatibility with this old system, thereby allowing backends that haven't yet transitioned to keep working.
This commit is contained in:
+297
-292
@@ -3494,6 +3494,7 @@ struct ma_context_config
|
|||||||
|
|
||||||
struct ma_context
|
struct ma_context
|
||||||
{
|
{
|
||||||
|
ma_backend_callbacks callbacks;
|
||||||
ma_backend backend; /* DirectSound, ALSA, etc. */
|
ma_backend backend; /* DirectSound, ALSA, etc. */
|
||||||
ma_log_proc logCallback;
|
ma_log_proc logCallback;
|
||||||
ma_thread_priority threadPriority;
|
ma_thread_priority threadPriority;
|
||||||
@@ -3836,9 +3837,6 @@ struct ma_context
|
|||||||
int _unused;
|
int _unused;
|
||||||
} webaudio;
|
} webaudio;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_SUPPORT_CUSTOM
|
|
||||||
ma_backend_callbacks custom;
|
|
||||||
#endif
|
|
||||||
#ifdef MA_SUPPORT_NULL
|
#ifdef MA_SUPPORT_NULL
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
@@ -11122,276 +11120,6 @@ static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
|
|
||||||
Custom Backend
|
|
||||||
|
|
||||||
*******************************************************************************/
|
|
||||||
#ifdef MA_HAS_CUSTOM
|
|
||||||
|
|
||||||
static ma_result ma_context_enumerate_devices__custom(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
||||||
{
|
|
||||||
MA_ASSERT(pContext != NULL);
|
|
||||||
MA_ASSERT(callback != NULL);
|
|
||||||
|
|
||||||
if (pContext->custom.onContextEnumerateDevices == NULL) {
|
|
||||||
return MA_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pContext->custom.onContextEnumerateDevices(pContext, callback, pUserData);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_context_get_device_info__custom(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
|
|
||||||
{
|
|
||||||
MA_ASSERT(pContext != NULL);
|
|
||||||
|
|
||||||
(void)shareMode; /* Not using the share mode. This is something I want to remove from device info queries. */
|
|
||||||
|
|
||||||
if (pContext->custom.onContextGetDeviceInfo == NULL) {
|
|
||||||
return MA_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Be as safe as possible and zero out the device info just in case the backend doesn't fill it out properly. */
|
|
||||||
MA_ZERO_OBJECT(pDeviceInfo);
|
|
||||||
|
|
||||||
return pContext->custom.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, pDeviceInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_context_uninit__custom(ma_context* pContext)
|
|
||||||
{
|
|
||||||
MA_ASSERT(pContext != NULL);
|
|
||||||
|
|
||||||
if (pContext->custom.onContextUninit == NULL) {
|
|
||||||
return MA_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pContext->custom.onContextUninit(pContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void ma_device_uninit__custom(ma_device* pDevice)
|
|
||||||
{
|
|
||||||
MA_ASSERT(pDevice != NULL);
|
|
||||||
|
|
||||||
if (pDevice->pContext->custom.onDeviceUninit == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pDevice->pContext->custom.onDeviceUninit(pDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_device_init__custom(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
|
|
||||||
{
|
|
||||||
ma_result result;
|
|
||||||
ma_device_descriptor descriptorPlayback;
|
|
||||||
ma_device_descriptor descriptorCapture;
|
|
||||||
|
|
||||||
MA_ASSERT(pContext != NULL);
|
|
||||||
MA_ASSERT(pConfig != NULL);
|
|
||||||
MA_ASSERT(pDevice != NULL);
|
|
||||||
|
|
||||||
if (pContext->custom.onDeviceInit == NULL) {
|
|
||||||
return MA_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
MA_ZERO_OBJECT(&descriptorPlayback);
|
|
||||||
descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID;
|
|
||||||
descriptorPlayback.shareMode = pConfig->playback.shareMode;
|
|
||||||
descriptorPlayback.format = pConfig->playback.format;
|
|
||||||
descriptorPlayback.channels = pConfig->playback.channels;
|
|
||||||
descriptorPlayback.sampleRate = pConfig->sampleRate;
|
|
||||||
ma_channel_map_copy(descriptorPlayback.channelMap, pConfig->playback.channelMap, pConfig->playback.channels);
|
|
||||||
descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames;
|
|
||||||
descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
|
|
||||||
descriptorPlayback.periodCount = pConfig->periods;
|
|
||||||
|
|
||||||
MA_ZERO_OBJECT(&descriptorCapture);
|
|
||||||
descriptorCapture.pDeviceID = pConfig->capture.pDeviceID;
|
|
||||||
descriptorCapture.shareMode = pConfig->capture.shareMode;
|
|
||||||
descriptorCapture.format = pConfig->capture.format;
|
|
||||||
descriptorCapture.channels = pConfig->capture.channels;
|
|
||||||
descriptorCapture.sampleRate = pConfig->sampleRate;
|
|
||||||
ma_channel_map_copy(descriptorCapture.channelMap, pConfig->capture.channelMap, pConfig->capture.channels);
|
|
||||||
descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames;
|
|
||||||
descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
|
|
||||||
descriptorCapture.periodCount = pConfig->periods;
|
|
||||||
|
|
||||||
result = pContext->custom.onDeviceInit(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
return result; /* Failed to initialize the device. */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between
|
|
||||||
the requested format and the internal format.
|
|
||||||
*/
|
|
||||||
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
|
|
||||||
ma_device_info deviceInfo; /* For retrieving the name. */
|
|
||||||
|
|
||||||
if (!ma_device_descriptor_is_valid(&descriptorCapture)) {
|
|
||||||
ma_device_uninit__custom(pDevice);
|
|
||||||
return MA_INVALID_ARGS;
|
|
||||||
}
|
|
||||||
|
|
||||||
pDevice->capture.internalFormat = descriptorCapture.format;
|
|
||||||
pDevice->capture.internalChannels = descriptorCapture.channels;
|
|
||||||
pDevice->capture.internalSampleRate = descriptorCapture.sampleRate;
|
|
||||||
ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels);
|
|
||||||
pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames;
|
|
||||||
pDevice->capture.internalPeriods = descriptorCapture.periodCount;
|
|
||||||
|
|
||||||
if (pDevice->capture.internalPeriodSizeInFrames == 0) {
|
|
||||||
pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info()` instead. */
|
|
||||||
result = ma_context_get_device_info(pContext, ma_device_type_capture, descriptorCapture.pDeviceID, descriptorCapture.shareMode, &deviceInfo);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
/* We failed to retrieve the device info. Fall back to a default name. */
|
|
||||||
if (descriptorCapture.pDeviceID == NULL) {
|
|
||||||
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
||||||
} else {
|
|
||||||
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
||||||
ma_device_info deviceInfo; /* For retrieving the name. */
|
|
||||||
|
|
||||||
if (!ma_device_descriptor_is_valid(&descriptorPlayback)) {
|
|
||||||
ma_device_uninit__custom(pDevice);
|
|
||||||
return MA_INVALID_ARGS;
|
|
||||||
}
|
|
||||||
|
|
||||||
pDevice->playback.internalFormat = descriptorPlayback.format;
|
|
||||||
pDevice->playback.internalChannels = descriptorPlayback.channels;
|
|
||||||
pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate;
|
|
||||||
ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels);
|
|
||||||
pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames;
|
|
||||||
pDevice->playback.internalPeriods = descriptorPlayback.periodCount;
|
|
||||||
|
|
||||||
if (pDevice->playback.internalPeriodSizeInFrames == 0) {
|
|
||||||
pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. */
|
|
||||||
result = ma_context_get_device_info(pContext, ma_device_type_playback, descriptorPlayback.pDeviceID, descriptorPlayback.shareMode, &deviceInfo);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
/* We failed to retrieve the device info. Fall back to a default name. */
|
|
||||||
if (descriptorPlayback.pDeviceID == NULL) {
|
|
||||||
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
||||||
} else {
|
|
||||||
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. */
|
|
||||||
if (pConfig->deviceType == ma_device_type_duplex) {
|
|
||||||
if (pContext->custom.onDeviceRead == NULL && pContext->custom.onDeviceWrite == NULL && pContext->custom.onDeviceAudioThread == NULL) {
|
|
||||||
result = ma_duplex_rb_init(pDevice->sampleRate, pDevice->capture.internalFormat, pDevice->capture.internalChannels, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
ma_device_uninit__custom(pDevice);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MA_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_device_start__custom(ma_device* pDevice)
|
|
||||||
{
|
|
||||||
MA_ASSERT(pDevice != NULL);
|
|
||||||
|
|
||||||
if (pDevice->pContext->custom.onDeviceStart == NULL) {
|
|
||||||
return MA_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pDevice->pContext->custom.onDeviceStart(pDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_device_stop__custom(ma_device* pDevice)
|
|
||||||
{
|
|
||||||
MA_ASSERT(pDevice != NULL);
|
|
||||||
|
|
||||||
if (pDevice->pContext->custom.onDeviceStop == NULL) {
|
|
||||||
return MA_NOT_IMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pDevice->pContext->custom.onDeviceStop(pDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_device_audio_thread__custom(ma_device* pDevice)
|
|
||||||
{
|
|
||||||
MA_ASSERT(pDevice != NULL);
|
|
||||||
|
|
||||||
if (pDevice->pContext->custom.onDeviceAudioThread == NULL) {
|
|
||||||
return MA_NOT_IMPLEMENTED; /* Should never happen, but check anyway. */
|
|
||||||
}
|
|
||||||
|
|
||||||
return pDevice->pContext->custom.onDeviceAudioThread(pDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_device_audio_thread__read_write__custom(ma_device* pDevice)
|
|
||||||
{
|
|
||||||
return ma_device_audio_thread__default_read_write(pDevice, &pDevice->pContext->custom);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ma_result ma_context_init__custom(const ma_context_config* pConfig, ma_context* pContext)
|
|
||||||
{
|
|
||||||
ma_result result;
|
|
||||||
|
|
||||||
MA_ASSERT(pContext != NULL);
|
|
||||||
MA_ASSERT(pConfig != NULL);
|
|
||||||
|
|
||||||
/* We need to ensure we have the necessary callbacks. */
|
|
||||||
if (pConfig->custom.onContextInit == NULL) {
|
|
||||||
return MA_NO_BACKEND; /* Need a context initialization callback. When set to NULL it means a custom backend is not defined. */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the custom callbacks before firing the backend's context initialization routine just in case the backend wants to reference them for whatever reason. */
|
|
||||||
pContext->custom = pConfig->custom;
|
|
||||||
|
|
||||||
/* Initialize the context first. If this fails we need to abort. */
|
|
||||||
result = pConfig->custom.onContextInit(pContext, &pContext->custom);
|
|
||||||
if (result != MA_SUCCESS) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* At this point the context should be initialized. We can now set up the callbacks for miniaudio's use internally. */
|
|
||||||
pContext->onUninit = ma_context_uninit__custom;
|
|
||||||
pContext->onEnumDevices = ma_context_enumerate_devices__custom;
|
|
||||||
pContext->onGetDeviceInfo = ma_context_get_device_info__custom;
|
|
||||||
pContext->onDeviceInit = ma_device_init__custom;
|
|
||||||
pContext->onDeviceUninit = ma_device_uninit__custom;
|
|
||||||
pContext->onDeviceStart = ma_device_start__custom;
|
|
||||||
pContext->onDeviceStop = ma_device_stop__custom;
|
|
||||||
|
|
||||||
/*
|
|
||||||
For now the context needs to be marked as asynchronous. This is required so that miniaudio knows how to handle data delivery and thread
|
|
||||||
management for the device, but the requirement for the backend itself to set this property will probably be removed in the future.
|
|
||||||
*/
|
|
||||||
if (pContext->custom.onDeviceRead == NULL && pContext->custom.onDeviceWrite == NULL) {
|
|
||||||
if (pContext->custom.onDeviceAudioThread == NULL) {
|
|
||||||
pContext->onDeviceMainLoop = NULL; /* Backend is asynchronous and expected to call ma_device_handle_backend_data_callback() from within their data callback. */
|
|
||||||
pContext->isBackendAsynchronous = MA_TRUE;
|
|
||||||
} else {
|
|
||||||
pContext->onDeviceMainLoop = ma_device_audio_thread__custom; /* Backend is doing a custom main loop. */
|
|
||||||
pContext->isBackendAsynchronous = MA_FALSE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pContext->isBackendAsynchronous = MA_FALSE;
|
|
||||||
pContext->onDeviceMainLoop = ma_device_audio_thread__read_write__custom; /* Backend is using blocking read/write calls. */
|
|
||||||
}
|
|
||||||
|
|
||||||
return MA_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* MA_HAS_CUSTOM */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
||||||
@@ -31814,6 +31542,15 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* TEMP: Helper for determining whether or not a context is using the new callback system. Eventually all backends will be using the new callback system. */
|
||||||
|
static ma_bool32 ma_context__is_using_new_callbacks(ma_context* pContext)
|
||||||
|
{
|
||||||
|
MA_ASSERT(pContext != NULL);
|
||||||
|
|
||||||
|
return pContext->callbacks.onContextInit != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
|
static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
|
||||||
{
|
{
|
||||||
ma_device* pDevice = (ma_device*)pData;
|
ma_device* pDevice = (ma_device*)pData;
|
||||||
@@ -31857,11 +31594,21 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
|
|||||||
ma_device__set_state(pDevice, MA_STATE_STARTED);
|
ma_device__set_state(pDevice, MA_STATE_STARTED);
|
||||||
ma_event_signal(&pDevice->startEvent);
|
ma_event_signal(&pDevice->startEvent);
|
||||||
|
|
||||||
|
if (ma_context__is_using_new_callbacks(pDevice->pContext)) {
|
||||||
|
if (pDevice->pContext->callbacks.onDeviceAudioThread != NULL) {
|
||||||
|
pDevice->pContext->callbacks.onDeviceAudioThread(pDevice);
|
||||||
|
} else {
|
||||||
|
/* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */
|
||||||
|
ma_device_audio_thread__default_read_write(pDevice, &pDevice->pContext->callbacks);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (pDevice->pContext->onDeviceMainLoop != NULL) {
|
if (pDevice->pContext->onDeviceMainLoop != NULL) {
|
||||||
pDevice->pContext->onDeviceMainLoop(pDevice);
|
pDevice->pContext->onDeviceMainLoop(pDevice);
|
||||||
} else {
|
} else {
|
||||||
ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "No main loop implementation.", MA_API_NOT_FOUND);
|
ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "No main loop implementation.", MA_API_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
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
|
||||||
@@ -32065,7 +31812,21 @@ static ma_result ma_context_uninit_backend_apis(ma_context* pContext)
|
|||||||
|
|
||||||
static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
|
static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
|
||||||
{
|
{
|
||||||
|
MA_ASSERT(pContext != NULL);
|
||||||
|
|
||||||
|
if (ma_context__is_using_new_callbacks(pContext)) {
|
||||||
|
if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) {
|
||||||
|
if (pContext->callbacks.onDeviceAudioThread == NULL) {
|
||||||
|
return MA_TRUE;
|
||||||
|
} else {
|
||||||
|
return MA_FALSE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return MA_FALSE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return pContext->isBackendAsynchronous;
|
return pContext->isBackendAsynchronous;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -32143,7 +31904,25 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC
|
|||||||
*/
|
*/
|
||||||
pContext->isBackendAsynchronous = MA_FALSE;
|
pContext->isBackendAsynchronous = MA_FALSE;
|
||||||
|
|
||||||
|
/* These backends are using the new callback system. */
|
||||||
|
switch (backend) {
|
||||||
|
#ifdef MA_HAS_CUSTOM
|
||||||
|
case ma_backend_custom:
|
||||||
|
{
|
||||||
|
/* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */
|
||||||
|
pContext->callbacks = config.custom;
|
||||||
|
} break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pContext->callbacks.onContextInit != NULL) {
|
||||||
|
result = pContext->callbacks.onContextInit(pContext, &pContext->callbacks);
|
||||||
|
} else {
|
||||||
result = MA_NO_BACKEND;
|
result = MA_NO_BACKEND;
|
||||||
|
|
||||||
|
/* TEMP. Try falling back to the old callback system. Eventually this switch will be removed completely. */
|
||||||
switch (backend) {
|
switch (backend) {
|
||||||
#ifdef MA_HAS_WASAPI
|
#ifdef MA_HAS_WASAPI
|
||||||
case ma_backend_wasapi:
|
case ma_backend_wasapi:
|
||||||
@@ -32226,7 +32005,7 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC
|
|||||||
#ifdef MA_HAS_CUSTOM
|
#ifdef MA_HAS_CUSTOM
|
||||||
case ma_backend_custom:
|
case ma_backend_custom:
|
||||||
{
|
{
|
||||||
result = ma_context_init__custom(&config, pContext);
|
/*result = ma_context_init__custom(&config, pContext);*/
|
||||||
} break;
|
} break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef MA_HAS_NULL
|
#ifdef MA_HAS_NULL
|
||||||
@@ -32238,6 +32017,7 @@ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendC
|
|||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* If this iteration was successful, return. */
|
/* If this iteration was successful, return. */
|
||||||
if (result == MA_SUCCESS) {
|
if (result == MA_SUCCESS) {
|
||||||
@@ -32294,15 +32074,31 @@ MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devi
|
|||||||
{
|
{
|
||||||
ma_result result;
|
ma_result result;
|
||||||
|
|
||||||
if (pContext == NULL || pContext->onEnumDevices == NULL || callback == NULL) {
|
if (pContext == NULL || callback == NULL) {
|
||||||
return MA_INVALID_ARGS;
|
return MA_INVALID_ARGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ma_context__is_using_new_callbacks(pContext)) {
|
||||||
|
if (pContext->callbacks.onContextEnumerateDevices == NULL) {
|
||||||
|
return MA_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_mutex_lock(&pContext->deviceEnumLock);
|
||||||
|
{
|
||||||
|
result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData);
|
||||||
|
}
|
||||||
|
ma_mutex_unlock(&pContext->deviceEnumLock);
|
||||||
|
} else {
|
||||||
|
if (pContext->onEnumDevices == NULL) {
|
||||||
|
return MA_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
|
||||||
ma_mutex_lock(&pContext->deviceEnumLock);
|
ma_mutex_lock(&pContext->deviceEnumLock);
|
||||||
{
|
{
|
||||||
result = pContext->onEnumDevices(pContext, callback, pUserData);
|
result = pContext->onEnumDevices(pContext, callback, pUserData);
|
||||||
}
|
}
|
||||||
ma_mutex_unlock(&pContext->deviceEnumLock);
|
ma_mutex_unlock(&pContext->deviceEnumLock);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -32371,6 +32167,16 @@ MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** p
|
|||||||
return MA_INVALID_ARGS;
|
return MA_INVALID_ARGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ma_context__is_using_new_callbacks(pContext)) {
|
||||||
|
if (pContext->callbacks.onContextEnumerateDevices == NULL) {
|
||||||
|
return MA_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pContext->onEnumDevices == NULL) {
|
||||||
|
return MA_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */
|
/* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */
|
||||||
ma_mutex_lock(&pContext->deviceEnumLock);
|
ma_mutex_lock(&pContext->deviceEnumLock);
|
||||||
{
|
{
|
||||||
@@ -32379,7 +32185,12 @@ MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** p
|
|||||||
pContext->captureDeviceInfoCount = 0;
|
pContext->captureDeviceInfoCount = 0;
|
||||||
|
|
||||||
/* Now enumerate over available devices. */
|
/* Now enumerate over available devices. */
|
||||||
|
if (ma_context__is_using_new_callbacks(pContext)) {
|
||||||
|
result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL);
|
||||||
|
} else {
|
||||||
result = pContext->onEnumDevices(pContext, ma_context_get_devices__enum_callback, NULL);
|
result = pContext->onEnumDevices(pContext, ma_context_get_devices__enum_callback, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (result == MA_SUCCESS) {
|
if (result == MA_SUCCESS) {
|
||||||
/* Playback devices. */
|
/* Playback devices. */
|
||||||
if (ppPlaybackDeviceInfos != NULL) {
|
if (ppPlaybackDeviceInfos != NULL) {
|
||||||
@@ -32405,6 +32216,7 @@ MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** p
|
|||||||
|
|
||||||
MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
|
MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
|
||||||
{
|
{
|
||||||
|
ma_result result;
|
||||||
ma_device_info deviceInfo;
|
ma_device_info deviceInfo;
|
||||||
|
|
||||||
/* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */
|
/* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */
|
||||||
@@ -32419,12 +32231,24 @@ MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type
|
|||||||
MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
|
MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pContext->onGetDeviceInfo != NULL) {
|
if (ma_context__is_using_new_callbacks(pContext)) {
|
||||||
ma_result result;
|
if (pContext->callbacks.onContextGetDeviceInfo == NULL) {
|
||||||
|
return MA_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pContext->onGetDeviceInfo == NULL) {
|
||||||
|
return MA_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ma_mutex_lock(&pContext->deviceInfoLock);
|
ma_mutex_lock(&pContext->deviceInfoLock);
|
||||||
{
|
{
|
||||||
|
if (ma_context__is_using_new_callbacks(pContext)) {
|
||||||
|
result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo);
|
||||||
|
} else {
|
||||||
result = pContext->onGetDeviceInfo(pContext, deviceType, pDeviceID, shareMode, &deviceInfo);
|
result = pContext->onGetDeviceInfo(pContext, deviceType, pDeviceID, shareMode, &deviceInfo);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ma_mutex_unlock(&pContext->deviceInfoLock);
|
ma_mutex_unlock(&pContext->deviceInfoLock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -32504,10 +32328,6 @@ MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type
|
|||||||
|
|
||||||
*pDeviceInfo = deviceInfo;
|
*pDeviceInfo = deviceInfo;
|
||||||
return result;
|
return result;
|
||||||
}
|
|
||||||
|
|
||||||
/* Getting here means onGetDeviceInfo has not been set. */
|
|
||||||
return MA_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext)
|
MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext)
|
||||||
@@ -32539,16 +32359,34 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC
|
|||||||
ma_result result;
|
ma_result result;
|
||||||
ma_device_config config;
|
ma_device_config config;
|
||||||
|
|
||||||
|
/* The context can be null, in which case we self-manage it. */
|
||||||
if (pContext == NULL) {
|
if (pContext == NULL) {
|
||||||
return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
|
return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pDevice == NULL) {
|
if (pDevice == NULL) {
|
||||||
return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
|
return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MA_ZERO_OBJECT(pDevice);
|
||||||
|
|
||||||
if (pConfig == NULL) {
|
if (pConfig == NULL) {
|
||||||
return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pConfig == NULL).", MA_INVALID_ARGS);
|
return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pConfig == NULL).", MA_INVALID_ARGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Check that we have our callbacks defined. */
|
||||||
|
if (ma_context__is_using_new_callbacks(pContext)) {
|
||||||
|
if (pContext->callbacks.onDeviceInit == NULL) {
|
||||||
|
return MA_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pContext->onDeviceInit == NULL) {
|
||||||
|
return MA_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* We need to make a copy of the config so we can set default values if they were left unset in the input config. */
|
/* We need to make a copy of the config so we can set default values if they were left unset in the input config. */
|
||||||
config = *pConfig;
|
config = *pConfig;
|
||||||
|
|
||||||
@@ -32575,8 +32413,6 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MA_ZERO_OBJECT(pDevice);
|
|
||||||
pDevice->pContext = pContext;
|
pDevice->pContext = pContext;
|
||||||
|
|
||||||
/* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
|
/* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
|
||||||
@@ -32593,6 +32429,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC
|
|||||||
if (config.playback.pDeviceID != NULL) {
|
if (config.playback.pDeviceID != NULL) {
|
||||||
MA_COPY_MEMORY(&pDevice->playback.id, config.playback.pDeviceID, sizeof(pDevice->playback.id));
|
MA_COPY_MEMORY(&pDevice->playback.id, config.playback.pDeviceID, sizeof(pDevice->playback.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.capture.pDeviceID != NULL) {
|
if (config.capture.pDeviceID != NULL) {
|
||||||
MA_COPY_MEMORY(&pDevice->capture.id, config.capture.pDeviceID, sizeof(pDevice->capture.id));
|
MA_COPY_MEMORY(&pDevice->capture.id, config.capture.pDeviceID, sizeof(pDevice->capture.id));
|
||||||
}
|
}
|
||||||
@@ -32720,11 +32557,131 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
result = pContext->onDeviceInit(pContext, &config, pDevice);
|
if (ma_context__is_using_new_callbacks(pContext)) {
|
||||||
|
ma_device_descriptor descriptorPlayback;
|
||||||
|
ma_device_descriptor descriptorCapture;
|
||||||
|
|
||||||
|
MA_ZERO_OBJECT(&descriptorPlayback);
|
||||||
|
descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID;
|
||||||
|
descriptorPlayback.shareMode = pConfig->playback.shareMode;
|
||||||
|
descriptorPlayback.format = pConfig->playback.format;
|
||||||
|
descriptorPlayback.channels = pConfig->playback.channels;
|
||||||
|
descriptorPlayback.sampleRate = pConfig->sampleRate;
|
||||||
|
ma_channel_map_copy(descriptorPlayback.channelMap, pConfig->playback.channelMap, pConfig->playback.channels);
|
||||||
|
descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames;
|
||||||
|
descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
|
||||||
|
descriptorPlayback.periodCount = pConfig->periods;
|
||||||
|
|
||||||
|
MA_ZERO_OBJECT(&descriptorCapture);
|
||||||
|
descriptorCapture.pDeviceID = pConfig->capture.pDeviceID;
|
||||||
|
descriptorCapture.shareMode = pConfig->capture.shareMode;
|
||||||
|
descriptorCapture.format = pConfig->capture.format;
|
||||||
|
descriptorCapture.channels = pConfig->capture.channels;
|
||||||
|
descriptorCapture.sampleRate = pConfig->sampleRate;
|
||||||
|
ma_channel_map_copy(descriptorCapture.channelMap, pConfig->capture.channelMap, pConfig->capture.channels);
|
||||||
|
descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames;
|
||||||
|
descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
|
||||||
|
descriptorCapture.periodCount = pConfig->periods;
|
||||||
|
|
||||||
|
result = pContext->callbacks.onDeviceInit(pDevice, config.deviceType, &descriptorPlayback, &descriptorCapture);
|
||||||
if (result != MA_SUCCESS) {
|
if (result != MA_SUCCESS) {
|
||||||
|
ma_event_uninit(&pDevice->startEvent);
|
||||||
|
ma_event_uninit(&pDevice->wakeupEvent);
|
||||||
|
ma_mutex_uninit(&pDevice->lock);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between
|
||||||
|
the requested format and the internal format.
|
||||||
|
*/
|
||||||
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
|
||||||
|
ma_device_info deviceInfo; /* For retrieving the name. */
|
||||||
|
|
||||||
|
if (!ma_device_descriptor_is_valid(&descriptorCapture)) {
|
||||||
|
ma_device_uninit(pDevice);
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
pDevice->capture.internalFormat = descriptorCapture.format;
|
||||||
|
pDevice->capture.internalChannels = descriptorCapture.channels;
|
||||||
|
pDevice->capture.internalSampleRate = descriptorCapture.sampleRate;
|
||||||
|
ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels);
|
||||||
|
pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames;
|
||||||
|
pDevice->capture.internalPeriods = descriptorCapture.periodCount;
|
||||||
|
|
||||||
|
if (pDevice->capture.internalPeriodSizeInFrames == 0) {
|
||||||
|
pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info()` instead. */
|
||||||
|
result = ma_context_get_device_info(pContext, ma_device_type_capture, descriptorCapture.pDeviceID, descriptorCapture.shareMode, &deviceInfo);
|
||||||
|
if (result == MA_SUCCESS) {
|
||||||
|
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
|
||||||
|
} else {
|
||||||
|
/* We failed to retrieve the device info. Fall back to a default name. */
|
||||||
|
if (descriptorCapture.pDeviceID == NULL) {
|
||||||
|
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
||||||
|
} else {
|
||||||
|
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
||||||
|
ma_device_info deviceInfo; /* For retrieving the name. */
|
||||||
|
|
||||||
|
if (!ma_device_descriptor_is_valid(&descriptorPlayback)) {
|
||||||
|
ma_device_uninit(pDevice);
|
||||||
|
return MA_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
pDevice->playback.internalFormat = descriptorPlayback.format;
|
||||||
|
pDevice->playback.internalChannels = descriptorPlayback.channels;
|
||||||
|
pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate;
|
||||||
|
ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels);
|
||||||
|
pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames;
|
||||||
|
pDevice->playback.internalPeriods = descriptorPlayback.periodCount;
|
||||||
|
|
||||||
|
if (pDevice->playback.internalPeriodSizeInFrames == 0) {
|
||||||
|
pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. */
|
||||||
|
result = ma_context_get_device_info(pContext, ma_device_type_playback, descriptorPlayback.pDeviceID, descriptorPlayback.shareMode, &deviceInfo);
|
||||||
|
if (result == MA_SUCCESS) {
|
||||||
|
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
|
||||||
|
} else {
|
||||||
|
/* We failed to retrieve the device info. Fall back to a default name. */
|
||||||
|
if (descriptorPlayback.pDeviceID == NULL) {
|
||||||
|
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
||||||
|
} else {
|
||||||
|
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. */
|
||||||
|
if (ma_context_is_backend_asynchronous(pContext)) {
|
||||||
|
if (pConfig->deviceType == ma_device_type_duplex) {
|
||||||
|
result = ma_duplex_rb_init(pDevice->sampleRate, pDevice->capture.internalFormat, pDevice->capture.internalChannels, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
ma_device_uninit(pDevice);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = pContext->onDeviceInit(pContext, &config, pDevice);
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
ma_event_uninit(&pDevice->startEvent);
|
||||||
|
ma_event_uninit(&pDevice->wakeupEvent);
|
||||||
|
ma_mutex_uninit(&pDevice->lock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ma_device__post_init_setup(pDevice, pConfig->deviceType);
|
ma_device__post_init_setup(pDevice, pConfig->deviceType);
|
||||||
|
|
||||||
|
|
||||||
@@ -32859,7 +32816,16 @@ MA_API void ma_device_uninit(ma_device* pDevice)
|
|||||||
ma_thread_wait(&pDevice->thread);
|
ma_thread_wait(&pDevice->thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ma_context__is_using_new_callbacks(pDevice->pContext)) {
|
||||||
|
if (pDevice->pContext->callbacks.onDeviceUninit != NULL) {
|
||||||
|
pDevice->pContext->callbacks.onDeviceUninit(pDevice);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pDevice->pContext->onDeviceUninit != NULL) {
|
||||||
pDevice->pContext->onDeviceUninit(pDevice);
|
pDevice->pContext->onDeviceUninit(pDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ma_event_uninit(&pDevice->stopEvent);
|
ma_event_uninit(&pDevice->stopEvent);
|
||||||
ma_event_uninit(&pDevice->startEvent);
|
ma_event_uninit(&pDevice->startEvent);
|
||||||
@@ -32902,7 +32868,20 @@ MA_API ma_result ma_device_start(ma_device* pDevice)
|
|||||||
|
|
||||||
/* Asynchronous backends need to be handled differently. */
|
/* Asynchronous backends need to be handled differently. */
|
||||||
if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
|
if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
|
||||||
|
if (ma_context__is_using_new_callbacks(pDevice->pContext)) {
|
||||||
|
if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
|
||||||
|
result = pDevice->pContext->callbacks.onDeviceStart(pDevice);
|
||||||
|
} else {
|
||||||
|
result = MA_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pDevice->pContext->onDeviceStart != NULL) {
|
||||||
result = pDevice->pContext->onDeviceStart(pDevice);
|
result = pDevice->pContext->onDeviceStart(pDevice);
|
||||||
|
} else {
|
||||||
|
result = MA_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (result == MA_SUCCESS) {
|
if (result == MA_SUCCESS) {
|
||||||
ma_device__set_state(pDevice, MA_STATE_STARTED);
|
ma_device__set_state(pDevice, MA_STATE_STARTED);
|
||||||
}
|
}
|
||||||
@@ -32920,6 +32899,11 @@ MA_API ma_result ma_device_start(ma_device* pDevice)
|
|||||||
ma_event_wait(&pDevice->startEvent);
|
ma_event_wait(&pDevice->startEvent);
|
||||||
result = pDevice->workResult;
|
result = pDevice->workResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */
|
||||||
|
if (result != MA_SUCCESS) {
|
||||||
|
ma_device__set_state(pDevice, MA_STATE_STOPPED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ma_mutex_unlock(&pDevice->lock);
|
ma_mutex_unlock(&pDevice->lock);
|
||||||
|
|
||||||
@@ -32950,18 +32934,39 @@ MA_API ma_result ma_device_stop(ma_device* pDevice)
|
|||||||
|
|
||||||
ma_device__set_state(pDevice, MA_STATE_STOPPING);
|
ma_device__set_state(pDevice, MA_STATE_STOPPING);
|
||||||
|
|
||||||
/* There's no need to wake up the thread like we do when starting. */
|
/* Asynchronous backends need to be handled differently. */
|
||||||
if (pDevice->pContext->onDeviceStop) {
|
if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
|
||||||
|
/* Asynchronous backends must have a stop operation. */
|
||||||
|
if (ma_context__is_using_new_callbacks(pDevice->pContext)) {
|
||||||
|
if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
|
||||||
|
result = pDevice->pContext->callbacks.onDeviceStop(pDevice);
|
||||||
|
} else {
|
||||||
|
result = MA_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pDevice->pContext->onDeviceStop != NULL) {
|
||||||
|
result = pDevice->pContext->onDeviceStop(pDevice);
|
||||||
|
} else {
|
||||||
|
result = MA_INVALID_OPERATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_device__set_state(pDevice, MA_STATE_STOPPED);
|
||||||
|
} else {
|
||||||
|
/* Synchronous backends. Devices can optionally have a stop operation here. */
|
||||||
|
if (ma_context__is_using_new_callbacks(pDevice->pContext)) {
|
||||||
|
if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
|
||||||
|
result = pDevice->pContext->callbacks.onDeviceStop(pDevice);
|
||||||
|
} else {
|
||||||
|
result = MA_SUCCESS;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pDevice->pContext->onDeviceStop != NULL) {
|
||||||
result = pDevice->pContext->onDeviceStop(pDevice);
|
result = pDevice->pContext->onDeviceStop(pDevice);
|
||||||
} else {
|
} else {
|
||||||
result = MA_SUCCESS;
|
result = MA_SUCCESS;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/* Asynchronous backends need to be handled differently. */
|
|
||||||
if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
|
|
||||||
ma_device__set_state(pDevice, MA_STATE_STOPPED);
|
|
||||||
} else {
|
|
||||||
/* Synchronous backends. */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
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
|
||||||
|
|||||||
Reference in New Issue
Block a user