home *** CD-ROM | disk | FTP | other *** search
- /*
- * midifile
- *
- * Read a MIDI file. Externally-assigned function pointers are called
- * upon recognizing things in the file.
- */
-
- #include "midifile.h"
-
- #define NULLFUNC 0
- #define NULL 0
- #define EOF (-1)
-
- char *strcpy(), *strcat();
- void exit(), free();
-
- /* public stuff */
-
- /* Functions to be called while processing the MIDI file. */
- int (*Mf_getc)() = NULLFUNC;
- int (*Mf_error)() = NULLFUNC;
- int (*Mf_header)() = NULLFUNC;
- int (*Mf_trackstart)() = NULLFUNC;
- int (*Mf_trackend)() = NULLFUNC;
- int (*Mf_noteon)() = NULLFUNC;
- int (*Mf_noteoff)() = NULLFUNC;
- int (*Mf_pressure)() = NULLFUNC;
- int (*Mf_parameter)() = NULLFUNC;
- int (*Mf_pitchbend)() = NULLFUNC;
- int (*Mf_program)() = NULLFUNC;
- int (*Mf_chanpressure)() = NULLFUNC;
- int (*Mf_sysex)() = NULLFUNC;
- int (*Mf_arbitrary)() = NULLFUNC;
- int (*Mf_metamisc)() = NULLFUNC;
- int (*Mf_seqnum)() = NULLFUNC;
- int (*Mf_eot)() = NULLFUNC;
- int (*Mf_smpte)() = NULLFUNC;
- int (*Mf_tempo)() = NULLFUNC;
- int (*Mf_timesig)() = NULLFUNC;
- int (*Mf_keysig)() = NULLFUNC;
- int (*Mf_seqspecific)() = NULLFUNC;
- int (*Mf_text)() = NULLFUNC;
-
- int Mf_nomerge = 0; /* 1 => continue'ed system exclusives are */
- /* not collapsed. */
- long Mf_currtime = 0L; /* current time in delta-time units */
-
- /* private stuff */
-
- static long Mf_toberead = 0L;
-
- static long readvarinum();
- static long read32bit();
- static long to32bit();
- static int read16bit();
- static int to16bit();
- static char *msg();
-
- midifile() /* The only non-static function in this file. */
- {
- if ( Mf_getc == NULLFUNC )
- mferror("");
-
- readheader();
- while ( readtrack() )
- ;
- }
-
- static
- readmt(s) /* read through the "MThd" or "MTrk" header string */
- char *s;
- {
- int n = 0;
- char *p = s;
- int c;
-
- while ( n++<4 && (c=(*Mf_getc)()) != EOF ) {
- if ( c != *p++ ) {
- char buff[32];
- (void) strcpy(buff,"expecting ");
- (void) strcat(buff,s);
- if (strcmp(s, "MTrk") == 0) {
- if ( Mf_error )
- (*Mf_error)("expecting MTrk - assuming EOF");
- return(EOF);
- }
- mferror(buff);
- }
- }
- return(c);
- }
-
- static
- egetc() /* read a single character and abort on EOF */
- {
- int c = (*Mf_getc)();
-
- if ( c == EOF )
- mferror("unexpected EOF");
- Mf_toberead--;
- return(c);
- }
-
- static
- readheader() /* read a header chunk */
- {
- int format, ntrks, division;
-
- if ( readmt("MThd") == EOF )
- return(0);
-
- Mf_toberead = read32bit();
- format = read16bit();
- ntrks = read16bit();
- division = read16bit();
-
- if ( Mf_header )
- (*Mf_header)(format,ntrks,division);
-
- /* flush any extra stuff, in case the length of header is not 6 */
- while ( Mf_toberead > 0 )
- (void) egetc();
- return(0);
- }
-
- static
- readtrack() /* read a track chunk */
- {
- /* This array is indexed by the high half of a status byte. It's */
- /* value is either the number of bytes needed (1 or 2) for a channel */
- /* message, or 0 (meaning it's not a channel message). */
- static int chantype[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 through 0x70 */
- 2, 2, 2, 2, 1, 1, 2, 0 /* 0x80 through 0xf0 */
- };
- long lookfor, lng;
- int c, c1, type;
- int sysexcontinue = 0; /* 1 if last message was an unfinished sysex */
- int running = 0; /* 1 when running status used */
- int status = 0; /* (possibly running) status byte */
- int needed;
-
- if ( readmt("MTrk") == EOF )
- return(0);
-
- Mf_toberead = read32bit();
- Mf_currtime = 0;
-
- if ( Mf_trackstart )
- (*Mf_trackstart)();
-
- while ( Mf_toberead > 0 ) {
-
- Mf_currtime += readvarinum(); /* delta time */
-
- c = egetc();
-
- if ( sysexcontinue && c != 0xf7 )
- mferror("didn't find expected continuation of a sysex");
-
- if ( (c & 0x80) == 0 ) { /* running status? */
- if ( status == 0 )
- mferror("unexpected running status");
- running = 1;
- }
- else {
- status = c;
- running = 0;
- }
-
- needed = chantype[ (status>>4) & 0xf ];
-
- if ( needed ) { /* ie. is it a channel message? */
-
- if ( running )
- c1 = c;
- else
- c1 = egetc();
- chanmessage( status, c1, (needed>1) ? egetc() : 0 );
- continue;;
- }
-
- switch ( c ) {
-
- case 0xff: /* meta event */
-
- type = egetc();
- lng = readvarinum();
- lookfor = Mf_toberead - lng;
- msginit();
-
- while ( Mf_toberead > lookfor )
- msgadd(egetc());
-
- metaevent(type);
- break;
-
- case 0xf0: /* start of system exclusive */
-
- lng = readvarinum();
- lookfor = Mf_toberead - lng;
- msginit();
- msgadd(0xf0);
-
- while ( Mf_toberead > lookfor )
- msgadd(c=egetc());
-
- if ( c==0xf7 || Mf_nomerge==0 )
- sysex();
- else
- sysexcontinue = 1; /* merge into next msg */
- break;
-
- case 0xf7: /* sysex continuation or arbitrary stuff */
-
- lng = readvarinum();
- lookfor = Mf_toberead - lng;
-
- if ( ! sysexcontinue )
- msginit();
-
- while ( Mf_toberead > lookfor )
- msgadd(c=egetc());
-
- if ( ! sysexcontinue ) {
- if ( Mf_arbitrary )
- (*Mf_arbitrary)(msgleng(),msg());
- }
- else if ( c == 0xf7 ) {
- sysex();
- sysexcontinue = 0;
- }
- break;
- default:
- badbyte(c);
- break;
- }
- }
- if ( Mf_trackend )
- (*Mf_trackend)();
- return(1);
- }
-
- static
- badbyte(c)
- int c;
- {
- char buff[32];
-
- (void) sprintf(buff,"unexpected byte: 0x%02x",c);
- mferror(buff);
- }
-
- static
- metaevent(type)
- {
- int leng = msgleng();
- char *m = msg();
-
- switch ( type ) {
- case 0x00:
- if ( Mf_seqnum )
- (*Mf_seqnum)(to16bit(m[0],m[1]));
- break;
- case 0x01: /* Text event */
- case 0x02: /* Copyright notice */
- case 0x03: /* Sequence/Track name */
- case 0x04: /* Instrument name */
- case 0x05: /* Lyric */
- case 0x06: /* Marker */
- case 0x07: /* Cue point */
- case 0x08:
- case 0x09:
- case 0x0a:
- case 0x0b:
- case 0x0c:
- case 0x0d:
- case 0x0e:
- case 0x0f:
- /* These are all text events */
- if ( Mf_text )
- (*Mf_text)(type,leng,m);
- break;
- case 0x2f: /* End of Track */
- if ( Mf_eot )
- (*Mf_eot)();
- break;
- case 0x51: /* Set tempo */
- if ( Mf_tempo )
- (*Mf_tempo)(to32bit(0,m[0],m[1],m[2]));
- break;
- case 0x54:
- if ( Mf_smpte )
- (*Mf_smpte)(m[0],m[1],m[2],m[3],m[4]);
- break;
- case 0x58:
- if ( Mf_timesig )
- (*Mf_timesig)(m[0],m[1],m[2],m[3]);
- break;
- case 0x59:
- if ( Mf_keysig )
- (*Mf_keysig)(m[0],m[1]);
- break;
- case 0x7f:
- if ( Mf_seqspecific )
- (*Mf_seqspecific)(leng,m);
- break;
- default:
- if ( Mf_metamisc )
- (*Mf_metamisc)(type,leng,m);
- }
- }
-
- static
- sysex()
- {
- if ( Mf_sysex )
- (*Mf_sysex)(msgleng(),msg());
- }
-
- static
- chanmessage(status,c1,c2)
- int status;
- int c1, c2;
- {
- int chan = status & 0xf;
-
- switch ( status & 0xf0 ) {
- case NOTEOFF:
- if ( Mf_noteoff )
- (*Mf_noteoff)(chan,c1,c2);
- break;
- case NOTEON:
- if ( Mf_noteon )
- (*Mf_noteon)(chan,c1,c2);
- break;
- case PRESSURE:
- if ( Mf_pressure )
- (*Mf_pressure)(chan,c1,c2);
- break;
- case PARAMETER:
- if ( Mf_parameter )
- (*Mf_parameter)(chan,c1,c2);
- break;
- case PITCHBEND:
- if ( Mf_pitchbend )
- (*Mf_pitchbend)(chan,c1,c2);
- break;
- case PROGRAM:
- if ( Mf_program )
- (*Mf_program)(chan,c1);
- break;
- case CHANPRESSURE:
- if ( Mf_chanpressure )
- (*Mf_chanpressure)(chan,c1);
- break;
- }
- }
-
- /* readvarinum - read a varying-length number, and return the */
- /* number of characters it took. */
-
- static long
- readvarinum()
- {
- long value;
- int c;
-
- c = egetc();
- value = c;
- if ( c & 0x80 ) {
- value &= 0x7f;
- do {
- c = egetc();
- value = (value << 7) + (c & 0x7f);
- } while (c & 0x80);
- }
- return (value);
- }
-
- static long
- to32bit(c1,c2,c3,c4)
- {
- long value = 0L;
-
- value = (c1 & 0xff);
- value = (value<<8) + (c2 & 0xff);
- value = (value<<8) + (c3 & 0xff);
- value = (value<<8) + (c4 & 0xff);
- return (value);
- }
-
- static
- to16bit(c1,c2)
- int c1, c2;
- {
- return ((c1 & 0xff ) << 8) + (c2 & 0xff);
- }
-
- static long
- read32bit()
- {
- int c1, c2, c3, c4;
-
- c1 = egetc();
- c2 = egetc();
- c3 = egetc();
- c4 = egetc();
- return to32bit(c1,c2,c3,c4);
- }
-
- static
- read16bit()
- {
- int c1, c2;
- c1 = egetc();
- c2 = egetc();
- return to16bit(c1,c2);
- }
-
- static
- mferror(s)
- char *s;
- {
- if ( Mf_error )
- (*Mf_error)(s);
- exit(1);
- }
-
- /* The code below allows collection of a system exclusive message of */
- /* arbitrary length. The Msgbuff is expanded as necessary. The only */
- /* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */
-
- #define MSGINCREMENT 256
- static char *Msgbuff = NULL; /* message buffer */
- static int Msgsize = 0; /* Size of currently allocated Msg */
- static int Msgindex = 0; /* index of next available location in Msg */
-
- static
- msginit()
- {
- Msgindex = 0;
- }
-
- static char *
- msg()
- {
- return(Msgbuff);
- }
-
- static
- msgleng()
- {
- return(Msgindex);
- }
-
- static
- msgadd(c)
- int c;
- {
- /* If necessary, allocate larger message buffer. */
- if ( Msgindex >= Msgsize )
- biggermsg();
- Msgbuff[Msgindex++] = c;
- }
-
- static
- biggermsg()
- {
- char *malloc();
- char *newmess;
- char *oldmess = Msgbuff;
- int oldleng = Msgsize;
-
- Msgsize += MSGINCREMENT;
- newmess = malloc( (unsigned)(sizeof(char)*Msgsize) );
-
- /* copy old message into larger new one */
- if ( oldmess != NULL ) {
- register char *p = newmess;
- register char *q = oldmess;
- register char *endq = &oldmess[oldleng];
-
- for ( ; q!=endq ; p++,q++ )
- *p = *q;
- /* free(oldmess); */
- }
- Msgbuff = newmess;
- }
-