From 19b53e8dbe70ad724c1c7b8efb1d023494054c7d Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 7 Dec 2021 15:27:18 +0100 Subject: [PATCH] jack: fix loopback links to client When a client produces output on a port and there is a link between the output port and an input port of the client, make sure that the new data appears on the input port. We do this by keeping track of what links are between our ports and when we get the peer id of a port input, we can related this to our port again. Some mixer inputs will then fetch content from our own ports and so we can prepare the output for them. This can be converting midi to control messages or moving the output buffer id to the io area so that the input mixer can pick up the new buffer. Fixes #1839 --- pipewire-jack/src/pipewire-jack.c | 75 +++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/pipewire-jack/src/pipewire-jack.c b/pipewire-jack/src/pipewire-jack.c index ccd53ae0b..72d75f317 100644 --- a/pipewire-jack/src/pipewire-jack.c +++ b/pipewire-jack/src/pipewire-jack.c @@ -139,6 +139,8 @@ struct object { bool is_complete; uint32_t src_idx; uint32_t dst_idx; + struct port *our_input; + struct port *our_output; } port_link; struct { unsigned long flags; @@ -205,6 +207,7 @@ struct mix { uint32_t id; uint32_t peer_id; struct port *port; + struct port *peer_port; struct spa_io_buffers *io; @@ -998,8 +1001,8 @@ static inline void *get_buffer_output(struct port *p, uint32_t frames, uint32_t if (SPA_UNLIKELY((mix = p->global_mix) == NULL)) return NULL; - pw_log_trace_fp("%p: port %p %d get buffer %d n_buffers:%d", - c, p, p->id, frames, mix->n_buffers); + pw_log_trace_fp("%p: port %s %d get buffer %d n_buffers:%d", + c, p->object->port.name, p->id, frames, mix->n_buffers); if (SPA_UNLIKELY(mix->n_buffers == 0)) return NULL; @@ -1057,6 +1060,19 @@ static inline void process_empty(struct port *p, uint32_t frames) } } +static void prepare_output(struct port *p, uint32_t frames) +{ + struct mix *mix; + + if (SPA_UNLIKELY(p->empty_out)) + process_empty(p, frames); + + spa_list_for_each(mix, &p->mix, port_link) { + if (SPA_LIKELY(mix->io != NULL)) + *mix->io = p->io; + } +} + static void complete_process(struct client *c, uint32_t frames) { struct port *p; @@ -1069,13 +1085,7 @@ static void complete_process(struct client *c, uint32_t frames) } } spa_list_for_each(p, &c->ports[SPA_DIRECTION_OUTPUT], link) { - if (SPA_UNLIKELY(p->empty_out)) - process_empty(p, frames); - - spa_list_for_each(mix, &p->mix, port_link) { - if (SPA_LIKELY(mix->io != NULL)) - *mix->io = p->io; - } + prepare_output(p, frames); p->io.status = SPA_STATUS_NEED_DATA; } } @@ -2386,15 +2396,25 @@ static int client_node_port_set_mix_info(void *object, dst = peer_id; } - if ((l = find_link(c, src, dst)) != NULL && !l->port_link.is_complete) { - l->port_link.is_complete = true; - pw_log_info("%p: our link %d/%u -> %d/%u completed", c, - l->port_link.src, l->port_link.src_idx, - l->port_link.dst, l->port_link.dst_idx); - do_callback(c, connect_callback, - l->port_link.src_idx, l->port_link.dst_idx, 1, c->connect_arg); - recompute_latencies(c); - do_callback(c, graph_callback, c->graph_arg); + if ((l = find_link(c, src, dst)) != NULL) { + if (direction == SPA_DIRECTION_INPUT) + mix->peer_port = l->port_link.our_output; + else + mix->peer_port = l->port_link.our_input; + + pw_log_info("peer port %p %p %p", mix->peer_port, + l->port_link.our_output, l->port_link.our_input); + + if (!l->port_link.is_complete) { + l->port_link.is_complete = true; + pw_log_info("%p: our link %d/%u -> %d/%u completed", c, + l->port_link.src, l->port_link.src_idx, + l->port_link.dst, l->port_link.dst_idx); + do_callback(c, connect_callback, + l->port_link.src_idx, l->port_link.dst_idx, 1, c->connect_arg); + recompute_latencies(c); + do_callback(c, graph_callback, c->graph_arg); + } } exit: @@ -2816,6 +2836,8 @@ static void registry_event_global(void *data, uint32_t id, o->port_link.src_ours = p->port.port != NULL && p->port.port->client == c; + if (o->port_link.src_ours) + o->port_link.our_output = p->port.port; if ((str = spa_dict_lookup(props, PW_KEY_LINK_INPUT_PORT)) == NULL) goto exit_free; @@ -2827,6 +2849,8 @@ static void registry_event_global(void *data, uint32_t id, o->port_link.dst_idx = p->idx; o->port_link.dst_ours = p->port.port != NULL && p->port.port->client == c; + if (o->port_link.dst_ours) + o->port_link.our_input = p->port.port; o->port_link.is_complete = !o->port_link.src_ours && !o->port_link.dst_ours; pw_log_debug("%p: add link %d %d->%d", c, id, @@ -4156,10 +4180,13 @@ done: return res; } -static struct buffer *get_mix_buffer(struct mix *mix) +static struct buffer *get_mix_buffer(struct mix *mix, jack_nframes_t frames) { struct spa_io_buffers *io; + if (mix->peer_port != NULL) + prepare_output(mix->peer_port, frames); + io = mix->io; if (io == NULL || io->status != SPA_STATUS_HAVE_DATA || @@ -4181,10 +4208,10 @@ static void *get_buffer_input_float(struct port *p, jack_nframes_t frames) uint32_t offset, size; void *np; - pw_log_trace_fp("%p: port %p mix %d.%d get buffer %d", - p->client, p, p->id, mix->id, frames); + pw_log_trace_fp("%p: port %s mix %d.%d get buffer %d", + p->client, p->object->port.name, p->id, mix->id, frames); - if ((b = get_mix_buffer(mix)) == NULL) + if ((b = get_mix_buffer(mix, frames)) == NULL) continue; d = &b->datas[0]; @@ -4224,7 +4251,7 @@ static void *get_buffer_input_midi(struct port *p, jack_nframes_t frames) pw_log_trace_fp("%p: port %p mix %d.%d get buffer %d", p->client, p, p->id, mix->id, frames); - if ((b = get_mix_buffer(mix)) == NULL) + if ((b = get_mix_buffer(mix, frames)) == NULL) continue; d = &b->datas[0]; @@ -4291,7 +4318,7 @@ void * jack_port_get_buffer (jack_port_t *port, jack_nframes_t frames) pw_log_trace("peer mix: %p %d", mix, mix->peer_id); - if ((b = get_mix_buffer(mix)) == NULL) + if ((b = get_mix_buffer(mix, frames)) == NULL) return NULL; d = &b->datas[0];