Mercurial > SDL_sound_CoreAudio
comparison decoders/timidity/readmidi.c @ 199:2d887640d300
Initial add.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Fri, 04 Jan 2002 06:49:49 +0000 |
parents | |
children | c66080364dff |
comparison
equal
deleted
inserted
replaced
198:f9a752f41ab6 | 199:2d887640d300 |
---|---|
1 /* | |
2 | |
3 TiMidity -- Experimental MIDI to WAVE converter | |
4 Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi> | |
5 | |
6 This program is free software; you can redistribute it and/or modify | |
7 it under the terms of the GNU General Public License as published by | |
8 the Free Software Foundation; either version 2 of the License, or | |
9 (at your option) any later version. | |
10 | |
11 This program is distributed in the hope that it will be useful, | |
12 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 GNU General Public License for more details. | |
15 | |
16 You should have received a copy of the GNU General Public License | |
17 along with this program; if not, write to the Free Software | |
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | |
20 */ | |
21 | |
22 #if HAVE_CONFIG_H | |
23 # include <config.h> | |
24 #endif | |
25 | |
26 #include <stdio.h> | |
27 #include <stdlib.h> | |
28 #include <string.h> | |
29 | |
30 #include "SDL_sound.h" | |
31 | |
32 #define __SDL_SOUND_INTERNAL__ | |
33 #include "SDL_sound_internal.h" | |
34 | |
35 #include "timidity.h" | |
36 #include "common.h" | |
37 #include "instrum.h" | |
38 #include "playmidi.h" | |
39 | |
40 /* Computes how many (fractional) samples one MIDI delta-time unit contains */ | |
41 static void compute_sample_increment(MidiSong *song, Sint32 tempo, | |
42 Sint32 divisions) | |
43 { | |
44 double a; | |
45 a = (double) (tempo) * (double) (song->rate) * (65536.0/1000000.0) / | |
46 (double)(divisions); | |
47 | |
48 song->sample_correction = (Sint32)(a) & 0xFFFF; | |
49 song->sample_increment = (Sint32)(a) >> 16; | |
50 | |
51 SNDDBG(("Samples per delta-t: %d (correction %d)", | |
52 song->sample_increment, song->sample_correction)); | |
53 } | |
54 | |
55 /* Read variable-length number (7 bits per byte, MSB first) */ | |
56 static Sint32 getvl(SDL_RWops *rw) | |
57 { | |
58 Sint32 l=0; | |
59 Uint8 c; | |
60 for (;;) | |
61 { | |
62 SDL_RWread(rw, &c, 1, 1); | |
63 l += (c & 0x7f); | |
64 if (!(c & 0x80)) return l; | |
65 l<<=7; | |
66 } | |
67 } | |
68 | |
69 /* Print a string from the file, followed by a newline. Any non-ASCII | |
70 or unprintable characters will be converted to periods. */ | |
71 static int dumpstring(SDL_RWops *rw, Sint32 len, char *label) | |
72 { | |
73 signed char *s=safe_malloc(len+1); | |
74 if (len != (Sint32) SDL_RWread(rw, s, 1, len)) | |
75 { | |
76 free(s); | |
77 return -1; | |
78 } | |
79 s[len]='\0'; | |
80 while (len--) | |
81 { | |
82 if (s[len]<32) | |
83 s[len]='.'; | |
84 } | |
85 SNDDBG(("%s%s", label, s)); | |
86 free(s); | |
87 return 0; | |
88 } | |
89 | |
90 #define MIDIEVENT(at,t,ch,pa,pb) \ | |
91 new=safe_malloc(sizeof(MidiEventList)); \ | |
92 new->event.time=at; new->event.type=t; new->event.channel=ch; \ | |
93 new->event.a=pa; new->event.b=pb; new->next=0;\ | |
94 return new; | |
95 | |
96 #define MAGIC_EOT ((MidiEventList *)(-1)) | |
97 | |
98 /* Read a MIDI event, returning a freshly allocated element that can | |
99 be linked to the event list */ | |
100 static MidiEventList *read_midi_event(MidiSong *song) | |
101 { | |
102 static Uint8 laststatus, lastchan; | |
103 static Uint8 nrpn=0, rpn_msb[16], rpn_lsb[16]; /* one per channel */ | |
104 Uint8 me, type, a,b,c; | |
105 Sint32 len; | |
106 MidiEventList *new; | |
107 | |
108 for (;;) | |
109 { | |
110 song->at += getvl(song->rw); | |
111 if (SDL_RWread(song->rw, &me, 1, 1) != 1) | |
112 { | |
113 SNDDBG(("read_midi_event: SDL_RWread() failure\n")); | |
114 return 0; | |
115 } | |
116 | |
117 if(me==0xF0 || me == 0xF7) /* SysEx event */ | |
118 { | |
119 len=getvl(song->rw); | |
120 SDL_RWseek(song->rw, len, SEEK_CUR); | |
121 } | |
122 else if(me==0xFF) /* Meta event */ | |
123 { | |
124 SDL_RWread(song->rw, &type, 1, 1); | |
125 len=getvl(song->rw); | |
126 if (type>0 && type<16) | |
127 { | |
128 static char *label[]={ | |
129 "Text event: ", "Text: ", "Copyright: ", "Track name: ", | |
130 "Instrument: ", "Lyric: ", "Marker: ", "Cue point: "}; | |
131 dumpstring(song->rw, len, label[(type>7) ? 0 : type]); | |
132 } | |
133 else | |
134 switch(type) | |
135 { | |
136 case 0x2F: /* End of Track */ | |
137 return MAGIC_EOT; | |
138 | |
139 case 0x51: /* Tempo */ | |
140 SDL_RWread(song->rw, &a, 1, 1); | |
141 SDL_RWread(song->rw, &b, 1, 1); | |
142 SDL_RWread(song->rw, &c, 1, 1); | |
143 MIDIEVENT(song->at, ME_TEMPO, c, a, b); | |
144 | |
145 default: | |
146 SNDDBG(("(Meta event type 0x%02x, length %d)\n", type, len)); | |
147 SDL_RWseek(song->rw, len, SEEK_CUR); | |
148 break; | |
149 } | |
150 } | |
151 else | |
152 { | |
153 a=me; | |
154 if (a & 0x80) /* status byte */ | |
155 { | |
156 lastchan=a & 0x0F; | |
157 laststatus=(a>>4) & 0x07; | |
158 SDL_RWread(song->rw, &a, 1, 1); | |
159 a &= 0x7F; | |
160 } | |
161 switch(laststatus) | |
162 { | |
163 case 0: /* Note off */ | |
164 SDL_RWread(song->rw, &b, 1, 1); | |
165 b &= 0x7F; | |
166 MIDIEVENT(song->at, ME_NOTEOFF, lastchan, a,b); | |
167 | |
168 case 1: /* Note on */ | |
169 SDL_RWread(song->rw, &b, 1, 1); | |
170 b &= 0x7F; | |
171 MIDIEVENT(song->at, ME_NOTEON, lastchan, a,b); | |
172 | |
173 case 2: /* Key Pressure */ | |
174 SDL_RWread(song->rw, &b, 1, 1); | |
175 b &= 0x7F; | |
176 MIDIEVENT(song->at, ME_KEYPRESSURE, lastchan, a, b); | |
177 | |
178 case 3: /* Control change */ | |
179 SDL_RWread(song->rw, &b, 1, 1); | |
180 b &= 0x7F; | |
181 { | |
182 int control=255; | |
183 switch(a) | |
184 { | |
185 case 7: control=ME_MAINVOLUME; break; | |
186 case 10: control=ME_PAN; break; | |
187 case 11: control=ME_EXPRESSION; break; | |
188 case 64: control=ME_SUSTAIN; break; | |
189 case 120: control=ME_ALL_SOUNDS_OFF; break; | |
190 case 121: control=ME_RESET_CONTROLLERS; break; | |
191 case 123: control=ME_ALL_NOTES_OFF; break; | |
192 | |
193 /* These should be the SCC-1 tone bank switch | |
194 commands. I don't know why there are two, or | |
195 why the latter only allows switching to bank 0. | |
196 Also, some MIDI files use 0 as some sort of | |
197 continuous controller. This will cause lots of | |
198 warnings about undefined tone banks. */ | |
199 case 0: control=ME_TONE_BANK; break; | |
200 case 32: | |
201 if (b!=0) | |
202 SNDDBG(("(Strange: tone bank change 0x20%02x)\n", b)); | |
203 else | |
204 control=ME_TONE_BANK; | |
205 break; | |
206 | |
207 case 100: nrpn=0; rpn_msb[lastchan]=b; break; | |
208 case 101: nrpn=0; rpn_lsb[lastchan]=b; break; | |
209 case 99: nrpn=1; rpn_msb[lastchan]=b; break; | |
210 case 98: nrpn=1; rpn_lsb[lastchan]=b; break; | |
211 | |
212 case 6: | |
213 if (nrpn) | |
214 { | |
215 SNDDBG(("(Data entry (MSB) for NRPN %02x,%02x: %d)\n", | |
216 rpn_msb[lastchan], rpn_lsb[lastchan], b)); | |
217 break; | |
218 } | |
219 | |
220 switch((rpn_msb[lastchan]<<8) | rpn_lsb[lastchan]) | |
221 { | |
222 case 0x0000: /* Pitch bend sensitivity */ | |
223 control=ME_PITCH_SENS; | |
224 break; | |
225 | |
226 case 0x7F7F: /* RPN reset */ | |
227 /* reset pitch bend sensitivity to 2 */ | |
228 MIDIEVENT(song->at, ME_PITCH_SENS, lastchan, 2, 0); | |
229 | |
230 default: | |
231 SNDDBG(("(Data entry (MSB) for RPN %02x,%02x: %d)\n", | |
232 rpn_msb[lastchan], rpn_lsb[lastchan], b)); | |
233 break; | |
234 } | |
235 break; | |
236 | |
237 default: | |
238 SNDDBG(("(Control %d: %d)\n", a, b)); | |
239 break; | |
240 } | |
241 if (control != 255) | |
242 { | |
243 MIDIEVENT(song->at, control, lastchan, b, 0); | |
244 } | |
245 } | |
246 break; | |
247 | |
248 case 4: /* Program change */ | |
249 a &= 0x7f; | |
250 MIDIEVENT(song->at, ME_PROGRAM, lastchan, a, 0); | |
251 | |
252 case 5: /* Channel pressure - NOT IMPLEMENTED */ | |
253 break; | |
254 | |
255 case 6: /* Pitch wheel */ | |
256 SDL_RWread(song->rw, &b, 1, 1); | |
257 b &= 0x7F; | |
258 MIDIEVENT(song->at, ME_PITCHWHEEL, lastchan, a, b); | |
259 | |
260 default: | |
261 SNDDBG(("*** Can't happen: status 0x%02X, channel 0x%02X\n", | |
262 laststatus, lastchan)); | |
263 break; | |
264 } | |
265 } | |
266 } | |
267 | |
268 return new; | |
269 } | |
270 | |
271 #undef MIDIEVENT | |
272 | |
273 /* Read a midi track into the linked list, either merging with any previous | |
274 tracks or appending to them. */ | |
275 static int read_track(MidiSong *song, int append) | |
276 { | |
277 MidiEventList *meep; | |
278 MidiEventList *next, *new; | |
279 Sint32 len; | |
280 char tmp[4]; | |
281 | |
282 meep = song->evlist; | |
283 if (append && meep) | |
284 { | |
285 /* find the last event in the list */ | |
286 for (; meep->next; meep=meep->next) | |
287 ; | |
288 song->at = meep->event.time; | |
289 } | |
290 else | |
291 song->at=0; | |
292 | |
293 /* Check the formalities */ | |
294 | |
295 if (SDL_RWread(song->rw, tmp, 1, 4) != 4 || SDL_RWread(song->rw, &len, 4, 1) != 1) | |
296 { | |
297 SNDDBG(("Can't read track header.\n")); | |
298 return -1; | |
299 } | |
300 len=SDL_SwapBE32(len); | |
301 if (memcmp(tmp, "MTrk", 4)) | |
302 { | |
303 SNDDBG(("Corrupt MIDI file.\n")); | |
304 return -2; | |
305 } | |
306 | |
307 for (;;) | |
308 { | |
309 if (!(new=read_midi_event(song))) /* Some kind of error */ | |
310 return -2; | |
311 | |
312 if (new==MAGIC_EOT) /* End-of-track Hack. */ | |
313 { | |
314 return 0; | |
315 } | |
316 | |
317 next=meep->next; | |
318 while (next && (next->event.time < new->event.time)) | |
319 { | |
320 meep=next; | |
321 next=meep->next; | |
322 } | |
323 | |
324 new->next=next; | |
325 meep->next=new; | |
326 | |
327 song->event_count++; /* Count the event. (About one?) */ | |
328 meep=new; | |
329 } | |
330 } | |
331 | |
332 /* Free the linked event list from memory. */ | |
333 static void free_midi_list(MidiSong *song) | |
334 { | |
335 MidiEventList *meep, *next; | |
336 if (!(meep = song->evlist)) return; | |
337 while (meep) | |
338 { | |
339 next=meep->next; | |
340 free(meep); | |
341 meep=next; | |
342 } | |
343 song->evlist=0; | |
344 } | |
345 | |
346 /* Allocate an array of MidiEvents and fill it from the linked list of | |
347 events, marking used instruments for loading. Convert event times to | |
348 samples: handle tempo changes. Strip unnecessary events from the list. | |
349 Free the linked list. */ | |
350 static MidiEvent *groom_list(MidiSong *song, Sint32 divisions,Sint32 *eventsp, | |
351 Sint32 *samplesp) | |
352 { | |
353 MidiEvent *groomed_list, *lp; | |
354 MidiEventList *meep; | |
355 Sint32 i, our_event_count, tempo, skip_this_event, new_value; | |
356 Sint32 sample_cum, samples_to_do, at, st, dt, counting_time; | |
357 | |
358 int current_bank[16], current_set[16], current_program[16]; | |
359 /* Or should each bank have its own current program? */ | |
360 | |
361 for (i=0; i<16; i++) | |
362 { | |
363 current_bank[i]=0; | |
364 current_set[i]=0; | |
365 current_program[i]=song->default_program; | |
366 } | |
367 | |
368 tempo=500000; | |
369 compute_sample_increment(song, tempo, divisions); | |
370 | |
371 /* This may allocate a bit more than we need */ | |
372 groomed_list=lp=safe_malloc(sizeof(MidiEvent) * (song->event_count+1)); | |
373 meep=song->evlist; | |
374 | |
375 our_event_count=0; | |
376 st=at=sample_cum=0; | |
377 counting_time=2; /* We strip any silence before the first NOTE ON. */ | |
378 | |
379 for (i = 0; i < song->event_count; i++) | |
380 { | |
381 skip_this_event=0; | |
382 | |
383 if (meep->event.type==ME_TEMPO) | |
384 { | |
385 tempo= | |
386 meep->event.channel + meep->event.b * 256 + meep->event.a * 65536; | |
387 compute_sample_increment(song, tempo, divisions); | |
388 skip_this_event=1; | |
389 } | |
390 else switch (meep->event.type) | |
391 { | |
392 case ME_PROGRAM: | |
393 if (ISDRUMCHANNEL(song, meep->event.channel)) | |
394 { | |
395 if (song->drumset[meep->event.a]) /* Is this a defined drumset? */ | |
396 new_value=meep->event.a; | |
397 else | |
398 { | |
399 SNDDBG(("Drum set %d is undefined\n", meep->event.a)); | |
400 new_value=meep->event.a=0; | |
401 } | |
402 if (current_set[meep->event.channel] != new_value) | |
403 current_set[meep->event.channel]=new_value; | |
404 else | |
405 skip_this_event=1; | |
406 } | |
407 else | |
408 { | |
409 new_value=meep->event.a; | |
410 if ((current_program[meep->event.channel] != SPECIAL_PROGRAM) | |
411 && (current_program[meep->event.channel] != new_value)) | |
412 current_program[meep->event.channel] = new_value; | |
413 else | |
414 skip_this_event=1; | |
415 } | |
416 break; | |
417 | |
418 case ME_NOTEON: | |
419 if (counting_time) | |
420 counting_time=1; | |
421 if (ISDRUMCHANNEL(song, meep->event.channel)) | |
422 { | |
423 /* Mark this instrument to be loaded */ | |
424 if (!(song->drumset[current_set[meep->event.channel]] | |
425 ->instrument[meep->event.a])) | |
426 song->drumset[current_set[meep->event.channel]] | |
427 ->instrument[meep->event.a] = MAGIC_LOAD_INSTRUMENT; | |
428 } | |
429 else | |
430 { | |
431 if (current_program[meep->event.channel]==SPECIAL_PROGRAM) | |
432 break; | |
433 /* Mark this instrument to be loaded */ | |
434 if (!(song->tonebank[current_bank[meep->event.channel]] | |
435 ->instrument[current_program[meep->event.channel]])) | |
436 song->tonebank[current_bank[meep->event.channel]] | |
437 ->instrument[current_program[meep->event.channel]] = | |
438 MAGIC_LOAD_INSTRUMENT; | |
439 } | |
440 break; | |
441 | |
442 case ME_TONE_BANK: | |
443 if (ISDRUMCHANNEL(song, meep->event.channel)) | |
444 { | |
445 skip_this_event=1; | |
446 break; | |
447 } | |
448 if (song->tonebank[meep->event.a]) /* Is this a defined tone bank? */ | |
449 new_value=meep->event.a; | |
450 else | |
451 { | |
452 SNDDBG(("Tone bank %d is undefined\n", meep->event.a)); | |
453 new_value=meep->event.a=0; | |
454 } | |
455 if (current_bank[meep->event.channel]!=new_value) | |
456 current_bank[meep->event.channel]=new_value; | |
457 else | |
458 skip_this_event=1; | |
459 break; | |
460 } | |
461 | |
462 /* Recompute time in samples*/ | |
463 if ((dt=meep->event.time - at) && !counting_time) | |
464 { | |
465 samples_to_do = song->sample_increment * dt; | |
466 sample_cum += song->sample_correction * dt; | |
467 if (sample_cum & 0xFFFF0000) | |
468 { | |
469 samples_to_do += ((sample_cum >> 16) & 0xFFFF); | |
470 sample_cum &= 0x0000FFFF; | |
471 } | |
472 st += samples_to_do; | |
473 } | |
474 else if (counting_time==1) counting_time=0; | |
475 if (!skip_this_event) | |
476 { | |
477 /* Add the event to the list */ | |
478 *lp=meep->event; | |
479 lp->time=st; | |
480 lp++; | |
481 our_event_count++; | |
482 } | |
483 at=meep->event.time; | |
484 meep=meep->next; | |
485 } | |
486 /* Add an End-of-Track event */ | |
487 lp->time=st; | |
488 lp->type=ME_EOT; | |
489 our_event_count++; | |
490 free_midi_list(song); | |
491 | |
492 *eventsp=our_event_count; | |
493 *samplesp=st; | |
494 return groomed_list; | |
495 } | |
496 | |
497 MidiEvent *read_midi_file(MidiSong *song, Sint32 *count, Sint32 *sp) | |
498 { | |
499 Sint32 len, divisions; | |
500 Sint16 format, tracks, divisions_tmp; | |
501 int i; | |
502 char tmp[4]; | |
503 | |
504 song->event_count=0; | |
505 song->at=0; | |
506 song->evlist=0; | |
507 | |
508 if (SDL_RWread(song->rw, tmp, 1, 4) != 4 || SDL_RWread(song->rw, &len, 4, 1) != 1) | |
509 { | |
510 SNDDBG(("Not a MIDI file!\n")); | |
511 return 0; | |
512 } | |
513 len=SDL_SwapBE32(len); | |
514 if (memcmp(tmp, "MThd", 4) || len < 6) | |
515 { | |
516 SNDDBG(("Not a MIDI file!\n")); | |
517 return 0; | |
518 } | |
519 | |
520 SDL_RWread(song->rw, &format, 2, 1); | |
521 SDL_RWread(song->rw, &tracks, 2, 1); | |
522 SDL_RWread(song->rw, &divisions_tmp, 2, 1); | |
523 format=SDL_SwapBE16(format); | |
524 tracks=SDL_SwapBE16(tracks); | |
525 divisions_tmp=SDL_SwapBE16(divisions_tmp); | |
526 | |
527 if (divisions_tmp<0) | |
528 { | |
529 /* SMPTE time -- totally untested. Got a MIDI file that uses this? */ | |
530 divisions= | |
531 (Sint32)(-(divisions_tmp/256)) * (Sint32)(divisions_tmp & 0xFF); | |
532 } | |
533 else divisions=(Sint32)(divisions_tmp); | |
534 | |
535 if (len > 6) | |
536 { | |
537 SNDDBG(("MIDI file header size %u bytes", len)); | |
538 SDL_RWseek(song->rw, len-6, SEEK_CUR); /* skip the excess */ | |
539 } | |
540 if (format<0 || format >2) | |
541 { | |
542 SNDDBG(("Unknown MIDI file format %d\n", format)); | |
543 return 0; | |
544 } | |
545 SNDDBG(("Format: %d Tracks: %d Divisions: %d\n", | |
546 format, tracks, divisions)); | |
547 | |
548 /* Put a do-nothing event first in the list for easier processing */ | |
549 song->evlist=safe_malloc(sizeof(MidiEventList)); | |
550 song->evlist->event.time=0; | |
551 song->evlist->event.type=ME_NONE; | |
552 song->evlist->next=0; | |
553 song->event_count++; | |
554 | |
555 switch(format) | |
556 { | |
557 case 0: | |
558 if (read_track(song, 0)) | |
559 { | |
560 free_midi_list(song); | |
561 return 0; | |
562 } | |
563 break; | |
564 | |
565 case 1: | |
566 for (i=0; i<tracks; i++) | |
567 if (read_track(song, 0)) | |
568 { | |
569 free_midi_list(song); | |
570 return 0; | |
571 } | |
572 break; | |
573 | |
574 case 2: /* We simply play the tracks sequentially */ | |
575 for (i=0; i<tracks; i++) | |
576 if (read_track(song, 1)) | |
577 { | |
578 free_midi_list(song); | |
579 return 0; | |
580 } | |
581 break; | |
582 } | |
583 return groom_list(song, divisions, count, sp); | |
584 } |