From 7e9bebad0b19434a35384ad306d5f5a415badb58 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Aug 2023 16:36:17 +0200 Subject: [PATCH] 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 --- seq/aseqdump/aseqdump.1 | 25 ++++ seq/aseqdump/aseqdump.c | 252 +++++++++++++++++++++++++++++++++------- 2 files changed, 234 insertions(+), 43 deletions(-) diff --git a/seq/aseqdump/aseqdump.1 b/seq/aseqdump/aseqdump.1 index 6f79041..7b389bd 100644 --- a/seq/aseqdump/aseqdump.1 +++ b/seq/aseqdump/aseqdump.1 @@ -36,6 +36,31 @@ Sets the client MIDI version. .I \-r,\-\-raw 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 .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 a15e075..f83392f 100644 --- a/seq/aseqdump/aseqdump.c +++ b/seq/aseqdump/aseqdump.c @@ -33,6 +33,10 @@ #include #endif +enum { + VIEW_RAW, VIEW_NORMALIZED, VIEW_PERCENT +}; + static snd_seq_t *seq; static int port_count; static snd_seq_addr_t *ports; @@ -42,6 +46,7 @@ static int ump_version; #else #define ump_version 0 #endif +static int view_mode = VIEW_RAW; /* prints an error message to stderr, and dies */ 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) { 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) { case SND_SEQ_EVENT_NOTEON: if (ev->data.note.velocity) - printf("Note on %2d, note %d, velocity %d\n", - ev->data.note.channel, ev->data.note.note, ev->data.note.velocity); + printf("Note on %2d, note %d, velocity %s\n", + channel_number(ev->data.note.channel), + ev->data.note.note, + midi1_data(ev->data.note.velocity)); else 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; case SND_SEQ_EVENT_NOTEOFF: - printf("Note off %2d, note %d, velocity %d\n", - ev->data.note.channel, ev->data.note.note, ev->data.note.velocity); + printf("Note off %2d, note %d, velocity %s\n", + channel_number(ev->data.note.channel), + ev->data.note.note, + midi1_data(ev->data.note.velocity)); break; case SND_SEQ_EVENT_KEYPRESS: - printf("Polyphonic aftertouch %2d, note %d, value %d\n", - ev->data.note.channel, ev->data.note.note, ev->data.note.velocity); + printf("Polyphonic aftertouch %2d, note %d, value %s\n", + channel_number(ev->data.note.channel), + ev->data.note.note, + midi1_data(ev->data.note.velocity)); break; case SND_SEQ_EVENT_CONTROLLER: 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; case SND_SEQ_EVENT_PGMCHANGE: 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; case SND_SEQ_EVENT_CHANPRESS: - printf("Channel aftertouch %2d, value %d\n", - ev->data.control.channel, ev->data.control.value); + printf("Channel aftertouch %2d, value %s\n", + channel_number(ev->data.control.channel), + midi1_data(ev->data.control.value)); break; case SND_SEQ_EVENT_PITCHBEND: - printf("Pitch bend %2d, value %d\n", - ev->data.control.channel, ev->data.control.value); + printf("Pitch bend %2d, value %s\n", + channel_number(ev->data.control.channel), + midi1_pitchbend(ev->data.control.value)); break; case SND_SEQ_EVENT_CONTROL14: 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; case SND_SEQ_EVENT_NONREGPARAM: 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; case SND_SEQ_EVENT_REGPARAM: 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; case SND_SEQ_EVENT_SONGPOS: 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 -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; - return pb - 8192; + + return midi1_pitchbend(pb - 8192); } 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 group = group_number(m->hdr.group); unsigned char status = m->hdr.status; - unsigned char channel = m->hdr.channel; + unsigned char channel = channel_number(m->hdr.channel); printf("Group %2d, ", group); switch (status) { case SND_UMP_MSG_NOTE_OFF: - printf("Note off %2d, note %d, velocity %d", - channel, m->note_off.note, m->note_off.velocity); + printf("Note off %2d, note %d, velocity %s", + channel, m->note_off.note, + midi1_data(m->note_off.velocity)); break; case SND_UMP_MSG_NOTE_ON: - printf("Note on %2d, note %d, velocity %d", - channel, m->note_off.note, m->note_off.velocity); + printf("Note on %2d, note %d, velocity %s", + channel, m->note_off.note, + midi1_data(m->note_off.velocity)); break; case SND_UMP_MSG_POLY_PRESSURE: - printf("Poly pressure %2d, note %d, value %d", - channel, m->poly_pressure.note, m->poly_pressure.data); + printf("Poly pressure %2d, note %d, value %s", + channel, m->poly_pressure.note, + midi1_data(m->poly_pressure.data)); break; case SND_UMP_MSG_CONTROL_CHANGE: 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); case SND_UMP_MSG_CHANNEL_PRESSURE: printf("Channel pressure %2d, value %d", - channel, m->channel_pressure.data); + channel, midi1_data(m->channel_pressure.data)); break; case SND_UMP_MSG_PITCHBEND: - printf("Pitchbend %2d, value %d", + printf("Pitchbend %2d, value %s", channel, pitchbend_value(m->pitchbend.data_msb, m->pitchbend.data_lsb)); break; @@ -357,12 +432,83 @@ static void dump_ump_midi1_event(const unsigned int *ump) 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) { 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 channel = m->hdr.channel; + unsigned char channel = channel_number(m->hdr.channel); unsigned int bank; 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); break; 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, - m->per_note_pitchbend.data); + midi2_pitchbend(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, + printf("Note off %2d, note %d, velocity %s, attr type = %d, data = 0x%x", + channel, m->note_off.note, + midi2_velocity(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, + printf("Note on %2d, note %d, velocity %s, attr type = %d, data = 0x%x", + channel, m->note_off.note, + midi2_velocity(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); + printf("Poly pressure %2d, note %d, value %s", + channel, m->poly_pressure.note, + midi2_data(m->poly_pressure.data)); break; case SND_UMP_MSG_CONTROL_CHANGE: 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); break; case SND_UMP_MSG_CHANNEL_PRESSURE: - printf("Channel pressure %2d, value 0x%x", - channel, m->channel_pressure.data); + printf("Channel pressure %2d, value %s", + channel, + midi2_data(m->channel_pressure.data)); break; case SND_UMP_MSG_PITCHBEND: - printf("Channel pressure %2d, value 0x%x", - channel, m->channel_pressure.data); + printf("Channel pressure %2d, value %s", + channel, + midi2_pitchbend(m->channel_pressure.data)); break; case SND_UMP_MSG_PER_NOTE_MGMT: printf("Per-note management %2d, value 0x%x", @@ -509,6 +660,9 @@ static void help(const char *argv0) " -h,--help this help\n" " -V,--version show version\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 " -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" @@ -529,7 +683,7 @@ static void sighandler(int sig) 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 "u:r" #endif @@ -539,6 +693,9 @@ int main(int argc, char *argv[]) {"version", 0, NULL, 'V'}, {"list", 0, NULL, 'l'}, {"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 {"ump", 1, NULL, 'u'}, {"raw", 0, NULL, 'r'}, @@ -568,6 +725,15 @@ int main(int argc, char *argv[]) case 'p': parse_ports(optarg); 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 case 'u': ump_version = atoi(optarg);