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