From 70fc09b8917f9633237643cee371e70a9f809c3e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Sun, 1 Mar 2020 20:39:07 +0100 Subject: [PATCH] midifile: add support for event dump Make a method to dump event info to stdout. --- src/tools/midifile.c | 244 +++++++++++++++++++++++++++++++++++++++++++ src/tools/midifile.h | 2 + 2 files changed, 246 insertions(+) diff --git a/src/tools/midifile.c b/src/tools/midifile.c index 950eba563..fa1ed1a8e 100644 --- a/src/tools/midifile.c +++ b/src/tools/midifile.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "midifile.h" @@ -450,3 +451,246 @@ int midi_file_write_event(struct midi_file *mf, const struct midi_event *event) return 0; } + +static const char *event_names[] = { + "Text", "Copyright", "Sequence/Track Name", + "Instrument", "Lyric", "Marker", "Cue Point", + "Program Name", "Device (Port) Name" +}; + +static const char *note_names[] = { + "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" +}; + +static const char *controller_names[128] = { + [0] = "Bank Select (coarse)", + [1] = "Modulation Wheel (coarse)", + [2] = "Breath controller (coarse)", + [4] = "Foot Pedal (coarse)", + [5] = "Portamento Time (coarse)", + [6] = "Data Entry (coarse)", + [7] = "Volume (coarse)", + [8] = "Balance (coarse)", + [10] = "Pan position (coarse)", + [11] = "Expression (coarse)", + [12] = "Effect Control 1 (coarse)", + [13] = "Effect Control 2 (coarse)", + [16] = "General Purpose Slider 1", + [17] = "General Purpose Slider 2", + [18] = "General Purpose Slider 3", + [19] = "General Purpose Slider 4", + [32] = "Bank Select (fine)", + [33] = "Modulation Wheel (fine)", + [34] = "Breath (fine)", + [36] = "Foot Pedal (fine)", + [37] = "Portamento Time (fine)", + [38] = "Data Entry (fine)", + [39] = "Volume (fine)", + [40] = "Balance (fine)", + [42] = "Pan position (fine)", + [43] = "Expression (fine)", + [44] = "Effect Control 1 (fine)", + [45] = "Effect Control 2 (fine)", + [64] = "Hold Pedal (on/off)", + [65] = "Portamento (on/off)", + [66] = "Sustenuto Pedal (on/off)", + [67] = "Soft Pedal (on/off)", + [68] = "Legato Pedal (on/off)", + [69] = "Hold 2 Pedal (on/off)", + [70] = "Sound Variation", + [71] = "Sound Timbre", + [72] = "Sound Release Time", + [73] = "Sound Attack Time", + [74] = "Sound Brightness", + [75] = "Sound Control 6", + [76] = "Sound Control 7", + [77] = "Sound Control 8", + [78] = "Sound Control 9", + [79] = "Sound Control 10", + [80] = "General Purpose Button 1 (on/off)", + [81] = "General Purpose Button 2 (on/off)", + [82] = "General Purpose Button 3 (on/off)", + [83] = "General Purpose Button 4 (on/off)", + [91] = "Effects Level", + [92] = "Tremulo Level", + [93] = "Chorus Level", + [94] = "Celeste Level", + [95] = "Phaser Level", + [96] = "Data Button increment", + [97] = "Data Button decrement", + [98] = "Non-registered Parameter (fine)", + [99] = "Non-registered Parameter (coarse)", + [100] = "Registered Parameter (fine)", + [101] = "Registered Parameter (coarse)", + [120] = "All Sound Off", + [121] = "All Controllers Off", + [122] = "Local Keyboard (on/off)", + [123] = "All Notes Off", + [124] = "Omni Mode Off", + [125] = "Omni Mode On", + [126] = "Mono Operation", + [127] = "Poly Operation", +}; + +static const char *program_names[] = { + "Acoustic Grand", "Bright Acoustic", "Electric Grand", "Honky-Tonk", + "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", + "Celesta", "Glockenspiel", "Music Box", "Vibraphone", "Marimba", + "Xylophone", "Tubular Bells", "Dulcimer", "Drawbar Organ", "Percussive Organ", + "Rock Organ", "Church Organ", "Reed Organ", "Accoridan", "Harmonica", + "Tango Accordian", "Nylon String Guitar", "Steel String Guitar", + "Electric Jazz Guitar", "Electric Clean Guitar", "Electric Muted Guitar", + "Overdriven Guitar", "Distortion Guitar", "Guitar Harmonics", + "Acoustic Bass", "Electric Bass (fingered)", "Electric Bass (picked)", + "Fretless Bass", "Slap Bass 1", "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", + "Violin", "Viola", "Cello", "Contrabass", "Tremolo Strings", "Pizzicato Strings", + "Orchestral Strings", "Timpani", "String Ensemble 1", "String Ensemble 2", + "SynthStrings 1", "SynthStrings 2", "Choir Aahs", "Voice Oohs", "Synth Voice", + "Orchestra Hit", "Trumpet", "Trombone", "Tuba", "Muted Trumpet", "French Horn", + "Brass Section", "SynthBrass 1", "SynthBrass 2", "Soprano Sax", "Alto Sax", + "Tenor Sax", "Baritone Sax", "Oboe", "English Horn", "Bassoon", "Clarinet", + "Piccolo", "Flute", "Recorder", "Pan Flute", "Blown Bottle", "Skakuhachi", + "Whistle", "Ocarina", "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (calliope)", + "Lead 4 (chiff)", "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", + "Lead 8 (bass+lead)", "Pad 1 (new age)", "Pad 2 (warm)", "Pad 3 (polysynth)", + "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", "Pad 7 (halo)", + "Pad 8 (sweep)", "FX 1 (rain)", "FX 2 (soundtrack)", "FX 3 (crystal)", + "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)", + "FX 8 (sci-fi)", "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bagpipe", + "Fiddle", "Shanai", "Tinkle Bell", "Agogo", "Steel Drums", "Woodblock", + "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", "Guitar Fret Noise", + "Breath Noise", "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", + "Applause", "Gunshot" +}; + +static const char *smpte_rates[] = { + "24 fps", + "25 fps", + "30 fps (drop frame)", + "30 fps (non drop frame)" +}; + +static const char *const major_keys[] = { + "Unknown major", "Fb", "Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", + "C", "G", "D", "A", "E", "B", "F#", "C#", "G#", "Unknown major" +}; + +static const char *const minor_keys[] = { + "Unknown minor", "Dbm", "Abm", "Ebm", "Bbm", "Fm", "Cm", "Gm", "Dm", + "Am", "Em", "Bm", "F#m", "C#m", "G#m", "D#m", "A#m", "E#m", "Unknown minor" +}; + +static const char *controller_name(uint8_t ctrl) +{ + if (ctrl > 127 || + controller_names[ctrl] == NULL) + return "Unknown"; + return controller_names[ctrl]; +} + +static void dump_mem(const char *label, uint8_t *data, uint32_t size) +{ + printf("%s: ", label); + while (size--) + printf("%02x ", *data++); +} + +int midi_file_dump_event(const struct midi_event *ev) +{ + printf("track:%2d sec:%f ", ev->track, ev->sec); + + switch (ev->data[0]) { + case 0x80 ... 0x8f: + printf("Note Off (channel %2d): note %3s%d, velocity %3d", + ev->data[0] & 0x0f, + note_names[ev->data[1] % 12], ev->data[1] / 12 -1, + ev->data[2]); + break; + case 0x90 ... 0x9f: + printf("Note On (channel %2d): note %3s%d, velocity %3d", + ev->data[0] & 0x0f, + note_names[ev->data[1] % 12], ev->data[1] / 12 -1, + ev->data[2]); + break; + case 0xa0 ... 0xaf: + printf("Aftertouch (channel %2d): note %3s%d, pressure %3d", + ev->data[0] & 0x0f, + note_names[ev->data[1] % 12], ev->data[1] / 12 -1, + ev->data[2]); + break; + case 0xb0 ... 0xbf: + printf("Controller (channel %2d): controller %3d (%s), value %3d", + ev->data[0] & 0x0f, ev->data[1], + controller_name(ev->data[1]), ev->data[2]); + break; + case 0xc0 ... 0xcf: + printf("Program (channel %2d): program %3d (%s)", + ev->data[0] & 0x0f, ev->data[1], + program_names[ev->data[1]]); + break; + case 0xd0 ... 0xdf: + printf("Channel Pressure (channel %2d): pressure %3d", + ev->data[0] & 0x0f, ev->data[1]); + break; + case 0xe0 ... 0xef: + printf("Pitch Bend (channel %2d): value %d", ev->data[0] & 0x0f, + ((int)ev->data[2] << 7 | ev->data[1]) - 0x2000); + break; + case 0xf0: + case 0xf7: + dump_mem("SysEx", ev->data, ev->size); + break; + case 0xff: + printf("Meta: "); + switch (ev->data[1]) { + case 0x00: + printf("Sequence Number %3d %3d", ev->data[3], ev->data[4]); + break; + case 0x01 ... 0x09: + printf("%s: %s", event_names[ev->data[1] - 1], &ev->data[ev->meta.offset]); + break; + case 0x20: + printf("Channel Prefix: %03d", ev->data[3]); + break; + case 0x21: + printf("Midi Port: %03d", ev->data[3]); + break; + case 0x2f: + printf("End Of Track"); + break; + case 0x51: + printf("Tempo: %d microseconds per quarter note, %.2f BPM", + ev->meta.parsed.tempo.uspqn, + 60000000.0 / (double)ev->meta.parsed.tempo.uspqn); + break; + case 0x54: + printf("SMPTE Offset: %s %02d:%02d:%02d:%02d.%03d", + smpte_rates[(ev->data[3] & 0x60) >> 5], + ev->data[3] & 0x1f, ev->data[4], ev->data[5], + ev->data[6], ev->data[7]); + break; + case 0x58: + printf("Time Signature: %d/%d, %d clocks per click, %d notated 32nd notes per quarter note", + ev->data[3], (int)pow(2, ev->data[4]), ev->data[5], ev->data[6]); + break; + case 0x59: + { + int sf = ev->data[3]; + printf("Key Signature: %d %s: %s", abs(sf), + sf > 0 ? "sharps" : "flats", + ev->data[4] == 0 ? + major_keys[SPA_CLAMP(sf + 9, 0, 19)] : + minor_keys[SPA_CLAMP(sf + 9, 0, 19)]); + break; + } + case 0x7f: + dump_mem("Sequencer", ev->data, ev->size); + break; + default: + dump_mem("Invalid", ev->data, ev->size); + } + break; + } + printf("\n"); + return 0; +} diff --git a/src/tools/midifile.h b/src/tools/midifile.h index 68edc76b3..96858e231 100644 --- a/src/tools/midifile.h +++ b/src/tools/midifile.h @@ -60,3 +60,5 @@ int midi_file_next_time(struct midi_file *mf, double *sec); int midi_file_read_event(struct midi_file *mf, struct midi_event *event); int midi_file_write_event(struct midi_file *mf, const struct midi_event *event); + +int midi_file_dump_event(const struct midi_event *event);