Skip to content

Commit e97ab1e

Browse files
committed
Another pass on the PipeWire backend.
This removes the "wait" concept and replaces it with an extra parameter for the step() callback for the blocking mode, which can be blocking or non-blocking. This also implements the wake() callback for waking up from a blocking step.
1 parent d6487d0 commit e97ab1e

File tree

1 file changed

+88
-90
lines changed

1 file changed

+88
-90
lines changed

extras/backends/pipewire/miniaudio_pipewire.c

Lines changed: 88 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ typedef int (* ma_pw_loop_set_name_proc )(struct
195195
typedef void (* ma_pw_loop_enter_proc )(struct ma_pw_loop* loop);
196196
typedef void (* ma_pw_loop_leave_proc )(struct ma_pw_loop* loop);
197197
typedef int (* ma_pw_loop_iterate_proc )(struct ma_pw_loop* loop, int timeout);
198+
typedef struct spa_source* (* ma_pw_loop_add_event_proc )(struct ma_pw_loop* loop, void (* func)(void* data, ma_uint64 count), void* data);
199+
typedef int (* ma_pw_loop_signal_event_proc )(struct ma_pw_loop* loop, struct spa_source* source);
198200
typedef struct ma_pw_thread_loop* (* ma_pw_thread_loop_new_proc )(const char* name, const struct spa_dict* props);
199201
typedef void (* ma_pw_thread_loop_destroy_proc )(struct ma_pw_thread_loop* loop);
200202
typedef struct ma_pw_loop* (* ma_pw_thread_loop_get_loop_proc )(struct ma_pw_thread_loop* loop);
@@ -237,6 +239,8 @@ typedef struct
237239
ma_pw_loop_enter_proc pw_loop_enter;
238240
ma_pw_loop_leave_proc pw_loop_leave;
239241
ma_pw_loop_iterate_proc pw_loop_iterate;
242+
ma_pw_loop_add_event_proc pw_loop_add_event;
243+
ma_pw_loop_signal_event_proc pw_loop_signal_event;
240244
ma_pw_thread_loop_new_proc pw_thread_loop_new;
241245
ma_pw_thread_loop_destroy_proc pw_thread_loop_destroy;
242246
ma_pw_thread_loop_get_loop_proc pw_thread_loop_get_loop;
@@ -297,6 +301,7 @@ typedef struct
297301
struct ma_pw_loop* pLoop;
298302
struct ma_pw_context* pContext;
299303
struct ma_pw_core* pCore;
304+
struct spa_source* pWakeup; /* This is for waking up the loop which we need to do after each data processing callback and the miniaudio wakeup callback. */
300305
ma_pipewire_stream_state playback;
301306
ma_pipewire_stream_state capture;
302307
struct
@@ -463,7 +468,7 @@ static ma_device_state_pipewire* ma_device_get_backend_state__pipewire(ma_device
463468
}
464469

465470

466-
static ma_result ma_device_step__pipewire(ma_device* pDevice);
471+
static ma_result ma_device_step__pipewire(ma_device* pDevice, ma_blocking_mode blockingMode);
467472

468473

469474
static void ma_backend_info__pipewire(ma_device_backend_info* pBackendInfo)
@@ -518,6 +523,8 @@ static ma_result ma_context_init__pipewire(ma_context* pContext, const void* pCo
518523
pContextStatePipeWire->pw_loop_enter = (ma_pw_loop_enter_proc )ma_dlsym(pLog, hPipeWire, "pw_loop_enter");
519524
pContextStatePipeWire->pw_loop_leave = (ma_pw_loop_leave_proc )ma_dlsym(pLog, hPipeWire, "pw_loop_leave");
520525
pContextStatePipeWire->pw_loop_iterate = (ma_pw_loop_iterate_proc )ma_dlsym(pLog, hPipeWire, "pw_loop_iterate");
526+
pContextStatePipeWire->pw_loop_add_event = (ma_pw_loop_add_event_proc )ma_dlsym(pLog, hPipeWire, "pw_loop_add_event");
527+
pContextStatePipeWire->pw_loop_signal_event = (ma_pw_loop_signal_event_proc )ma_dlsym(pLog, hPipeWire, "pw_loop_signal_event");
521528
pContextStatePipeWire->pw_thread_loop_new = (ma_pw_thread_loop_new_proc )ma_dlsym(pLog, hPipeWire, "pw_thread_loop_new");
522529
pContextStatePipeWire->pw_thread_loop_destroy = (ma_pw_thread_loop_destroy_proc )ma_dlsym(pLog, hPipeWire, "pw_thread_loop_destroy");
523530
pContextStatePipeWire->pw_thread_loop_get_loop = (ma_pw_thread_loop_get_loop_proc )ma_dlsym(pLog, hPipeWire, "pw_thread_loop_get_loop");
@@ -1252,6 +1259,9 @@ static void ma_stream_event_process__pipewire(void* pUserData, ma_device_type de
12521259
pBuffer->buffer->datas[0].chunk->size = frameCount * bytesPerFrame;
12531260

12541261
pContextStatePipeWire->pw_stream_queue_buffer(pStreamState->pStream, pBuffer);
1262+
1263+
/* We need to make sure the loop is woken up so we can refill the intermediary buffer in the step function. */
1264+
pContextStatePipeWire->pw_loop_signal_event(pDeviceStatePipeWire->pLoop, pDeviceStatePipeWire->pWakeup);
12551265
}
12561266

12571267

@@ -1419,6 +1429,13 @@ static ma_result ma_device_init_internal__pipewire(ma_device* pDevice, ma_contex
14191429
return MA_SUCCESS;
14201430
}
14211431

1432+
static void ma_device_on_wakupe__pipewire(void* pUserData, ma_uint64 count)
1433+
{
1434+
/* Nothing to do here. This is only used for waking up the loop. */
1435+
(void)pUserData;
1436+
(void)count;
1437+
}
1438+
14221439
static ma_result ma_device_init__pipewire(ma_device* pDevice, const void* pDeviceBackendConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture, void** ppDeviceState)
14231440
{
14241441
ma_result result;
@@ -1503,6 +1520,17 @@ static ma_result ma_device_init__pipewire(ma_device* pDevice, const void* pDevic
15031520
return result;
15041521
}
15051522

1523+
/* We need an event for waking up the loop. */
1524+
pDeviceStatePipeWire->pWakeup = pContextStatePipeWire->pw_loop_add_event(pLoop, ma_device_on_wakupe__pipewire, pDeviceStatePipeWire);
1525+
if (pDeviceStatePipeWire->pWakeup == NULL) {
1526+
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to create PipeWire loop wakeup event.");
1527+
pContextStatePipeWire->pw_core_disconnect(pCore);
1528+
pContextStatePipeWire->pw_context_destroy(pPipeWireContext);
1529+
pContextStatePipeWire->pw_loop_destroy(pLoop);
1530+
ma_free(pDeviceStatePipeWire, ma_device_get_allocation_callbacks(pDevice));
1531+
return MA_ERROR;
1532+
}
1533+
15061534
*ppDeviceState = pDeviceStatePipeWire;
15071535

15081536
return MA_SUCCESS;
@@ -1540,7 +1568,7 @@ static ma_result ma_device_start__pipewire(ma_device* pDevice)
15401568
ma_context_state_pipewire* pContextStatePipeWire = ma_context_get_backend_state__pipewire(ma_device_get_context(pDevice));
15411569

15421570
/* Prepare our buffers before starting the streams. To do this we just need to step. */
1543-
ma_device_step__pipewire(pDevice);
1571+
ma_device_step__pipewire(pDevice, MA_BLOCKING_MODE_NON_BLOCKING);
15441572

15451573
if (pDeviceStatePipeWire->capture.pStream != NULL) {
15461574
pContextStatePipeWire->pw_stream_set_active(pDeviceStatePipeWire->capture.pStream, MA_TRUE);
@@ -1569,106 +1597,78 @@ static ma_result ma_device_stop__pipewire(ma_device* pDevice)
15691597
return MA_SUCCESS;
15701598
}
15711599

1572-
1573-
static ma_result ma_device_wait__pipewire(ma_device* pDevice)
1600+
static ma_result ma_device_step__pipewire(ma_device* pDevice, ma_blocking_mode blockingMode)
15741601
{
15751602
ma_device_state_pipewire* pDeviceStatePipeWire = ma_device_get_backend_state__pipewire(pDevice);
15761603
ma_context_state_pipewire* pContextStatePipeWire = ma_context_get_backend_state__pipewire(ma_device_get_context(pDevice));
15771604
ma_device_type deviceType = ma_device_get_type(pDevice);
1605+
int timeout;
1606+
ma_bool32 hasProcessedData = MA_FALSE;
15781607

1579-
for (;;) {
1580-
int iterateResult;
1581-
1582-
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
1583-
if (ma_pcm_rb_available_read(&pDeviceStatePipeWire->capture.rb) > 0) {
1584-
return MA_SUCCESS;
1585-
}
1586-
}
1587-
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
1588-
if (ma_pcm_rb_available_write(&pDeviceStatePipeWire->playback.rb) > 0) {
1589-
return MA_SUCCESS;
1590-
}
1591-
}
1592-
1593-
iterateResult = pContextStatePipeWire->pw_loop_iterate(pDeviceStatePipeWire->pLoop, -1);
1594-
if (iterateResult < 0) {
1595-
/*
1596-
Getting here means the loop iteration failed. I've had this happen in cases where we really don't want to
1597-
be stopping the device, one example being when I insert a breakpoint while debugging. I'm just going to
1598-
break from the loop to ensure we don't get stuck.
1599-
*/
1600-
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "PipeWire loop iterate failed.");
1601-
break;
1602-
}
1608+
if (blockingMode == MA_BLOCKING_MODE_BLOCKING) {
1609+
timeout = -1;
1610+
} else {
1611+
timeout = 0;
16031612
}
16041613

1605-
return MA_SUCCESS;
1606-
}
1607-
1608-
static ma_result ma_device_step__pipewire(ma_device* pDevice)
1609-
{
1610-
ma_device_state_pipewire* pDeviceStatePipeWire = ma_device_get_backend_state__pipewire(pDevice);
1611-
ma_context_state_pipewire* pContextStatePipeWire = ma_context_get_backend_state__pipewire(ma_device_get_context(pDevice));
1612-
ma_device_type deviceType = ma_device_get_type(pDevice);
1613-
1614-
pContextStatePipeWire->pw_loop_iterate(pDeviceStatePipeWire->pLoop, 0);
1615-
1616-
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
1617-
ma_uint32 framesAvailable;
1614+
/* We will keep looping until we've processed some data. This should keep our stepping in time with data processing. */
1615+
for (;;) {
1616+
pContextStatePipeWire->pw_loop_iterate(pDeviceStatePipeWire->pLoop, timeout);
16181617

1619-
framesAvailable = ma_pcm_rb_available_read(&pDeviceStatePipeWire->capture.rb);
1620-
if (framesAvailable > 0) {
1621-
/*printf("framesAvailable (Capture): %d\n", (int)framesAvailable);*/
1618+
if (!ma_device_is_started(pDevice)) {
1619+
return MA_DEVICE_NOT_STARTED;
16221620
}
16231621

1624-
while (framesAvailable > 0) {
1625-
void* pMappedBuffer;
1626-
ma_uint32 framesToRead = framesAvailable;
1627-
ma_result result;
1622+
/* We want to handle both playback and capture in a single iteration for duplex mode. */
1623+
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
1624+
ma_uint32 framesAvailable;
16281625

1629-
result = ma_pcm_rb_acquire_read(&pDeviceStatePipeWire->capture.rb, &framesToRead, &pMappedBuffer);
1630-
if (result == MA_SUCCESS) {
1631-
ma_device_handle_backend_data_callback(pDevice, NULL, pMappedBuffer, framesToRead);
1626+
framesAvailable = ma_pcm_rb_available_read(&pDeviceStatePipeWire->capture.rb);
1627+
if (framesAvailable > 0) {
1628+
hasProcessedData = MA_TRUE;
1629+
}
16321630

1633-
result = ma_pcm_rb_commit_read(&pDeviceStatePipeWire->capture.rb, framesToRead);
1634-
framesAvailable -= framesToRead;
1631+
while (framesAvailable > 0) {
1632+
void* pMappedBuffer;
1633+
ma_uint32 framesToRead = framesAvailable;
1634+
ma_result result;
16351635

1636-
if (result != MA_SUCCESS) {
1637-
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "(PipeWire) Failed to commit read to ring buffer.");
1636+
result = ma_pcm_rb_acquire_read(&pDeviceStatePipeWire->capture.rb, &framesToRead, &pMappedBuffer);
1637+
if (result == MA_SUCCESS) {
1638+
ma_device_handle_backend_data_callback(pDevice, NULL, pMappedBuffer, framesToRead);
1639+
1640+
result = ma_pcm_rb_commit_read(&pDeviceStatePipeWire->capture.rb, framesToRead);
1641+
framesAvailable -= framesToRead;
16381642
}
1639-
} else {
1640-
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "(PipeWire) Failed to acquire read to ring buffer.");
16411643
}
16421644
}
1643-
}
1644-
1645-
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
1646-
ma_uint32 framesAvailable;
1647-
1648-
framesAvailable = ma_pcm_rb_available_write(&pDeviceStatePipeWire->playback.rb);
1649-
if (framesAvailable > 0) {
1650-
/*printf("framesAvailable (Playback): %d\n", (int)framesAvailable);*/
1651-
}
1645+
1646+
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
1647+
ma_uint32 framesAvailable;
16521648

1653-
while (framesAvailable > 0) {
1654-
void* pMappedBuffer;
1655-
ma_uint32 framesToWrite = framesAvailable;
1656-
ma_result result;
1649+
framesAvailable = ma_pcm_rb_available_write(&pDeviceStatePipeWire->playback.rb);
1650+
if (framesAvailable > 0) {
1651+
hasProcessedData = MA_TRUE;
1652+
}
16571653

1658-
result = ma_pcm_rb_acquire_write(&pDeviceStatePipeWire->playback.rb, &framesToWrite, &pMappedBuffer);
1659-
if (result == MA_SUCCESS) {
1660-
ma_device_handle_backend_data_callback(pDevice, pMappedBuffer, NULL, framesToWrite);
1654+
while (framesAvailable > 0) {
1655+
void* pMappedBuffer;
1656+
ma_uint32 framesToWrite = framesAvailable;
1657+
ma_result result;
16611658

1662-
result = ma_pcm_rb_commit_write(&pDeviceStatePipeWire->playback.rb, framesToWrite);
1663-
framesAvailable -= framesToWrite;
1659+
result = ma_pcm_rb_acquire_write(&pDeviceStatePipeWire->playback.rb, &framesToWrite, &pMappedBuffer);
1660+
if (result == MA_SUCCESS) {
1661+
ma_device_handle_backend_data_callback(pDevice, pMappedBuffer, NULL, framesToWrite);
16641662

1665-
if (result != MA_SUCCESS) {
1666-
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "(PipeWire) Failed to commit write to ring buffer.");
1663+
result = ma_pcm_rb_commit_write(&pDeviceStatePipeWire->playback.rb, framesToWrite);
1664+
framesAvailable -= framesToWrite;
16671665
}
1668-
} else {
1669-
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "(PipeWire) Failed to acquire write to ring buffer.");
16701666
}
16711667
}
1668+
1669+
if (hasProcessedData) {
1670+
break;
1671+
}
16721672
}
16731673

16741674
return MA_SUCCESS;
@@ -1677,21 +1677,19 @@ static ma_result ma_device_step__pipewire(ma_device* pDevice)
16771677
static void ma_device_loop__pipewire(ma_device* pDevice)
16781678
{
16791679
for (;;) {
1680-
ma_result result = ma_device_wait__pipewire(pDevice);
1680+
ma_result result = ma_device_step__pipewire(pDevice, MA_BLOCKING_MODE_BLOCKING);
16811681
if (result != MA_SUCCESS) {
16821682
break;
16831683
}
1684+
}
1685+
}
16841686

1685-
/* If the wait terminated due to the device being stopped, abort now. */
1686-
if (!ma_device_is_started(pDevice)) {
1687-
break;
1688-
}
1687+
static void ma_device_wake__pipewire(ma_device* pDevice)
1688+
{
1689+
ma_device_state_pipewire* pDeviceStatePipeWire = ma_device_get_backend_state__pipewire(pDevice);
1690+
ma_context_state_pipewire* pContextStatePipeWire = ma_context_get_backend_state__pipewire(ma_device_get_context(pDevice));
16891691

1690-
result = ma_device_step__pipewire(pDevice);
1691-
if (result != MA_SUCCESS) {
1692-
break;
1693-
}
1694-
}
1692+
pContextStatePipeWire->pw_loop_signal_event(pDeviceStatePipeWire->pLoop, pDeviceStatePipeWire->pWakeup);
16951693
}
16961694

16971695

@@ -1708,7 +1706,7 @@ static ma_device_backend_vtable ma_gDeviceBackendVTable_PipeWire =
17081706
NULL, /* onDeviceRead */
17091707
NULL, /* onDeviceWrite */
17101708
ma_device_loop__pipewire,
1711-
NULL /* onDeviceWakeup */
1709+
ma_device_wake__pipewire
17121710
};
17131711

17141712
ma_device_backend_vtable* ma_device_backend_pipewire = &ma_gDeviceBackendVTable_PipeWire;

0 commit comments

Comments
 (0)