From 2b08d585a31d88e69e998e7d4e59070803ee79fa Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 17 Nov 2022 17:48:23 +0100 Subject: [PATCH] aseqdump: Add UMP support Add the support for showing the UMP events to aseqdump. With the new option -u, the program can start as a UMP sequencer client and receive UMP events instead of the legacy MIDI events. Also, the automatic event conversion among legacy and UMP clients can be suppressed by the new -r option, too. Signed-off-by: Takashi Iwai --- configure.ac | 4 + seq/aseqdump/aseqdump.1 | 9 ++ seq/aseqdump/aseqdump.c | 211 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 219 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index c91817a..8102bc3 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,10 @@ AC_CHECK_LIB([asound], [snd_seq_client_info_get_pid], [HAVE_SEQ_CLIENT_INFO_GET_ if test "$HAVE_SEQ_CLIENT_INFO_GET_PID" = "yes" ; then AC_DEFINE([HAVE_SEQ_CLIENT_INFO_GET_PID], 1, [alsa-lib supports snd_seq_client_info_get_pid]) fi +AC_CHECK_LIB([asound], [snd_seq_client_info_get_midi_version], [HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION="yes"]) +if test "$HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION" = "yes" ; then + AC_DEFINE([HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION], 1, [alsa-lib supports snd_seq_client_info_get_midi_version]) +fi AC_CHECK_LIB([atopology], [snd_tplg_save], [have_topology="yes"], [have_topology="no"]) # diff --git a/seq/aseqdump/aseqdump.1 b/seq/aseqdump/aseqdump.1 index f6f2aa9..6f79041 100644 --- a/seq/aseqdump/aseqdump.1 +++ b/seq/aseqdump/aseqdump.1 @@ -27,6 +27,15 @@ Prints the current version. .I \-l,\-\-list Prints a list of possible input ports. +.TP +.I \-u,\-\-ump=version +Sets the client MIDI version. +0 is for legacy mode, 1 is UMP MIDI 1.0 mode, and 2 is UMP MIDI 2.0 mode. + +.TP +.I \-r,\-\-raw +Suppress the automatic conversion of events among UMP and legacy clients. + .TP .I \-p,\-\-port=client:port,... Sets the sequencer port(s) from which events are received. diff --git a/seq/aseqdump/aseqdump.c b/seq/aseqdump/aseqdump.c index 44ae3bb..1fee043 100644 --- a/seq/aseqdump/aseqdump.c +++ b/seq/aseqdump/aseqdump.c @@ -29,12 +29,19 @@ #include #include "aconfig.h" #include "version.h" +#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION +#include +#endif static snd_seq_t *seq; static int port_count; static snd_seq_addr_t *ports; static volatile sig_atomic_t stop = 0; - +#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION +static int ump_version; +#else +#define ump_version 0 +#endif /* prints an error message to stderr, and dies */ static void fatal(const char *msg, ...) @@ -131,6 +138,7 @@ static void connect_ports(void) static void dump_event(const snd_seq_event_t *ev) { printf("%3d:%-3d ", ev->source.client, ev->source.port); + switch (ev->type) { case SND_SEQ_EVENT_NOTEON: if (ev->data.note.velocity) @@ -297,6 +305,165 @@ static void dump_event(const snd_seq_event_t *ev) } } +#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION +static void dump_ump_midi1_event(const unsigned int *ump) +{ + const snd_ump_msg_midi1_t *m = (const snd_ump_msg_midi1_t *)ump; + unsigned char group = m->hdr.group; + unsigned char status = m->hdr.status; + unsigned char channel = m->hdr.channel; + + printf("Group %2d, ", group); + switch (status) { + case SND_UMP_MSG_NOTE_OFF: + printf("Note off %2d, note %d, velocity 0x%x", + channel, m->note_off.note, m->note_off.velocity); + break; + case SND_UMP_MSG_NOTE_ON: + printf("Note on %2d, note %d, velocity 0x%x", + channel, m->note_off.note, m->note_off.velocity); + break; + case SND_UMP_MSG_POLY_PRESSURE: + printf("Poly pressure %2d, note %d, value 0x%x", + channel, m->poly_pressure.note, m->poly_pressure.data); + break; + case SND_UMP_MSG_CONTROL_CHANGE: + printf("Control change %2d, controller %d, value 0x%x", + channel, m->control_change.index, m->control_change.data); + break; + case SND_UMP_MSG_PROGRAM_CHANGE: + printf("Program change %2d, program %d", + channel, m->program_change.program); + case SND_UMP_MSG_CHANNEL_PRESSURE: + printf("Channel pressure %2d, value 0x%x", + channel, m->channel_pressure.data); + break; + case SND_UMP_MSG_PITCHBEND: + printf("Pitchbend %2d, value 0x%x", + channel, (m->pitchbend.data_msb << 7) | m->pitchbend.data_lsb); + break; + default: + printf("UMP MIDI1 event: status = %d, channel = %d, 0x%08x", + status, channel, *ump); + break; + } + printf("\n"); +} + +static void dump_ump_midi2_event(const unsigned int *ump) +{ + const snd_ump_msg_midi2_t *m = (const snd_ump_msg_midi2_t *)ump; + unsigned char group = m->hdr.group; + unsigned char status = m->hdr.status; + unsigned char channel = m->hdr.channel; + unsigned int bank; + + printf("Group %2d, ", group); + switch (status) { + case SND_UMP_MSG_PER_NOTE_RCC: + printf("Per-note RCC %2u, note %u, index %u, value 0x%x", + channel, m->per_note_rcc.note, + m->per_note_rcc.index, m->per_note_rcc.data); + break; + case SND_UMP_MSG_PER_NOTE_ACC: + printf("Per-note ACC %2u, note %u, index %u, value 0x%x", + channel, m->per_note_acc.note, + m->per_note_acc.index, m->per_note_acc.data); + break; + case SND_UMP_MSG_RPN: + printf("RPN %2u, bank %u:%u, value 0x%x", + channel, m->rpn.bank, m->rpn.index, m->rpn.data); + break; + case SND_UMP_MSG_NRPN: + printf("NRPN %2u, bank %u:%u, value 0x%x", + channel, m->rpn.bank, m->rpn.index, m->rpn.data); + break; + case SND_UMP_MSG_RELATIVE_RPN: + printf("relative RPN %2u, bank %u:%u, value 0x%x", + channel, m->rpn.bank, m->rpn.index, m->rpn.data); + break; + case SND_UMP_MSG_RELATIVE_NRPN: + printf("relative NRP %2u, bank %u:%u, value 0x%x", + channel, m->rpn.bank, m->rpn.index, m->rpn.data); + break; + case SND_UMP_MSG_PER_NOTE_PITCHBEND: + printf("Per-note pitchbend %2d, note %d, value 0x%x", + channel, m->per_note_pitchbend.note, + m->per_note_pitchbend.data); + break; + case SND_UMP_MSG_NOTE_OFF: + printf("Note off %2d, note %d, velocity 0x%x, attr type = %d, data = 0x%x", + channel, m->note_off.note, m->note_off.velocity, + m->note_off.attr_type, m->note_off.attr_data); + break; + case SND_UMP_MSG_NOTE_ON: + printf("Note on %2d, note %d, velocity 0x%x, attr type = %d, data = 0x%x", + channel, m->note_off.note, m->note_off.velocity, + m->note_off.attr_type, m->note_off.attr_data); + break; + case SND_UMP_MSG_POLY_PRESSURE: + printf("Poly pressure %2d, note %d, value 0x%x", + channel, m->poly_pressure.note, m->poly_pressure.data); + break; + case SND_UMP_MSG_CONTROL_CHANGE: + printf("Control change %2d, controller %d, value 0x%x", + channel, m->control_change.index, m->control_change.data); + break; + case SND_UMP_MSG_PROGRAM_CHANGE: + printf("Program change %2d, program %d", + channel, m->program_change.program); + if (m->program_change.bank_valid) + printf(", Bank select %d:%d", + m->program_change.bank_msb, + m->program_change.bank_lsb); + break; + case SND_UMP_MSG_CHANNEL_PRESSURE: + printf("Channel pressure %2d, value 0x%x", + channel, m->channel_pressure.data); + break; + case SND_UMP_MSG_PITCHBEND: + printf("Channel pressure %2d, value 0x%x", + channel, m->channel_pressure.data); + break; + case SND_UMP_MSG_PER_NOTE_MGMT: + printf("Per-note management %2d, value 0x%x", + channel, m->per_note_mgmt.flags); + break; + default: + printf("UMP MIDI2 event: status = %d, channel = %x, 0x%08x", + status, status, *ump); + break; + } + printf("\n"); +} + +static void dump_ump_event(const snd_seq_ump_event_t *ev) +{ + if (!snd_seq_ev_is_ump(ev)) { + dump_event((const snd_seq_event_t *)ev); + return; + } + + printf("%3d:%-3d ", ev->source.client, ev->source.port); + + switch (snd_ump_msg_type(ev->ump)) { + case SND_UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE: + dump_ump_midi1_event(ev->ump); + break; + case SND_UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE: + dump_ump_midi2_event(ev->ump); + break; + default: + printf("UMP event: type = %d, group = %d, status = %d, 0x%08x\n", + snd_ump_msg_type(ev->ump), + snd_ump_msg_group(ev->ump), + snd_ump_msg_status(ev->ump), + *ev->ump); + break; + } +} +#endif /* HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION */ + static void list_ports(void) { snd_seq_client_info_t *cinfo; @@ -335,6 +502,10 @@ static void help(const char *argv0) " -h,--help this help\n" " -V,--version show version\n" " -l,--list list input ports\n" +#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION + " -u,--ump=version set client MIDI version (0=legacy, 1= UMP MIDI 1.0, 2=UMP MIDI2.0)\n" + " -r,--raw do not convert UMP and legacy events\n" +#endif " -p,--port=client:port,... source port(s)\n", argv0); } @@ -351,12 +522,20 @@ static void sighandler(int sig) int main(int argc, char *argv[]) { - static const char short_options[] = "hVlp:"; + static const char short_options[] = "hVlp:" +#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION + "u:r" +#endif + ; static const struct option long_options[] = { {"help", 0, NULL, 'h'}, {"version", 0, NULL, 'V'}, {"list", 0, NULL, 'l'}, {"port", 1, NULL, 'p'}, +#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION + {"ump", 1, NULL, 'u'}, + {"raw", 0, NULL, 'r'}, +#endif {0} }; @@ -382,6 +561,15 @@ int main(int argc, char *argv[]) case 'p': parse_ports(optarg); break; +#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION + case 'u': + ump_version = atoi(optarg); + snd_seq_set_client_midi_version(seq, ump_version); + break; + case 'r': + snd_seq_set_client_ump_conversion(seq, 0); + break; +#endif default: help(argv[0]); return 1; @@ -409,7 +597,8 @@ int main(int argc, char *argv[]) printf("Waiting for data at port %d:0.", snd_seq_client_id(seq)); printf(" Press Ctrl+C to end.\n"); - printf("Source Event Ch Data\n"); + printf("Source %sEvent Ch Data\n", + ump_version ? "Group " : ""); signal(SIGINT, sighandler); signal(SIGTERM, sighandler); @@ -420,14 +609,26 @@ int main(int argc, char *argv[]) snd_seq_poll_descriptors(seq, pfds, npfds, POLLIN); if (poll(pfds, npfds, -1) < 0) break; - do { + for (;;) { snd_seq_event_t *event; +#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION + snd_seq_ump_event_t *ump_ev; + if (ump_version > 0) { + err = snd_seq_ump_event_input(seq, &ump_ev); + if (err < 0) + break; + if (ump_ev) + dump_ump_event(ump_ev); + continue; + } +#endif + err = snd_seq_event_input(seq, &event); if (err < 0) break; if (event) dump_event(event); - } while (err > 0); + } fflush(stdout); if (stop) break;