mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-24 09:14:04 +02:00
Version 0.10.34
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
@@ -7630,9 +7634,9 @@ 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;
|
||||
|
||||
@@ -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. */
|
||||
@@ -9682,9 +9687,17 @@ 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) {
|
||||
/*
|
||||
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. */
|
||||
result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
|
||||
@@ -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. */
|
||||
@@ -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,23 +14336,49 @@ 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. */
|
||||
#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) {
|
||||
#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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -14277,6 +14386,63 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice)
|
||||
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;
|
||||
}
|
||||
@@ -18875,9 +19093,9 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_
|
||||
continue;
|
||||
}
|
||||
|
||||
uniqueFormats[uniqueFormatCount] = format;
|
||||
uniqueFormatCount += 1;
|
||||
|
||||
|
||||
/* Sample Rates */
|
||||
result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
|
||||
if (result != MA_SUCCESS) {
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
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__)
|
||||
@@ -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;
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user