spa: avoid casting between snd_seq_event_t & snd_seq_ump_event_t

Although the two structs have same initial sequence, it's not really
correct to cast between their pointers. Alsa-lib also does this only
internally, but not in API.
This commit is contained in:
Pauli Virtanen 2025-03-09 21:24:47 +02:00 committed by P V
parent fd5bd5ca6e
commit a503244c10

View file

@ -213,43 +213,74 @@ static void debug_event(struct seq_state *state, snd_seq_event_t *ev)
ev->queue);
}
#ifdef HAVE_ALSA_UMP
static void debug_ump_event(struct seq_state *state, snd_seq_ump_event_t *ev)
{
if (SPA_LIKELY(!spa_log_level_topic_enabled(state->log, SPA_LOG_TOPIC_DEFAULT, SPA_LOG_LEVEL_TRACE)))
return;
spa_log_trace(state->log, "event type:%d flags:0x%x", ev->type, ev->flags);
switch (ev->flags & SND_SEQ_TIME_STAMP_MASK) {
case SND_SEQ_TIME_STAMP_TICK:
spa_log_trace(state->log, " time: %d ticks", ev->time.tick);
break;
case SND_SEQ_TIME_STAMP_REAL:
spa_log_trace(state->log, " time = %d.%09d",
(int)ev->time.time.tv_sec,
(int)ev->time.time.tv_nsec);
break;
}
spa_log_trace(state->log, " source:%d.%d dest:%d.%d queue:%d",
ev->source.client,
ev->source.port,
ev->dest.client,
ev->dest.port,
ev->queue);
}
#endif
static void alsa_seq_on_sys(struct spa_source *source)
{
struct seq_state *state = source->data;
snd_seq_event_t *ev;
const bool ump = state->ump;
int res;
while (1) {
const snd_seq_addr_t *addr;
snd_seq_event_type_t type;
if (state->ump) {
if (ump) {
#ifdef HAVE_ALSA_UMP
snd_seq_ump_event_t *ump_ev;
snd_seq_ump_event_t *ev;
res = snd_seq_ump_event_input(state->sys.hndl, &ump_ev);
res = snd_seq_ump_event_input(state->sys.hndl, &ev);
if (res <= 0)
break;
/* Structures are layout-compatible */
ev = (snd_seq_event_t *)ump_ev;
addr = &ump_ev->data.addr;
debug_ump_event(state, ev);
addr = &ev->data.addr;
type = ev->type;
#else
spa_assert_not_reached();
#endif
} else {
snd_seq_event_t *ev;
res = snd_seq_event_input(state->sys.hndl, &ev);
if (res <= 0)
break;
debug_event(state, ev);
addr = &ev->data.addr;
type = ev->type;
}
if (addr->client == state->event.addr.client)
continue;
debug_event(state, ev);
switch (ev->type) {
switch (type) {
case SND_SEQ_EVENT_CLIENT_START:
case SND_SEQ_EVENT_CLIENT_CHANGE:
spa_log_info(state->log, "client add/change %d", addr->client);
@ -283,7 +314,7 @@ static void alsa_seq_on_sys(struct spa_source *source)
break;
default:
spa_log_info(state->log, "unhandled event %d: %d:%d",
ev->type, addr->client, addr->port);
type, addr->client, addr->port);
break;
}
@ -568,8 +599,8 @@ static int process_recycle(struct seq_state *state)
static int process_read(struct seq_state *state)
{
snd_seq_event_t *ev;
struct seq_stream *stream = &state->streams[SPA_DIRECTION_OUTPUT];
const bool ump = state->ump;
uint32_t i;
uint32_t *data;
uint8_t midi1_data[MAX_EVENT_SIZE];
@ -583,34 +614,43 @@ static int process_read(struct seq_state *state)
struct seq_port *port;
uint64_t ev_time, diff;
uint32_t offset;
#ifdef HAVE_ALSA_UMP
snd_seq_ump_event_t *ump_ev;
#endif
void *event;
uint8_t *midi1_ptr;
size_t midi1_size;
uint64_t ump_state = 0;
snd_seq_event_type_t SPA_UNUSED type;
if (state->ump) {
if (ump) {
#ifdef HAVE_ALSA_UMP
res = snd_seq_ump_event_input(state->event.hndl, &ump_ev);
snd_seq_ump_event_t *ev;
res = snd_seq_ump_event_input(state->event.hndl, &ev);
if (res <= 0)
break;
/* Structures are layout-compatible */
ev = (snd_seq_event_t *)ump_ev;
addr = &ump_ev->source;
debug_ump_event(state, ev);
event = ev;
addr = &ev->source;
ev_time = SPA_TIMESPEC_TO_NSEC(&ev->time.time);
type = ev->type;
#else
spa_assert_not_reached();
#endif
} else {
snd_seq_event_t *ev;
res = snd_seq_event_input(state->event.hndl, &ev);
if (res <= 0)
break;
addr = &ev->source;
}
debug_event(state, ev);
debug_event(state, ev);
event = ev;
addr = &ev->source;
ev_time = SPA_TIMESPEC_TO_NSEC(&ev->time.time);
type = ev->type;
}
if ((port = find_port(state, stream, addr)) == NULL) {
spa_log_debug(state->log, "unknown port %d.%d",
@ -626,27 +666,8 @@ static int process_read(struct seq_state *state)
continue;
}
if (state->ump) {
#ifdef HAVE_ALSA_UMP
data = (uint32_t*)&ump_ev->ump[0];
size = spa_ump_message_size(snd_ump_msg_hdr_type(ump_ev->ump[0])) * 4;
#else
spa_assert_not_reached();
#endif
} else {
snd_midi_event_reset_decode(stream->codec);
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));
continue;
}
midi1_ptr = midi1_data;
midi1_size = size;
}
/* queue_time is the estimated current time of the queue as calculated by
* the DLL. Calculate the age of the event. */
ev_time = SPA_TIMESPEC_TO_NSEC(&ev->time.time);
if (state->queue_time > ev_time)
diff = state->queue_time - ev_time;
else
@ -659,8 +680,30 @@ static int process_read(struct seq_state *state)
else
offset = 0;
if (ump) {
#ifdef HAVE_ALSA_UMP
snd_seq_ump_event_t *ev = event;
data = (uint32_t*)&ev->ump[0];
size = spa_ump_message_size(snd_ump_msg_hdr_type(ev->ump[0])) * 4;
#else
spa_assert_not_reached();
#endif
} else {
snd_seq_event_t *ev = event;
snd_midi_event_reset_decode(stream->codec);
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));
continue;
}
midi1_ptr = midi1_data;
midi1_size = size;
}
do {
if (!state->ump) {
if (!ump) {
data = ump_data;
size = spa_ump_from_midi(&midi1_ptr, &midi1_size,
ump_data, sizeof(ump_data), 0, &ump_state);
@ -669,7 +712,7 @@ static int process_read(struct seq_state *state)
}
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);
type, ev_time, offset, size, addr->client, addr->port);
spa_pod_builder_control(&port->builder, offset, SPA_CONTROL_UMP);
spa_pod_builder_bytes(&port->builder, data, size);
@ -681,7 +724,7 @@ static int process_read(struct seq_state *state)
MAX_EVENT_SIZE > port->buffer->buf->datas[0].maxsize)
goto done;
} while (!state->ump);
} while (!ump);
}
done:
@ -746,6 +789,7 @@ done:
static int process_write(struct seq_state *state)
{
struct seq_stream *stream = &state->streams[SPA_DIRECTION_INPUT];
const bool ump = state->ump;
uint32_t i;
int err, res = 0;
@ -781,11 +825,6 @@ static int process_write(struct seq_state *state)
}
SPA_POD_SEQUENCE_FOREACH(pod, c) {
snd_seq_event_t *ev;
snd_seq_event_t midi1_ev;
#ifdef HAVE_ALSA_UMP
snd_seq_ump_event_t ump_ev;
#endif
size_t body_size;
uint8_t *body;
@ -795,48 +834,25 @@ static int process_write(struct seq_state *state)
body = SPA_POD_BODY(&c->value);
body_size = SPA_POD_BODY_SIZE(&c->value);
if (state->ump) {
#ifdef HAVE_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
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;
snd_seq_ev_clear(&midi1_ev);
snd_midi_event_reset_encode(stream->codec);
if ((size = snd_midi_event_encode(stream->codec, data, size, &midi1_ev)) <= 0) {
spa_log_warn(state->log, "failed to encode event: %s", snd_strerror(size));
continue;
}
ev = &midi1_ev;
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_rt.tv_nsec = 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);
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);
spa_log_trace_fp(state->log, "event time:%"PRIu64" offset:%d size:%zd port:%d.%d",
out_time, c->offset, body_size, port->addr.client, port->addr.port);
if (state->ump) {
if (ump) {
#ifdef HAVE_ALSA_UMP
if ((err = snd_seq_ump_event_output(state->event.hndl, &ump_ev)) < 0) {
snd_seq_ump_event_t ev;
spa_zero(ev);
memcpy(ev.ump, body, SPA_MIN(sizeof(ev.ump), (size_t)body_size));
snd_seq_ev_set_source(&ev, state->event.addr.port);
snd_seq_ev_set_dest(&ev, port->addr.client, port->addr.port);
snd_seq_ev_schedule_real(&ev, state->event.queue_id, 0, &out_rt);
if ((err = snd_seq_ump_event_output(state->event.hndl, &ev)) < 0) {
spa_log_warn(state->log, "failed to output event: %s",
snd_strerror(err));
}
@ -844,7 +860,26 @@ static int process_write(struct seq_state *state)
spa_assert_not_reached();
#endif
} else {
if ((err = snd_seq_event_output(state->event.hndl, ev)) < 0) {
snd_seq_event_t ev;
uint8_t data[MAX_EVENT_SIZE];
int size;
if ((size = spa_ump_to_midi((uint32_t *)body, body_size, data, sizeof(data))) <= 0)
continue;
snd_seq_ev_clear(&ev);
snd_midi_event_reset_encode(stream->codec);
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));
continue;
}
snd_seq_ev_set_source(&ev, state->event.addr.port);
snd_seq_ev_set_dest(&ev, port->addr.client, port->addr.port);
snd_seq_ev_schedule_real(&ev, state->event.queue_id, 0, &out_rt);
if ((err = snd_seq_event_output(state->event.hndl, &ev)) < 0) {
spa_log_warn(state->log, "failed to output event: %s",
snd_strerror(err));
}