mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
spa: alsa: support also MIDI-1.0 IO for ALSA seq
Support also non-UMP IO with ALSA seq, in case either alsa-lib or the kernel does not have UMP enabled. Add configuration option "api.alsa.seq.ump" for optionally turning UMP I/O off, for easier debugging.
This commit is contained in:
parent
2397a984f7
commit
2167945eff
3 changed files with 163 additions and 85 deletions
|
|
@ -931,6 +931,7 @@ impl_init(const struct spa_handle_factory *factory,
|
||||||
this->quantum_limit = 8192;
|
this->quantum_limit = 8192;
|
||||||
this->min_pool_size = 500;
|
this->min_pool_size = 500;
|
||||||
this->max_pool_size = 2000;
|
this->max_pool_size = 2000;
|
||||||
|
this->ump = true;
|
||||||
|
|
||||||
for (i = 0; info && i < info->n_items; i++) {
|
for (i = 0; info && i < info->n_items; i++) {
|
||||||
const char *k = info->items[i].key;
|
const char *k = info->items[i].key;
|
||||||
|
|
@ -949,6 +950,8 @@ impl_init(const struct spa_handle_factory *factory,
|
||||||
spa_atou32(s, &this->min_pool_size, 0);
|
spa_atou32(s, &this->min_pool_size, 0);
|
||||||
} else if (spa_streq(k, "api.alsa.seq.max-pool")) {
|
} else if (spa_streq(k, "api.alsa.seq.max-pool")) {
|
||||||
spa_atou32(s, &this->max_pool_size, 0);
|
spa_atou32(s, &this->max_pool_size, 0);
|
||||||
|
} else if (spa_streq(k, "api.alsa.seq.ump")) {
|
||||||
|
this->ump = spa_atob(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -992,6 +995,7 @@ static const struct spa_dict_item info_items[] = {
|
||||||
"["SPA_KEY_API_ALSA_DISABLE_LONGNAME"=<bool, default false>] "
|
"["SPA_KEY_API_ALSA_DISABLE_LONGNAME"=<bool, default false>] "
|
||||||
"[ api.alsa.seq.min-pool=<min-pool, default 500>] "
|
"[ api.alsa.seq.min-pool=<min-pool, default 500>] "
|
||||||
"[ api.alsa.seq.max-pool=<max-pool, default 2000>]"
|
"[ api.alsa.seq.max-pool=<max-pool, default 2000>]"
|
||||||
|
"[ api.alsa.seq.ump = <boolean> ]"
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
#define CHECK(s,msg,...) if ((res = (s)) < 0) { spa_log_error(state->log, msg ": %s", ##__VA_ARGS__, snd_strerror(res)); return res; }
|
#define CHECK(s,msg,...) if ((res = (s)) < 0) { spa_log_error(state->log, msg ": %s", ##__VA_ARGS__, snd_strerror(res)); return res; }
|
||||||
|
|
||||||
static int seq_open(struct seq_state *state, struct seq_conn *conn, bool with_queue)
|
static int seq_open(struct seq_state *state, struct seq_conn *conn, bool with_queue, bool probe_ump)
|
||||||
{
|
{
|
||||||
struct props *props = &state->props;
|
struct props *props = &state->props;
|
||||||
int res;
|
int res;
|
||||||
|
|
@ -37,14 +37,29 @@ static int seq_open(struct seq_state *state, struct seq_conn *conn, bool with_qu
|
||||||
0)) < 0)
|
0)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
#ifdef ALSA_UMP
|
if (!state->ump) {
|
||||||
if ((res = snd_seq_set_client_midi_version(conn->hndl, SND_SEQ_CLIENT_UMP_MIDI_2_0)) < 0) {
|
spa_log_info(state->log, "%p: ALSA UMP MIDI disabled", state);
|
||||||
snd_seq_close(conn->hndl);
|
return 0;
|
||||||
spa_log_info(state->log, "%p: ALSA failed to enable UMP MIDI: %s",
|
|
||||||
state, snd_strerror(res));
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ALSA_UMP
|
||||||
|
res = snd_seq_set_client_midi_version(conn->hndl, SND_SEQ_CLIENT_UMP_MIDI_2_0);
|
||||||
|
#else
|
||||||
|
res = -EOPNOTSUPP;
|
||||||
#endif
|
#endif
|
||||||
|
if (res < 0) {
|
||||||
|
spa_log_lev(state->log, (probe_ump ? SPA_LOG_LEVEL_INFO : SPA_LOG_LEVEL_ERROR),
|
||||||
|
"%p: ALSA failed to enable UMP MIDI: %s", state, snd_strerror(res));
|
||||||
|
if (!probe_ump) {
|
||||||
|
snd_seq_close(conn->hndl);
|
||||||
|
return res; /* either all are UMP or none are UMP */
|
||||||
|
}
|
||||||
|
|
||||||
|
state->ump = false;
|
||||||
|
} else {
|
||||||
|
spa_log_debug(state->log, "%p: ALSA UMP MIDI enabled", state);
|
||||||
|
state->ump = true;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -174,11 +189,7 @@ static void init_ports(struct seq_state *state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ALSA_UMP
|
|
||||||
static void debug_event(struct seq_state *state, snd_seq_ump_event_t *ev)
|
|
||||||
#else
|
|
||||||
static void debug_event(struct seq_state *state, snd_seq_event_t *ev)
|
static void debug_event(struct seq_state *state, snd_seq_event_t *ev)
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
if (SPA_LIKELY(!spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_TRACE)))
|
if (SPA_LIKELY(!spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_TRACE)))
|
||||||
return;
|
return;
|
||||||
|
|
@ -205,19 +216,33 @@ static void debug_event(struct seq_state *state, snd_seq_event_t *ev)
|
||||||
static void alsa_seq_on_sys(struct spa_source *source)
|
static void alsa_seq_on_sys(struct spa_source *source)
|
||||||
{
|
{
|
||||||
struct seq_state *state = source->data;
|
struct seq_state *state = source->data;
|
||||||
#ifdef ALSA_UMP
|
|
||||||
snd_seq_ump_event_t *ev;
|
|
||||||
#else
|
|
||||||
snd_seq_event_t *ev;
|
snd_seq_event_t *ev;
|
||||||
#endif
|
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
#ifdef ALSA_UMP
|
while (1) {
|
||||||
while (snd_seq_ump_event_input(state->sys.hndl, &ev) > 0) {
|
const snd_seq_addr_t *addr;
|
||||||
|
|
||||||
|
if (state->ump) {
|
||||||
|
#if ALSA_UMP
|
||||||
|
snd_seq_ump_event_t *ump_ev;
|
||||||
|
|
||||||
|
res = snd_seq_ump_event_input(state->sys.hndl, &ump_ev);
|
||||||
|
if (res <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Structures are layout-compatible */
|
||||||
|
ev = (snd_seq_event_t *)ump_ev;
|
||||||
|
addr = &ump_ev->data.addr;
|
||||||
#else
|
#else
|
||||||
while (snd_seq_event_input(state->sys.hndl, &ev) > 0) {
|
spa_assert_not_reached();
|
||||||
#endif
|
#endif
|
||||||
const snd_seq_addr_t *addr = &ev->data.addr;
|
} else {
|
||||||
|
res = snd_seq_event_input(state->sys.hndl, &ev);
|
||||||
|
if (res <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
addr = &ev->data.addr;
|
||||||
|
}
|
||||||
|
|
||||||
if (addr->client == state->event.addr.client)
|
if (addr->client == state->event.addr.client)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -283,8 +308,8 @@ int spa_alsa_seq_open(struct seq_state *state)
|
||||||
|
|
||||||
spa_zero(reserve);
|
spa_zero(reserve);
|
||||||
for (i = 0; i < 16; i++) {
|
for (i = 0; i < 16; i++) {
|
||||||
spa_log_debug(state->log, "close %d", i);
|
spa_log_debug(state->log, "open %d", i);
|
||||||
if ((res = seq_open(state, &reserve[i], false)) < 0)
|
if ((res = seq_open(state, &reserve[i], false, (i == 0))) < 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (i >= 2) {
|
if (i >= 2) {
|
||||||
|
|
@ -543,31 +568,47 @@ static int process_recycle(struct seq_state *state)
|
||||||
|
|
||||||
static int process_read(struct seq_state *state)
|
static int process_read(struct seq_state *state)
|
||||||
{
|
{
|
||||||
#ifdef ALSA_UMP
|
|
||||||
snd_seq_ump_event_t *ev;
|
|
||||||
#else
|
|
||||||
snd_seq_event_t *ev;
|
snd_seq_event_t *ev;
|
||||||
#endif
|
|
||||||
struct seq_stream *stream = &state->streams[SPA_DIRECTION_OUTPUT];
|
struct seq_stream *stream = &state->streams[SPA_DIRECTION_OUTPUT];
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
#ifdef ALSA_UMP
|
|
||||||
uint32_t *data;
|
uint32_t *data;
|
||||||
#else
|
uint8_t midi1_data[MAX_EVENT_SIZE];
|
||||||
uint8_t data[MAX_EVENT_SIZE];
|
uint32_t ump_data[MAX_EVENT_SIZE];
|
||||||
#endif
|
|
||||||
long size;
|
long size;
|
||||||
int res = -1;
|
int res = -1;
|
||||||
|
|
||||||
/* copy all new midi events into their port buffers */
|
/* copy all new midi events into their port buffers */
|
||||||
#ifdef ALSA_UMP
|
while (1) {
|
||||||
while ((res = snd_seq_ump_event_input(state->event.hndl, &ev)) > 0) {
|
const snd_seq_addr_t *addr;
|
||||||
#else
|
|
||||||
while (snd_seq_event_input(state->event.hndl, &ev) > 0) {
|
|
||||||
#endif
|
|
||||||
const snd_seq_addr_t *addr = &ev->source;
|
|
||||||
struct seq_port *port;
|
struct seq_port *port;
|
||||||
uint64_t ev_time, diff;
|
uint64_t ev_time, diff;
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
|
#ifdef ALSA_UMP
|
||||||
|
snd_seq_ump_event_t *ump_ev;
|
||||||
|
#endif
|
||||||
|
uint8_t *midi1_ptr;
|
||||||
|
size_t midi1_size;
|
||||||
|
uint64_t ump_state = 0;
|
||||||
|
|
||||||
|
if (state->ump) {
|
||||||
|
#ifdef ALSA_UMP
|
||||||
|
res = snd_seq_ump_event_input(state->event.hndl, &ump_ev);
|
||||||
|
if (res <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Structures are layout-compatible */
|
||||||
|
ev = (snd_seq_event_t *)ump_ev;
|
||||||
|
addr = &ump_ev->source;
|
||||||
|
#else
|
||||||
|
spa_assert_not_reached();
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
res = snd_seq_event_input(state->event.hndl, &ev);
|
||||||
|
if (res <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
addr = &ev->source;
|
||||||
|
}
|
||||||
|
|
||||||
debug_event(state, ev);
|
debug_event(state, ev);
|
||||||
|
|
||||||
|
|
@ -585,16 +626,23 @@ static int process_read(struct seq_state *state)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state->ump) {
|
||||||
#ifdef ALSA_UMP
|
#ifdef ALSA_UMP
|
||||||
data = (uint32_t*)&ev->ump[0];
|
data = (uint32_t*)&ump_ev->ump[0];
|
||||||
size = spa_ump_message_size(snd_ump_msg_hdr_type(ev->ump[0])) * 4;
|
size = spa_ump_message_size(snd_ump_msg_hdr_type(ump_ev->ump[0])) * 4;
|
||||||
#else
|
#else
|
||||||
|
spa_assert_not_reached();
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
snd_midi_event_reset_decode(stream->codec);
|
snd_midi_event_reset_decode(stream->codec);
|
||||||
if ((size = snd_midi_event_decode(stream->codec, data, MAX_EVENT_SIZE, ev)) < 0) {
|
if ((size = snd_midi_event_decode(stream->codec, midi1_data, sizeof(midi1_data), ev)) < 0) {
|
||||||
spa_log_warn(state->log, "decode failed: %s", snd_strerror(size));
|
spa_log_warn(state->log, "decode failed: %s", snd_strerror(size));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
midi1_ptr = midi1_data;
|
||||||
|
midi1_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
/* queue_time is the estimated current time of the queue as calculated by
|
/* queue_time is the estimated current time of the queue as calculated by
|
||||||
* the DLL. Calculate the age of the event. */
|
* the DLL. Calculate the age of the event. */
|
||||||
|
|
@ -611,6 +659,15 @@ static int process_read(struct seq_state *state)
|
||||||
else
|
else
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (!state->ump) {
|
||||||
|
data = ump_data;
|
||||||
|
size = spa_ump_from_midi(&midi1_ptr, &midi1_size,
|
||||||
|
ump_data, sizeof(ump_data), 0, &ump_state);
|
||||||
|
if (size <= 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
spa_log_trace_fp(state->log, "event %d time:%"PRIu64" offset:%d size:%ld port:%d.%d",
|
spa_log_trace_fp(state->log, "event %d time:%"PRIu64" offset:%d size:%ld port:%d.%d",
|
||||||
ev->type, ev_time, offset, size, addr->client, addr->port);
|
ev->type, ev_time, offset, size, addr->client, addr->port);
|
||||||
|
|
||||||
|
|
@ -622,8 +679,12 @@ static int process_read(struct seq_state *state)
|
||||||
if (port->builder.state.offset +
|
if (port->builder.state.offset +
|
||||||
sizeof(struct spa_pod_control) +
|
sizeof(struct spa_pod_control) +
|
||||||
MAX_EVENT_SIZE > port->buffer->buf->datas[0].maxsize)
|
MAX_EVENT_SIZE > port->buffer->buf->datas[0].maxsize)
|
||||||
break;
|
goto done;
|
||||||
|
|
||||||
|
} while (!state->ump);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
if (res < 0 && res != -EAGAIN)
|
if (res < 0 && res != -EAGAIN)
|
||||||
spa_log_warn(state->log, "event read failed: %s", snd_strerror(res));
|
spa_log_warn(state->log, "event read failed: %s", snd_strerror(res));
|
||||||
|
|
||||||
|
|
@ -695,11 +756,6 @@ static int process_write(struct seq_state *state)
|
||||||
struct spa_pod_sequence *pod;
|
struct spa_pod_sequence *pod;
|
||||||
struct spa_data *d;
|
struct spa_data *d;
|
||||||
struct spa_pod_control *c;
|
struct spa_pod_control *c;
|
||||||
#ifdef ALSA_UMP
|
|
||||||
snd_seq_ump_event_t ev;
|
|
||||||
#else
|
|
||||||
snd_seq_event_t ev;
|
|
||||||
#endif
|
|
||||||
uint64_t out_time;
|
uint64_t out_time;
|
||||||
snd_seq_real_time_t out_rt;
|
snd_seq_real_time_t out_rt;
|
||||||
|
|
||||||
|
|
@ -725,8 +781,12 @@ static int process_write(struct seq_state *state)
|
||||||
}
|
}
|
||||||
|
|
||||||
SPA_POD_SEQUENCE_FOREACH(pod, c) {
|
SPA_POD_SEQUENCE_FOREACH(pod, c) {
|
||||||
size_t body_size;
|
snd_seq_event_t *ev;
|
||||||
|
snd_seq_event_t midi1_ev;
|
||||||
#ifdef ALSA_UMP
|
#ifdef ALSA_UMP
|
||||||
|
snd_seq_ump_event_t ump_ev;
|
||||||
|
#endif
|
||||||
|
size_t body_size;
|
||||||
uint8_t *body;
|
uint8_t *body;
|
||||||
|
|
||||||
if (c->type != SPA_CONTROL_UMP)
|
if (c->type != SPA_CONTROL_UMP)
|
||||||
|
|
@ -734,48 +794,61 @@ static int process_write(struct seq_state *state)
|
||||||
|
|
||||||
body = SPA_POD_BODY(&c->value);
|
body = SPA_POD_BODY(&c->value);
|
||||||
body_size = SPA_POD_BODY_SIZE(&c->value);
|
body_size = SPA_POD_BODY_SIZE(&c->value);
|
||||||
spa_zero(ev);
|
|
||||||
|
|
||||||
memcpy(ev.ump, body, SPA_MIN(sizeof(ev.ump), (size_t)body_size));
|
if (state->ump) {
|
||||||
|
#ifdef ALSA_UMP
|
||||||
|
spa_zero(ump_ev);
|
||||||
|
memcpy(ump_ev.ump, body, SPA_MIN(sizeof(ump_ev.ump), (size_t)body_size));
|
||||||
|
ev = (snd_seq_event_t *)&ump_ev;
|
||||||
#else
|
#else
|
||||||
if (c->type != SPA_CONTROL_Midi)
|
spa_assert_not_reached();
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
uint8_t data[MAX_EVENT_SIZE];
|
||||||
|
int size;
|
||||||
|
|
||||||
|
if ((size = spa_ump_to_midi((uint32_t *)body, body_size, data, sizeof(data))) <= 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
snd_seq_ev_clear(&ev);
|
snd_seq_ev_clear(&midi1_ev);
|
||||||
|
|
||||||
snd_midi_event_reset_encode(stream->codec);
|
snd_midi_event_reset_encode(stream->codec);
|
||||||
if ((body_size = snd_midi_event_encode(stream->codec,
|
if ((size = snd_midi_event_encode(stream->codec, data, size, &midi1_ev)) <= 0) {
|
||||||
SPA_POD_BODY(&c->value),
|
spa_log_warn(state->log, "failed to encode event: %s", snd_strerror(size));
|
||||||
SPA_POD_BODY_SIZE(&c->value), &ev)) <= 0) {
|
|
||||||
spa_log_warn(state->log, "failed to encode event: %s",
|
|
||||||
snd_strerror(body_size));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
snd_seq_ev_set_source(&ev, state->event.addr.port);
|
ev = &midi1_ev;
|
||||||
snd_seq_ev_set_dest(&ev, port->addr.client, port->addr.port);
|
body_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_seq_ev_set_source(ev, state->event.addr.port);
|
||||||
|
snd_seq_ev_set_dest(ev, port->addr.client, port->addr.port);
|
||||||
|
|
||||||
out_time = state->queue_time + NSEC_FROM_CLOCK(&state->rate, c->offset);
|
out_time = state->queue_time + NSEC_FROM_CLOCK(&state->rate, c->offset);
|
||||||
|
|
||||||
out_rt.tv_nsec = out_time % SPA_NSEC_PER_SEC;
|
out_rt.tv_nsec = out_time % SPA_NSEC_PER_SEC;
|
||||||
out_rt.tv_sec = out_time / SPA_NSEC_PER_SEC;
|
out_rt.tv_sec = out_time / SPA_NSEC_PER_SEC;
|
||||||
snd_seq_ev_schedule_real(&ev, state->event.queue_id, 0, &out_rt);
|
snd_seq_ev_schedule_real(ev, state->event.queue_id, 0, &out_rt);
|
||||||
|
|
||||||
spa_log_trace_fp(state->log, "event %d time:%"PRIu64" offset:%d size:%zd port:%d.%d",
|
spa_log_trace_fp(state->log, "event %d time:%"PRIu64" offset:%d size:%zd port:%d.%d",
|
||||||
ev.type, out_time, c->offset, body_size, port->addr.client, port->addr.port);
|
ev->type, out_time, c->offset, body_size, port->addr.client, port->addr.port);
|
||||||
|
|
||||||
|
if (state->ump) {
|
||||||
#ifdef ALSA_UMP
|
#ifdef ALSA_UMP
|
||||||
if ((err = snd_seq_ump_event_output(state->event.hndl, &ev)) < 0) {
|
if ((err = snd_seq_ump_event_output(state->event.hndl, &ump_ev)) < 0) {
|
||||||
spa_log_warn(state->log, "failed to output event: %s",
|
spa_log_warn(state->log, "failed to output event: %s",
|
||||||
snd_strerror(err));
|
snd_strerror(err));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if ((err = snd_seq_event_output(state->event.hndl, &ev)) < 0) {
|
spa_assert_not_reached();
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
if ((err = snd_seq_event_output(state->event.hndl, ev)) < 0) {
|
||||||
spa_log_warn(state->log, "failed to output event: %s",
|
spa_log_warn(state->log, "failed to output event: %s",
|
||||||
snd_strerror(err));
|
snd_strerror(err));
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
snd_seq_drain_output(state->event.hndl);
|
snd_seq_drain_output(state->event.hndl);
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,7 @@ struct seq_state {
|
||||||
unsigned int opened:1;
|
unsigned int opened:1;
|
||||||
unsigned int started:1;
|
unsigned int started:1;
|
||||||
unsigned int following:1;
|
unsigned int following:1;
|
||||||
|
unsigned int ump:1;
|
||||||
|
|
||||||
struct seq_stream streams[2];
|
struct seq_stream streams[2];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue