mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
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:
parent
ada2146692
commit
33584dae1d
4 changed files with 143 additions and 34 deletions
|
|
@ -1546,6 +1546,29 @@ 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,
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,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;
|
||||
}
|
||||
|
|
@ -802,6 +803,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;
|
||||
|
|
@ -866,13 +868,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;
|
||||
|
||||
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);
|
||||
|
|
@ -882,6 +891,7 @@ static int process_write(struct seq_state *state)
|
|||
spa_log_warn(state->log, "failed to output event: %s",
|
||||
snd_strerror(err));
|
||||
}
|
||||
first = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -256,6 +256,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)
|
||||
|
|
@ -269,23 +272,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)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue