ump: handle sysex from UMP to MIDI1 better

SysEx in UMP can span multiple packets. In MIDI1 we can't split them up
into multiple events so we need to collect the complete sysex and then
write out the event.

Fixes SysEx writes to ALSA seq by running the event encoder until a
valid packet is completed.

Also fixes split MIDI1 packets in the JACK API when going through the
tunnel or via netjack.
This commit is contained in:
Wim Taymans 2025-03-17 18:05:06 +01:00
parent ada2146692
commit 33584dae1d
4 changed files with 143 additions and 34 deletions

View file

@ -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));
}