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 <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2022-11-17 17:48:23 +01:00
parent ee3965f6fa
commit 2b08d585a3
3 changed files with 219 additions and 5 deletions

View file

@ -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 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]) AC_DEFINE([HAVE_SEQ_CLIENT_INFO_GET_PID], 1, [alsa-lib supports snd_seq_client_info_get_pid])
fi 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"]) AC_CHECK_LIB([atopology], [snd_tplg_save], [have_topology="yes"], [have_topology="no"])
# #

View file

@ -27,6 +27,15 @@ Prints the current version.
.I \-l,\-\-list .I \-l,\-\-list
Prints a list of possible input ports. 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 .TP
.I \-p,\-\-port=client:port,... .I \-p,\-\-port=client:port,...
Sets the sequencer port(s) from which events are received. Sets the sequencer port(s) from which events are received.

View file

@ -29,12 +29,19 @@
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include "aconfig.h" #include "aconfig.h"
#include "version.h" #include "version.h"
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
#include <alsa/ump_msg.h>
#endif
static snd_seq_t *seq; static snd_seq_t *seq;
static int port_count; static int port_count;
static snd_seq_addr_t *ports; static snd_seq_addr_t *ports;
static volatile sig_atomic_t stop = 0; 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 */ /* prints an error message to stderr, and dies */
static void fatal(const char *msg, ...) 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) static void dump_event(const snd_seq_event_t *ev)
{ {
printf("%3d:%-3d ", ev->source.client, ev->source.port); printf("%3d:%-3d ", ev->source.client, ev->source.port);
switch (ev->type) { switch (ev->type) {
case SND_SEQ_EVENT_NOTEON: case SND_SEQ_EVENT_NOTEON:
if (ev->data.note.velocity) 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) static void list_ports(void)
{ {
snd_seq_client_info_t *cinfo; snd_seq_client_info_t *cinfo;
@ -335,6 +502,10 @@ static void help(const char *argv0)
" -h,--help this help\n" " -h,--help this help\n"
" -V,--version show version\n" " -V,--version show version\n"
" -l,--list list input ports\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", " -p,--port=client:port,... source port(s)\n",
argv0); argv0);
} }
@ -351,12 +522,20 @@ static void sighandler(int sig)
int main(int argc, char *argv[]) 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[] = { static const struct option long_options[] = {
{"help", 0, NULL, 'h'}, {"help", 0, NULL, 'h'},
{"version", 0, NULL, 'V'}, {"version", 0, NULL, 'V'},
{"list", 0, NULL, 'l'}, {"list", 0, NULL, 'l'},
{"port", 1, NULL, 'p'}, {"port", 1, NULL, 'p'},
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
{"ump", 1, NULL, 'u'},
{"raw", 0, NULL, 'r'},
#endif
{0} {0}
}; };
@ -382,6 +561,15 @@ int main(int argc, char *argv[])
case 'p': case 'p':
parse_ports(optarg); parse_ports(optarg);
break; 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: default:
help(argv[0]); help(argv[0]);
return 1; return 1;
@ -409,7 +597,8 @@ int main(int argc, char *argv[])
printf("Waiting for data at port %d:0.", printf("Waiting for data at port %d:0.",
snd_seq_client_id(seq)); snd_seq_client_id(seq));
printf(" Press Ctrl+C to end.\n"); 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(SIGINT, sighandler);
signal(SIGTERM, sighandler); signal(SIGTERM, sighandler);
@ -420,14 +609,26 @@ int main(int argc, char *argv[])
snd_seq_poll_descriptors(seq, pfds, npfds, POLLIN); snd_seq_poll_descriptors(seq, pfds, npfds, POLLIN);
if (poll(pfds, npfds, -1) < 0) if (poll(pfds, npfds, -1) < 0)
break; break;
do { for (;;) {
snd_seq_event_t *event; 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); err = snd_seq_event_input(seq, &event);
if (err < 0) if (err < 0)
break; break;
if (event) if (event)
dump_event(event); dump_event(event);
} while (err > 0); }
fflush(stdout); fflush(stdout);
if (stop) if (stop)
break; break;