Commit 994cda44 authored by David Reid's avatar David Reid

Version 0.10.34

parent 16a605ee
/* /*
Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
miniaudio - v0.10.33 - 2021-04-04 miniaudio - v0.10.34 - 2021-04-26
David Reid - mackron@gmail.com David Reid - mackron@gmail.com
...@@ -6038,6 +6038,7 @@ static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enu ...@@ -6038,6 +6038,7 @@ static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enu
ma_device_info deviceInfo; ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo); MA_ZERO_OBJECT(&deviceInfo);
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1); ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
} }
...@@ -6046,6 +6047,7 @@ static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enu ...@@ -6046,6 +6047,7 @@ static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enu
ma_device_info deviceInfo; ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo); MA_ZERO_OBJECT(&deviceInfo);
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1); ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
} }
...@@ -6069,6 +6071,8 @@ static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_devic ...@@ -6069,6 +6071,8 @@ static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_devic
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1); ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
} }
pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
/* Support everything on the null backend. */ /* Support everything on the null backend. */
pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
pDeviceInfo->nativeDataFormats[0].channels = 0; pDeviceInfo->nativeDataFormats[0].channels = 0;
...@@ -7630,9 +7634,9 @@ static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pU ...@@ -7630,9 +7634,9 @@ static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pU
case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI: case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI:
{ {
if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) { if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) {
result = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService)); *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService));
} else { } else {
result = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService)); *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService));
} }
} break; } break;
...@@ -7675,18 +7679,19 @@ static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pU ...@@ -7675,18 +7679,19 @@ static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pU
static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService) static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService)
{ {
ma_result result; ma_result result;
ma_result cmdResult;
ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI); ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI);
cmd.data.createAudioClient.deviceType = deviceType; cmd.data.createAudioClient.deviceType = deviceType;
cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient; cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient;
cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService; cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService;
cmd.data.createAudioClient.result = MA_SUCCESS; cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */
result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */ result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
return cmd.data.createAudioClient.result; return *cmd.data.createAudioClient.pResult;
} }
#if 0 /* Not used at the moment, but leaving here for future use. */ #if 0 /* Not used at the moment, but leaving here for future use. */
...@@ -9682,9 +9687,17 @@ static ma_result ma_device_data_loop__wasapi(ma_device* pDevice) ...@@ -9682,9 +9687,17 @@ static ma_result ma_device_data_loop__wasapi(ma_device* pDevice)
/* Wait for data to become available first. */ /* Wait for data to become available first. */
if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
/*
For capture we can terminate here because it probably means the microphone just isn't delivering data for whatever reason, but
for loopback is most likely means nothing is actually playing. We want to keep trying in this situation.
*/
if (pDevice->type == ma_device_type_loopback) {
continue; /* Keep waiting in loopback mode. */
} else {
exitLoop = MA_TRUE; exitLoop = MA_TRUE;
break; /* Wait failed. */ break; /* Wait failed. */
} }
}
/* See how many frames are available. Since we waited at the top, I don't think this should ever return 0. I'm checking for this anyway. */ /* See how many frames are available. Since we waited at the top, I don't think this should ever return 0. I'm checking for this anyway. */
result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture); result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
...@@ -12757,6 +12770,9 @@ ALSA Backend ...@@ -12757,6 +12770,9 @@ ALSA Backend
******************************************************************************/ ******************************************************************************/
#ifdef MA_HAS_ALSA #ifdef MA_HAS_ALSA
#include <poll.h> /* poll(), struct pollfd */
#include <sys/eventfd.h> /* eventfd() */
#ifdef MA_NO_RUNTIME_LINKING #ifdef MA_NO_RUNTIME_LINKING
/* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ /* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
...@@ -13015,6 +13031,7 @@ typedef int (* ma_snd_pcm_prepare_proc) ( ...@@ -13015,6 +13031,7 @@ typedef int (* ma_snd_pcm_prepare_proc) (
typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm); typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm);
typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm); typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm);
typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm); typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm);
typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm);
typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints);
typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id); typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id);
typedef int (* ma_snd_card_get_index_proc) (const char *name); typedef int (* ma_snd_card_get_index_proc) (const char *name);
...@@ -13027,9 +13044,13 @@ typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) ( ...@@ -13027,9 +13044,13 @@ typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (
typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm); typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm);
typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm); typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm);
typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout); typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout);
typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock);
typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info); typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);
typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void); typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void);
typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info); typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info);
typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm);
typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
typedef int (* ma_snd_config_update_free_global_proc) (void); typedef int (* ma_snd_config_update_free_global_proc) (void);
/* This array specifies each of the common devices that can be used for both playback and capture. */ /* This array specifies each of the common devices that can be used for both playback and capture. */
...@@ -13839,10 +13860,14 @@ static ma_result ma_device_uninit__alsa(ma_device* pDevice) ...@@ -13839,10 +13860,14 @@ static ma_result ma_device_uninit__alsa(ma_device* pDevice)
if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) { if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
close(pDevice->alsa.wakeupfdCapture);
ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks);
} }
if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) { if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
close(pDevice->alsa.wakeupfdPlayback);
ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks);
} }
return MA_SUCCESS; return MA_SUCCESS;
...@@ -13865,6 +13890,9 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic ...@@ -13865,6 +13890,9 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic
ma_snd_pcm_hw_params_t* pHWParams; ma_snd_pcm_hw_params_t* pHWParams;
ma_snd_pcm_sw_params_t* pSWParams; ma_snd_pcm_sw_params_t* pSWParams;
ma_snd_pcm_uframes_t bufferBoundary; ma_snd_pcm_uframes_t bufferBoundary;
int pollDescriptorCount;
struct pollfd* pPollDescriptors;
int wakeupfd;
MA_ASSERT(pConfig != NULL); MA_ASSERT(pConfig != NULL);
MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */ MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */
...@@ -14177,9 +14205,64 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic ...@@ -14177,9 +14205,64 @@ static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_devic
} }
/*
We need to retrieve the poll descriptors so we can use poll() to wait for data to become
available for reading or writing. There's no well defined maximum for this so we're just going
to allocate this on the heap.
*/
pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM);
if (pollDescriptorCount <= 0) {
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count.", MA_ERROR);
}
pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks/*, MA_ALLOCATION_TYPE_GENERAL*/); /* +1 because we want room for the wakeup descriptor. */
if (pPollDescriptors == NULL) {
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors.", MA_OUT_OF_MEMORY);
}
/*
We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver
never returns from writei() and readi(). This has been observed with the "pulse" device.
*/
wakeupfd = eventfd(0, 0);
if (wakeupfd < 0) {
ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup.", ma_result_from_errno(errno));
}
/* We'll place the wakeup fd at the start of the buffer. */
pPollDescriptors[0].fd = wakeupfd;
pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */
pPollDescriptors[0].revents = 0;
/* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */
pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount); /* +1 because we want to place these descriptors after the wakeup descriptor. */
if (pollDescriptorCount <= 0) {
close(wakeupfd);
ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors.", MA_ERROR);
}
if (deviceType == ma_device_type_capture) {
pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount;
pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors;
pDevice->alsa.wakeupfdCapture = wakeupfd;
} else {
pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount;
pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors;
pDevice->alsa.wakeupfdPlayback = wakeupfd;
}
/* We're done. Prepare the device. */ /* We're done. Prepare the device. */
resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM); resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM);
if (resultALSA < 0) { if (resultALSA < 0) {
close(wakeupfd);
ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", ma_result_from_errno(-resultALSA)); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", ma_result_from_errno(-resultALSA));
} }
...@@ -14253,30 +14336,113 @@ static ma_result ma_device_start__alsa(ma_device* pDevice) ...@@ -14253,30 +14336,113 @@ static ma_result ma_device_start__alsa(ma_device* pDevice)
static ma_result ma_device_stop__alsa(ma_device* pDevice) static ma_result ma_device_stop__alsa(ma_device* pDevice)
{ {
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); #ifdef MA_DEBUG_OUTPUT
printf("[ALSA] Dropping capture device... ");
#endif
((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
#ifdef MA_DEBUG_OUTPUT
printf("Done\n");
#endif
/* We need to prepare the device again, otherwise we won't be able to restart the device. */ /* We need to prepare the device again, otherwise we won't be able to restart the device. */
#ifdef MA_DEBUG_OUTPUT
printf("[ALSA] Preparing capture device... ");
#endif
if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
#ifdef MA_DEBUG_OUTPUT #ifdef MA_DEBUG_OUTPUT
printf("[ALSA] Failed to prepare capture device after stopping.\n"); printf("Failed\n");
#endif
} else {
#ifdef MA_DEBUG_OUTPUT
printf("Done\n");
#endif #endif
} }
} }
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); #ifdef MA_DEBUG_OUTPUT
printf("[ALSA] Dropping playback device... ");
#endif
((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
#ifdef MA_DEBUG_OUTPUT
printf("Done\n");
#endif
/* We need to prepare the device again, otherwise we won't be able to restart the device. */ /* We need to prepare the device again, otherwise we won't be able to restart the device. */
#ifdef MA_DEBUG_OUTPUT
printf("[ALSA] Preparing playback device... ");
#endif
if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
#ifdef MA_DEBUG_OUTPUT #ifdef MA_DEBUG_OUTPUT
printf("[ALSA] Failed to prepare playback device after stopping.\n"); printf("Failed\n");
#endif
} else {
#ifdef MA_DEBUG_OUTPUT
printf("Done\n");
#endif
}
}
return MA_SUCCESS;
}
static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent)
{
for (;;) {
unsigned short revents;
int resultALSA;
int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1);
if (resultPoll < 0) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.", ma_result_from_errno(errno));
}
/*
Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor
has had it's POLLIN flag set. If so, we need to actually read the data and then exit
function. The wakeup descriptor will be the first item in the descriptors buffer.
*/
if ((pPollDescriptors[0].revents & POLLIN) != 0) {
ma_uint64 t;
read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */
#ifdef MA_DEBUG_OUTPUT
printf("[ALSA] POLLIN set for wakeupfd\n");
#endif #endif
return MA_DEVICE_NOT_STARTED;
}
/*
Getting here means that some data should be able to be read. We need to use ALSA to
translate the revents flags for us.
*/
resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */
if (resultALSA < 0) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.", ma_result_from_errno(-resultALSA));
}
if ((revents & POLLERR) != 0) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] POLLERR detected.", ma_result_from_errno(errno));
}
if ((revents & requiredEvent) == requiredEvent) {
break; /* We're done. Data available for reading or writing. */
} }
} }
return MA_SUCCESS; return MA_SUCCESS;
} }
static ma_result ma_device_wait_read__alsa(ma_device* pDevice)
{
return ma_device_wait__alsa(pDevice, pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */
}
static ma_result ma_device_wait_write__alsa(ma_device* pDevice)
{
return ma_device_wait__alsa(pDevice, pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */
}
static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead) static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
{ {
ma_snd_pcm_sframes_t resultALSA; ma_snd_pcm_sframes_t resultALSA;
...@@ -14288,7 +14454,16 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u ...@@ -14288,7 +14454,16 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u
*pFramesRead = 0; *pFramesRead = 0;
} }
for (;;) { while (ma_device_get_state(pDevice) == MA_STATE_STARTED) {
ma_result result;
/* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */
result = ma_device_wait_read__alsa(pDevice);
if (result != MA_SUCCESS) {
return result;
}
/* Getting here means we should have data available. */
resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount); resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
if (resultALSA >= 0) { if (resultALSA >= 0) {
break; /* Success. */ break; /* Success. */
...@@ -14312,10 +14487,7 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u ...@@ -14312,10 +14487,7 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA)); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA));
} }
resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount); continue; /* Try reading again. */
if (resultALSA < 0) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to read data from the internal device.", ma_result_from_errno((int)-resultALSA));
}
} }
} }
} }
...@@ -14338,7 +14510,15 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ...@@ -14338,7 +14510,15 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames,
*pFramesWritten = 0; *pFramesWritten = 0;
} }
for (;;) { while (ma_device_get_state(pDevice) == MA_STATE_STARTED) {
ma_result result;
/* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */
result = ma_device_wait_write__alsa(pDevice);
if (result != MA_SUCCESS) {
return result;
}
resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount); resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
if (resultALSA >= 0) { if (resultALSA >= 0) {
break; /* Success. */ break; /* Success. */
...@@ -14352,8 +14532,8 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ...@@ -14352,8 +14532,8 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames,
#endif #endif
/* Underrun. Recover and try again. If this fails we need to return an error. */ /* Underrun. Recover and try again. If this fails we need to return an error. */
resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */
if (resultALSA < 0) { /* MA_TRUE=silent (don't print anything on error). */ if (resultALSA < 0) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", ma_result_from_errno((int)-resultALSA)); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", ma_result_from_errno((int)-resultALSA));
} }
...@@ -14369,10 +14549,7 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ...@@ -14369,10 +14549,7 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames,
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA)); return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA));
} }
resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount); continue; /* Try writing again. */
if (resultALSA < 0) {
return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write data to device after underrun.", ma_result_from_errno((int)-resultALSA));
}
} }
} }
} }
...@@ -14384,6 +14561,31 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ...@@ -14384,6 +14561,31 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames,
return MA_SUCCESS; return MA_SUCCESS;
} }
static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice)
{
ma_uint64 t = 1;
MA_ASSERT(pDevice != NULL);
#ifdef MA_DEBUG_OUTPUT
printf("[ALSA] Waking up... ");
#endif
/* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */
if (pDevice->alsa.pPollDescriptorsCapture != NULL) {
write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t));
}
if (pDevice->alsa.pPollDescriptorsPlayback != NULL) {
write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t));
}
#ifdef MA_DEBUG_OUTPUT
printf("Done\n");
#endif
return MA_SUCCESS;
}
static ma_result ma_context_uninit__alsa(ma_context* pContext) static ma_result ma_context_uninit__alsa(ma_context* pContext)
{ {
MA_ASSERT(pContext != NULL); MA_ASSERT(pContext != NULL);
...@@ -14469,6 +14671,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co ...@@ -14469,6 +14671,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co
pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start"); pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start");
pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop"); pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop");
pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain"); pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain");
pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_reset");
pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint"); pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint");
pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint"); pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint");
pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index"); pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index");
...@@ -14481,9 +14684,13 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co ...@@ -14481,9 +14684,13 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co
pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail"); pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail");
pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update"); pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update");
pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait"); pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait");
pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_nonblock");
pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info"); pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info");
pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name"); pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name");
pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors");
pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count");
pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents");
pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global"); pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global");
#else #else
/* The system below is just for type safety. */ /* The system below is just for type safety. */
...@@ -14532,6 +14739,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co ...@@ -14532,6 +14739,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co
ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start;
ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop;
ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain; ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain;
ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset;
ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint;
ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint;
ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index;
...@@ -14544,9 +14752,13 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co ...@@ -14544,9 +14752,13 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co
ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail;
ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait;
ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock;
ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info;
ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof;
ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name;
ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors;
ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count;
ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents;
ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global;
pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open;
...@@ -14594,6 +14806,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co ...@@ -14594,6 +14806,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co
pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start; pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop; pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain; pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain;
pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset;
pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint; pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint;
pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint; pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint;
pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index; pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index;
...@@ -14606,9 +14819,13 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co ...@@ -14606,9 +14819,13 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co
pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail; pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail;
pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update; pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update;
pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait; pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait;
pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock;
pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info; pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info;
pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof; pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof;
pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name; pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name;
pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors;
pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count;
pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents;
pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global; pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
#endif #endif
...@@ -14629,6 +14846,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co ...@@ -14629,6 +14846,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co
pCallbacks->onDeviceRead = ma_device_read__alsa; pCallbacks->onDeviceRead = ma_device_read__alsa;
pCallbacks->onDeviceWrite = ma_device_write__alsa; pCallbacks->onDeviceWrite = ma_device_write__alsa;
pCallbacks->onDeviceDataLoop = NULL; pCallbacks->onDeviceDataLoop = NULL;
pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa;
return MA_SUCCESS; return MA_SUCCESS;
} }
...@@ -18875,9 +19093,9 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_ ...@@ -18875,9 +19093,9 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_
continue; continue;
} }
uniqueFormats[uniqueFormatCount] = format;
uniqueFormatCount += 1; uniqueFormatCount += 1;
/* Sample Rates */ /* Sample Rates */
result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
...@@ -25674,7 +25892,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d ...@@ -25674,7 +25892,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d
MA_ASSERT(pDevice != NULL); MA_ASSERT(pDevice != NULL);
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
if (pDevice->capture.format == ma_format_unknown) { if (pDevice->capture.format == ma_format_unknown) {
pDevice->capture.format = pDevice->capture.internalFormat; pDevice->capture.format = pDevice->capture.internalFormat;
} }
...@@ -25717,7 +25935,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d ...@@ -25717,7 +25935,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d
} }
if (pDevice->sampleRate == 0) { if (pDevice->sampleRate == 0) {
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
pDevice->sampleRate = pDevice->capture.internalSampleRate; pDevice->sampleRate = pDevice->capture.internalSampleRate;
} else { } else {
pDevice->sampleRate = pDevice->playback.internalSampleRate; pDevice->sampleRate = pDevice->playback.internalSampleRate;
...@@ -41786,7 +42004,7 @@ MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encod ...@@ -41786,7 +42004,7 @@ MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encod
/* Now open the file. If this fails we don't need to uninitialize the encoder. */ /* Now open the file. If this fails we don't need to uninitialize the encoder. */
result = ma_wfopen(&pFile, pFilePath, L"wb", &pEncoder->config.allocationCallbacks); result = ma_wfopen(&pFile, pFilePath, L"wb", &pEncoder->config.allocationCallbacks);
if (pFile != NULL) { if (pFile == NULL) {
return result; return result;
} }
/* /*
Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
miniaudio - v0.10.33 - 2021-04-04 miniaudio - v0.10.34 - 2021-04-26
David Reid - mackron@gmail.com David Reid - mackron@gmail.com
...@@ -20,7 +20,7 @@ extern "C" { ...@@ -20,7 +20,7 @@ extern "C" {
#define MA_VERSION_MAJOR 0 #define MA_VERSION_MAJOR 0
#define MA_VERSION_MINOR 10 #define MA_VERSION_MINOR 10
#define MA_VERSION_REVISION 33 #define MA_VERSION_REVISION 34
#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION)
#if defined(_MSC_VER) && !defined(__clang__) #if defined(_MSC_VER) && !defined(__clang__)
...@@ -2039,7 +2039,7 @@ easier, some helper callbacks are available. If the backend uses a blocking read ...@@ -2039,7 +2039,7 @@ easier, some helper callbacks are available. If the backend uses a blocking read
backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback. backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback.
This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback. This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback.
If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceWorkerThread()` callback If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback
which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional.
The audio thread should run data delivery logic in a loop while `ma_device_get_state() == MA_STATE_STARTED` and no errors have been The audio thread should run data delivery logic in a loop while `ma_device_get_state() == MA_STATE_STARTED` and no errors have been
...@@ -2115,7 +2115,7 @@ typedef struct ...@@ -2115,7 +2115,7 @@ typedef struct
ma_device_type deviceType; ma_device_type deviceType;
void* pAudioClient; void* pAudioClient;
void** ppAudioClientService; void** ppAudioClientService;
ma_result result; /* The result from creating the audio client service. */ ma_result* pResult; /* The result from creating the audio client service. */
} createAudioClient; } createAudioClient;
struct struct
{ {
...@@ -2236,6 +2236,7 @@ struct ma_context ...@@ -2236,6 +2236,7 @@ struct ma_context
ma_proc snd_pcm_start; ma_proc snd_pcm_start;
ma_proc snd_pcm_drop; ma_proc snd_pcm_drop;
ma_proc snd_pcm_drain; ma_proc snd_pcm_drain;
ma_proc snd_pcm_reset;
ma_proc snd_device_name_hint; ma_proc snd_device_name_hint;
ma_proc snd_device_name_get_hint; ma_proc snd_device_name_get_hint;
ma_proc snd_card_get_index; ma_proc snd_card_get_index;
...@@ -2248,9 +2249,13 @@ struct ma_context ...@@ -2248,9 +2249,13 @@ struct ma_context
ma_proc snd_pcm_avail; ma_proc snd_pcm_avail;
ma_proc snd_pcm_avail_update; ma_proc snd_pcm_avail_update;
ma_proc snd_pcm_wait; ma_proc snd_pcm_wait;
ma_proc snd_pcm_nonblock;
ma_proc snd_pcm_info; ma_proc snd_pcm_info;
ma_proc snd_pcm_info_sizeof; ma_proc snd_pcm_info_sizeof;
ma_proc snd_pcm_info_get_name; ma_proc snd_pcm_info_get_name;
ma_proc snd_pcm_poll_descriptors;
ma_proc snd_pcm_poll_descriptors_count;
ma_proc snd_pcm_poll_descriptors_revents;
ma_proc snd_config_update_free_global; ma_proc snd_config_update_free_global;
ma_mutex internalDeviceEnumLock; ma_mutex internalDeviceEnumLock;
...@@ -2659,6 +2664,12 @@ struct ma_device ...@@ -2659,6 +2664,12 @@ struct ma_device
{ {
/*snd_pcm_t**/ ma_ptr pPCMPlayback; /*snd_pcm_t**/ ma_ptr pPCMPlayback;
/*snd_pcm_t**/ ma_ptr pPCMCapture; /*snd_pcm_t**/ ma_ptr pPCMCapture;
/*struct pollfd**/ void* pPollDescriptorsPlayback;
/*struct pollfd**/ void* pPollDescriptorsCapture;
int pollDescriptorCountPlayback;
int pollDescriptorCountCapture;
int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */
int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */
ma_bool8 isUsingMMapPlayback; ma_bool8 isUsingMMapPlayback;
ma_bool8 isUsingMMapCapture; ma_bool8 isUsingMMapCapture;
} alsa; } alsa;
......
/* /*
Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
miniaudio - v0.10.34 - TBD miniaudio - v0.10.34 - 2021-04-26
David Reid - mackron@gmail.com David Reid - mackron@gmail.com
...@@ -64800,7 +64800,7 @@ The following miscellaneous changes have also been made. ...@@ -64800,7 +64800,7 @@ The following miscellaneous changes have also been made.
/* /*
REVISION HISTORY REVISION HISTORY
================ ================
v0.10.34 - TBD v0.10.34 - 2021-04-26
- WASAPI: Fix a bug where a result code is not getting checked at initialization time. - WASAPI: Fix a bug where a result code is not getting checked at initialization time.
- WASAPI: Bug fixes for loopback mode. - WASAPI: Bug fixes for loopback mode.
- ALSA: Fix a possible deadlock when stopping devices. - ALSA: Fix a possible deadlock when stopping devices.
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