#include #include #include #include "midifile.h" #define NULLFUNC 0 /* 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 Mf_numbyteswritten = 0L; static long readvarinum(); static long read32bit(); static long to32bit(); static int read16bit(); static int to16bit(); static char *msg(); static int format, ntrks, division; 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); mferror(buff); } } return(c); } static egetc() /* read a single character and abort on EOF */ { int c = (*Mf_getc)(); if ( c == EOF ) mferror("premature EOF"); Mf_toberead--; return(c); } static readheader() /* read a header chunk */ { if ( readmt("MThd") == EOF ) return; 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 > 0L ) (void) egetc(); } 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; 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; /* status value (e.g. 0x90==note-on) */ int needed; if ( readmt("MTrk") == EOF ) return(0); Mf_toberead = read32bit(); Mf_currtime = 0L; if ( Mf_trackstart ) (*Mf_trackstart)(); while ( Mf_toberead > 0L ) { 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(); lookfor = Mf_toberead - readvarinum(); msginit(); while ( Mf_toberead > lookfor ) msgadd(egetc()); metaevent(type); break; case 0xf0: /* start of system exclusive */ lookfor = Mf_toberead - readvarinum(); 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 */ lookfor = Mf_toberead - readvarinum(); 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 0x80: if ( Mf_noteoff ) (*Mf_noteoff)(chan,c1,c2); break; case 0x90: if ( Mf_noteon ) (*Mf_noteon)(chan,c1,c2); break; case 0xa0: if ( Mf_pressure ) (*Mf_pressure)(chan,c1,c2); break; case 0xb0: if ( Mf_parameter ) (*Mf_parameter)(chan,c1,c2); break; case 0xe0: if ( Mf_pitchbend ) (*Mf_pitchbend)(chan,c1,c2); break; case 0xc0: if ( Mf_program ) (*Mf_program)(chan,c1); break; case 0xd0: 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 = (long)c; if ( c & 0x80 ) { value &= 0x7FL; do { c = egetc(); value = (value << 7) + (long)(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); /* freemem(bufsegp); */ 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 = 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 *newmess; char *oldmess = Msgbuff; int oldleng = Msgsize; Msgsize += MSGINCREMENT; newmess = (char *) malloc( (unsigned)(sizeof(char)*Msgsize) ); if(newmess == NULL) mferror("malloc error!"); /* 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; } /* * This routine converts delta times in ticks into milliseconds. */ unsigned long mf_ticks2msec(unsigned long ticks,int division,unsigned long tempo) { return( (unsigned long)( (((float)tempo/(float)1000 )/(float)division) * (float)ticks )); } mfread() /* The only non-static function in this file. */ { int i; if ( Mf_getc == NULLFUNC ) mferror("mfread() called without setting Mf_getc"); readheader(); for(i = 0; i < ntrks; i++) readtrack(); } .