1 /*
2 midifile.c:
3
4 Copyright (C) 2005 Istvan Varga
5
6 This file is part of Csound.
7
8 The Csound Library is free software; you can redistribute it
9 and/or modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
12
13 Csound is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with Csound; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 02110-1301 USA
22 */
23
24 #include "csoundCore.h"
25 #include "midifile.h"
26 #include <errno.h>
27
28 static const char *midiFile_ID = "MThd";
29 static const char *midiTrack_ID = "MTrk";
30 /* default tempo in beats per minute */
31 static const double default_tempo = 120.0;
32
33 typedef struct tempoEvent_s {
34 unsigned long kcnt; /* time (in ticks while reading */
35 /* MIDI file, will be converted */
36 /* to kperiods once file is read) */
37 double tempoVal; /* tempo value in beats per minute */
38 } tempoEvent_t;
39
40 typedef struct midiEvent_s {
41 unsigned long kcnt; /* time (in ticks while reading */
42 /* MIDI file, will be converted */
43 /* to kperiods once file is read) */
44 #if 0
45 unsigned char *data; /* pointer to sysex or meta event */
46 /* data (currently not used) */
47 #endif
48 unsigned char st; /* status byte (0x80-0xFF) */
49 unsigned char d1; /* data byte 1 (0x00-0x7F) */
50 unsigned char d2; /* data byte 2 (0x00-0x7F) */
51 } midiEvent_t;
52
53 typedef struct midiFile_s {
54 /* static file data, not changed at performance */
55 double timeCode; /* > 0: ticks per beat */
56 /* < 0: ticks per second */
57 unsigned long totalKcnt; /* total duration of file */
58 /* (in ticks while reading file, */
59 /* converted to kperiods) */
60 int nEvents; /* number of events */
61 int maxEvents; /* event array size */
62 int nTempo; /* number of tempo changes */
63 int maxTempo; /* tempo change array size */
64 midiEvent_t *eventList; /* array of MIDI events */
65 tempoEvent_t *tempoList; /* array of tempo changes */
66 /* performance time state variables */
67 double currentTempo; /* current tempo in BPM */
68 int eventListIndex; /* index of next MIDI event in list */
69 int tempoListIndex; /* index of next tempo change */
70 } midiFile_t;
71
72 #define MIDIFILE (csound->midiGlobals->midiFileData)
73 #define MF(x) (((midiFile_t*) MIDIFILE)->x)
74
getCh(CSOUND * csound,FILE * f,int * bytesLeft)75 static int getCh(CSOUND *csound, FILE *f, int *bytesLeft)
76 {
77 int c;
78
79 if (f == NULL)
80 return -1;
81 c = getc(f);
82 if (UNLIKELY(c == EOF)) {
83 csound->Message(csound, Str(" *** unexpected end of MIDI file\n"));
84 return -1;
85 }
86 if (bytesLeft != NULL) {
87 if (UNLIKELY(--(*bytesLeft) < 0)) {
88 csound->Message(csound, Str(" *** unexpected end of MIDI track\n"));
89 return -1;
90 }
91 }
92 return (c & 0xFF);
93 }
94
getVLenData(CSOUND * csound,FILE * f,int * bytesLeft)95 static int getVLenData(CSOUND *csound, FILE *f, int *bytesLeft)
96 {
97 int c, n, cnt;
98
99 n = cnt = 0;
100 do {
101 if (UNLIKELY(++cnt > 4)) {
102 csound->Message(csound,
103 Str(" *** invalid dynamic length data in MIDI file\n"));
104 return -1;
105 }
106 c = getCh(csound, f, bytesLeft);
107 if (c < 0)
108 return -1;
109 n = (n << 7) | (c & 0x7F);
110 } while (c & 0x80);
111 return n;
112 }
113
114 /* return the number of bytes to read for status byte 'c' */
115 /* return value is 0, 1, 2, or -1 in the case of an unknown */
116 /* or special message */
117
msgDataBytes(int c)118 static int msgDataBytes(int c)
119 {
120 switch (c & 0xF0) {
121 case 0x80: /* note off */
122 case 0x90: /* note on */
123 case 0xA0: /* polyphonic pressure */
124 case 0xB0: /* control change */
125 case 0xE0: /* pitch bend */
126 return 2;
127 case 0xC0: /* program change */
128 case 0xD0: /* channel pressure */
129 return 1;
130 case 0xF0:
131 switch (c) {
132 case 0xF2: /* song position */
133 return 2;
134 case 0xF1: /* MTC quarter frame */
135 case 0xF3: /* song select */
136 return 1;
137 case 0xF0: /* system exclusive */
138 case 0xF7: /* escape sequence */
139 case 0xFF: /* meta event */
140 case 0xF4: /* unknown */
141 case 0xF5: /* unknown */
142 case 0xF9: /* unknown */
143 case 0xFD: /* unknown */
144 return -1;
145 default: /* tune request, real time system messages */
146 return 0;
147 }
148 }
149 /* anything else is unknown */
150 return -1;
151 }
152
alloc_event(CSOUND * csound,unsigned long kcnt,unsigned char * data,int st,int d1,int d2)153 static int alloc_event(CSOUND *csound, unsigned long kcnt, unsigned char *data,
154 int st, int d1, int d2)
155 {
156 midiEvent_t *tmp;
157 IGN(data);
158 /* expand array if necessary */
159 if (MF(nEvents) >= MF(maxEvents)) {
160 MF(maxEvents) += (MF(maxEvents) >> 3);
161 MF(maxEvents) = (MF(maxEvents) + 64) & (~63);
162 tmp = (midiEvent_t*) csound->ReAlloc(csound, MF(eventList),
163 sizeof(midiEvent_t) * MF(maxEvents));
164 MF(eventList) = tmp;
165 tmp = &(MF(eventList)[MF(nEvents)]);
166 memset(tmp, 0, sizeof(midiEvent_t) * (MF(maxEvents) - MF(nEvents)));
167 }
168 /* store new event */
169 tmp = &(MF(eventList)[MF(nEvents)]);
170 MF(nEvents)++;
171 tmp->kcnt = kcnt;
172 /* tmp->data = data; */ /* not used yet */
173 tmp->st = (unsigned char) st;
174 tmp->d1 = (unsigned char) d1;
175 tmp->d2 = (unsigned char) d2;
176 /* done */
177 return 0;
178 }
179
alloc_tempo(CSOUND * csound,unsigned long kcnt,double tempoVal)180 static int alloc_tempo(CSOUND *csound, unsigned long kcnt, double tempoVal)
181 {
182 tempoEvent_t *tmp;
183 /* expand array if necessary */
184 if (MF(nTempo) >= MF(maxTempo)) {
185 MF(maxTempo) += (MF(maxTempo) >> 3);
186 MF(maxTempo) = (MF(maxTempo) + 64) & (~63);
187 tmp = (tempoEvent_t*) csound->ReAlloc(csound, MF(tempoList),
188 sizeof(tempoEvent_t) * MF(maxTempo));
189 MF(tempoList) = tmp;
190 tmp = &(MF(tempoList)[MF(nTempo)]);
191 memset(tmp, 0, sizeof(tempoEvent_t) * (MF(maxTempo) - MF(nTempo)));
192 }
193 /* store new event */
194 tmp = &(MF(tempoList)[MF(nTempo)]);
195 MF(nTempo)++;
196 tmp->kcnt = kcnt; tmp->tempoVal = tempoVal;
197 /* done */
198 return 0;
199 }
200
201 static int readEvent(CSOUND *csound, FILE *f, int *tlen,
202 unsigned long tickCnt, int st, int *saved_st);
203
checkRealTimeEvent(CSOUND * csound,FILE * f,int * tlen,unsigned long tickCnt,int st,int * saved_st)204 static int checkRealTimeEvent(CSOUND *csound, FILE *f, int *tlen,
205 unsigned long tickCnt, int st, int *saved_st)
206 {
207 if (st & 0x80) {
208 if (UNLIKELY(st < 0xF8 || st > 0xFE)) {
209 csound->Message(csound, Str(" *** unexpected event 0x%02X\n"),
210 (unsigned int) st);
211 return -1;
212 }
213 /* handle real time message (return code -2) */
214 if (readEvent(csound, f, tlen, tickCnt, st, saved_st) != 0)
215 return -1;
216 return -2;
217 }
218 return st;
219 }
220
readEvent(CSOUND * csound,FILE * f,int * tlen,unsigned long tickCnt,int st,int * saved_st)221 static int readEvent(CSOUND *csound, FILE *f, int *tlen,
222 unsigned long tickCnt, int st, int *saved_st)
223 {
224 int i, c, d, cnt, dataBytes[2];
225
226 cnt = dataBytes[0] = dataBytes[1] = 0;
227 if (st < 0x80) {
228 /* repeat previous status byte */
229 dataBytes[cnt++] = st;
230 st = *saved_st;
231 if (UNLIKELY(st < 0x80)) {
232 csound->Message(csound, Str(" *** invalid MIDI file data\n"));
233 return -1;
234 }
235 }
236 c = msgDataBytes(st);
237 if (c >= 0) {
238 if (c > 0) {
239 /* save status byte for repeat */
240 *saved_st = st;
241 }
242 while (cnt < c) {
243 /* read data byte(s) */
244 d = getCh(csound, f, tlen);
245 if (d < 0 || *tlen < 0) return -1;
246 d = checkRealTimeEvent(csound, f, tlen, tickCnt, d, saved_st);
247 if (d == -2) /* read real time event: continue with reading data */
248 continue;
249 if (d < 0) return -1;
250 dataBytes[cnt++] = d;
251 }
252 return alloc_event(csound, tickCnt, NULL, st,
253 dataBytes[0], dataBytes[1]);
254 }
255 /* message is of unknown or special type */
256 if (st == 0xF0) {
257 /* system exclusive */
258 i = getVLenData(csound, f, tlen);
259 if (i < 0 || *tlen < 0) return -1;
260 /* read message */
261 while (--i >= 0) {
262 d = getCh(csound, f, tlen);
263 if (d < 0 || *tlen < 0) return -1;
264 if (d == 0xF7) { /* EOX */
265 if (LIKELY(!i))
266 return 0; /* should be at end of message */
267 csound->Message(csound, Str(" *** unexpected end of system "
268 "exclusive message\n"));
269 return -1;
270 }
271 d = checkRealTimeEvent(csound, f, tlen, tickCnt, d, saved_st);
272 if (d == -2) /* if read real time event, */
273 i++; /* continue with reading message bytes */
274 else if (UNLIKELY(d < 0))
275 return -1; /* error */
276 }
277 /* zero length or EOX not found */
278 csound->Message(csound, Str(" *** invalid system exclusive "
279 "message in MIDI file\n"));
280 return -1;
281 }
282 else if (st == 0xF7) {
283 /* escape sequence: skip message */
284 i = getVLenData(csound, f, tlen); /* message length */
285 if (i < 0 || *tlen < 0) return -1;
286 while (--i >= 0) {
287 c = getCh(csound, f, tlen);
288 if (c < 0 || *tlen < 0) return -1;
289 }
290 return 0;
291 }
292 else if (st == 0xFF) {
293 /* meta event */
294 st = getCh(csound, f, tlen); /* message type */
295 if (st < 0 || *tlen < 0) return -1;
296 i = getVLenData(csound, f, tlen); /* message length */
297 if (i < 0 || *tlen < 0) return -1;
298 if (i > 0 &&
299 ((st >= 1 && st <= 5 && (csound->oparms->msglevel & 7) == 7) ||
300 (st == 3 && csound->oparms->msglevel != 0))) {
301 /* print non-empty text meta events, depending on message level */
302 switch (st) {
303 case 0x01: csound->Message(csound, Str(" Message: ")); break;
304 case 0x02: csound->Message(csound, Str(" Copyright info: ")); break;
305 case 0x03: csound->Message(csound, Str(" Track name: ")); break;
306 case 0x04: csound->Message(csound, Str(" Instrument name: ")); break;
307 case 0x05: csound->Message(csound, Str(" Song lyric: ")); break;
308 }
309 while (--i >= 0) {
310 c = getCh(csound, f, tlen);
311 if (c < 0 || *tlen < 0) return -1;
312 csound->Message(csound, "%c", c);
313 }
314 csound->Message(csound, "\n");
315 return 0;
316 }
317 switch (st) {
318 case 0x51: /* tempo change */
319 d = 0;
320 while (--i >= 0) {
321 c = getCh(csound, f, tlen);
322 if (c < 0 || *tlen < 0) return -1;
323 d = (d << 8) | c;
324 }
325 if (UNLIKELY(d < 1)) {
326 csound->Message(csound, Str(" *** invalid tempo\n"));
327 return -1;
328 }
329 return alloc_tempo(csound, tickCnt, (60000000.0 / (double) d));
330 case 0x2F: /* end of track */
331 if (UNLIKELY(i)) {
332 csound->Message(csound, Str(" *** invalid end of track event\n"));
333 return -1;
334 }
335 if (UNLIKELY(*tlen > 0)) {
336 csound->Message(csound, Str(" *** trailing garbage at end of "
337 "MIDI track\n"));
338 return -1;
339 }
340 /* update file length info */
341 if (tickCnt > MF(totalKcnt))
342 MF(totalKcnt) = tickCnt;
343 return 0;
344 default: /* skip any other meta event */
345 while (--i >= 0) {
346 c = getCh(csound, f, tlen);
347 if (c < 0 || *tlen < 0) return -1;
348 }
349 return 0;
350 }
351 }
352 csound->Message(csound, Str(" *** unknown MIDI message: 0x%02X\n"),
353 (unsigned int) st);
354 return -1;
355 }
356
readTrack(CSOUND * csound,FILE * f)357 static int readTrack(CSOUND *csound, FILE *f)
358 {
359 unsigned int tickCnt;
360 int i, c, tlen, st, saved_st;
361
362 /* check for track header */
363 for (i = 0; i < 4; i++) {
364 c = getCh(csound, f, NULL);
365 if (c < 0)
366 return -1;
367 if (UNLIKELY(c != (int) midiTrack_ID[i])) {
368 csound->Message(csound, Str(" *** invalid MIDI track header\n"));
369 return -1;
370 }
371 }
372 /* read track length */
373 tlen = 0;
374 for (i = 0; i < 4; i++) {
375 c = getCh(csound, f, NULL);
376 if (c < 0)
377 return -1;
378 tlen = (tlen << 8) | c;
379 }
380 /* read track data */
381 tickCnt = 0UL;
382 saved_st = -1;
383 while (tlen > 0) {
384 /* get delta time */
385 c = getVLenData(csound, f, &tlen);
386 if (c < 0 || tlen < 0)
387 return -1;
388 tickCnt += (unsigned long) c;
389 /* get status byte */
390 st = getCh(csound, f, &tlen);
391 if (st < 0 || tlen < 0)
392 return -1;
393 /* process event */
394 if (readEvent(csound, f, &tlen, tickCnt, st, &saved_st) != 0)
395 return -1;
396 }
397 /* successfully read track */
398 return 0;
399 }
400
401 /**
402 * Sorts an array of midiEvent_t structures using merge sort algorithm
403 * so that the order of events at the same time is preserved.
404 * 'p' is the array to be sorted, and 'cnt' is the number of elements
405 * (should be > 1). 'tmp' is temporary space of the same length as 'p'.
406 */
407
midiEvent_sort(midiEvent_t * p,midiEvent_t * tmp,size_t cnt)408 static CS_NOINLINE void midiEvent_sort(midiEvent_t *p, midiEvent_t *tmp,
409 size_t cnt)
410 {
411 size_t p1, p2, dstp;
412 size_t n = cnt >> 1;
413
414 if (n > (size_t) 1)
415 midiEvent_sort(p, tmp, n);
416 if (cnt > (size_t) 2)
417 midiEvent_sort(&(p[n]), tmp, cnt - n);
418 dstp = (size_t) 0;
419 p1 = (size_t) 0;
420 p2 = n;
421 do {
422 size_t srcp;
423 if (p2 >= cnt || (p1 < n && p[p1].kcnt <= p[p2].kcnt))
424 srcp = p1++;
425 else
426 srcp = p2++;
427 tmp[dstp] = p[srcp];
428 } while (++dstp < cnt);
429 /* copy result back to original array */
430 memcpy(p, tmp, cnt * sizeof(midiEvent_t));
431 }
432
433 /**
434 * Sorts an array of tempoEvent_t structures using merge sort algorithm
435 * so that the order of events at the same time is preserved.
436 * 'p' is the array to be sorted, and 'cnt' is the number of elements
437 * (should be > 1). 'tmp' is temporary space of the same length as 'p'.
438 */
439
tempoEvent_sort(tempoEvent_t * p,tempoEvent_t * tmp,size_t cnt)440 static CS_NOINLINE void tempoEvent_sort(tempoEvent_t *p, tempoEvent_t *tmp,
441 size_t cnt)
442 {
443 size_t p1, p2, dstp;
444 size_t n = cnt >> 1;
445
446 if (n > (size_t) 1)
447 tempoEvent_sort(p, tmp, n);
448 if (cnt > (size_t) 2)
449 tempoEvent_sort(&(p[n]), tmp, cnt - n);
450 dstp = (size_t) 0;
451 p1 = (size_t) 0;
452 p2 = n;
453 do {
454 size_t srcp;
455 if (p2 >= cnt || (p1 < n && p[p1].kcnt <= p[p2].kcnt))
456 srcp = p1++;
457 else
458 srcp = p2++;
459 tmp[dstp] = p[srcp];
460 } while (++dstp < cnt);
461 /* copy result back to original array */
462 memcpy(p, tmp, cnt * sizeof(tempoEvent_t));
463 }
464
465 /* sort event lists by time and convert tick times to Csound k-periods */
466
sortEventLists(CSOUND * csound)467 static void sortEventLists(CSOUND *csound)
468 {
469 double timeVal, tempoVal;
470 unsigned long prvTicks, curTicks, tickEvent, tickTempo;
471 int i, j;
472
473 /* sort events by time in ascending order */
474 if (MF(nEvents) > 1 || MF(nTempo) > 1) {
475 void *tmp;
476 size_t nbytes;
477 nbytes = (size_t) MF(nEvents) * sizeof(midiEvent_t);
478 if (((size_t) MF(nTempo) * sizeof(tempoEvent_t)) > nbytes)
479 nbytes = (size_t) MF(nTempo) * sizeof(tempoEvent_t);
480 tmp = csound->Malloc(csound, nbytes);
481 if (MF(nEvents) > 1)
482 midiEvent_sort(MF(eventList), (midiEvent_t *) tmp,
483 (size_t) MF(nEvents));
484 if (MF(nTempo) > 1)
485 tempoEvent_sort(MF(tempoList), (tempoEvent_t *) tmp,
486 (size_t) MF(nTempo));
487 csound->Free(csound, tmp);
488 }
489 if (MF(timeCode) > 0.0) {
490 /* tick values are in fractions of a beat */
491 timeVal = 0.0;
492 tempoVal = default_tempo;
493 prvTicks = curTicks = 0UL;
494 /* k-periods per tick */
495 tempoVal = (double) csound->ekr / (tempoVal * MF(timeCode) / 60.0);
496 i = j = 0;
497 while (i < MF(nEvents) || j < MF(nTempo)) {
498 prvTicks = curTicks;
499 tickEvent = tickTempo = 0UL;
500 tickEvent--; tickTempo--; /* will be max value for unsigned long */
501 if (i < MF(nEvents)) tickEvent = MF(eventList)[i].kcnt;
502 if (j < MF(nTempo)) tickTempo = MF(tempoList)[j].kcnt;
503 if (tickEvent < tickTempo) {
504 curTicks = tickEvent;
505 timeVal += ((double) ((long) (curTicks - prvTicks)) * tempoVal);
506 MF(eventList)[i++].kcnt = (unsigned long) (timeVal + 0.5);
507 }
508 else {
509 curTicks = tickTempo;
510 timeVal += ((double) ((long) (curTicks - prvTicks)) * tempoVal);
511 tempoVal = MF(tempoList)[j].tempoVal; /* new tempo */
512 /* k-periods per tick */
513 tempoVal = (double) csound->ekr / (tempoVal * MF(timeCode) / 60.0);
514 MF(tempoList)[j++].kcnt = (unsigned long) (timeVal + 0.5);
515 }
516 }
517 /* calculate total file length in k-periods */
518 timeVal += ((double) ((long) (MF(totalKcnt) - curTicks)) * tempoVal);
519 MF(totalKcnt) = (unsigned long) (timeVal + 0.5);
520 }
521 else {
522 /* simple case: time based tick values */
523 tempoVal = -(MF(timeCode));
524 /* k-periods per tick */
525 tempoVal = (double) csound->ekr / tempoVal;
526 i = -1;
527 while (++i < MF(nEvents)) {
528 curTicks = MF(eventList)[i].kcnt;
529 timeVal = (double) curTicks * tempoVal;
530 curTicks = (unsigned long) (timeVal + 0.5);
531 MF(eventList)[i].kcnt = curTicks;
532 }
533 i = -1;
534 while (++i < MF(nTempo)) {
535 curTicks = MF(tempoList)[i].kcnt;
536 timeVal = (double) curTicks * tempoVal;
537 curTicks = (unsigned long) (timeVal + 0.5);
538 MF(tempoList)[i].kcnt = curTicks;
539 }
540 /* calculate total file length in k-periods */
541 MF(totalKcnt) = (unsigned long) ((double) MF(totalKcnt) * tempoVal + 0.5);
542 }
543 }
544
545 /* ------------------------------------------------------------------------ */
546
547 /* open MIDI file, read all tracks, and create event list */
548
csoundMIDIFileOpen(CSOUND * csound,const char * name)549 int csoundMIDIFileOpen(CSOUND *csound, const char *name)
550 {
551 FILE *f = NULL;
552 void *fd = NULL;
553 char *m;
554 int i, c, hdrLen, fileFormat, nTracks, timeCode, saved_nEvents;
555 int mute_track;
556
557 if (MIDIFILE != NULL)
558 return 0; /* already opened */
559 /* open file */
560 if (UNLIKELY(name == NULL || name[0] == '\0'))
561 return -1;
562 //if (*name==3) name++; /* Because of ETX added bt readOptions */
563 if (strcmp(name, "stdin") == 0)
564 f = stdin;
565 else {
566 fd = csound->FileOpen2(csound, &f, CSFILE_STD, name, "rb",
567 "SFDIR;SSDIR;MFDIR", CSFTYPE_STD_MIDI, 0);
568 if (UNLIKELY(fd == NULL)) {
569 csound->ErrorMsg(csound, Str(" *** error opening MIDI file '%s': %s"),
570 name, strerror(errno));
571 return -1;
572 }
573 }
574 csound->Message(csound, Str("Reading MIDI file '%s'...\n"), name);
575 /* check header */
576 for (i = 0; i < 4; i++) {
577 c = getCh(csound, f, NULL);
578 if (UNLIKELY(c < 0)) goto err_return;
579 if (UNLIKELY(c != (int) midiFile_ID[i])) {
580 csound->Message(csound, Str(" *** invalid MIDI file header\n"));
581 goto err_return;
582 }
583 }
584 /* header length: must be 6 bytes */
585 hdrLen = 0;
586 for (i = 0; i < 4; i++) {
587 c = getCh(csound, f, NULL);
588 if (UNLIKELY(c < 0)) goto err_return;
589 hdrLen = (hdrLen << 8) | c;
590 }
591 if (UNLIKELY(hdrLen != 6)) {
592 csound->Message(csound, Str(" *** invalid MIDI file header\n"));
593 goto err_return;
594 }
595 /* file format (only 0 and 1 are supported) */
596 fileFormat = 0;
597 for (i = 0; i < 2; i++) {
598 c = getCh(csound, f, NULL);
599 if (UNLIKELY(c < 0)) goto err_return;
600 fileFormat = (fileFormat << 8) | c;
601 }
602 if (UNLIKELY(fileFormat != 0 && fileFormat != 1)) {
603 csound->Message(csound,
604 Str(" *** MIDI file format %d is not supported\n"),
605 fileFormat);
606 goto err_return;
607 }
608 /* number of tracks */
609 nTracks = 0;
610 for (i = 0; i < 2; i++) {
611 c = getCh(csound, f, NULL);
612 if (UNLIKELY(c < 0)) goto err_return;
613 nTracks = (nTracks << 8) | c;
614 }
615 if (UNLIKELY(nTracks < 1)) {
616 csound->Message(csound, Str(" *** invalid number of tracks\n"));
617 goto err_return;
618 }
619 if (UNLIKELY(nTracks > 1 && !fileFormat)) {
620 csound->Message(csound, Str("WARNING: format 0 MIDI file with "
621 "multiple tracks\n"));
622 }
623 /* time code */
624 timeCode = 0;
625 for (i = 0; i < 2; i++) {
626 c = getCh(csound, f, NULL);
627 if (UNLIKELY(c < 0)) goto err_return;
628 timeCode = (timeCode << 8) | c;
629 }
630 /* allocate structure */
631 MIDIFILE = (void*) csound->Calloc(csound, sizeof(midiFile_t));
632 /* calculate ticks per second or beat based on time code */
633 if (UNLIKELY(timeCode < 1 || (timeCode >= 0x8000 && (timeCode & 0xFF) == 0))) {
634 csound->Message(csound, Str(" *** invalid time code: %d\n"), timeCode);
635 goto err_return;
636 }
637 if (timeCode < 0x8000)
638 MF(timeCode) = (double) timeCode;
639 else {
640 switch (timeCode & 0xFF00) {
641 case 0xE800:
642 case 0xE700:
643 case 0xE200:
644 MF(timeCode) = (double) ((timeCode >> 8) - 256);
645 break;
646 case 0xE300:
647 MF(timeCode) = -29.97;
648 break;
649 default:
650 csound->Message(csound,
651 Str(" *** invalid time code: %d\n"), timeCode);
652 goto err_return;
653 }
654 MF(timeCode) *= (double) (timeCode & 0xFF);
655 }
656 /* initialise structure data */
657 MF(totalKcnt) = csound->global_kcounter;
658 MF(nEvents) = 0; MF(maxEvents) = 0;
659 MF(nTempo) = 0; MF(maxTempo) = 0;
660 MF(eventList) = (midiEvent_t*) NULL;
661 MF(tempoList) = (tempoEvent_t*) NULL;
662 MF(currentTempo) = default_tempo;
663 MF(eventListIndex) = 0;
664 MF(tempoListIndex) = 0;
665 /* read all tracks */
666 m = &(csound->midiGlobals->muteTrackList[0]);
667 for (i = 0; i < nTracks; i++) {
668 saved_nEvents = MF(nEvents);
669 mute_track = 0;
670 if (*m != '\0') { /* is this track muted ? */
671 if (*m == '1')
672 mute_track = 1;
673 else if (UNLIKELY(*m != '0')) {
674 csound->Message(csound, Str(" *** invalid mute track list format\n"));
675 goto err_return;
676 }
677 m++;
678 }
679 if (!mute_track)
680 csound->Message(csound, Str(" Track %2d\n"), i);
681 else
682 csound->Message(csound, Str(" Track %2d is muted\n"), i);
683 if (readTrack(csound, f) != 0)
684 goto err_return;
685 if (mute_track) /* if track is muted, discard any */
686 MF(nEvents) = saved_nEvents; /* non-tempo events read */
687 }
688 if (fd != NULL)
689 csound->FileClose(csound, fd);
690 /* prepare event and tempo list for reading */
691 sortEventLists(csound);
692 /* successfully read MIDI file */
693 csound->Message(csound, Str("done.\n"));
694 return 0;
695
696 /* in case of error: clean up and report error */
697 err_return:
698 if (fd != NULL)
699 csound->FileClose(csound, fd);
700 csoundMIDIFileClose(csound);
701 return -1;
702 }
703
704 /* read MIDI file event data at performace time */
705
csoundMIDIFileRead(CSOUND * csound,unsigned char * buf,int nBytes)706 int csoundMIDIFileRead(CSOUND *csound, unsigned char *buf, int nBytes)
707 {
708 midiFile_t *mf;
709 int i, j, n, nRead;
710
711 mf = (midiFile_t*) MIDIFILE;
712 if (mf == NULL)
713 return 0;
714 i = mf->eventListIndex;
715 j = mf->tempoListIndex;
716 if (i >= mf->nEvents && j >= mf->nTempo) {
717 /* there are no more events, */
718 if ((unsigned long) csound->global_kcounter >= mf->totalKcnt &&
719 !(csound->MTrkend)) {
720 /* and end of file is reached: */
721 csound->Message(csound, Str("end of midi track in '%s'\n"),
722 csound->oparms->FMidiname);
723 csound->Message(csound, Str("%d forced decays, %d extra noteoffs\n"),
724 csound->Mforcdecs, csound->Mxtroffs);
725 csound->MTrkend = 1;
726 csoundMIDIFileClose(csound);
727 csound->oparms->FMidiin = 0;
728 if (csound->oparms->ringbell && !(csound->oparms->termifend))
729 csound->Message(csound, "\a");
730 }
731 return 0;
732 }
733 /* otherwise read any events with time less than or equal to */
734 /* current orchestra time */
735 while (j < mf->nTempo &&
736 (unsigned long) csound->global_kcounter >= mf->tempoList[j].kcnt) {
737 /* tempo change */
738 mf->currentTempo = mf->tempoList[j++].tempoVal;
739 }
740 mf->tempoListIndex = j;
741 nRead = 0;
742 while (i < mf->nEvents &&
743 (unsigned long) csound->global_kcounter >= mf->eventList[i].kcnt) {
744 n = msgDataBytes((int) mf->eventList[i].st) + 1;
745 if (n < 1) {
746 i++; continue; /* unknown or system event: skip */
747 }
748 nBytes -= n;
749 if (UNLIKELY(nBytes < 0)) {
750 csound->Message(csound, Str(" *** buffer overflow while reading "
751 "MIDI file events\n"));
752 break; /* return with whatever has been read so far */
753 }
754 nRead += n;
755 *buf++ = mf->eventList[i].st;
756 if (n > 1) *buf++ = mf->eventList[i].d1;
757 if (n > 2) *buf++ = mf->eventList[i].d2;
758 i++;
759 }
760 mf->eventListIndex = i;
761 /* return the number of bytes read */
762 return nRead;
763 }
764
765 /* destroy MIDI file event list */
766
csoundMIDIFileClose(CSOUND * csound)767 int csoundMIDIFileClose(CSOUND *csound)
768 {
769 /* nothing to do: memRESET() will free any allocated memory */
770 MIDIFILE = (void*) NULL;
771 return 0;
772 }
773
774 /* midirecv.c, resets MIDI controllers on a channel */
775 extern void midi_ctl_reset(CSOUND *csound, int16 chan);
776
777 /* called by csoundRewindScore() to reset performance to time zero */
778
midifile_rewind_score(CSOUND * csound)779 void midifile_rewind_score(CSOUND *csound)
780 {
781 int i;
782 OPARMS *O = csound->oparms;
783
784 if (MIDIFILE != NULL) {
785 /* reset event index and tempo */
786 MF(currentTempo) = default_tempo;
787 MF(eventListIndex) = 0;
788 MF(tempoListIndex) = 0;
789 csound->MTrkend = csound->Mxtroffs = csound->Mforcdecs = 0;
790 /* reset controllers on all channels */
791 for (i = 0; i < MAXCHAN; i++)
792 midi_ctl_reset(csound, (int16) i);
793 } else if (LIKELY(O->FMidiname != NULL)) {
794 csound->MTrkend = 0;
795 if (UNLIKELY(csoundMIDIFileOpen(csound, O->FMidiname) != 0))
796 csound->Die(csound, Str("Failed to load MIDI file."));
797 O->FMidiin = 1;
798 }
799 else csound->Warning(csound, Str("Cannot rewind MIDI score\n"));
800 }
801
802 /* ------------------------------------------------------------------------ */
803
804 /* miditempo opcode: returns the current tempo of MIDI file or score */
805
midiTempoOpcode(CSOUND * csound,MIDITEMPO * p)806 int midiTempoOpcode(CSOUND *csound, MIDITEMPO *p)
807 {
808 if (MIDIFILE == NULL)
809 *(p->kResult) = FL(60.0) *csound->esr / (MYFLT)(csound->ibeatTime);
810 else
811 *(p->kResult) = (MYFLT) MF(currentTempo);
812 return OK;
813 }
814
midiFileStatus(CSOUND * csound,MIDITEMPO * p)815 int midiFileStatus(CSOUND *csound, MIDITEMPO *p){
816 *p->kResult = csound->oparms->FMidiin;
817 return OK;
818 }
819