#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, ...)
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)
}
}
+#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;
" -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);
}
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}
};
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;
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);
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;