rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <stdlib.h>
|
nuclear@0
|
3 #include <string.h>
|
nuclear@0
|
4 #include <errno.h>
|
nuclear@0
|
5 #include <inttypes.h>
|
nuclear@0
|
6 #include <unistd.h>
|
nuclear@0
|
7 #include <fcntl.h>
|
nuclear@0
|
8 #include <sys/mman.h>
|
nuclear@0
|
9 #include <sys/stat.h>
|
nuclear@0
|
10 #include <arpa/inet.h>
|
nuclear@0
|
11 #include "midi.h"
|
nuclear@0
|
12
|
nuclear@0
|
13 #define FMT_SINGLE 0
|
nuclear@0
|
14 #define FMT_MULTI_TRACK 1
|
nuclear@0
|
15 #define FMT_MULTI_SEQ 2
|
nuclear@0
|
16
|
nuclear@0
|
17 /* meta events */
|
nuclear@0
|
18 #define META_SEQ 0
|
nuclear@0
|
19 #define META_TEXT 1
|
nuclear@0
|
20 #define META_COPYRIGHT 2
|
nuclear@0
|
21 #define META_NAME 3
|
nuclear@0
|
22 #define META_INSTR 4
|
nuclear@0
|
23 #define META_LYRICS 5
|
nuclear@0
|
24 #define META_MARKER 6
|
nuclear@0
|
25 #define META_CUE 7
|
nuclear@0
|
26 #define META_CHANPREFIX 32
|
nuclear@0
|
27 #define META_END_TRACK 47
|
nuclear@0
|
28 #define META_TEMPO 81
|
nuclear@0
|
29 #define META_SMPTE_OFFS 84
|
nuclear@0
|
30 #define META_TMSIG 88
|
nuclear@0
|
31 #define META_KEYSIG 89
|
nuclear@0
|
32 #define META_SPECIFIC 127
|
nuclear@0
|
33
|
nuclear@0
|
34 struct midi {
|
nuclear@0
|
35 int bpm;
|
nuclear@0
|
36
|
nuclear@0
|
37 int num_tracks;
|
nuclear@0
|
38 struct midi_track *tracks;
|
nuclear@0
|
39 };
|
nuclear@0
|
40
|
nuclear@0
|
41 struct midi_track {
|
nuclear@0
|
42 char *name;
|
nuclear@0
|
43 struct midi_event *head, *tail;
|
nuclear@0
|
44 int num_ev;
|
nuclear@0
|
45 };
|
nuclear@0
|
46
|
nuclear@0
|
47 struct midi_event {
|
nuclear@0
|
48 long dt;
|
nuclear@0
|
49 int type;
|
nuclear@0
|
50 int channel;
|
nuclear@0
|
51 int arg[2];
|
nuclear@0
|
52
|
nuclear@0
|
53 struct midi_event *next;
|
nuclear@0
|
54 };
|
nuclear@0
|
55
|
nuclear@0
|
56 #define CHUNK_HDR_SIZE 8
|
nuclear@0
|
57 struct chunk_hdr {
|
nuclear@0
|
58 char id[4];
|
nuclear@0
|
59 uint32_t size;
|
nuclear@0
|
60 unsigned char data[1];
|
nuclear@0
|
61 };
|
nuclear@0
|
62
|
nuclear@0
|
63 struct midi_hdr {
|
nuclear@0
|
64 uint16_t fmt; /* 0: single, 1: multi-track, 2: multiple independent */
|
nuclear@0
|
65 uint16_t num_tracks;
|
nuclear@0
|
66 uint16_t tm_div;
|
nuclear@0
|
67
|
nuclear@0
|
68 } __attribute__ ((packed));
|
nuclear@0
|
69
|
nuclear@0
|
70 static void destroy_track(struct midi_track *trk);
|
nuclear@0
|
71 static int read_track(struct midi *midi, struct chunk_hdr *chunk);
|
nuclear@0
|
72 static long read_vardata(unsigned char **pptr);
|
nuclear@0
|
73 static int read_meta_event(struct midi *midi, struct midi_track *trk, unsigned char **pptr);
|
nuclear@0
|
74 static int read_sysex_event(struct midi *midi, unsigned char **pptr);
|
nuclear@0
|
75 static int ischunk(struct chunk_hdr *chunk, const char *name);
|
nuclear@0
|
76 static struct chunk_hdr *mkchunk(void *ptr);
|
nuclear@0
|
77 static struct chunk_hdr *skip_chunk(struct chunk_hdr *chunk);
|
nuclear@0
|
78 static struct midi_hdr *mkmidi(void *ptr);
|
nuclear@0
|
79 static void bigend(void *ptr, int sz);
|
nuclear@0
|
80 static void *map_file(const char *fname, int *size);
|
nuclear@0
|
81 static void unmap_file(void *mem, int size);
|
nuclear@0
|
82
|
nuclear@0
|
83 #define IS_VALID_EVTYPE(x) ((x) >= MIDI_NOTE_OFF && (x) <= MIDI_PITCH_BEND)
|
nuclear@0
|
84
|
nuclear@0
|
85 /* XXX the event arity table must match the MIDI_* defines in midi.h */
|
nuclear@0
|
86 static int ev_arity[] = {
|
nuclear@0
|
87 0, 0, 0, 0, 0, 0, 0, 0,
|
nuclear@0
|
88 2, /* note off (note, velocity)*/
|
nuclear@0
|
89 2, /* note on (note, velocity)*/
|
nuclear@0
|
90 2, /* note aftertouch (note, aftertouch value) */
|
nuclear@0
|
91 2, /* controller (controller number, value) */
|
nuclear@0
|
92 1, /* prog change (prog number) */
|
nuclear@0
|
93 1, /* channel aftertouch (aftertouch value) */
|
nuclear@0
|
94 2 /* pitch bend (pitch LSB, pitch MSB) */
|
nuclear@0
|
95 };
|
nuclear@0
|
96
|
nuclear@0
|
97
|
nuclear@0
|
98 struct midi *load_midi(const char *fname)
|
nuclear@0
|
99 {
|
nuclear@0
|
100 struct midi *midi;
|
nuclear@0
|
101 char *mem;
|
nuclear@0
|
102 int size;
|
nuclear@0
|
103 struct chunk_hdr *chunk;
|
nuclear@0
|
104 struct midi_hdr *hdr;
|
nuclear@0
|
105
|
nuclear@0
|
106 if(!(mem = map_file(fname, &size))) {
|
nuclear@0
|
107 return 0;
|
nuclear@0
|
108 }
|
nuclear@0
|
109 chunk = mkchunk(mem);
|
nuclear@0
|
110
|
nuclear@0
|
111 if(!ischunk(chunk, "MThd") || chunk->size != 6) {
|
nuclear@0
|
112 fprintf(stderr, "invalid or corrupted midi file: %s\n", fname);
|
nuclear@0
|
113 goto end;
|
nuclear@0
|
114 }
|
nuclear@0
|
115 hdr = mkmidi(chunk->data);
|
nuclear@0
|
116
|
nuclear@0
|
117 printf("format: %d\n", (int)hdr->fmt);
|
nuclear@0
|
118 printf("tracks: %d\n", (int)hdr->num_tracks);
|
nuclear@0
|
119
|
nuclear@0
|
120 if((hdr->tm_div & 0x8000) == 0) {
|
nuclear@0
|
121 /* division is in pulses / quarter note */
|
nuclear@0
|
122 printf("time division: %d ppqn\n", (int)hdr->tm_div);
|
nuclear@0
|
123 } else {
|
nuclear@0
|
124 /* division in frames / sec */
|
nuclear@0
|
125 int fps = (hdr->tm_div & 0x7f00) >> 8;
|
nuclear@0
|
126 int ticks_per_frame = hdr->tm_div & 0xff;
|
nuclear@0
|
127 printf("time division: %d fps, %d ticks/frame\n", fps, ticks_per_frame);
|
nuclear@0
|
128 }
|
nuclear@0
|
129
|
nuclear@0
|
130 if(!(midi = malloc(sizeof *midi))) {
|
nuclear@0
|
131 perror("failed to allocate memory");
|
nuclear@0
|
132 goto end;
|
nuclear@0
|
133 }
|
nuclear@0
|
134 if(!(midi->tracks = malloc(hdr->num_tracks * sizeof *midi->tracks))) {
|
nuclear@0
|
135 perror("failed to allocate memory");
|
nuclear@0
|
136 goto end;
|
nuclear@0
|
137 }
|
nuclear@0
|
138 midi->num_tracks = 0;
|
nuclear@0
|
139
|
nuclear@0
|
140 while((chunk = skip_chunk(chunk))) {
|
nuclear@0
|
141 if(ischunk(chunk, "MTrk")) {
|
nuclear@0
|
142 if(read_track(midi, chunk) == -1) {
|
nuclear@0
|
143 fprintf(stderr, "failed to read track\n");
|
nuclear@0
|
144 }
|
nuclear@0
|
145 } else {
|
nuclear@0
|
146 printf("ignoring chunk: %c%c%c%c\n", chunk->id[0], chunk->id[1], chunk->id[2], chunk->id[3]);
|
nuclear@0
|
147 }
|
nuclear@0
|
148 }
|
nuclear@0
|
149
|
nuclear@0
|
150 end:
|
nuclear@0
|
151 unmap_file(mem, size);
|
nuclear@0
|
152 if(midi) {
|
nuclear@0
|
153 free_midi(midi);
|
nuclear@0
|
154 midi = 0;
|
nuclear@0
|
155 }
|
nuclear@0
|
156 return midi;
|
nuclear@0
|
157 }
|
nuclear@0
|
158
|
nuclear@0
|
159 void free_midi(struct midi *midi)
|
nuclear@0
|
160 {
|
nuclear@0
|
161 int i;
|
nuclear@0
|
162
|
nuclear@0
|
163 if(!midi) return;
|
nuclear@0
|
164
|
nuclear@0
|
165 for(i=0; i<midi->num_tracks; i++) {
|
nuclear@0
|
166 destroy_track(midi->tracks + i);
|
nuclear@0
|
167 }
|
nuclear@0
|
168
|
nuclear@0
|
169 free(midi->tracks); /* TODO free tracks properly */
|
nuclear@0
|
170 free(midi);
|
nuclear@0
|
171 }
|
nuclear@0
|
172
|
nuclear@0
|
173 static void destroy_track(struct midi_track *trk)
|
nuclear@0
|
174 {
|
nuclear@0
|
175 free(trk->name);
|
nuclear@0
|
176 while(trk->head) {
|
nuclear@0
|
177 void *tmp = trk->head;
|
nuclear@0
|
178 trk->head = trk->head->next;
|
nuclear@0
|
179 free(tmp);
|
nuclear@0
|
180 }
|
nuclear@0
|
181 }
|
nuclear@0
|
182
|
nuclear@0
|
183 static int read_track(struct midi *midi, struct chunk_hdr *chunk)
|
nuclear@0
|
184 {
|
nuclear@0
|
185 unsigned char *ptr;
|
nuclear@0
|
186 struct midi_track trk = {0, 0, 0, 0};
|
nuclear@0
|
187
|
nuclear@0
|
188 if(!ischunk(chunk, "MTrk")) {
|
nuclear@0
|
189 return -1;
|
nuclear@0
|
190 }
|
nuclear@0
|
191
|
nuclear@0
|
192 ptr = chunk->data;
|
nuclear@0
|
193 while(ptr < chunk->data + chunk->size) {
|
nuclear@0
|
194 long dt;
|
nuclear@0
|
195 unsigned char stat;
|
nuclear@0
|
196
|
nuclear@0
|
197 /* TODO also convert dt to some standard unit */
|
nuclear@0
|
198 dt = read_vardata(&ptr);
|
nuclear@0
|
199 stat = *ptr++;
|
nuclear@0
|
200
|
nuclear@0
|
201 if(stat == 0xff) {
|
nuclear@0
|
202 read_meta_event(midi, &trk, &ptr);
|
nuclear@0
|
203 } else if(stat == 0xf0) {
|
nuclear@0
|
204 read_sysex_event(midi, &ptr);
|
nuclear@0
|
205 } else {
|
nuclear@0
|
206 struct midi_event *ev = malloc(sizeof *ev);
|
nuclear@0
|
207
|
nuclear@0
|
208 if(trk.head) {
|
nuclear@0
|
209 trk.tail->next = ev;
|
nuclear@0
|
210 } else {
|
nuclear@0
|
211 trk.head = ev;
|
nuclear@0
|
212 }
|
nuclear@0
|
213 trk.tail = ev;
|
nuclear@0
|
214 ev->next = 0;
|
nuclear@0
|
215 trk.num_ev++;
|
nuclear@0
|
216
|
nuclear@0
|
217 ev->dt = dt;
|
nuclear@0
|
218 ev->type = (stat >> 4) & 0xf;
|
nuclear@0
|
219 if(!IS_VALID_EVTYPE(ev->type)) {
|
nuclear@0
|
220 fprintf(stderr, "warning, skipping track with unknown event %d\n", ev->type);
|
nuclear@0
|
221 return -1;
|
nuclear@0
|
222 }
|
nuclear@0
|
223 ev->channel = stat & 0xf;
|
nuclear@0
|
224
|
nuclear@0
|
225 ev->arg[0] = *ptr++;
|
nuclear@0
|
226 if(ev_arity[ev->type] > 1) {
|
nuclear@0
|
227 ev->arg[1] = *ptr++;
|
nuclear@0
|
228 }
|
nuclear@0
|
229 }
|
nuclear@0
|
230 }
|
nuclear@0
|
231
|
nuclear@0
|
232 /* if we did actually add any events ... */
|
nuclear@0
|
233 if(trk.num_ev) {
|
nuclear@0
|
234 midi->tracks[midi->num_tracks++] = trk;
|
nuclear@0
|
235 }
|
nuclear@0
|
236 return 0;
|
nuclear@0
|
237 }
|
nuclear@0
|
238
|
nuclear@0
|
239 static long read_vardata(unsigned char **pptr)
|
nuclear@0
|
240 {
|
nuclear@0
|
241 int i;
|
nuclear@0
|
242 long res = 0;
|
nuclear@0
|
243 unsigned char *ptr = *pptr;
|
nuclear@0
|
244
|
nuclear@0
|
245 for(i=0; i<4; i++) {
|
nuclear@0
|
246 res |= (long)(*ptr & 0x7f) << (i * 8);
|
nuclear@0
|
247
|
nuclear@0
|
248 /* if first bit is not set we're done */
|
nuclear@0
|
249 if((*ptr++ & 0x80) == 0)
|
nuclear@0
|
250 break;
|
nuclear@0
|
251 }
|
nuclear@0
|
252 *pptr = ptr;
|
nuclear@0
|
253 return res;
|
nuclear@0
|
254 }
|
nuclear@0
|
255
|
nuclear@0
|
256 static int read_meta_event(struct midi *midi, struct midi_track *trk, unsigned char **pptr)
|
nuclear@0
|
257 {
|
nuclear@0
|
258 unsigned char *ptr = *pptr;
|
nuclear@0
|
259 unsigned char type;
|
nuclear@0
|
260 long size;
|
nuclear@0
|
261
|
nuclear@0
|
262 type = *ptr++;
|
nuclear@0
|
263 size = read_vardata(&ptr);
|
nuclear@0
|
264
|
nuclear@0
|
265 switch(type) {
|
nuclear@0
|
266 case META_NAME:
|
nuclear@0
|
267 free(trk->name);
|
nuclear@0
|
268 trk->name = malloc(size + 1);
|
nuclear@0
|
269 memcpy(trk->name, ptr, size);
|
nuclear@0
|
270 trk->name[size] = 0;
|
nuclear@0
|
271 break;
|
nuclear@0
|
272
|
nuclear@0
|
273 case META_TEMPO:
|
nuclear@0
|
274 /* TODO add a tempo change event to the midi struct */
|
nuclear@0
|
275 break;
|
nuclear@0
|
276
|
nuclear@0
|
277 default:
|
nuclear@0
|
278 break;
|
nuclear@0
|
279 }
|
nuclear@0
|
280 *pptr = ptr + size;
|
nuclear@0
|
281 return 0;
|
nuclear@0
|
282 }
|
nuclear@0
|
283
|
nuclear@0
|
284 /* ignore sysex events */
|
nuclear@0
|
285 static int read_sysex_event(struct midi *midi, unsigned char **pptr)
|
nuclear@0
|
286 {
|
nuclear@0
|
287 long size = read_vardata(pptr);
|
nuclear@0
|
288 *pptr += size;
|
nuclear@0
|
289 return 0;
|
nuclear@0
|
290 }
|
nuclear@0
|
291
|
nuclear@0
|
292 static int ischunk(struct chunk_hdr *chunk, const char *name)
|
nuclear@0
|
293 {
|
nuclear@0
|
294 return memcmp(chunk->id, name, 4) == 0;
|
nuclear@0
|
295 }
|
nuclear@0
|
296
|
nuclear@0
|
297 static struct chunk_hdr *mkchunk(void *ptr)
|
nuclear@0
|
298 {
|
nuclear@0
|
299 struct chunk_hdr *chdr = ptr;
|
nuclear@0
|
300 bigend(&chdr->size, sizeof chdr->size);
|
nuclear@0
|
301 return chdr;
|
nuclear@0
|
302 }
|
nuclear@0
|
303
|
nuclear@0
|
304 static struct chunk_hdr *skip_chunk(struct chunk_hdr *chunk)
|
nuclear@0
|
305 {
|
nuclear@0
|
306 return mkchunk((char*)chunk + CHUNK_HDR_SIZE + chunk->size);
|
nuclear@0
|
307 }
|
nuclear@0
|
308
|
nuclear@0
|
309 static struct midi_hdr *mkmidi(void *ptr)
|
nuclear@0
|
310 {
|
nuclear@0
|
311 struct midi_hdr *midi = ptr;
|
nuclear@0
|
312
|
nuclear@0
|
313 bigend(&midi->fmt, sizeof midi->fmt);
|
nuclear@0
|
314 bigend(&midi->num_tracks, sizeof midi->num_tracks);
|
nuclear@0
|
315 bigend(&midi->tm_div, sizeof midi->tm_div);
|
nuclear@0
|
316 return midi;
|
nuclear@0
|
317 }
|
nuclear@0
|
318
|
nuclear@0
|
319 static void bigend(void *ptr, int sz)
|
nuclear@0
|
320 {
|
nuclear@0
|
321 switch(sz) {
|
nuclear@0
|
322 case 4:
|
nuclear@0
|
323 *((uint32_t*)ptr) = ntohl(*(uint32_t*)ptr);
|
nuclear@0
|
324 break;
|
nuclear@0
|
325
|
nuclear@0
|
326 case 2:
|
nuclear@0
|
327 *(uint16_t*)ptr = ntohs(*(uint16_t*)ptr);
|
nuclear@0
|
328 break;
|
nuclear@0
|
329
|
nuclear@0
|
330 case 1:
|
nuclear@0
|
331 default:
|
nuclear@0
|
332 break;
|
nuclear@0
|
333 }
|
nuclear@0
|
334 }
|
nuclear@0
|
335
|
nuclear@0
|
336 static void *map_file(const char *fname, int *size)
|
nuclear@0
|
337 {
|
nuclear@0
|
338 int fd;
|
nuclear@0
|
339 struct stat st;
|
nuclear@0
|
340 void *mem;
|
nuclear@0
|
341
|
nuclear@0
|
342 if((fd = open(fname, O_RDONLY)) == -1) {
|
nuclear@0
|
343 fprintf(stderr, "failed to open midi file: %s: %s\n", fname, strerror(errno));
|
nuclear@0
|
344 return 0;
|
nuclear@0
|
345 }
|
nuclear@0
|
346 fstat(fd, &st);
|
nuclear@0
|
347
|
nuclear@0
|
348 if((mem = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)) == (void*)-1) {
|
nuclear@0
|
349 fprintf(stderr, "failed to map midi file: %s: %s\n", fname, strerror(errno));
|
nuclear@0
|
350 close(fd);
|
nuclear@0
|
351 return 0;
|
nuclear@0
|
352 }
|
nuclear@0
|
353 close(fd);
|
nuclear@0
|
354
|
nuclear@0
|
355 *size = st.st_size;
|
nuclear@0
|
356 return mem;
|
nuclear@0
|
357 }
|
nuclear@0
|
358
|
nuclear@0
|
359 static void unmap_file(void *mem, int size)
|
nuclear@0
|
360 {
|
nuclear@0
|
361 munmap(mem, size);
|
nuclear@0
|
362 }
|