diff mbox series

[alsa-utils,1/3] aseqdump: Add UMP support

Message ID 20230520070738.8382-2-tiwai@suse.de
State New
Headers show
Series Add MIDI 2.0 support | expand

Commit Message

Takashi Iwai May 20, 2023, 7:07 a.m. UTC
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 <tiwai@suse.de>
---
 configure.ac            |   4 +
 seq/aseqdump/aseqdump.1 |   9 ++
 seq/aseqdump/aseqdump.c | 211 +++++++++++++++++++++++++++++++++++++++-
 3 files changed, 219 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/configure.ac b/configure.ac
index e079e241aa29..2ce8a62c585c 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 f6f2aa9cb12b..6f7904159b90 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 44ae3bbc5654..1fee0430f9b3 100644
--- a/seq/aseqdump/aseqdump.c
+++ b/seq/aseqdump/aseqdump.c
@@ -29,12 +29,19 @@ 
 #include <alsa/asoundlib.h>
 #include "aconfig.h"
 #include "version.h"
+#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
+#include <alsa/ump_msg.h>
+#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;