-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathshowmidi.c
More file actions
200 lines (179 loc) · 7.13 KB
/
showmidi.c
File metadata and controls
200 lines (179 loc) · 7.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "midi_file.h"
struct OPTIONS {
const char *filename;
int show_all_notes;
};
static void print_help(const char *progname)
{
printf("USAGE: %s [options] filename.mid\n", progname);
printf("\n");
printf("options:\n");
printf(" -n show all notes\n");
}
static int read_options(int argc, char **argv, struct OPTIONS *opt)
{
opt->filename = NULL;
opt->show_all_notes = 0;
for (int i = 1; i < argc; i++) {
if (argv[i][0] != '-') {
if (opt->filename == NULL) {
opt->filename = argv[i];
continue;
}
fprintf(stderr, "%s: invalid option '%s'\n", argv[0], argv[i]);
return 1;
}
if (strcmp(argv[i], "-h") == 0) {
print_help(argv[0]);
return 1;
}
if (strcmp(argv[i], "-a") == 0) {
opt->show_all_notes = 1;
continue;
}
fprintf(stderr, "%s: invalid option '%s'\n", argv[0], argv[i]);
return 1;
}
if (opt->filename == NULL) {
printf("%s: no file specified! (use -h for help)\n", argv[0]);
return 1;
}
return 0;
}
static const char *get_key_signature_name(int accidentals, int is_minor)
{
static const char *majors[] = {
"Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F",
"C",
"G", "D", "A", "E", "B", "F#", "C#"
};
static const char *minors[] = {
"Ab", "Eb", "Bb", "F", "C", "G", "D",
"A",
"E", "B", "F#", "C#", "G#", "D#", "A#"
};
if (accidentals < -7 || accidentals > 7) {
return "??";
}
return (is_minor) ? minors[accidentals+7] : majors[accidentals+7];
}
static void show_note(struct MIDI_EVENT *ev, int hr, int min, int sec)
{
static const char *notes[] = {
"C", "C#", "D", "D#", "E",
"F", "F#", "G", "G#", "A", "A#", "B",
};
printf("[%02d:%02d:%02d] %-2s%d\n", hr, min, sec, notes[ev->midi.data1%12], ev->midi.data1/12-1);
}
int main(int argc, char *argv[])
{
struct OPTIONS options;
if (read_options(argc, argv, &options) != 0) {
return 1;
}
struct MIDI_DATA *midi = midi_file_read(options.filename);
if (midi == NULL) {
fprintf(stderr, "%s: error reading MIDI file '%s'\n", argv[0], options.filename);
return 1;
}
uint32_t midi_tempo = 1000000;
printf("-> midi with %d tracks (format %d), %d ticks per beat\n", midi->n_tracks, midi->format, midi->ticks_per_beat);
for (int t = 0; t < midi->n_tracks; t++) {
printf(" -> track %d:\n", t);
struct MIDI_TRACK *track = &midi->tracks[t];
uint32_t cur_time = 0;
int got_first_note = 0;
for (int e = 0; e < track->n_events; e++) {
struct MIDI_EVENT *ev = &track->events[e];
cur_time += ev->delta_time;
switch (ev->type) {
case MIDI_EVENT_TYPE_META:
if (ev->meta.meta_type >= 1 && ev->meta.meta_type <= 7) {
printf(" -> [meta 0x%02x] \"%.*s\"\n",
ev->meta.meta_type,
ev->meta.data_len,
(ev->meta.type == MIDI_EVENT_TYPE_META_SHORT) ? ev->meta.short_data.data : ev->meta.long_data.data);
} else if (ev->meta.meta_type == 0x51) {
int tempo = ((ev->meta.short_data.data[0]<<16) |
(ev->meta.short_data.data[1]<< 8) |
(ev->meta.short_data.data[2]<< 0));
printf(" -> tempo: %dus/beat\n", tempo);
midi_tempo = tempo;
} else if (ev->meta.meta_type == 0x58) {
printf(" -> time signature: %d/%d %d %d\n",
ev->meta.short_data.data[0], 1<<ev->meta.short_data.data[1],
ev->meta.short_data.data[2], ev->meta.short_data.data[3]);
} else if (ev->meta.meta_type == 0x59) {
int8_t accidentals = (int8_t) ev->meta.short_data.data[0];
int is_minor = ev->meta.short_data.data[1];
printf(" -> key signature: %s %s (%d %s)\n",
get_key_signature_name(accidentals, is_minor),
(is_minor) ? "minor" : "major",
abs(accidentals),
(accidentals >= 0) ? "sharps" : "flats");
} else if (ev->meta.meta_type == 0x2f) {
uint32_t seconds = (uint32_t) ((int64_t)cur_time * midi_tempo / midi->ticks_per_beat / 1000000);
uint32_t sec = seconds;
int hr = sec / 3600;
sec %= 3600;
int min = sec / 60;
sec %= 60;
printf(" -> end of track at %u ticks (%us = %02u:%02u:%02u)\n", cur_time, seconds, hr, min, sec);
} else {
printf(" -> [meta 0x%02x] with %d bytes\n", ev->meta.meta_type, ev->meta.data_len);
}
break;
case MIDI_EVENT_TYPE_SYSEX:
printf(" -> sysex 0x%02x with %u bytes\n",
ev->sysex.sysex_type,
ev->sysex.data_len);
break;
case MIDI_EVENT_TYPE_MIDI:
if ((ev->midi.status & 0xf0) == 0x90 && ! got_first_note) {
got_first_note = 1;
uint32_t seconds = (uint32_t) ((int64_t)cur_time * midi_tempo / midi->ticks_per_beat / 1000000);
uint32_t sec = seconds;
int hr = sec / 3600;
sec %= 3600;
int min = sec / 60;
sec %= 60;
printf(" -> first note at %u ticks (%us = %02u:%02u:%02u)\n", cur_time, seconds, hr, min, sec);
}
break;
}
}
}
if (options.show_all_notes) {
for (int t = 0; t < midi->n_tracks; t++) {
printf("\n");
printf("==================================\n");
printf("=== track %d:\n", t);
struct MIDI_TRACK *track = &midi->tracks[t];
uint32_t cur_time = 0;
for (int e = 0; e < track->n_events; e++) {
struct MIDI_EVENT *ev = &track->events[e];
cur_time += ev->delta_time;
switch (ev->type) {
case MIDI_EVENT_TYPE_MIDI:
if ((ev->midi.status & 0xf0) == 0x90 && ev->midi.data2 != 0) {
uint32_t seconds = (uint32_t) ((int64_t)cur_time * midi_tempo / midi->ticks_per_beat / 1000000);
uint32_t sec = seconds;
int hr = sec / 3600;
sec %= 3600;
int min = sec / 60;
sec %= 60;
show_note(ev, hr, min, sec);
}
break;
default:
break;
}
}
}
}
midi_free(midi);
return 0;
}