ALSA: Refactoring for the new backend architecture.

This commit is contained in:
David Reid
2025-12-24 14:35:06 +10:00
parent 61fd14955a
commit a2c7e697d1
+95 -26
View File
@@ -28566,6 +28566,8 @@ typedef struct ma_device_state_alsa
{ {
ma_snd_pcm_t* pPCMPlayback; ma_snd_pcm_t* pPCMPlayback;
ma_snd_pcm_t* pPCMCapture; ma_snd_pcm_t* pPCMCapture;
void* pIntermediaryBufferPlayback;
void* pIntermediaryBufferCapture;
struct pollfd* pPollDescriptorsPlayback; struct pollfd* pPollDescriptorsPlayback;
struct pollfd* pPollDescriptorsCapture; struct pollfd* pPollDescriptorsCapture;
int pollDescriptorCountPlayback; int pollDescriptorCountPlayback;
@@ -29655,6 +29657,7 @@ static ma_result ma_device_init_by_type__alsa(ma_context* pContext, ma_context_s
int pollDescriptorCount; int pollDescriptorCount;
struct pollfd* pPollDescriptors; struct pollfd* pPollDescriptors;
int wakeupfd; int wakeupfd;
void* pIntermediaryBuffer;
MA_ASSERT(pContextStateALSA != NULL); MA_ASSERT(pContextStateALSA != NULL);
MA_ASSERT(pDeviceStateALSA != NULL); MA_ASSERT(pDeviceStateALSA != NULL);
@@ -29993,6 +29996,13 @@ static ma_result ma_device_init_by_type__alsa(ma_context* pContext, ma_context_s
} }
/* Now that we know our internal data format and period size we can allocate an intermediary buffer. */
pIntermediaryBuffer = ma_malloc(ma_get_bytes_per_frame(internalFormat, internalChannels) * internalPeriodSizeInFrames, ma_context_get_allocation_callbacks(pContext));
if (pIntermediaryBuffer == NULL) {
pContextStateALSA->snd_pcm_close(pPCM);
}
/* /*
We need to retrieve the poll descriptors so we can use poll() to wait for data to become 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 available for reading or writing. There's no well defined maximum for this so we're just going
@@ -30001,6 +30011,7 @@ static ma_result ma_device_init_by_type__alsa(ma_context* pContext, ma_context_s
pollDescriptorCount = pContextStateALSA->snd_pcm_poll_descriptors_count(pPCM); pollDescriptorCount = pContextStateALSA->snd_pcm_poll_descriptors_count(pPCM);
if (pollDescriptorCount <= 0) { if (pollDescriptorCount <= 0) {
pContextStateALSA->snd_pcm_close(pPCM); pContextStateALSA->snd_pcm_close(pPCM);
ma_free(pIntermediaryBuffer, ma_context_get_allocation_callbacks(pContext));
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count.");
return MA_ERROR; return MA_ERROR;
} }
@@ -30008,6 +30019,7 @@ static ma_result ma_device_init_by_type__alsa(ma_context* pContext, ma_context_s
pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), ma_context_get_allocation_callbacks(pContext)); /* +1 because we want room for the wakeup descriptor. */ pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), ma_context_get_allocation_callbacks(pContext)); /* +1 because we want room for the wakeup descriptor. */
if (pPollDescriptors == NULL) { if (pPollDescriptors == NULL) {
pContextStateALSA->snd_pcm_close(pPCM); pContextStateALSA->snd_pcm_close(pPCM);
ma_free(pIntermediaryBuffer, ma_context_get_allocation_callbacks(pContext));
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors.");
return MA_OUT_OF_MEMORY; return MA_OUT_OF_MEMORY;
} }
@@ -30020,6 +30032,7 @@ static ma_result ma_device_init_by_type__alsa(ma_context* pContext, ma_context_s
if (wakeupfd < 0) { if (wakeupfd < 0) {
ma_free(pPollDescriptors, ma_context_get_allocation_callbacks(pContext)); ma_free(pPollDescriptors, ma_context_get_allocation_callbacks(pContext));
pContextStateALSA->snd_pcm_close(pPCM); pContextStateALSA->snd_pcm_close(pPCM);
ma_free(pIntermediaryBuffer, ma_context_get_allocation_callbacks(pContext));
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup.");
return ma_result_from_errno(errno); return ma_result_from_errno(errno);
} }
@@ -30035,38 +30048,36 @@ static ma_result ma_device_init_by_type__alsa(ma_context* pContext, ma_context_s
close(wakeupfd); close(wakeupfd);
ma_free(pPollDescriptors, ma_context_get_allocation_callbacks(pContext)); ma_free(pPollDescriptors, ma_context_get_allocation_callbacks(pContext));
pContextStateALSA->snd_pcm_close(pPCM); pContextStateALSA->snd_pcm_close(pPCM);
ma_free(pIntermediaryBuffer, ma_context_get_allocation_callbacks(pContext));
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors.");
return MA_ERROR; return MA_ERROR;
} }
if (deviceType == ma_device_type_capture) {
pDeviceStateALSA->pollDescriptorCountCapture = pollDescriptorCount;
pDeviceStateALSA->pPollDescriptorsCapture = pPollDescriptors;
pDeviceStateALSA->wakeupfdCapture = wakeupfd;
} else {
pDeviceStateALSA->pollDescriptorCountPlayback = pollDescriptorCount;
pDeviceStateALSA->pPollDescriptorsPlayback = pPollDescriptors;
pDeviceStateALSA->wakeupfdPlayback = wakeupfd;
}
/* We're done. Prepare the device. */ /* We're done. Prepare the device. */
resultALSA = pContextStateALSA->snd_pcm_prepare(pPCM); resultALSA = pContextStateALSA->snd_pcm_prepare(pPCM);
if (resultALSA < 0) { if (resultALSA < 0) {
close(wakeupfd); close(wakeupfd);
ma_free(pPollDescriptors, ma_context_get_allocation_callbacks(pContext)); ma_free(pPollDescriptors, ma_context_get_allocation_callbacks(pContext));
pContextStateALSA->snd_pcm_close(pPCM); pContextStateALSA->snd_pcm_close(pPCM);
ma_free(pIntermediaryBuffer, ma_context_get_allocation_callbacks(pContext));
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.");
return ma_result_from_errno(-resultALSA); return ma_result_from_errno(-resultALSA);
} }
if (deviceType == ma_device_type_capture) { if (deviceType == ma_device_type_capture) {
pDeviceStateALSA->pPCMCapture = pPCM; pDeviceStateALSA->pPCMCapture = pPCM;
pDeviceStateALSA->pIntermediaryBufferCapture = pIntermediaryBuffer;
pDeviceStateALSA->isUsingMMapCapture = isUsingMMap; pDeviceStateALSA->isUsingMMapCapture = isUsingMMap;
pDeviceStateALSA->pollDescriptorCountCapture = pollDescriptorCount;
pDeviceStateALSA->pPollDescriptorsCapture = pPollDescriptors;
pDeviceStateALSA->wakeupfdCapture = wakeupfd;
} else { } else {
pDeviceStateALSA->pPCMPlayback = pPCM; pDeviceStateALSA->pPCMPlayback = pPCM;
pDeviceStateALSA->pIntermediaryBufferPlayback = pIntermediaryBuffer;
pDeviceStateALSA->isUsingMMapPlayback = isUsingMMap; pDeviceStateALSA->isUsingMMapPlayback = isUsingMMap;
pDeviceStateALSA->pollDescriptorCountPlayback = pollDescriptorCount;
pDeviceStateALSA->pPollDescriptorsPlayback = pPollDescriptors;
pDeviceStateALSA->wakeupfdPlayback = wakeupfd;
} }
pDescriptor->format = internalFormat; pDescriptor->format = internalFormat;
@@ -30133,12 +30144,14 @@ static void ma_device_uninit__alsa(ma_device* pDevice)
pContextStateALSA->snd_pcm_close(pDeviceStateALSA->pPCMCapture); pContextStateALSA->snd_pcm_close(pDeviceStateALSA->pPCMCapture);
close(pDeviceStateALSA->wakeupfdCapture); close(pDeviceStateALSA->wakeupfdCapture);
ma_free(pDeviceStateALSA->pPollDescriptorsCapture, ma_device_get_allocation_callbacks(pDevice)); ma_free(pDeviceStateALSA->pPollDescriptorsCapture, ma_device_get_allocation_callbacks(pDevice));
ma_free(pDeviceStateALSA->pIntermediaryBufferCapture, ma_device_get_allocation_callbacks(pDevice));
} }
if (pDeviceStateALSA->pPCMPlayback) { if (pDeviceStateALSA->pPCMPlayback) {
pContextStateALSA->snd_pcm_close(pDeviceStateALSA->pPCMPlayback); pContextStateALSA->snd_pcm_close(pDeviceStateALSA->pPCMPlayback);
close(pDeviceStateALSA->wakeupfdPlayback); close(pDeviceStateALSA->wakeupfdPlayback);
ma_free(pDeviceStateALSA->pPollDescriptorsPlayback, ma_device_get_allocation_callbacks(pDevice)); ma_free(pDeviceStateALSA->pPollDescriptorsPlayback, ma_device_get_allocation_callbacks(pDevice));
ma_free(pDeviceStateALSA->pIntermediaryBufferPlayback, ma_device_get_allocation_callbacks(pDevice));
} }
ma_free(pDeviceStateALSA, ma_device_get_allocation_callbacks(pDevice)); ma_free(pDeviceStateALSA, ma_device_get_allocation_callbacks(pDevice));
@@ -30242,12 +30255,16 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice)
return MA_SUCCESS; return MA_SUCCESS;
} }
static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_context_state_alsa* pContextStateALSA, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent) static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_context_state_alsa* pContextStateALSA, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent, int timeout, ma_bool32* pIsDataAvailable)
{ {
MA_ASSERT(pIsDataAvailable != NULL);
*pIsDataAvailable = MA_FALSE;
for (;;) { for (;;) {
unsigned short revents; unsigned short revents;
int resultALSA; int resultALSA;
int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); int resultPoll = poll(pPollDescriptors, pollDescriptorCount, timeout);
if (resultPoll < 0) { if (resultPoll < 0) {
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] poll() failed."); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] poll() failed.");
@@ -30297,6 +30314,7 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_context_state_alsa*
} }
if ((revents & requiredEvent) == requiredEvent) { if ((revents & requiredEvent) == requiredEvent) {
*pIsDataAvailable = MA_TRUE;
break; /* We're done. Data available for reading or writing. */ break; /* We're done. Data available for reading or writing. */
} }
} }
@@ -30304,17 +30322,17 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_context_state_alsa*
return MA_SUCCESS; return MA_SUCCESS;
} }
static ma_result ma_device_wait_read__alsa(ma_device* pDevice, ma_context_state_alsa* pContextStateALSA, ma_device_state_alsa* pDeviceStateALSA) static ma_result ma_device_wait_read__alsa(ma_device* pDevice, ma_context_state_alsa* pContextStateALSA, ma_device_state_alsa* pDeviceStateALSA, int timeout, ma_bool32* pIsDataAvailable)
{ {
return ma_device_wait__alsa(pDevice, pContextStateALSA, pDeviceStateALSA->pPCMCapture, pDeviceStateALSA->pPollDescriptorsCapture, pDeviceStateALSA->pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */ return ma_device_wait__alsa(pDevice, pContextStateALSA, pDeviceStateALSA->pPCMCapture, pDeviceStateALSA->pPollDescriptorsCapture, pDeviceStateALSA->pollDescriptorCountCapture + 1, POLLIN, timeout, pIsDataAvailable); /* +1 to account for the wakeup descriptor. */
} }
static ma_result ma_device_wait_write__alsa(ma_device* pDevice, ma_context_state_alsa* pContextStateALSA, ma_device_state_alsa* pDeviceStateALSA) static ma_result ma_device_wait_write__alsa(ma_device* pDevice, ma_context_state_alsa* pContextStateALSA, ma_device_state_alsa* pDeviceStateALSA, int timeout, ma_bool32* pIsDataAvailable)
{ {
return ma_device_wait__alsa(pDevice, pContextStateALSA, pDeviceStateALSA->pPCMPlayback, pDeviceStateALSA->pPollDescriptorsPlayback, pDeviceStateALSA->pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */ return ma_device_wait__alsa(pDevice, pContextStateALSA, pDeviceStateALSA->pPCMPlayback, pDeviceStateALSA->pPollDescriptorsPlayback, pDeviceStateALSA->pollDescriptorCountPlayback + 1, POLLOUT, timeout, pIsDataAvailable); /* +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, int timeout)
{ {
ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice); ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice);
ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(ma_device_get_context(pDevice)); ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(ma_device_get_context(pDevice));
@@ -30327,14 +30345,15 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u
for (;;) { for (;;) {
ma_result result; ma_result result;
ma_bool32 isDataAvailable;
/* 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. */ /* 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, pContextStateALSA, pDeviceStateALSA); result = ma_device_wait_read__alsa(pDevice, pContextStateALSA, pDeviceStateALSA, timeout, &isDataAvailable);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
/* Getting here means we should have data available. */ if (isDataAvailable) {
resultALSA = pContextStateALSA->snd_pcm_readi(pDeviceStateALSA->pPCMCapture, pFramesOut, frameCount); resultALSA = pContextStateALSA->snd_pcm_readi(pDeviceStateALSA->pPCMCapture, pFramesOut, frameCount);
if (resultALSA >= 0) { if (resultALSA >= 0) {
break; /* Success. */ break; /* Success. */
@@ -30362,6 +30381,7 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u
} }
} }
} }
}
if (pFramesRead != NULL) { if (pFramesRead != NULL) {
*pFramesRead = resultALSA; *pFramesRead = resultALSA;
@@ -30370,7 +30390,7 @@ static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_u
return MA_SUCCESS; return MA_SUCCESS;
} }
static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFramesIn, ma_uint32 frameCount, ma_uint32* pFramesWritten) static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFramesIn, ma_uint32 frameCount, ma_uint32* pFramesWritten, int timeout)
{ {
ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice); ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice);
ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(ma_device_get_context(pDevice)); ma_context_state_alsa* pContextStateALSA = ma_context_get_backend_state__alsa(ma_device_get_context(pDevice));
@@ -30383,13 +30403,15 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFramesIn
for (;;) { for (;;) {
ma_result result; ma_result result;
ma_bool32 isDataAvailable;
/* 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. */ /* 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, pContextStateALSA, pDeviceStateALSA); result = ma_device_wait_write__alsa(pDevice, pContextStateALSA, pDeviceStateALSA, timeout, &isDataAvailable);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
if (isDataAvailable) {
resultALSA = pContextStateALSA->snd_pcm_writei(pDeviceStateALSA->pPCMPlayback, pFramesIn, frameCount); resultALSA = pContextStateALSA->snd_pcm_writei(pDeviceStateALSA->pPCMPlayback, pFramesIn, frameCount);
if (resultALSA >= 0) { if (resultALSA >= 0) {
break; /* Success. */ break; /* Success. */
@@ -30424,6 +30446,7 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFramesIn
} }
} }
} }
}
if (pFramesWritten != NULL) { if (pFramesWritten != NULL) {
*pFramesWritten = resultALSA; *pFramesWritten = resultALSA;
@@ -30432,6 +30455,52 @@ static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFramesIn
return MA_SUCCESS; return MA_SUCCESS;
} }
static ma_result ma_device_step__alsa(ma_device* pDevice, ma_blocking_mode blockingMode)
{
ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice);
ma_device_type deviceType = ma_device_get_type(pDevice);
ma_result result;
int timeout = (blockingMode == MA_BLOCKING_MODE_BLOCKING) ? -1 : 0;
if (!ma_device_is_started(pDevice)) {
return MA_DEVICE_NOT_STARTED;
}
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
ma_uint32 framesRead;
result = ma_device_read__alsa(pDevice, pDeviceStateALSA->pIntermediaryBufferCapture, pDevice->capture.internalPeriodSizeInFrames, &framesRead, timeout);
if (result != MA_SUCCESS) {
return result;
}
ma_device_handle_backend_data_callback(pDevice, NULL, pDeviceStateALSA->pIntermediaryBufferCapture, framesRead);
}
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
ma_uint32 framesWritten;
result = ma_device_write__alsa(pDevice, pDeviceStateALSA->pIntermediaryBufferPlayback, pDevice->playback.internalPeriodSizeInFrames, &framesWritten, timeout);
if (result != MA_SUCCESS) {
return result;
}
ma_device_handle_backend_data_callback(pDevice, pDeviceStateALSA->pIntermediaryBufferPlayback, NULL, framesWritten);
}
return MA_SUCCESS;
}
static void ma_device_loop__alsa(ma_device* pDevice)
{
while (ma_device_is_started(pDevice)) {
ma_result result = ma_device_step__alsa(pDevice, MA_BLOCKING_MODE_BLOCKING);
if (result != MA_SUCCESS) {
break;
}
}
}
static void ma_device_wakeup__alsa(ma_device* pDevice) static void ma_device_wakeup__alsa(ma_device* pDevice)
{ {
ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice); ma_device_state_alsa* pDeviceStateALSA = ma_device_get_backend_state__alsa(pDevice);
@@ -30468,9 +30537,9 @@ static ma_device_backend_vtable ma_gDeviceBackendVTable_ALSA =
ma_device_uninit__alsa, ma_device_uninit__alsa,
ma_device_start__alsa, ma_device_start__alsa,
ma_device_stop__alsa, ma_device_stop__alsa,
ma_device_read__alsa, NULL,
ma_device_write__alsa, NULL,
NULL, /* onDeviceLoop */ ma_device_loop__alsa,
ma_device_wakeup__alsa ma_device_wakeup__alsa
}; };