home *** CD-ROM | disk | FTP | other *** search
-
- /*
- * Read a Standard MIDI File. Externally-assigned function pointers are
- * called upon recognizing things in the file. See midifile(3).
- */
-
- #include "midi.h"
- #include "midifile.h"
-
- #define EOF (-1)
- #define NOARGS VOID
- #define VOID void
-
- /* public stuff */
-
- /* Functions to be called while processing the MIDI file. */
- VOID (*Mf_starttrack)(NOARGS) = 0;
- VOID (*Mf_endtrack)(NOARGS) = 0;
- int (*Mf_getc)(NOARGS) = 0;
- VOID (*Mf_eot)(NOARGS) = 0;
-
-
- VOID (*Mf_error)(char *) = 0;
- VOID (*Mf_header)(int,int,int) = 0;
- VOID (*Mf_on)(int,int,int) = 0;
- VOID (*Mf_off)(int,int,int) = 0;
- VOID (*Mf_pressure)(int,int,int) = 0;
- VOID (*Mf_controller)(int,int,int) = 0;
- VOID (*Mf_pitchbend)(int,int,int) = 0;
- VOID (*Mf_program)(int,int) = 0;
- VOID (*Mf_chanpressure)(int,int) = 0;
- VOID (*Mf_sysex)(int,char*) = 0;
- VOID (*Mf_arbitrary)(int,char*) = 0;
- VOID (*Mf_metamisc)(int,int,char*) = 0;
- VOID (*Mf_seqnum)(int) = 0;
- VOID (*Mf_smpte)(int,int,int,int,int) = 0;
- VOID (*Mf_timesig)(int,int,int,int) = 0;
- VOID (*Mf_tempo)(int) = 0;
- VOID (*Mf_keysig)(int,int) = 0;
- VOID (*Mf_sqspecific)(int,char*) = 0;
- VOID (*Mf_text)(int,int,char*) = 0;
-
- int Mf_nomerge = 0; /* 1 => continue'ed system exclusives are */
- /* not collapsed. */
- long Mf_currtime = 0L; /* current time in delta-time units */
- int Mf_skipinit = 0; /* 1 if initial garbage should be skipped */
-
- /* private stuff */
-
- static long Mf_toberead = 0L;
-
- static long readvarinum(NOARGS);
- static long read32bit(NOARGS);
- static int read16bit(NOARGS);
- static VOID msgenlarge(NOARGS);
- static char *msg(NOARGS);
- static int readheader(NOARGS);
- static VOID readtrack(NOARGS);
- static VOID sysex(NOARGS), msginit(NOARGS);
- static int egetc(NOARGS);
- static int msgleng(NOARGS);
-
- static int readmt(char*,int);
- static long to32bit(int,int,int,int);
- static int to16bit(int,int);
- static VOID mferror(char *);
- static VOID badbyte(int);
- static VOID metaevent(int);
- static VOID msgadd(int);
- static VOID chanmessage(int,int,int);
-
-
- /*----- midifile -----*/
-
- VOID midifile() /* The only non-static function in this file. */
- {
- int ntrks;
-
- if ( Mf_getc == 0 )
- mferror("midifile() called without setting Mf_getc");
-
- ntrks = readheader();
- if ( ntrks <= 0 )
- mferror("No tracks!");
- while ( ntrks-- > 0 )
- readtrack();
- }
-
- static int
- readmt(s,skip) /* read through the "MThd" or "MTrk" header string */
- char *s;
- int skip; /* if 1, we attempt to skip initial garbage */
- {
- int nread = 0;
- char b[4];
- char buff[32];
- int c;
- char *errmsg = "expecting ";
- retry:
- while ( nread<4 ) {
- c = (*Mf_getc)();
- if ( c == EOF ) {
- errmsg = "EOF while expecting ";
- goto err;
- }
- b[nread++] = c;
- }
- /* See if we found the 4 characters we're looking for */
- if ( s[0]==b[0] && s[1]==b[1] && s[2]==b[2] && s[3]==b[3] )
- return(0);
- if ( skip ) {
- /* If we are supposed to skip initial garbage, */
- /* try again with the next character. */
- b[0]=b[1];
- b[1]=b[2];
- b[2]=b[3];
- nread = 3;
- goto retry;
- }
- err:
- (VOID) strcpy(buff,errmsg);
- (VOID) strcat(buff,s);
- mferror(buff);
- return(0);
- }
-
- static int
- egetc() /* to read a single character and abort on EOF */
- {
- int c = (*Mf_getc)();
-
- if ( c == EOF )
- error("premature EOF");
- Mf_toberead--;
- return(c);
- }
-
- static int
- readheader() /* read a header chunk */
- {
- int format, ntrks, division;
-
- if ( readmt("MThd",Mf_skipinit) == 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(ntrks);
- }
-
- static VOID
- 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",0) == EOF )
- return;
-
- Mf_toberead = read32bit();
- Mf_currtime = 0;
-
- if ( Mf_starttrack )
- (*Mf_starttrack)();
-
- 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() & 0x7f;
-
- /* The &0x7f here may seem unnecessary, but I've seen */
- /* 'bad' midi files that had, some, volume bytes */
- /* with the upper bit set. This code should not harm */
- /* proper data. */
-
- chanmessage( status, c1, (needed>1) ? (egetc()&0x7f) : 0 );
- continue;
- }
-
- switch ( c ) {
-
- case 0xff: /* meta event */
-
- type = egetc();
- /* watch out - Don't combine the next 2 statements */
- lng = readvarinum();
- lookfor = Mf_toberead - lng;
- msginit();
-
- while ( Mf_toberead > lookfor )
- msgadd(egetc());
-
- metaevent(type);
- break;
-
- case 0xf0: /* start of system exclusive */
-
- /* watch out - Don't combine the next 2 statements */
- 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 */
-
- /* watch out - Don't combine the next 2 statements */
- 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_endtrack )
- (*Mf_endtrack)();
- return;
- }
-
- static VOID
- badbyte(c)
- int c;
- {
- char buff[32];
-
- (VOID) sprintf(buff,"unexpected byte: 0x%02x",c);
- mferror(buff);
- }
-
- static VOID
- 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_sqspecific )
- (*Mf_sqspecific)(leng,m);
- break;
- default:
- if ( Mf_metamisc )
- (*Mf_metamisc)(type,leng,m);
- }
- }
-
- static VOID
- sysex()
- {
- if ( Mf_sysex )
- (*Mf_sysex)(msgleng(),msg());
- }
-
- static VOID chanmessage(status,c1,c2)
- int status;
- int c1, c2;
- {
- int chan = status & 0xf;
-
- switch ( status & 0xf0 ) {
- case NOTEOFF:
- if ( Mf_off )
- (*Mf_off)(chan,c1,c2);
- break;
- case NOTEON:
- if ( Mf_on )
- (*Mf_on)(chan,c1,c2);
- break;
- case PRESSURE:
- if ( Mf_pressure )
- (*Mf_pressure)(chan,c1,c2);
- break;
- case CONTROLLER:
- if ( Mf_controller )
- (*Mf_controller)(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 int
- read16bit()
- {
- int c1, c2;
- c1 = egetc();
- c2 = egetc();
- return to16bit(c1,c2);
- }
-
- static VOID
- mferror(s)
- char *s;
- {
- if ( Mf_error )
- (*Mf_error)(s);
- else
- 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 128
- static char *Msgbuff = 0; /* message buffer */
- static int Msgsize = 0; /* Size of currently allocated Msg */
- static int Msgindex = 0; /* index of next available location in Msg */
-
- static VOID
- msginit()
- {
- Msgindex = 0;
- }
-
- static char *
- msg()
- {
- return(Msgbuff);
- }
-
- static int
- msgleng()
- {
- return(Msgindex);
- }
-
- static VOID
- msgadd(c)
- int c;
- {
- /* if necessary,relocate larger message buffer. */
- if ( Msgindex >= Msgsize )
- msgenlarge();
- Msgbuff[Msgindex++] = c;
- }
-
- static VOID
- msgenlarge()
- {
- char *newmess;
- char *oldmess = Msgbuff;
- int oldleng = Msgsize;
- char *malloc();
-
- Msgsize += MSGINCREMENT;
- newmess = malloc( (unsigned)(sizeof(char)*Msgsize) );
-
- /* copy old message into larger new one */
- if ( oldmess != 0 ) {
- register char *p = newmess;
- register char *q = oldmess;
- register char *endq = &oldmess[oldleng];
-
- for ( ; q!=endq ; p++,q++ )
- *p = *q;
- free(oldmess);
- }
- Msgbuff = newmess;
- }
-
-