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