mixer: handle control.ump property

Add a control.ump port property. When true, the port wants UMP and the
mixer will convert to it. When false, the port supports both UMP and
Midi1 and no conversions will happen. When unset, the mixer will always
convert UMP to midi1.

Remove the CONTROL_types property from the filter. This causes problems
because this is the format negotiated with peers, which might not
support the types but can still be linked because the mixer will
convert.

The control.ump port property is supposed to be a temporary fix until we
can negotiate the mixer ports properly with the CONTROL_types.

Remove UMP handling from bluetooth midi, just use the raw Midi1 events
now that the mixer will give those and we are supposed to output our
unconverted format.

Fix midi events in-place in netjack because we can.

Update docs and pw-mididump to note that we are back to midi1 as the
default format.

With this, most of the midi<->UMP conversion should be gone again and we
should be able to avoid conversion problems in ALSA and PipeWire.

Fixes #5183
This commit is contained in:
Wim Taymans 2026-03-25 11:47:01 +01:00
parent 7fe191bc10
commit 9eeb2f1930
9 changed files with 88 additions and 105 deletions

View file

@ -227,7 +227,7 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f
if (full)
port->info.change_mask = port->info_all;
if (port->info.change_mask) {
struct spa_dict_item items[6];
struct spa_dict_item items[7];
uint32_t n_items = 0;
int card_id;
snd_seq_port_info_t *info;
@ -284,6 +284,9 @@ static void emit_port_info(struct seq_state *this, struct seq_port *port, bool f
snprintf(card, sizeof(card), "%d", card_id);
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, card);
}
if (this->ump)
items[n_items++] = SPA_DICT_ITEM_INIT("control.ump", "true");
port->info.props = &SPA_DICT_INIT(items, n_items);
spa_node_emit_port_info(&this->hooks,

View file

@ -23,7 +23,6 @@
#include <spa/utils/ringbuffer.h>
#include <spa/monitor/device.h>
#include <spa/control/control.h>
#include <spa/control/ump-utils.h>
#include <spa/node/node.h>
#include <spa/node/utils.h>
@ -450,7 +449,7 @@ static void midi_event_recv(void *user_data, uint16_t timestamp, uint8_t *data,
struct impl *this = user_data;
struct port *port = &this->ports[PORT_OUT];
struct time_sync *sync = &port->sync;
uint64_t time, state = 0;
uint64_t time;
int res;
spa_assert(size > 0);
@ -460,19 +459,11 @@ static void midi_event_recv(void *user_data, uint16_t timestamp, uint8_t *data,
spa_log_trace(this->log, "%p: event:0x%x size:%d timestamp:%d time:%"PRIu64"",
this, (int)data[0], (int)size, (int)timestamp, (uint64_t)time);
while (size > 0) {
uint32_t ump[4];
int ump_size = spa_ump_from_midi(&data, &size,
ump, sizeof(ump), 0, &state);
if (ump_size <= 0)
break;
res = midi_event_ringbuffer_push(&this->event_rbuf, time, (uint8_t*)ump, ump_size);
if (res < 0) {
midi_event_ringbuffer_init(&this->event_rbuf);
spa_log_warn(this->log, "%p: MIDI receive buffer overflow: %s",
this, spa_strerror(res));
}
res = midi_event_ringbuffer_push(&this->event_rbuf, time, data, size);
if (res < 0) {
midi_event_ringbuffer_init(&this->event_rbuf);
spa_log_warn(this->log, "%p: MIDI receive buffer overflow: %s",
this, spa_strerror(res));
}
}
@ -713,7 +704,7 @@ static int process_output(struct impl *this)
offset = time * this->rate / SPA_NSEC_PER_SEC;
offset = SPA_CLAMP(offset, 0u, this->duration - 1);
spa_pod_builder_control(&port->builder, offset, SPA_CONTROL_UMP);
spa_pod_builder_control(&port->builder, offset, SPA_CONTROL_Midi);
buf = spa_pod_builder_reserve_bytes(&port->builder, size);
if (buf) {
midi_event_ringbuffer_pop(&this->event_rbuf, buf, size);
@ -786,37 +777,28 @@ static int write_data(struct impl *this, struct spa_data *d)
time = 0;
while (spa_pod_parser_get_control_body(&parser, &c, &c_body) >= 0) {
int size;
uint8_t event[32];
const uint32_t *ump = c_body;
size_t ump_size = c.value.size;
uint64_t state = 0;
const uint8_t *event = c_body;
uint32_t size = c.value.size;
if (c.type != SPA_CONTROL_UMP)
if (c.type != SPA_CONTROL_Midi)
continue;
time = SPA_MAX(time, this->current_time + c.offset * SPA_NSEC_PER_SEC / this->rate);
while (ump_size > 0) {
size = spa_ump_to_midi(&ump, &ump_size, event, sizeof(event), &state);
if (size <= 0)
break;
spa_log_trace(this->log, "%p: output event:0x%x time:%"PRIu64, this,
(size > 0) ? event[0] : 0, time);
spa_log_trace(this->log, "%p: output event:0x%x time:%"PRIu64, this,
(size > 0) ? event[0] : 0, time);
do {
res = spa_bt_midi_writer_write(&this->writer,
time, event, size);
if (res < 0) {
return res;
} else if (res) {
int res2;
if ((res2 = flush_packet(this)) < 0)
return res2;
}
} while (res);
}
do {
res = spa_bt_midi_writer_write(&this->writer,
time, event, size);
if (res < 0) {
return res;
} else if (res) {
int res2;
if ((res2 = flush_packet(this)) < 0)
return res2;
}
} while (res);
}
if ((res = flush_packet(this)) < 0)

View file

@ -72,6 +72,7 @@ struct impl {
struct spa_node node;
uint32_t quantum_limit;
uint32_t control_types;
struct spa_log *log;
@ -473,9 +474,9 @@ static int port_set_format(void *object,
if (!port->have_format) {
this->n_formats++;
port->have_format = true;
port->types = types;
spa_log_debug(this->log, "%p: set format on port %d:%d",
this, direction, port_id);
port->types = types == 0 ? this->control_types : types;
spa_log_debug(this->log, "%p: set format on port %d:%d types:%08x %08x",
this, direction, port_id, port->types, this->control_types);
}
}
port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
@ -955,6 +956,8 @@ impl_init(const struct spa_handle_factory *factory,
}
this->quantum_limit = 8192;
/* by default we convert to midi1 */
this->control_types = 1u<<SPA_CONTROL_Midi;
for (i = 0; info && i < info->n_items; i++) {
const char *k = info->items[i].key;
@ -962,6 +965,14 @@ impl_init(const struct spa_handle_factory *factory,
if (spa_streq(k, "clock.quantum-limit")) {
spa_atou32(s, &this->quantum_limit, 0);
}
else if (spa_streq(k, "control.ump")) {
if (spa_atob(s))
/* we convert to UMP when forced */
this->control_types = 1u<<SPA_CONTROL_UMP;
else
/* when unforced we let both midi1 and UMP through */
this->control_types = 0;
}
}
spa_hook_list_init(&this->hooks);