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.
miniaudio - v0.10.33 - 2021-04-04
miniaudio - v0.10.34 - 2021-04-26
David Reid - mackron@gmail.com
......@@ -540,7 +540,7 @@ static MA_INLINE void ma_yield()
#else
#if defined(__DMC__)
/* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */
__asm nop;
__asm nop;
#else
__asm pause;
#endif
......@@ -6038,6 +6038,7 @@ static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enu
ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo);
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);
}
......@@ -6046,6 +6047,7 @@ static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enu
ma_device_info deviceInfo;
MA_ZERO_OBJECT(&deviceInfo);
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);
}
......@@ -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);
}
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. */
pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
pDeviceInfo->nativeDataFormats[0].channels = 0;
......@@ -7291,7 +7295,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(m
ma_bool32 isThisDevice = MA_FALSE;
ma_bool32 isCapture = MA_FALSE;
ma_bool32 isPlayback = MA_FALSE;
#ifdef MA_DEBUG_OUTPUT
printf("IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);
......@@ -7463,7 +7467,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged
if (pThis->pDevice->wasapi.isDetachedPlayback) {
pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) {
restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */
} else {
......@@ -7475,7 +7479,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged
if (pThis->pDevice->wasapi.isDetachedCapture) {
pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) {
restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */
} else {
......@@ -7531,7 +7535,7 @@ typedef ma_IUnknown ma_WASAPIDeviceInterface;
static ma_context_command__wasapi ma_context_init_command__wasapi(int code)
{
ma_context_command__wasapi cmd;
MA_ZERO_OBJECT(&cmd);
cmd.code = code;
......@@ -7576,7 +7580,7 @@ static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_
/* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */
ma_semaphore_release(&pContext->wasapi.commandSem);
}
ma_mutex_unlock(&pContext->wasapi.commandLock);
ma_mutex_unlock(&pContext->wasapi.commandLock);
if (isUsingLocalEvent) {
ma_event_wait(&localEvent);
......@@ -7602,7 +7606,7 @@ static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_contex
pContext->wasapi.commandCount -= 1;
}
ma_mutex_unlock(&pContext->wasapi.commandLock);
}
}
return result;
}
......@@ -7630,15 +7634,15 @@ static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pU
case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI:
{
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 {
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;
case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI:
{
if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) {
if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) {
if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) {
ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback);
cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL;
......@@ -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)
{
ma_result result;
ma_result cmdResult;
ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI);
cmd.data.createAudioClient.deviceType = deviceType;
cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient;
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. */
if (result != MA_SUCCESS) {
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. */
......@@ -8727,7 +8732,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device
pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
if (deviceType == ma_device_type_playback) {
result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient);
} else {
......@@ -9682,8 +9687,16 @@ static ma_result ma_device_data_loop__wasapi(ma_device* pDevice)
/* Wait for data to become available first. */
if (WaitForSingleObject(pDevice->wasapi.hEventCapture, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
exitLoop = MA_TRUE;
break; /* Wait failed. */
/*
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;
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. */
......@@ -9871,7 +9884,7 @@ static ma_result ma_context_uninit__wasapi(ma_context* pContext)
{
MA_ASSERT(pContext != NULL);
MA_ASSERT(pContext->backend == ma_backend_wasapi);
if (pContext->wasapi.commandThread != NULL) {
ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI);
ma_context_post_command__wasapi(pContext, &cmd);
......@@ -12757,6 +12770,9 @@ ALSA Backend
******************************************************************************/
#ifdef MA_HAS_ALSA
#include <poll.h> /* poll(), struct pollfd */
#include <sys/eventfd.h> /* eventfd() */
#ifdef MA_NO_RUNTIME_LINKING
/* 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) (
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_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 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);
......@@ -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_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_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 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 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);
/* This array specifies each of the common devices that can be used for both playback and capture. */
......@@ -13755,7 +13776,7 @@ static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_devic
We want to ensure the the first data formats are the best. We have a list of favored sample
formats and sample rates, so these will be the basis of our iteration.
*/
/* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */
for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
ma_format format = g_maFormatPriorities[iFormat];
......@@ -13839,10 +13860,14 @@ static ma_result ma_device_uninit__alsa(ma_device* pDevice)
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);
close(pDevice->alsa.wakeupfdCapture);
ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks);
}
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);
close(pDevice->alsa.wakeupfdPlayback);
ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks);
}
return MA_SUCCESS;
......@@ -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_sw_params_t* pSWParams;
ma_snd_pcm_uframes_t bufferBoundary;
int pollDescriptorCount;
struct pollfd* pPollDescriptors;
int wakeupfd;
MA_ASSERT(pConfig != NULL);
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
}
/*
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. */
resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM);
if (resultALSA < 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 prepare device.", ma_result_from_errno(-resultALSA));
}
......@@ -14253,30 +14336,113 @@ static ma_result ma_device_start__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) {
((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. */
if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
#ifdef MA_DEBUG_OUTPUT
printf("[ALSA] Failed to prepare capture device after stopping.\n");
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) {
#ifdef MA_DEBUG_OUTPUT
printf("Failed\n");
#endif
} else {
#ifdef MA_DEBUG_OUTPUT
printf("Done\n");
#endif
}
}
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. */
#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) {
#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
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;
}
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)
{
ma_snd_pcm_sframes_t resultALSA;
......@@ -14288,7 +14454,16 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u
*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);
if (resultALSA >= 0) {
break; /* Success. */
......@@ -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));
}
resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
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));
}
continue; /* Try reading again. */
}
}
}
......@@ -14338,7 +14510,15 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames,
*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);
if (resultALSA >= 0) {
break; /* Success. */
......@@ -14352,8 +14532,8 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames,
#endif
/* 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);
if (resultALSA < 0) { /* MA_TRUE=silent (don't print anything on error). */
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) {
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,
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);
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));
}
continue; /* Try writing again. */
}
}
}
......@@ -14384,6 +14561,31 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames,
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)
{
MA_ASSERT(pContext != NULL);
......@@ -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_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_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_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");
......@@ -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_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_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_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_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");
#else
/* 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
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_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_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;
......@@ -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_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
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_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_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;
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
pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
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_get_hint = (ma_proc)_snd_device_name_get_hint;
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
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_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_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_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;
#endif
......@@ -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->onDeviceWrite = ma_device_write__alsa;
pCallbacks->onDeviceDataLoop = NULL;
pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa;
return MA_SUCCESS;
}
......@@ -16234,7 +16452,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
if (pDescriptorCapture->pDeviceID != NULL) {
devCapture = pDescriptorCapture->pDeviceID->pulse;
}
format = pDescriptorCapture->format;
channels = pDescriptorCapture->channels;
sampleRate = pDescriptorCapture->sampleRate;
......@@ -18820,7 +19038,7 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_
if (deviceObjectID == defaultDeviceObjectID) {
pDeviceInfo->isDefault = MA_TRUE;
}
/*
There could be a large number of permutations here. Fortunately there is only a single channel count
being reported which reduces this quite a bit. For sample rates we're only reporting those that are
......@@ -18830,19 +19048,19 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_
sample rate.
*/
pDeviceInfo->nativeDataFormatCount = 0;
/* Formats. */
{
ma_format uniqueFormats[ma_format_count];
ma_uint32 uniqueFormatCount = 0;
ma_uint32 channels;
/* Channels. */
result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);
if (result != MA_SUCCESS) {
return result;
}
/* Formats. */
result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
if (result != MA_SUCCESS) {
......@@ -18869,21 +19087,21 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_
break;
}
}
/* If we've already handled this format just skip it. */
if (hasFormatBeenHandled) {
continue;
}
uniqueFormats[uniqueFormatCount] = format;
uniqueFormatCount += 1;
/* Sample Rates */
result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
if (result != MA_SUCCESS) {
return result;
}
/*
Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are
between this range.
......@@ -18899,14 +19117,14 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate;
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
pDeviceInfo->nativeDataFormatCount += 1;
if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
break; /* No more room for any more formats. */
}
}
}
}
ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
......@@ -19000,12 +19218,12 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_
/* Only a single format is being reported for iOS. */
pDeviceInfo->nativeDataFormatCount = 1;
result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);
if (result != MA_SUCCESS) {
return result;
}
pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
/*
......@@ -19163,7 +19381,7 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl
if (framesToRead > framesRemaining) {
framesToRead = framesRemaining;
}
ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead);
for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
......@@ -19268,7 +19486,7 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla
if (framesToSend > framesRemaining) {
framesToSend = framesRemaining;
}
ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend);
framesRemaining -= framesToSend;
......@@ -20391,7 +20609,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c
#endif
}
/*
When stopping the device, a callback is called on another thread. We need to wait for this callback
......@@ -20678,7 +20896,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte
#endif
pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate;
pCallbacks->onContextInit = ma_context_init__coreaudio;
pCallbacks->onContextUninit = ma_context_uninit__coreaudio;
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio;
......@@ -21315,7 +21533,7 @@ static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_devic
par.sig = 1;
} break;
}
if (deviceType == ma_device_type_capture) {
par.rchan = channels;
} else {
......@@ -22722,7 +22940,7 @@ static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_conf
MA_ASSERT(pDevice != NULL);
MA_ASSERT(pConfig != NULL);
MA_ASSERT(deviceType != ma_device_type_duplex);
pDeviceID = pDescriptor->pDeviceID;
shareMode = pDescriptor->shareMode;
ossFormat = ma_format_to_oss((pDescriptor->format != ma_format_unknown) ? pDescriptor->format : ma_format_s16); /* Use s16 by default because OSS doesn't like floating point. */
......@@ -23310,7 +23528,7 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context*
/* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */
((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE);
/* We need to set an error callback to detect device changes. */
if (pDevice != NULL) { /* <-- pDevice should never be null if pDescriptor is not null, which is always the case if we hit this branch. Check anyway for safety. */
((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice);
......@@ -25674,7 +25892,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d
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) {
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
}
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;
} else {
pDevice->sampleRate = pDevice->playback.internalSampleRate;
......@@ -26615,13 +26833,13 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC
pDevice->capture.channels = pConfig->capture.channels;
ma_channel_map_copy(pDevice->capture.channelMap, pConfig->capture.channelMap, pConfig->capture.channels);
pDevice->capture.channelMixMode = pConfig->capture.channelMixMode;
pDevice->playback.shareMode = pConfig->playback.shareMode;
pDevice->playback.format = pConfig->playback.format;
pDevice->playback.channels = pConfig->playback.channels;
ma_channel_map_copy(pDevice->playback.channelMap, pConfig->playback.channelMap, pConfig->playback.channels);
pDevice->playback.channelMixMode = pConfig->playback.channelMixMode;
result = ma_mutex_init(&pDevice->startStopLock);
if (result != MA_SUCCESS) {
......@@ -37001,7 +37219,7 @@ static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig,
if (result != MA_SUCCESS) {
return result;
}
ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks);
if (doCopy) {
......@@ -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. */
result = ma_wfopen(&pFile, pFilePath, L"wb", &pEncoder->config.allocationCallbacks);
if (pFile != NULL) {
if (pFile == NULL) {
return result;
}
/*
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
......@@ -20,7 +20,7 @@ extern "C" {
#define MA_VERSION_MAJOR 0
#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)
#if defined(_MSC_VER) && !defined(__clang__)
......@@ -411,7 +411,7 @@ typedef enum
ma_standard_sample_rate_32000 = 32000, /* Lows */
ma_standard_sample_rate_24000 = 24000,
ma_standard_sample_rate_22050 = 22050,
ma_standard_sample_rate_88200 = 88200, /* Highs */
ma_standard_sample_rate_96000 = 96000,
ma_standard_sample_rate_176400 = 176400,
......@@ -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.
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.
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
ma_device_type deviceType;
void* pAudioClient;
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;
struct
{
......@@ -2236,6 +2236,7 @@ struct ma_context
ma_proc snd_pcm_start;
ma_proc snd_pcm_drop;
ma_proc snd_pcm_drain;
ma_proc snd_pcm_reset;
ma_proc snd_device_name_hint;
ma_proc snd_device_name_get_hint;
ma_proc snd_card_get_index;
......@@ -2248,9 +2249,13 @@ struct ma_context
ma_proc snd_pcm_avail;
ma_proc snd_pcm_avail_update;
ma_proc snd_pcm_wait;
ma_proc snd_pcm_nonblock;
ma_proc snd_pcm_info;
ma_proc snd_pcm_info_sizeof;
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_mutex internalDeviceEnumLock;
......@@ -2659,6 +2664,12 @@ struct ma_device
{
/*snd_pcm_t**/ ma_ptr pPCMPlayback;
/*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 isUsingMMapCapture;
} alsa;
......
/*
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
......@@ -64800,7 +64800,7 @@ The following miscellaneous changes have also been made.
/*
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: Bug fixes for loopback mode.
- 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