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.
|
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
|
||||||
|
|
||||||
@@ -540,7 +540,7 @@ static MA_INLINE void ma_yield()
|
|||||||
#else
|
#else
|
||||||
#if defined(__DMC__)
|
#if defined(__DMC__)
|
||||||
/* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */
|
/* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */
|
||||||
__asm nop;
|
__asm nop;
|
||||||
#else
|
#else
|
||||||
__asm pause;
|
__asm pause;
|
||||||
#endif
|
#endif
|
||||||
@@ -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
|
|||||||
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
|
|||||||
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;
|
||||||
@@ -7291,7 +7295,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(m
|
|||||||
ma_bool32 isThisDevice = MA_FALSE;
|
ma_bool32 isThisDevice = MA_FALSE;
|
||||||
ma_bool32 isCapture = MA_FALSE;
|
ma_bool32 isCapture = MA_FALSE;
|
||||||
ma_bool32 isPlayback = MA_FALSE;
|
ma_bool32 isPlayback = MA_FALSE;
|
||||||
|
|
||||||
|
|
||||||
#ifdef MA_DEBUG_OUTPUT
|
#ifdef MA_DEBUG_OUTPUT
|
||||||
printf("IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);
|
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) {
|
if (pThis->pDevice->wasapi.isDetachedPlayback) {
|
||||||
pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
|
pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
|
||||||
|
|
||||||
if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) {
|
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. */
|
restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */
|
||||||
} else {
|
} else {
|
||||||
@@ -7475,7 +7479,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged
|
|||||||
|
|
||||||
if (pThis->pDevice->wasapi.isDetachedCapture) {
|
if (pThis->pDevice->wasapi.isDetachedCapture) {
|
||||||
pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
|
pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
|
||||||
|
|
||||||
if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) {
|
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. */
|
restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */
|
||||||
} else {
|
} else {
|
||||||
@@ -7531,7 +7535,7 @@ typedef ma_IUnknown ma_WASAPIDeviceInterface;
|
|||||||
static ma_context_command__wasapi ma_context_init_command__wasapi(int code)
|
static ma_context_command__wasapi ma_context_init_command__wasapi(int code)
|
||||||
{
|
{
|
||||||
ma_context_command__wasapi cmd;
|
ma_context_command__wasapi cmd;
|
||||||
|
|
||||||
MA_ZERO_OBJECT(&cmd);
|
MA_ZERO_OBJECT(&cmd);
|
||||||
cmd.code = code;
|
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. */
|
/* 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_semaphore_release(&pContext->wasapi.commandSem);
|
||||||
}
|
}
|
||||||
ma_mutex_unlock(&pContext->wasapi.commandLock);
|
ma_mutex_unlock(&pContext->wasapi.commandLock);
|
||||||
|
|
||||||
if (isUsingLocalEvent) {
|
if (isUsingLocalEvent) {
|
||||||
ma_event_wait(&localEvent);
|
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;
|
pContext->wasapi.commandCount -= 1;
|
||||||
}
|
}
|
||||||
ma_mutex_unlock(&pContext->wasapi.commandLock);
|
ma_mutex_unlock(&pContext->wasapi.commandLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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:
|
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;
|
||||||
|
|
||||||
case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI:
|
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) {
|
if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) {
|
||||||
ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback);
|
ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback);
|
||||||
cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL;
|
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)
|
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. */
|
||||||
@@ -8727,7 +8732,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device
|
|||||||
|
|
||||||
pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
|
pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
|
||||||
|
|
||||||
|
|
||||||
if (deviceType == ma_device_type_playback) {
|
if (deviceType == ma_device_type_playback) {
|
||||||
result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient);
|
result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient);
|
||||||
} else {
|
} else {
|
||||||
@@ -9682,8 +9687,16 @@ 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) {
|
||||||
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. */
|
/* 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 != NULL);
|
||||||
MA_ASSERT(pContext->backend == ma_backend_wasapi);
|
MA_ASSERT(pContext->backend == ma_backend_wasapi);
|
||||||
|
|
||||||
if (pContext->wasapi.commandThread != NULL) {
|
if (pContext->wasapi.commandThread != NULL) {
|
||||||
ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI);
|
ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI);
|
||||||
ma_context_post_command__wasapi(pContext, &cmd);
|
ma_context_post_command__wasapi(pContext, &cmd);
|
||||||
@@ -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) (
|
|||||||
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) (
|
|||||||
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. */
|
||||||
@@ -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
|
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 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. */
|
/* 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) {
|
for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
|
||||||
ma_format format = g_maFormatPriorities[iFormat];
|
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) {
|
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
|
|||||||
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
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
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)
|
|||||||
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. */
|
||||||
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("[ALSA] Preparing capture device... ");
|
||||||
#endif
|
#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) {
|
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
|
#endif
|
||||||
|
} else {
|
||||||
|
#ifdef MA_DEBUG_OUTPUT
|
||||||
|
printf("Done\n");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return MA_SUCCESS;
|
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)
|
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
|
|||||||
*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
|
|||||||
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,
|
|||||||
*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,
|
|||||||
#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,
|
|||||||
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,
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
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;
|
||||||
}
|
}
|
||||||
@@ -16234,7 +16452,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi
|
|||||||
if (pDescriptorCapture->pDeviceID != NULL) {
|
if (pDescriptorCapture->pDeviceID != NULL) {
|
||||||
devCapture = pDescriptorCapture->pDeviceID->pulse;
|
devCapture = pDescriptorCapture->pDeviceID->pulse;
|
||||||
}
|
}
|
||||||
|
|
||||||
format = pDescriptorCapture->format;
|
format = pDescriptorCapture->format;
|
||||||
channels = pDescriptorCapture->channels;
|
channels = pDescriptorCapture->channels;
|
||||||
sampleRate = pDescriptorCapture->sampleRate;
|
sampleRate = pDescriptorCapture->sampleRate;
|
||||||
@@ -18820,7 +19038,7 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_
|
|||||||
if (deviceObjectID == defaultDeviceObjectID) {
|
if (deviceObjectID == defaultDeviceObjectID) {
|
||||||
pDeviceInfo->isDefault = MA_TRUE;
|
pDeviceInfo->isDefault = MA_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
There could be a large number of permutations here. Fortunately there is only a single channel count
|
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
|
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.
|
sample rate.
|
||||||
*/
|
*/
|
||||||
pDeviceInfo->nativeDataFormatCount = 0;
|
pDeviceInfo->nativeDataFormatCount = 0;
|
||||||
|
|
||||||
/* Formats. */
|
/* Formats. */
|
||||||
{
|
{
|
||||||
ma_format uniqueFormats[ma_format_count];
|
ma_format uniqueFormats[ma_format_count];
|
||||||
ma_uint32 uniqueFormatCount = 0;
|
ma_uint32 uniqueFormatCount = 0;
|
||||||
ma_uint32 channels;
|
ma_uint32 channels;
|
||||||
|
|
||||||
/* Channels. */
|
/* Channels. */
|
||||||
result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);
|
result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);
|
||||||
if (result != MA_SUCCESS) {
|
if (result != MA_SUCCESS) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Formats. */
|
/* Formats. */
|
||||||
result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
|
result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
|
||||||
if (result != MA_SUCCESS) {
|
if (result != MA_SUCCESS) {
|
||||||
@@ -18869,21 +19087,21 @@ static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we've already handled this format just skip it. */
|
/* If we've already handled this format just skip it. */
|
||||||
if (hasFormatBeenHandled) {
|
if (hasFormatBeenHandled) {
|
||||||
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) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are
|
Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are
|
||||||
between this range.
|
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].sampleRate = standardSampleRate;
|
||||||
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
|
||||||
pDeviceInfo->nativeDataFormatCount += 1;
|
pDeviceInfo->nativeDataFormatCount += 1;
|
||||||
|
|
||||||
if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
|
if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
|
||||||
break; /* No more room for any more formats. */
|
break; /* No more room for any more formats. */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
|
ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
|
||||||
|
|
||||||
if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
|
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. */
|
/* Only a single format is being reported for iOS. */
|
||||||
pDeviceInfo->nativeDataFormatCount = 1;
|
pDeviceInfo->nativeDataFormatCount = 1;
|
||||||
|
|
||||||
result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);
|
result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);
|
||||||
if (result != MA_SUCCESS) {
|
if (result != MA_SUCCESS) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
|
pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -19163,7 +19381,7 @@ static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFl
|
|||||||
if (framesToRead > framesRemaining) {
|
if (framesToRead > framesRemaining) {
|
||||||
framesToRead = framesRemaining;
|
framesToRead = framesRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead);
|
ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead);
|
||||||
|
|
||||||
for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
|
for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
|
||||||
@@ -19268,7 +19486,7 @@ static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFla
|
|||||||
if (framesToSend > framesRemaining) {
|
if (framesToSend > framesRemaining) {
|
||||||
framesToSend = framesRemaining;
|
framesToSend = framesRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend);
|
ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend);
|
||||||
|
|
||||||
framesRemaining -= framesToSend;
|
framesRemaining -= framesToSend;
|
||||||
@@ -20391,7 +20609,7 @@ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_c
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
When stopping the device, a callback is called on another thread. We need to wait for this callback
|
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
|
#endif
|
||||||
|
|
||||||
pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate;
|
pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate;
|
||||||
|
|
||||||
pCallbacks->onContextInit = ma_context_init__coreaudio;
|
pCallbacks->onContextInit = ma_context_init__coreaudio;
|
||||||
pCallbacks->onContextUninit = ma_context_uninit__coreaudio;
|
pCallbacks->onContextUninit = ma_context_uninit__coreaudio;
|
||||||
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__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;
|
par.sig = 1;
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deviceType == ma_device_type_capture) {
|
if (deviceType == ma_device_type_capture) {
|
||||||
par.rchan = channels;
|
par.rchan = channels;
|
||||||
} else {
|
} 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(pDevice != NULL);
|
||||||
MA_ASSERT(pConfig != NULL);
|
MA_ASSERT(pConfig != NULL);
|
||||||
MA_ASSERT(deviceType != ma_device_type_duplex);
|
MA_ASSERT(deviceType != ma_device_type_duplex);
|
||||||
|
|
||||||
pDeviceID = pDescriptor->pDeviceID;
|
pDeviceID = pDescriptor->pDeviceID;
|
||||||
shareMode = pDescriptor->shareMode;
|
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. */
|
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. */
|
/* 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);
|
((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. */
|
/* 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. */
|
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);
|
((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);
|
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
|
|||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -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;
|
pDevice->capture.channels = pConfig->capture.channels;
|
||||||
ma_channel_map_copy(pDevice->capture.channelMap, pConfig->capture.channelMap, pConfig->capture.channels);
|
ma_channel_map_copy(pDevice->capture.channelMap, pConfig->capture.channelMap, pConfig->capture.channels);
|
||||||
pDevice->capture.channelMixMode = pConfig->capture.channelMixMode;
|
pDevice->capture.channelMixMode = pConfig->capture.channelMixMode;
|
||||||
|
|
||||||
pDevice->playback.shareMode = pConfig->playback.shareMode;
|
pDevice->playback.shareMode = pConfig->playback.shareMode;
|
||||||
pDevice->playback.format = pConfig->playback.format;
|
pDevice->playback.format = pConfig->playback.format;
|
||||||
pDevice->playback.channels = pConfig->playback.channels;
|
pDevice->playback.channels = pConfig->playback.channels;
|
||||||
ma_channel_map_copy(pDevice->playback.channelMap, pConfig->playback.channelMap, pConfig->playback.channels);
|
ma_channel_map_copy(pDevice->playback.channelMap, pConfig->playback.channelMap, pConfig->playback.channels);
|
||||||
pDevice->playback.channelMixMode = pConfig->playback.channelMixMode;
|
pDevice->playback.channelMixMode = pConfig->playback.channelMixMode;
|
||||||
|
|
||||||
|
|
||||||
result = ma_mutex_init(&pDevice->startStopLock);
|
result = ma_mutex_init(&pDevice->startStopLock);
|
||||||
if (result != MA_SUCCESS) {
|
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) {
|
if (result != MA_SUCCESS) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks);
|
ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks);
|
||||||
|
|
||||||
if (doCopy) {
|
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. */
|
/* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
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" {
|
|||||||
|
|
||||||
#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__)
|
||||||
@@ -411,7 +411,7 @@ typedef enum
|
|||||||
ma_standard_sample_rate_32000 = 32000, /* Lows */
|
ma_standard_sample_rate_32000 = 32000, /* Lows */
|
||||||
ma_standard_sample_rate_24000 = 24000,
|
ma_standard_sample_rate_24000 = 24000,
|
||||||
ma_standard_sample_rate_22050 = 22050,
|
ma_standard_sample_rate_22050 = 22050,
|
||||||
|
|
||||||
ma_standard_sample_rate_88200 = 88200, /* Highs */
|
ma_standard_sample_rate_88200 = 88200, /* Highs */
|
||||||
ma_standard_sample_rate_96000 = 96000,
|
ma_standard_sample_rate_96000 = 96000,
|
||||||
ma_standard_sample_rate_176400 = 176400,
|
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.
|
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
|
|||||||
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
|
|||||||
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
|
|||||||
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
|
|||||||
{
|
{
|
||||||
/*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;
|
||||||
|
|||||||
+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.
|
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.
|
|||||||
/*
|
/*
|
||||||
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.
|
||||||
|
|||||||
Reference in New Issue
Block a user