diff --git a/audio/common/pipewire.c b/audio/common/pipewire.c index b4d01744063..d81f68657ab 100644 --- a/audio/common/pipewire.c +++ b/audio/common/pipewire.c @@ -16,12 +16,11 @@ #include "pipewire.h" #include - #include #include -#include "verbosity.h" +#include "../../verbosity.h" static void core_error_cb(void *data, uint32_t id, int seq, int res, const char *message) @@ -141,35 +140,89 @@ bool pipewire_stream_set_active(struct pw_thread_loop *loop, struct pw_stream *s return active ? st == PW_STREAM_STATE_STREAMING : st == PW_STREAM_STATE_PAUSED; } -bool pipewire_core_init(pipewire_core_t *pw, const char *loop_name) +bool pipewire_core_init(pipewire_core_t **pw, const char *loop_name, const struct pw_registry_events *events) { - retro_assert(pw); + retro_assert(!*pw); - pw->thread_loop = pw_thread_loop_new(loop_name, NULL); - if (!pw->thread_loop) + *pw = (pipewire_core_t*)calloc(1, sizeof(pipewire_core_t)); + if (!*pw) return false; - pw->ctx = pw_context_new(pw_thread_loop_get_loop(pw->thread_loop), NULL, 0); - if (!pw->ctx) + (*pw)->devicelist = string_list_new(); + if (!(*pw)->devicelist) + { + free(*pw); + *pw = NULL; return false; + } - if (pw_thread_loop_start(pw->thread_loop) < 0) + pw_init(NULL, NULL); + + (*pw)->thread_loop = pw_thread_loop_new(loop_name, NULL); + if (!(*pw)->thread_loop) return false; - pw_thread_loop_lock(pw->thread_loop); + (*pw)->ctx = pw_context_new(pw_thread_loop_get_loop((*pw)->thread_loop), NULL, 0); + if (!(*pw)->ctx) + return false; - pw->core = pw_context_connect(pw->ctx, NULL, 0); - if (!pw->core) + if (pw_thread_loop_start((*pw)->thread_loop) < 0) + return false; + + pw_thread_loop_lock((*pw)->thread_loop); + + (*pw)->core = pw_context_connect((*pw)->ctx, NULL, 0); + if (!(*pw)->core) goto unlock; - if (pw_core_add_listener(pw->core, - &pw->core_listener, - &core_events, pw) < 0) + if (pw_core_add_listener((*pw)->core, + &(*pw)->core_listener, + &core_events, *pw) < 0) goto unlock; + if (events) + { + (*pw)->registry = pw_core_get_registry((*pw)->core, PW_VERSION_REGISTRY, 0); + spa_zero((*pw)->registry_listener); + pw_registry_add_listener((*pw)->registry, &(*pw)->registry_listener, events, *pw); + } + return true; unlock: - pw_thread_loop_unlock(pw->thread_loop); + pw_thread_loop_unlock((*pw)->thread_loop); return false; } + +void pipewire_core_deinit(pipewire_core_t *pw) +{ + if (!pw) + return pw_deinit(); + + if (pw->thread_loop) + { + pw_thread_loop_unlock(pw->thread_loop); + pw_thread_loop_stop(pw->thread_loop); + } + + if (pw->registry) + pw_proxy_destroy((struct pw_proxy*)pw->registry); + + if (pw->core) + { + spa_hook_remove(&pw->core_listener); + spa_zero(pw->core_listener); + pw_core_disconnect(pw->core); + } + + if (pw->ctx) + pw_context_destroy(pw->ctx); + + pw_thread_loop_destroy(pw->thread_loop); + + if (pw->devicelist) + string_list_free(pw->devicelist); + + free(pw); + pw_deinit(); +} diff --git a/audio/common/pipewire.h b/audio/common/pipewire.h index 7b9a0dce4a7..cb69c3c5a40 100644 --- a/audio/common/pipewire.h +++ b/audio/common/pipewire.h @@ -21,9 +21,11 @@ #include #include - #include +#include + + #define PW_RARCH_APPNAME "RetroArch" /* String literals are part of the PipeWire specification */ @@ -54,9 +56,11 @@ size_t pipewire_calc_frame_size(enum spa_audio_format fmt, uint32_t nchannels); void pipewire_set_position(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS]); -bool pipewire_core_init(pipewire_core_t *pipewire, const char *loop_name); +bool pipewire_core_init(pipewire_core_t **pw, const char *loop_name, const struct pw_registry_events *events); + +void pipewire_core_deinit(pipewire_core_t *pw); -void pipewire_core_wait_resync(pipewire_core_t *pipewire); +void pipewire_core_wait_resync(pipewire_core_t *pw); bool pipewire_stream_set_active(struct pw_thread_loop *loop, struct pw_stream *stream, bool active); diff --git a/audio/drivers/pipewire.c b/audio/drivers/pipewire.c index c819dd0a196..934983e7f03 100644 --- a/audio/drivers/pipewire.c +++ b/audio/drivers/pipewire.c @@ -13,26 +13,20 @@ * If not, see . */ -#include -#include - -#include -#include - #include #include #include #include - #include #include +#include #include #include -#include "audio/common/pipewire.h" -#include "audio/audio_driver.h" -#include "verbosity.h" +#include "../common/pipewire.h" +#include "../audio_driver.h" +#include "../../verbosity.h" #define DEFAULT_CHANNELS 2 @@ -71,7 +65,7 @@ static void playback_process_cb(void *data) if ((b = pw_stream_dequeue_buffer(audio->stream)) == NULL) { - RARCH_WARN("[PipeWire]: Out of buffers: %s\n", strerror(errno)); + RARCH_WARN("[Audio] [PipeWire]: Out of buffers: %s\n", strerror(errno)); return; } @@ -82,7 +76,7 @@ static void playback_process_cb(void *data) /* calculate the total no of bytes to read data from buffer */ n_bytes = buf->datas[0].maxsize; if (b->requested) - n_bytes = SPA_MIN(b->requested * audio->frame_size, n_bytes); + n_bytes = MIN(b->requested * audio->frame_size, n_bytes); avail = spa_ringbuffer_get_read_index(&audio->ring, &idx); @@ -118,27 +112,11 @@ static void stream_state_changed_cb(void *data, { pipewire_audio_t *audio = (pipewire_audio_t*)data; - RARCH_DBG("[PipeWire]: New state for Sink Node %d : %s\n", - pw_stream_get_node_id(audio->stream), + RARCH_DBG("[Audio] [PipeWire]: Stream state changed %s -> %s\n", + pw_stream_state_as_string(old), pw_stream_state_as_string(state)); - switch(state) - { - case PW_STREAM_STATE_ERROR: - RARCH_ERR("[PipeWire]: Stream error\n"); - pw_thread_loop_signal(audio->pw->thread_loop, false); - break; - case PW_STREAM_STATE_UNCONNECTED: - RARCH_WARN("[PipeWire]: Stream unconnected\n"); - pw_thread_loop_stop(audio->pw->thread_loop); - break; - case PW_STREAM_STATE_STREAMING: - case PW_STREAM_STATE_PAUSED: - pw_thread_loop_signal(audio->pw->thread_loop, false); - break; - default: - break; - } + pw_thread_loop_signal(audio->pw->thread_loop, false); } static const struct pw_stream_events playback_stream_events = { @@ -161,19 +139,19 @@ static void registry_event_global(void *data, uint32_t id, if (spa_streq(type, PW_TYPE_INTERFACE_Node)) { media = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); - if (media && strcmp(media, "Audio/Sink") == 0) + if (media && spa_streq(media, "Audio/Sink")) { sink = spa_dict_lookup(props, PW_KEY_NODE_NAME); if (sink && pw->devicelist) { attr.i = id; string_list_append(pw->devicelist, sink, attr); - RARCH_LOG("[PipeWire]: Found Sink Node: %s\n", sink); + RARCH_LOG("[Audio] [PipeWire]: Found Sink Node: %s\n", sink); } - RARCH_DBG("[PipeWire]: Object: id:%u Type:%s/%d\n", id, type, version); + RARCH_DBG("[Audio] [PipeWire]: Object: id:%u Type:%s/%d\n", id, type, version); spa_dict_for_each(item, props) - RARCH_DBG("[PipeWire]: \t\t%s: \"%s\"\n", item->key, item->value); + RARCH_DBG("[Audio] [PipeWire]: \t\t%s: \"%s\"\n", item->key, item->value); } } } @@ -195,27 +173,16 @@ static void *pipewire_init(const char *device, unsigned rate, struct pw_properties *props = NULL; const char *error = NULL; pipewire_audio_t *audio = (pipewire_audio_t*)calloc(1, sizeof(*audio)); - pipewire_core_t *pw = NULL; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - pw_init(NULL, NULL); - if (!audio) goto error; - pw = audio->pw = (pipewire_core_t*)calloc(1, sizeof(*audio->pw)); - pw->devicelist = string_list_new(); - - if (!pipewire_core_init(pw, "audio_driver")) + if (!pipewire_core_init(&audio->pw, "audio_driver", ®istry_events)) goto error; - pw->registry = pw_core_get_registry(pw->core, PW_VERSION_REGISTRY, 0); - - spa_zero(pw->registry_listener); - pw_registry_add_listener(pw->registry, &pw->registry_listener, ®istry_events, pw); - /* unlock, run the loop and wait, this will trigger the callbacks */ - pipewire_core_wait_resync(pw); + pipewire_core_wait_resync(audio->pw); audio->info.format = is_little_endian() ? SPA_AUDIO_FORMAT_F32_LE : SPA_AUDIO_FORMAT_F32_BE; audio->info.channels = DEFAULT_CHANNELS; @@ -241,7 +208,7 @@ static void *pipewire_init(const char *device, unsigned rate, buf_samples = latency * rate / 1000; pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%" PRIu64 "/%u", buf_samples, rate); pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%d", rate); - audio->stream = pw_stream_new(pw->core, PW_RARCH_APPNAME, props); + audio->stream = pw_stream_new(audio->pw->core, PW_RARCH_APPNAME, props); if (!audio->stream) goto unlock_error; @@ -266,14 +233,14 @@ static void *pipewire_init(const char *device, unsigned rate, audio->highwater_mark = MIN(RINGBUFFER_SIZE, latency * (uint64_t)rate / 1000 * audio->frame_size); - pw_thread_loop_unlock(pw->thread_loop); + pw_thread_loop_unlock(audio->pw->thread_loop); return audio; unlock_error: pw_thread_loop_unlock(audio->pw->thread_loop); error: - RARCH_ERR("[PipeWire]: Failed to initialize audio\n"); + RARCH_ERR("[Audio] [PipeWire]: Failed to initialize audio\n"); pipewire_free(audio); return NULL; } @@ -290,7 +257,7 @@ static ssize_t pipewire_write(void *data, const void *buf_, size_t len) if (len > audio->highwater_mark) { - RARCH_ERR("[PipeWire]: Buffer too small! Please try increasing the latency.\n"); + RARCH_ERR("[Audio] [PipeWire]: Buffer too small! Please try increasing the latency.\n"); return 0; } @@ -302,7 +269,7 @@ static ssize_t pipewire_write(void *data, const void *buf_, size_t len) avail = audio->highwater_mark - filled; #if 0 /* Useful for tracing */ - RARCH_DBG("[PipeWire]: Ringbuffer utilization: filled %d, avail %d, index %d, size %d\n", + RARCH_DBG("[Audio] [PipeWire]: Ringbuffer utilization: filled %d, avail %d, index %d, size %d\n", filled, avail, idx, len); #endif @@ -317,18 +284,20 @@ static ssize_t pipewire_write(void *data, const void *buf_, size_t len) } pw_thread_loop_wait(audio->pw->thread_loop); + if (pw_stream_get_state(audio->stream, &error) != PW_STREAM_STATE_STREAMING) + return -1; } else break; } if (filled < 0) - RARCH_ERR("[Pipewire]: %p: underrun write:%u filled:%d\n", audio, idx, filled); + RARCH_ERR("[Audio] [Pipewire]: %p: underrun write:%u filled:%d\n", audio, idx, filled); else { if ((uint32_t) filled + len > RINGBUFFER_SIZE) { - RARCH_ERR("[PipeWire]: %p: overrun write:%u filled:%d + size:%zu > max:%u\n", + RARCH_ERR("[Audio] [PipeWire]: %p: overrun write:%u filled:%d + size:%zu > max:%u\n", audio, idx, filled, len, RINGBUFFER_SIZE); } } @@ -350,23 +319,38 @@ static bool pipewire_stop(void *data) if (!audio || !audio->pw) return false; - if (pw_stream_get_state(audio->stream, &error) == PW_STREAM_STATE_PAUSED) - return true; - return pipewire_stream_set_active(audio->pw->thread_loop, audio->stream, false); + if (pw_stream_get_state(audio->stream, &error) == PW_STREAM_STATE_STREAMING) + return pipewire_stream_set_active(audio->pw->thread_loop, audio->stream, false); + + /* For other states we assume that the stream is inactive */ + return true; } static bool pipewire_start(void *data, bool is_shutdown) { + enum pw_stream_state st; pipewire_audio_t *audio = (pipewire_audio_t*)data; const char *error = NULL; + bool res = false; if (!audio || !audio->pw) return false; - if (pw_stream_get_state(audio->stream, &error) == PW_STREAM_STATE_STREAMING) - return true; - return pipewire_stream_set_active(audio->pw->thread_loop, audio->stream, true); + st = pw_stream_get_state(audio->stream, &error); + switch (st) + { + case PW_STREAM_STATE_STREAMING: + res = true; + break; + case PW_STREAM_STATE_PAUSED: + res = pipewire_stream_set_active(audio->pw->thread_loop, audio->stream, true); + break; + default: + break; + } + + return res; } static bool pipewire_alive(void *data) @@ -394,36 +378,13 @@ static void pipewire_free(void *data) if (!audio) return pw_deinit(); - if (audio->pw->thread_loop) - pw_thread_loop_stop(audio->pw->thread_loop); - if (audio->stream) { pw_stream_destroy(audio->stream); audio->stream = NULL; } - - if (audio->pw->registry) - pw_proxy_destroy((struct pw_proxy*)audio->pw->registry); - - if (audio->pw->core) - { - spa_hook_remove(&audio->pw->core_listener); - spa_zero(audio->pw->core_listener); - pw_core_disconnect(audio->pw->core); - } - - if (audio->pw->ctx) - pw_context_destroy(audio->pw->ctx); - - pw_thread_loop_destroy(audio->pw->thread_loop); - - if (audio->pw->devicelist) - string_list_free(audio->pw->devicelist); - - free(audio->pw); + pipewire_core_deinit(audio->pw); free(audio); - pw_deinit(); } static bool pipewire_use_float(void *data) { return true; } diff --git a/audio/drivers_microphone/pipewire.c b/audio/drivers_microphone/pipewire.c index 76db9b3e9c9..41860429ad4 100644 --- a/audio/drivers_microphone/pipewire.c +++ b/audio/drivers_microphone/pipewire.c @@ -13,17 +13,14 @@ * If not, see . */ -#include -#include - #include #include #include #include - #include #include +#include #include #include @@ -45,7 +42,6 @@ typedef struct pipewire_microphone uint32_t frame_size; struct spa_ringbuffer ring; uint8_t buffer[RINGBUFFER_SIZE]; - bool is_ready; } pipewire_microphone_t; static void stream_state_changed_cb(void *data, @@ -53,24 +49,11 @@ static void stream_state_changed_cb(void *data, { pipewire_microphone_t *mic = (pipewire_microphone_t*)data; - RARCH_DBG("[PipeWire]: New state for Source Node %d : %s\n", - pw_stream_get_node_id(mic->stream), + RARCH_DBG("[Microphone] [PipeWire]: Stream state changed %s -> %s\n", + pw_stream_state_as_string(old), pw_stream_state_as_string(state)); - switch(state) - { - case PW_STREAM_STATE_UNCONNECTED: - mic->is_ready = false; - pw_thread_loop_stop(mic->pw->thread_loop); - break; - case PW_STREAM_STATE_STREAMING: - case PW_STREAM_STATE_ERROR: - case PW_STREAM_STATE_PAUSED: - pw_thread_loop_signal(mic->pw->thread_loop, false); - break; - default: - break; - } + pw_thread_loop_signal(mic->pw->thread_loop, false); } static void stream_destroy_cb(void *data) @@ -93,7 +76,7 @@ static void capture_process_cb(void *data) if (!(b = pw_stream_dequeue_buffer(mic->stream))) { - RARCH_ERR("[PipeWire]: out of buffers: %s\n", strerror(errno)); + RARCH_ERR("[Microphone] [PipeWire]: Out of buffers: %s\n", strerror(errno)); return; } @@ -101,15 +84,15 @@ static void capture_process_cb(void *data) if ((p = buf->datas[0].data) == NULL) goto done; - offs = SPA_MIN(buf->datas[0].chunk->offset, buf->datas[0].maxsize); - n_bytes = SPA_MIN(buf->datas[0].chunk->size, buf->datas[0].maxsize - offs); + offs = MIN(buf->datas[0].chunk->offset, buf->datas[0].maxsize); + n_bytes = MIN(buf->datas[0].chunk->size, buf->datas[0].maxsize - offs); if ((filled = spa_ringbuffer_get_write_index(&mic->ring, &idx)) < 0) - RARCH_ERR("[PipeWire]: %p: underrun write:%u filled:%d\n", p, idx, filled); + RARCH_ERR("[Microphone] [PipeWire]: %p: underrun write:%u filled:%d\n", p, idx, filled); else { if ((uint32_t)filled + n_bytes > RINGBUFFER_SIZE) - RARCH_ERR("[PipeWire]: %p: overrun write:%u filled:%d + size:%u > max:%u\n", + RARCH_ERR("[Microphone] [PipeWire]: %p: overrun write:%u filled:%d + size:%u > max:%u\n", p, idx, filled, n_bytes, RINGBUFFER_SIZE); } spa_ringbuffer_write_data(&mic->ring, @@ -147,18 +130,18 @@ static void registry_event_global(void *data, uint32_t id, if (spa_streq(type, PW_TYPE_INTERFACE_Node)) { media = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); - if (media && strcmp(media, "Audio/Source") == 0) + if (media && spa_streq(media, "Audio/Source")) { if ((sink = spa_dict_lookup(props, PW_KEY_NODE_NAME)) != NULL) { attr.i = id; string_list_append(pw->devicelist, sink, attr); - RARCH_LOG("[PipeWire]: Found Source Node: %s\n", sink); + RARCH_LOG("[Microphone] [PipeWire]: Found Source Node: %s\n", sink); } - RARCH_DBG("[PipeWire]: Object: id:%u Type:%s/%d\n", id, type, version); + RARCH_DBG("[Microphone] [PipeWire]: Object: id:%u Type:%s/%d\n", id, type, version); spa_dict_for_each(item, props) - RARCH_DBG("[PipeWire]: \t\t%s: \"%s\"\n", item->key, item->value); + RARCH_DBG("[Microphone] [PipeWire]: \t\t%s: \"%s\"\n", item->key, item->value); } } } @@ -170,34 +153,7 @@ static const struct pw_registry_events registry_events = { static void pipewire_microphone_free(void *driver_context) { - pipewire_core_t *pw = (pipewire_core_t*)driver_context; - - if (!pw) - return pw_deinit(); - - if (pw->thread_loop) - pw_thread_loop_stop(pw->thread_loop); - - if (pw->registry) - pw_proxy_destroy((struct pw_proxy*)pw->registry); - - if (pw->core) - { - spa_hook_remove(&pw->core_listener); - spa_zero(pw->core_listener); - pw_core_disconnect(pw->core); - } - - if (pw->ctx) - pw_context_destroy(pw->ctx); - - pw_thread_loop_destroy(pw->thread_loop); - - if (pw->devicelist) - string_list_free(pw->devicelist); - - free(pw); - pw_deinit(); + pipewire_core_deinit((pipewire_core_t*)driver_context); } static void *pipewire_microphone_init(void) @@ -208,33 +164,19 @@ static void *pipewire_microphone_init(void) const struct spa_pod *params[1]; struct pw_properties *props = NULL; const char *error = NULL; - pipewire_core_t *pw = (pipewire_core_t*)calloc(1, sizeof(*pw)); + pipewire_core_t *pw = NULL; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); - if (!pw) - goto error; - - pw_init(NULL, NULL); - - pw->devicelist = string_list_new(); - if (!pw->devicelist) + if (!pipewire_core_init(&pw, "microphone_driver", ®istry_events)) goto error; - if (!pipewire_core_init(pw, "microphone_driver")) - goto error; - - pw->registry = pw_core_get_registry(pw->core, PW_VERSION_REGISTRY, 0); - - spa_zero(pw->registry_listener); - pw_registry_add_listener(pw->registry, &pw->registry_listener, ®istry_events, pw); - pipewire_core_wait_resync(pw); pw_thread_loop_unlock(pw->thread_loop); return pw; error: - RARCH_ERR("[PipeWire]: Failed to initialize microphone\n"); + RARCH_ERR("[Microphone] [PipeWire]: Failed to initialize microphone\n"); pipewire_microphone_free(pw); return NULL; } @@ -262,8 +204,7 @@ static int pipewire_microphone_read(void *driver_context, void *mic_context, voi pipewire_core_t *pw = (pipewire_core_t*)driver_context; pipewire_microphone_t *mic = (pipewire_microphone_t*)mic_context; - if ( !mic->is_ready - || pw_stream_get_state(mic->stream, &error) != PW_STREAM_STATE_STREAMING) + if (pw_stream_get_state(mic->stream, &error) != PW_STREAM_STATE_STREAMING) return -1; pw_thread_loop_lock(pw->thread_loop); @@ -282,6 +223,8 @@ static int pipewire_microphone_read(void *driver_context, void *mic_context, voi } pw_thread_loop_wait(pw->thread_loop); + if (pw_stream_get_state(mic->stream, &error) != PW_STREAM_STATE_STREAMING) + return -1; } else break; @@ -347,7 +290,6 @@ static void *pipewire_microphone_open_mic(void *driver_context, if (!mic) goto error; - mic->is_ready = false; mic->pw = (pipewire_core_t*)driver_context; pw_thread_loop_lock(mic->pw->thread_loop); @@ -401,27 +343,41 @@ static void *pipewire_microphone_open_mic(void *driver_context, *new_rate = mic->info.rate; - mic->is_ready = true; return mic; unlock_error: pw_thread_loop_unlock(mic->pw->thread_loop); error: - RARCH_ERR("[PipeWire]: Failed to initialize microphone...\n"); + RARCH_ERR("[Microphone] [PipeWire]: Failed to initialize microphone...\n"); pipewire_microphone_close_mic(mic->pw, mic); return NULL; } static bool pipewire_microphone_start_mic(void *driver_context, void *mic_context) { - pipewire_core_t *pw = (pipewire_core_t*)driver_context; + enum pw_stream_state st; + pipewire_core_t *pw = (pipewire_core_t*)driver_context; pipewire_microphone_t *mic = (pipewire_microphone_t*)mic_context; const char *error = NULL; - if (!pw || !mic || !mic->is_ready) + bool res = false; + + if (!pw || !mic) return false; - if (pw_stream_get_state(mic->stream, &error) == PW_STREAM_STATE_STREAMING) - return true; - return pipewire_stream_set_active(pw->thread_loop, mic->stream, true); + + st = pw_stream_get_state(mic->stream, &error); + switch (st) + { + case PW_STREAM_STATE_STREAMING: + res = true; + break; + case PW_STREAM_STATE_PAUSED: + res = pipewire_stream_set_active(pw->thread_loop, mic->stream, true); + break; + default: + break; + } + + return res; } static bool pipewire_microphone_stop_mic(void *driver_context, void *mic_context) @@ -431,12 +387,15 @@ static bool pipewire_microphone_stop_mic(void *driver_context, void *mic_context const char *error = NULL; bool res = false; - if (!pw || !mic || !mic->is_ready) + if (!pw || !mic) return false; - if (pw_stream_get_state(mic->stream, &error) == PW_STREAM_STATE_PAUSED) - return true; - res = pipewire_stream_set_active(pw->thread_loop, mic->stream, false); + if (pw_stream_get_state(mic->stream, &error) == PW_STREAM_STATE_STREAMING) + res = pipewire_stream_set_active(pw->thread_loop, mic->stream, false); + else + /* For other states we assume that the stream is inactive */ + res = true; + spa_ringbuffer_read_update(&mic->ring, 0); spa_ringbuffer_write_update(&mic->ring, 0);