aseqdump: Add options to switch view mode

This patch adds to switch the operation mode of aseqdump to specify
how the values are shown.  Namely, it allows to show the MIDI 2.0
values in two more different ways: compatible "normalized" view and
percentage view, in addition to the default "raw" view.

The "raw" view mode just shows the value found in the event almost as
is.  The MIDI 2.0 values are shown in 16 or 32bit hex numbers.
The channel and UMP group numbers are 0-based, taking from 0 to 15.

OTOH, in the normalized view, the 16bit or 32bit velocity and data
values of MIDI 2.0 are normalized to the value fit in MIDI 1.0,
i.e. from 0 to 127, but with decimal points.  Similarly, the pitch
wheel values are normalized between -8192 to 8191.
Also, the channel numbers and UMP group numbers are 1-based, taking
from 1 to 16.

In the percentage view, the velocity and data values are normalized
and shown in percentage, from 0% to 100%.  The pitch wheel is
normalized from -100% to 100%.  The channel and UMP groups are 1-based
as well as in normalized view mode.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2023-08-28 16:36:17 +02:00
parent ad09344d4c
commit 7e9bebad0b
2 changed files with 234 additions and 43 deletions

View file

@ -36,6 +36,31 @@ Sets the client MIDI version.
.I \-r,\-\-raw .I \-r,\-\-raw
Suppress the automatic conversion of events among UMP and legacy clients. Suppress the automatic conversion of events among UMP and legacy clients.
.TP
.I \-R,\-\-raw-view
Shows the raw values as is.
The channel numbers and UMP group numbers are 0-based in this mode.
This is the default behavior.
.TP
.I \-N,\-\-normalized-view
Shows the normalized values to be aligned with MIDI 1.0.
The channel numbers and UMP group numbers are shown as 1-based values,
i.e. 1 is the lowest number.
The velocity and data values are normalized between 0 and 127.
The values for MIDI 2.0 are shown with two digits decimal points (from
0.00 to 127.00).
The pitch wheel is normalized between -8192 to 8192, too.
.TP
.I \-P,\-\-percent-view
Shows the values in percentage.
The channel numbers and UMP group numbers are shown as 1-based values
in this mode, too.
The velocity and data values are normalized between 0 and 100%, shown
with two digits decimal points.
The pitch wheel is normalized between -100% to 100%, too.
.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

@ -33,6 +33,10 @@
#include <alsa/ump_msg.h> #include <alsa/ump_msg.h>
#endif #endif
enum {
VIEW_RAW, VIEW_NORMALIZED, VIEW_PERCENT
};
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;
@ -42,6 +46,7 @@ static int ump_version;
#else #else
#define ump_version 0 #define ump_version 0
#endif #endif
static int view_mode = VIEW_RAW;
/* 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, ...)
@ -135,6 +140,50 @@ static void connect_ports(void)
} }
} }
static int channel_number(unsigned char c)
{
if (view_mode != VIEW_RAW)
return c + 1;
else
return c;
}
static const char *midi1_data(unsigned int v)
{
static char tmp[32];
if (view_mode == VIEW_PERCENT) {
if (v <= 64)
snprintf(tmp, sizeof(tmp), "%.2f%%",
((double)v * 50.0) / 64);
else
snprintf(tmp, sizeof(tmp), "%.2f%%",
((double)(v - 64) * 50.0) / 63 + 50.0);
return tmp;
}
sprintf(tmp, "%d", v);
return tmp;
}
static const char *midi1_pitchbend(int v)
{
static char tmp[32];
if (view_mode == VIEW_PERCENT) {
if (v < 0)
snprintf(tmp, sizeof(tmp), "%.2f%%",
((double)v * 100.0) / 8192);
else
snprintf(tmp, sizeof(tmp), "%.2f%%",
((double)v * 100.0) / 8191);
return tmp;
}
sprintf(tmp, "%d", v);
return tmp;
}
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);
@ -142,47 +191,61 @@ static void dump_event(const snd_seq_event_t *ev)
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)
printf("Note on %2d, note %d, velocity %d\n", printf("Note on %2d, note %d, velocity %s\n",
ev->data.note.channel, ev->data.note.note, ev->data.note.velocity); channel_number(ev->data.note.channel),
ev->data.note.note,
midi1_data(ev->data.note.velocity));
else else
printf("Note off %2d, note %d\n", printf("Note off %2d, note %d\n",
ev->data.note.channel, ev->data.note.note); channel_number(ev->data.note.channel),
ev->data.note.note);
break; break;
case SND_SEQ_EVENT_NOTEOFF: case SND_SEQ_EVENT_NOTEOFF:
printf("Note off %2d, note %d, velocity %d\n", printf("Note off %2d, note %d, velocity %s\n",
ev->data.note.channel, ev->data.note.note, ev->data.note.velocity); channel_number(ev->data.note.channel),
ev->data.note.note,
midi1_data(ev->data.note.velocity));
break; break;
case SND_SEQ_EVENT_KEYPRESS: case SND_SEQ_EVENT_KEYPRESS:
printf("Polyphonic aftertouch %2d, note %d, value %d\n", printf("Polyphonic aftertouch %2d, note %d, value %s\n",
ev->data.note.channel, ev->data.note.note, ev->data.note.velocity); channel_number(ev->data.note.channel),
ev->data.note.note,
midi1_data(ev->data.note.velocity));
break; break;
case SND_SEQ_EVENT_CONTROLLER: case SND_SEQ_EVENT_CONTROLLER:
printf("Control change %2d, controller %d, value %d\n", printf("Control change %2d, controller %d, value %d\n",
ev->data.control.channel, ev->data.control.param, ev->data.control.value); channel_number(ev->data.control.channel),
ev->data.control.param, ev->data.control.value);
break; break;
case SND_SEQ_EVENT_PGMCHANGE: case SND_SEQ_EVENT_PGMCHANGE:
printf("Program change %2d, program %d\n", printf("Program change %2d, program %d\n",
ev->data.control.channel, ev->data.control.value); channel_number(ev->data.control.channel),
ev->data.control.value);
break; break;
case SND_SEQ_EVENT_CHANPRESS: case SND_SEQ_EVENT_CHANPRESS:
printf("Channel aftertouch %2d, value %d\n", printf("Channel aftertouch %2d, value %s\n",
ev->data.control.channel, ev->data.control.value); channel_number(ev->data.control.channel),
midi1_data(ev->data.control.value));
break; break;
case SND_SEQ_EVENT_PITCHBEND: case SND_SEQ_EVENT_PITCHBEND:
printf("Pitch bend %2d, value %d\n", printf("Pitch bend %2d, value %s\n",
ev->data.control.channel, ev->data.control.value); channel_number(ev->data.control.channel),
midi1_pitchbend(ev->data.control.value));
break; break;
case SND_SEQ_EVENT_CONTROL14: case SND_SEQ_EVENT_CONTROL14:
printf("Control change %2d, controller %d, value %5d\n", printf("Control change %2d, controller %d, value %5d\n",
ev->data.control.channel, ev->data.control.param, ev->data.control.value); channel_number(ev->data.control.channel),
ev->data.control.param, ev->data.control.value);
break; break;
case SND_SEQ_EVENT_NONREGPARAM: case SND_SEQ_EVENT_NONREGPARAM:
printf("Non-reg. parameter %2d, parameter %d, value %d\n", printf("Non-reg. parameter %2d, parameter %d, value %d\n",
ev->data.control.channel, ev->data.control.param, ev->data.control.value); channel_number(ev->data.control.channel),
ev->data.control.param, ev->data.control.value);
break; break;
case SND_SEQ_EVENT_REGPARAM: case SND_SEQ_EVENT_REGPARAM:
printf("Reg. parameter %2d, parameter %d, value %d\n", printf("Reg. parameter %2d, parameter %d, value %d\n",
ev->data.control.channel, ev->data.control.param, ev->data.control.value); channel_number(ev->data.control.channel),
ev->data.control.param, ev->data.control.value);
break; break;
case SND_SEQ_EVENT_SONGPOS: case SND_SEQ_EVENT_SONGPOS:
printf("Song position pointer value %d\n", printf("Song position pointer value %d\n",
@ -306,32 +369,44 @@ static void dump_event(const snd_seq_event_t *ev)
} }
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION #ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
static int pitchbend_value(u8 msb, u8 lsb) static int group_number(unsigned char c)
{
if (view_mode != VIEW_RAW)
return c + 1;
else
return c;
}
static const char *pitchbend_value(u8 msb, u8 lsb)
{ {
int pb = (msb << 7) | lsb; int pb = (msb << 7) | lsb;
return pb - 8192;
return midi1_pitchbend(pb - 8192);
} }
static void dump_ump_midi1_event(const unsigned int *ump) static void dump_ump_midi1_event(const unsigned int *ump)
{ {
const snd_ump_msg_midi1_t *m = (const snd_ump_msg_midi1_t *)ump; const snd_ump_msg_midi1_t *m = (const snd_ump_msg_midi1_t *)ump;
unsigned char group = m->hdr.group; unsigned char group = group_number(m->hdr.group);
unsigned char status = m->hdr.status; unsigned char status = m->hdr.status;
unsigned char channel = m->hdr.channel; unsigned char channel = channel_number(m->hdr.channel);
printf("Group %2d, ", group); printf("Group %2d, ", group);
switch (status) { switch (status) {
case SND_UMP_MSG_NOTE_OFF: case SND_UMP_MSG_NOTE_OFF:
printf("Note off %2d, note %d, velocity %d", printf("Note off %2d, note %d, velocity %s",
channel, m->note_off.note, m->note_off.velocity); channel, m->note_off.note,
midi1_data(m->note_off.velocity));
break; break;
case SND_UMP_MSG_NOTE_ON: case SND_UMP_MSG_NOTE_ON:
printf("Note on %2d, note %d, velocity %d", printf("Note on %2d, note %d, velocity %s",
channel, m->note_off.note, m->note_off.velocity); channel, m->note_off.note,
midi1_data(m->note_off.velocity));
break; break;
case SND_UMP_MSG_POLY_PRESSURE: case SND_UMP_MSG_POLY_PRESSURE:
printf("Poly pressure %2d, note %d, value %d", printf("Poly pressure %2d, note %d, value %s",
channel, m->poly_pressure.note, m->poly_pressure.data); channel, m->poly_pressure.note,
midi1_data(m->poly_pressure.data));
break; break;
case SND_UMP_MSG_CONTROL_CHANGE: case SND_UMP_MSG_CONTROL_CHANGE:
printf("Control change %2d, controller %d, value %d", printf("Control change %2d, controller %d, value %d",
@ -342,10 +417,10 @@ static void dump_ump_midi1_event(const unsigned int *ump)
channel, m->program_change.program); channel, m->program_change.program);
case SND_UMP_MSG_CHANNEL_PRESSURE: case SND_UMP_MSG_CHANNEL_PRESSURE:
printf("Channel pressure %2d, value %d", printf("Channel pressure %2d, value %d",
channel, m->channel_pressure.data); channel, midi1_data(m->channel_pressure.data));
break; break;
case SND_UMP_MSG_PITCHBEND: case SND_UMP_MSG_PITCHBEND:
printf("Pitchbend %2d, value %d", printf("Pitchbend %2d, value %s",
channel, pitchbend_value(m->pitchbend.data_msb, channel, pitchbend_value(m->pitchbend.data_msb,
m->pitchbend.data_lsb)); m->pitchbend.data_lsb));
break; break;
@ -357,12 +432,83 @@ static void dump_ump_midi1_event(const unsigned int *ump)
printf("\n"); printf("\n");
} }
static const char *midi2_velocity(unsigned int v)
{
static char tmp[32];
if (view_mode == VIEW_NORMALIZED) {
if (v <= 0x8000)
snprintf(tmp, sizeof(tmp), "%.2f",
((double)v * 64.0) / 0x8000);
else
snprintf(tmp, sizeof(tmp), ".2%f",
((double)(v - 0x8000) * 63.0) / 0x7fff + 64.0);
return tmp;
} else if (view_mode == VIEW_PERCENT) {
snprintf(tmp, sizeof(tmp), "%.2f%%", (double)v * 100.0) / 0xffff);
return tmp;
}
sprintf(tmp, "0x%x", v);
return tmp;
}
static const char *midi2_data(unsigned int v)
{
static char tmp[32];
if (view_mode == VIEW_NORMALIZED) {
if (!v)
return "0";
else if (v == 0xffffffffU)
return "127";
if (v <= 0x80000000)
snprintf(tmp, sizeof(tmp), "%.2f",
((double)v * 64.0) / 0x80000000U);
else
snprintf(tmp, sizeof(tmp), "%.2f",
((double)(v - 0x80000000U) * 63.0) / 0x7fffffffU + 64.0);
return tmp;
} else if (view_mode == VIEW_PERCENT) {
snprintf(tmp, sizeof(tmp), "%.2f%%", (double)v * 100.0) / 0xffffffffU);
return tmp;
}
sprintf(tmp, "0x%x", v);
return tmp;
}
static const char *midi2_pitchbend(unsigned int v)
{
static char tmp[32];
if (view_mode == VIEW_NORMALIZED) {
if (!v)
return "-8192";
else if (v == 0xffffffffU)
return "8191";
if (v <= 0x80000000)
snprintf(tmp, sizeof(tmp), "%.2f",
((int)(v ^ 0x80000000U) * 8192.0) / 0x80000000U);
else
snprintf(tmp, sizeof(tmp), "%.2f",
((double)(v - 0x80000000U) * 8191.0) / 0x7fffffffU + 8192.0);
return tmp;
} else if (view_mode == VIEW_PERCENT) {
snprintf(tmp, sizeof(tmp), "%.2f%%", ((int)(v ^ 0x80000000U) * 100.0) / 0xffffffffU);
return tmp;
}
sprintf(tmp, "0x%x", v);
return tmp;
}
static void dump_ump_midi2_event(const unsigned int *ump) static void dump_ump_midi2_event(const unsigned int *ump)
{ {
const snd_ump_msg_midi2_t *m = (const snd_ump_msg_midi2_t *)ump; const snd_ump_msg_midi2_t *m = (const snd_ump_msg_midi2_t *)ump;
unsigned char group = m->hdr.group; unsigned char group = group_number(m->hdr.group);
unsigned char status = m->hdr.status; unsigned char status = m->hdr.status;
unsigned char channel = m->hdr.channel; unsigned char channel = channel_number(m->hdr.channel);
unsigned int bank; unsigned int bank;
printf("Group %2d, ", group); printf("Group %2d, ", group);
@ -394,23 +540,26 @@ static void dump_ump_midi2_event(const unsigned int *ump)
channel, m->rpn.bank, m->rpn.index, m->rpn.data); channel, m->rpn.bank, m->rpn.index, m->rpn.data);
break; break;
case SND_UMP_MSG_PER_NOTE_PITCHBEND: case SND_UMP_MSG_PER_NOTE_PITCHBEND:
printf("Per-note pitchbend %2d, note %d, value 0x%x", printf("Per-note pitchbend %2d, note %d, value %s",
channel, m->per_note_pitchbend.note, channel, m->per_note_pitchbend.note,
m->per_note_pitchbend.data); midi2_pitchbend(m->per_note_pitchbend.data));
break; break;
case SND_UMP_MSG_NOTE_OFF: case SND_UMP_MSG_NOTE_OFF:
printf("Note off %2d, note %d, velocity 0x%x, attr type = %d, data = 0x%x", printf("Note off %2d, note %d, velocity %s, attr type = %d, data = 0x%x",
channel, m->note_off.note, m->note_off.velocity, channel, m->note_off.note,
midi2_velocity(m->note_off.velocity),
m->note_off.attr_type, m->note_off.attr_data); m->note_off.attr_type, m->note_off.attr_data);
break; break;
case SND_UMP_MSG_NOTE_ON: case SND_UMP_MSG_NOTE_ON:
printf("Note on %2d, note %d, velocity 0x%x, attr type = %d, data = 0x%x", printf("Note on %2d, note %d, velocity %s, attr type = %d, data = 0x%x",
channel, m->note_off.note, m->note_off.velocity, channel, m->note_off.note,
midi2_velocity(m->note_off.velocity),
m->note_off.attr_type, m->note_off.attr_data); m->note_off.attr_type, m->note_off.attr_data);
break; break;
case SND_UMP_MSG_POLY_PRESSURE: case SND_UMP_MSG_POLY_PRESSURE:
printf("Poly pressure %2d, note %d, value 0x%x", printf("Poly pressure %2d, note %d, value %s",
channel, m->poly_pressure.note, m->poly_pressure.data); channel, m->poly_pressure.note,
midi2_data(m->poly_pressure.data));
break; break;
case SND_UMP_MSG_CONTROL_CHANGE: case SND_UMP_MSG_CONTROL_CHANGE:
printf("Control change %2d, controller %d, value 0x%x", printf("Control change %2d, controller %d, value 0x%x",
@ -425,12 +574,14 @@ static void dump_ump_midi2_event(const unsigned int *ump)
m->program_change.bank_lsb); m->program_change.bank_lsb);
break; break;
case SND_UMP_MSG_CHANNEL_PRESSURE: case SND_UMP_MSG_CHANNEL_PRESSURE:
printf("Channel pressure %2d, value 0x%x", printf("Channel pressure %2d, value %s",
channel, m->channel_pressure.data); channel,
midi2_data(m->channel_pressure.data));
break; break;
case SND_UMP_MSG_PITCHBEND: case SND_UMP_MSG_PITCHBEND:
printf("Channel pressure %2d, value 0x%x", printf("Channel pressure %2d, value %s",
channel, m->channel_pressure.data); channel,
midi2_pitchbend(m->channel_pressure.data));
break; break;
case SND_UMP_MSG_PER_NOTE_MGMT: case SND_UMP_MSG_PER_NOTE_MGMT:
printf("Per-note management %2d, value 0x%x", printf("Per-note management %2d, value 0x%x",
@ -509,6 +660,9 @@ 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"
" -N,--normalized-view show normalized values\n"
" -P,--percent-view show percent values\n"
" -R,--raw-view show raw values (default)\n"
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION #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" " -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" " -r,--raw do not convert UMP and legacy events\n"
@ -529,7 +683,7 @@ 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:NPR"
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION #ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
"u:r" "u:r"
#endif #endif
@ -539,6 +693,9 @@ int main(int argc, char *argv[])
{"version", 0, NULL, 'V'}, {"version", 0, NULL, 'V'},
{"list", 0, NULL, 'l'}, {"list", 0, NULL, 'l'},
{"port", 1, NULL, 'p'}, {"port", 1, NULL, 'p'},
{"normalized-view", 0, NULL, 'N'},
{"percent-view", 0, NULL, 'P'},
{"raw-view", 0, NULL, 'R'},
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION #ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
{"ump", 1, NULL, 'u'}, {"ump", 1, NULL, 'u'},
{"raw", 0, NULL, 'r'}, {"raw", 0, NULL, 'r'},
@ -568,6 +725,15 @@ int main(int argc, char *argv[])
case 'p': case 'p':
parse_ports(optarg); parse_ports(optarg);
break; break;
case 'R':
view_mode = VIEW_RAW;
break;
case 'P':
view_mode = VIEW_PERCENT;
break;
case 'N':
view_mode = VIEW_NORMALIZED;
break;
#ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION #ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
case 'u': case 'u':
ump_version = atoi(optarg); ump_version = atoi(optarg);