diff --git a/pipewire-jack/src/pipewire-jack.c b/pipewire-jack/src/pipewire-jack.c index 839a9fc24..e85947124 100644 --- a/pipewire-jack/src/pipewire-jack.c +++ b/pipewire-jack/src/pipewire-jack.c @@ -1546,14 +1546,37 @@ static inline jack_midi_data_t* midi_event_reserve(void *port_buffer, return res; } +static inline int midi_event_append(void *port_buffer, const jack_midi_data_t *data, size_t data_size) +{ + struct midi_buffer *mb = port_buffer; + struct midi_event *events = SPA_PTROFF(mb, sizeof(*mb), struct midi_event); + struct midi_event *ev; + size_t old_size; + uint8_t *old, *buf; + + ev = &events[--mb->event_count]; + mb->write_pos -= ev->size; + old_size = ev->size; + if (old_size <= MIDI_INLINE_MAX) + old = ev->inline_data; + else + old = SPA_PTROFF(mb, ev->byte_offset, uint8_t); + buf = midi_event_reserve(port_buffer, ev->time, old_size + data_size); + if (SPA_UNLIKELY(buf == NULL)) + return -ENOBUFS; + memmove(buf, old, old_size); + memcpy(buf+old_size, data, data_size); + return 0; +} + static inline int midi_event_write(void *port_buffer, jack_nframes_t time, const jack_midi_data_t *data, size_t data_size, bool fix) { jack_midi_data_t *retbuf = midi_event_reserve (port_buffer, time, data_size); - if (SPA_UNLIKELY(retbuf == NULL)) - return -ENOBUFS; + if (SPA_UNLIKELY(retbuf == NULL)) + return -ENOBUFS; memcpy (retbuf, data, data_size); if (fix) fix_midi_event(retbuf, data_size); @@ -1566,6 +1589,7 @@ static void convert_to_event(struct spa_pod_sequence **seq, uint32_t n_seq, void uint64_t state = 0; uint32_t i; int res = 0; + bool in_sysex = false; for (i = 0; i < n_seq; i++) c[i] = spa_pod_control_first(&seq[i]->body); @@ -1620,17 +1644,30 @@ static void convert_to_event(struct spa_pod_sequence **seq, uint32_t n_seq, void void *data = SPA_POD_BODY(&next->value); size_t size = SPA_POD_BODY_SIZE(&next->value); uint8_t ev[32]; + bool was_sysex = in_sysex; if (type == TYPE_ID_MIDI) { int ev_size = spa_ump_to_midi(data, size, ev, sizeof(ev)); if (ev_size <= 0) break; + size = ev_size; data = ev; + + if (!in_sysex && ev[0] == 0xf0) + in_sysex = true; + if (in_sysex && ev[ev_size-1] == 0xf7) + in_sysex = false; + } else if (type != TYPE_ID_UMP) break; - if ((res = midi_event_write(midi, next->offset, data, size, fix)) < 0) + if (was_sysex) + res = midi_event_append(midi, data, size); + else + res = midi_event_write(midi, next->offset, data, size, fix); + + if (res < 0) pw_log_warn("midi %p: can't write event: %s", midi, spa_strerror(res)); } diff --git a/spa/plugins/alsa/alsa-seq.c b/spa/plugins/alsa/alsa-seq.c index dac268376..b0be92098 100644 --- a/spa/plugins/alsa/alsa-seq.c +++ b/spa/plugins/alsa/alsa-seq.c @@ -78,6 +78,7 @@ static int seq_open(struct seq_state *state, struct seq_conn *conn, bool with_qu spa_log_debug(state->log, "%p: ALSA UMP MIDI enabled", state); state->ump = true; } + state->ump = false; return 0; } @@ -821,6 +822,7 @@ static int process_write(struct seq_state *state) struct spa_pod_control *c; uint64_t out_time; snd_seq_real_time_t out_rt; + bool first = true; if (!port->valid || io == NULL) continue; @@ -885,13 +887,20 @@ static int process_write(struct seq_state *state) if ((size = spa_ump_to_midi((uint32_t *)body, body_size, data, sizeof(data))) <= 0) continue; - snd_seq_ev_clear(&ev); + if (first) + snd_seq_ev_clear(&ev); - snd_midi_event_reset_encode(stream->codec); - if ((size = snd_midi_event_encode(stream->codec, data, size, &ev)) <= 0) { + if ((size = snd_midi_event_encode(stream->codec, data, size, &ev)) < 0) { spa_log_warn(state->log, "failed to encode event: %s", snd_strerror(size)); + snd_midi_event_reset_encode(stream->codec); + first = true; continue; } + first = false; + if (ev.type == SND_SEQ_EVENT_NONE) + /* this can happen when the event is not complete yet, like + * a sysex message and we need to encode some more data. */ + continue; snd_seq_ev_set_source(&ev, state->event.addr.port); snd_seq_ev_set_dest(&ev, port->addr.client, port->addr.port); @@ -901,6 +910,7 @@ static int process_write(struct seq_state *state) spa_log_warn(state->log, "failed to output event: %s", snd_strerror(err)); } + first = true; } } } diff --git a/src/modules/module-jack-tunnel.c b/src/modules/module-jack-tunnel.c index 48e8c6f8e..7ab4c9faf 100644 --- a/src/modules/module-jack-tunnel.c +++ b/src/modules/module-jack-tunnel.c @@ -247,6 +247,9 @@ static void midi_to_jack(struct impl *impl, float *dst, float *src, uint32_t n_s struct spa_pod_sequence *seq; struct spa_pod_control *c; int res; + bool in_sysex = false; + uint8_t tmp[n_samples * 4]; + size_t tmp_size = 0; jack.midi_clear_buffer(dst); if (src == NULL) @@ -260,23 +263,32 @@ static void midi_to_jack(struct impl *impl, float *dst, float *src, uint32_t n_s seq = (struct spa_pod_sequence*)pod; SPA_POD_SEQUENCE_FOREACH(seq, c) { - uint8_t data[16]; int size; if (c->type != SPA_CONTROL_UMP) continue; size = spa_ump_to_midi(SPA_POD_BODY(&c->value), - SPA_POD_BODY_SIZE(&c->value), data, sizeof(data)); + SPA_POD_BODY_SIZE(&c->value), &tmp[tmp_size], sizeof(tmp) - tmp_size); if (size <= 0) continue; if (impl->fix_midi) - fix_midi_event(data, size); + fix_midi_event(&tmp[tmp_size], size); - if ((res = jack.midi_event_write(dst, c->offset, data, size)) < 0) - pw_log_warn("midi %p: can't write event: %s", dst, - spa_strerror(res)); + if (!in_sysex && tmp[tmp_size] == 0xf0) + in_sysex = true; + + tmp_size += size; + if (in_sysex && tmp[tmp_size-1] == 0xf7) + in_sysex = false; + + if (!in_sysex) { + if ((res = jack.midi_event_write(dst, c->offset, tmp, tmp_size)) < 0) + pw_log_warn("midi %p: can't write event: %s", dst, + spa_strerror(res)); + tmp_size = 0; + } } } diff --git a/src/modules/module-netjack2/peer.c b/src/modules/module-netjack2/peer.c index bac6f1322..8016a048a 100644 --- a/src/modules/module-netjack2/peer.c +++ b/src/modules/module-netjack2/peer.c @@ -242,14 +242,71 @@ static inline void fix_midi_event(uint8_t *data, size_t size) } } +static inline void *n2j_midi_buffer_reserve(struct nj2_midi_buffer *buf, + uint32_t offset, uint32_t size) +{ + struct nj2_midi_event *ev; + void *ptr; + + if (size <= 0) + return NULL; + + size_t used_size = sizeof(*buf) + buf->write_pos + + ((buf->event_count + 1) * sizeof(struct nj2_midi_event)); + + ev = &buf->event[buf->event_count]; + ev->time = offset; + ev->size = size; + if (size <= MIDI_INLINE_MAX) { + ptr = ev->buffer; + } else { + if (used_size + size > buf->buffer_size) + return NULL; + buf->write_pos += size; + ev->offset = buf->buffer_size - 1 - buf->write_pos; + ptr = SPA_PTROFF(buf, ev->offset, void); + } + buf->event_count++; + return ptr; +} + +static inline void n2j_midi_buffer_write(struct nj2_midi_buffer *buf, + uint32_t offset, void *data, uint32_t size) +{ + void *ptr = n2j_midi_buffer_reserve(buf, offset, size); + if (ptr != NULL) + memcpy(ptr, data, size); + else + buf->lost_events++; +} + +static inline void n2j_midi_buffer_append(struct nj2_midi_buffer *buf, + void *data, uint32_t size) +{ + struct nj2_midi_event *ev; + uint32_t old_size; + uint8_t *old_ptr, *new_ptr; + + ev = &buf->event[--buf->event_count]; + old_size = ev->size; + if (old_size <= MIDI_INLINE_MAX) { + old_ptr = ev->buffer; + } else { + buf->write_pos -= old_size; + old_ptr = SPA_PTROFF(buf, ev->offset, void); + } + new_ptr = n2j_midi_buffer_reserve(buf, ev->time, old_size + size); + memmove(new_ptr, old_ptr, old_size); + memcpy(new_ptr+old_size, data, size); +} + static void midi_to_netjack2(struct netjack2_peer *peer, struct nj2_midi_buffer *buf, float *src, uint32_t n_samples) { struct spa_pod *pod; struct spa_pod_sequence *seq; struct spa_pod_control *c; - struct nj2_midi_event *ev; - int free_size; + bool in_sysex = false; buf->magic = MIDI_BUFFER_MAGIC; buf->buffer_size = peer->quantum_limit * sizeof(float); @@ -269,12 +326,10 @@ static void midi_to_netjack2(struct netjack2_peer *peer, seq = (struct spa_pod_sequence*)pod; - free_size = buf->buffer_size - sizeof(*buf); - SPA_POD_SEQUENCE_FOREACH(seq, c) { int size; uint8_t data[16]; - void *ptr; + bool was_sysex = in_sysex; if (c->type != SPA_CONTROL_UMP) continue; @@ -284,29 +339,24 @@ static void midi_to_netjack2(struct netjack2_peer *peer, if (size <= 0) continue; - if (c->offset >= n_samples || - size >= free_size) { + if (c->offset >= n_samples) { buf->lost_events++; continue; } - if (peer->fix_midi) + if (!in_sysex && data[0] == 0xf0) + in_sysex = true; + + if (!in_sysex && peer->fix_midi) fix_midi_event(data, size); - ev = &buf->event[buf->event_count]; - ev->time = c->offset; - ev->size = size; - if (size <= MIDI_INLINE_MAX) { - ptr = ev->buffer; - } else { - buf->write_pos += size; - ev->offset = buf->buffer_size - 1 - buf->write_pos; - free_size -= size; - ptr = SPA_PTROFF(buf, ev->offset, void); - } - memcpy(ptr, data, size); - buf->event_count++; - free_size -= sizeof(*ev); + if (in_sysex && data[size-1] == 0xf7) + in_sysex = false; + + if (was_sysex) + n2j_midi_buffer_append(buf, data, size); + else + n2j_midi_buffer_write(buf, c->offset, data, size); } if (buf->write_pos > 0) memmove(SPA_PTROFF(buf, sizeof(*buf) + buf->event_count * sizeof(struct nj2_midi_event), void),