mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-12-22 18:36:30 +01:00
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:
parent
ee3965f6fa
commit
2b08d585a3
3 changed files with 219 additions and 5 deletions
|
@ -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"])
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue