123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 |
- /*
- * Read a Standard MIDI File. Externally-assigned function pointers are
- * called upon recognizing things in the file. See midifile(3).
- */
- /*****************************************************************************
- * Change Log
- * Date | who : Change
- *-----------+-----------------------------------------------------------------
- * 2-Mar-92 | GWL : created changelog; MIDIFILE_ERROR to satisfy compiler
- *****************************************************************************/
- #include "stdio.h"
- #include "mfmidi.h"
- #include "string.h"
- #include "assert.h"
- #define MIDIFILE_ERROR -1
- /* public stuff */
- extern int abort_flag;
- void Midifile_reader::midifile()
- {
- int ntrks;
- midifile_error = 0;
- ntrks = readheader();
- if (midifile_error) return;
- if (ntrks <= 0) {
- mferror("No tracks!");
- /* no need to return since midifile_error is set */
- }
- while (ntrks-- > 0 && !midifile_error) readtrack();
- }
- int Midifile_reader::readmt(const char *s, int skip)
- /* read through the "MThd" or "MTrk" header string */
- /* if skip == 1, we attempt to skip initial garbage. */
- {
- assert(strlen(s) == 4); // must be "MThd" or "MTrk"
- int nread = 0;
- char b[4];
- char buff[32];
- int c;
- const 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:
- //#pragma warning(disable: 4996) // strcpy is safe since strings have known lengths
- (void) strcpy(buff,errmsg);
- (void) strcat(buff,s);
- //#pragma warning(default: 4996) // turn it back on
- mferror(buff);
- return(0);
- }
- int Midifile_reader::egetc()
- /* read a single character and abort on EOF */
- {
- int c = Mf_getc();
- if ( c == EOF ) {
- mferror("premature EOF");
- return EOF;
- }
- Mf_toberead--;
- return(c);
- }
- int Midifile_reader::readheader()
- /* read a header chunk */
- {
- int format, ntrks, division;
- if ( readmt("MThd",Mf_skipinit) == EOF )
- return(0);
- Mf_toberead = read32bit();
- if (midifile_error) return MIDIFILE_ERROR;
- format = read16bit();
- if (midifile_error) return MIDIFILE_ERROR;
- ntrks = read16bit();
- if (midifile_error) return MIDIFILE_ERROR;
- division = read16bit();
- if (midifile_error) return MIDIFILE_ERROR;
- Mf_header(format,ntrks,division);
- /* flush any extra stuff, in case the length of header is not 6 */
- while ( Mf_toberead > 0 && !midifile_error)
- (void) egetc();
- return(ntrks);
- }
- void Midifile_reader::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();
- if (midifile_error) return;
- Mf_currtime = 0L;
- Mf_starttrack();
- while ( Mf_toberead > 0 ) {
- Mf_currtime += readvarinum(); /* delta time */
- if (midifile_error) return;
- c = egetc();
- if (midifile_error) return;
- if ( sysexcontinue && c != 0xf7 ) {
- mferror("didn't find expected continuation of a sysex");
- return;
- }
- if ( (c & 0x80) == 0 ) { /* running status? */
- if ( status == 0 ) {
- mferror("unexpected running status");
- return;
- }
- 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();
- if (midifile_error) return;
- }
- chanmessage( status, c1, (needed>1) ? egetc() : 0 );
- if (midifile_error) return;
- continue;;
- }
- switch ( c ) {
- case 0xff: /* meta event */
- type = egetc();
- if (midifile_error) return;
- /* watch out - Don't combine the next 2 statements */
- lng = readvarinum();
- if (midifile_error) return;
- lookfor = Mf_toberead - lng;
- msginit();
- while ( Mf_toberead > lookfor ) {
- unsigned char c = egetc();
- if (midifile_error) return;
- msgadd(c);
- }
- metaevent(type);
- break;
- case 0xf0: /* start of system exclusive */
- /* watch out - Don't combine the next 2 statements */
- lng = readvarinum();
- if (midifile_error) return;
- lookfor = Mf_toberead - lng;
- msginit();
- msgadd(0xf0);
- while ( Mf_toberead > lookfor ) {
- c = egetc();
- if (midifile_error) return;
- msgadd(c);
- }
- 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();
- if (midifile_error) return;
- lookfor = Mf_toberead - lng;
- if ( ! sysexcontinue )
- msginit();
- while ( Mf_toberead > lookfor ) {
- c = egetc();
- if (midifile_error) return;
- msgadd(c);
- }
- if ( ! sysexcontinue ) {
- Mf_arbitrary(msgleng(), msg());
- }
- else if ( c == 0xf7 ) {
- sysex();
- sysexcontinue = 0;
- }
- break;
- default:
- badbyte(c);
- break;
- }
- }
- Mf_endtrack();
- return;
- }
- void Midifile_reader::badbyte(int c)
- {
- char buff[32];
- //#pragma warning(disable: 4996) // safe in this case
- (void) sprintf(buff,"unexpected byte: 0x%02x",c);
- //#pragma warning(default: 4996)
- mferror(buff);
- }
- void Midifile_reader::metaevent(int type)
- {
- int leng = msgleng();
- // made this unsigned to avoid sign extend
- unsigned char *m = msg();
- switch ( type ) {
- case 0x00:
- 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 */
- Mf_text(type,leng,m);
- break;
- case 0x20:
- Mf_chanprefix(m[0]);
- break;
- case 0x21:
- Mf_portprefix(m[0]);
- break;
- case 0x2f: /* End of Track */
- Mf_eot();
- break;
- case 0x51: /* Set tempo */
- Mf_tempo(to32bit(0,m[0],m[1],m[2]));
- break;
- case 0x54:
- Mf_smpte(m[0],m[1],m[2],m[3],m[4]);
- break;
- case 0x58:
- Mf_timesig(m[0],m[1],m[2],m[3]);
- break;
- case 0x59:
- Mf_keysig(m[0],m[1]);
- break;
- case 0x7f:
- Mf_sqspecific(leng,m);
- break;
- default:
- Mf_metamisc(type,leng,m);
- }
- }
- void Midifile_reader::sysex()
- {
- Mf_sysex(msgleng(), msg());
- }
- void Midifile_reader::chanmessage(int status, int c1, int c2)
- {
- int chan = status & 0xf;
- switch ( status & 0xf0 ) {
- case NOTEOFF:
- Mf_off(chan,c1,c2);
- break;
- case NOTEON:
- Mf_on(chan,c1,c2);
- break;
- case PRESSURE:
- Mf_pressure(chan,c1,c2);
- break;
- case CONTROLLER:
- Mf_controller(chan,c1,c2);
- break;
- case PITCHBEND:
- Mf_pitchbend(chan,c1,c2);
- break;
- case PROGRAM:
- Mf_program(chan,c1);
- break;
- case CHANPRESSURE:
- Mf_chanpressure(chan,c1);
- break;
- }
- }
- /* readvarinum - read a varying-length number, and return the */
- /* number of characters it took. */
- long Midifile_reader::readvarinum()
- {
- long value;
- int c;
- c = egetc();
- if (midifile_error) return 0;
- value = (long) c;
- if ( c & 0x80 ) {
- value &= 0x7f;
- do {
- c = egetc();
- if (midifile_error) return 0;
- value = (value << 7) + (c & 0x7f);
- } while (c & 0x80);
- }
- return (value);
- }
- long Midifile_reader::to32bit(int c1, int c2, int c3, int c4)
- {
- long value = 0L;
- value = (c1 & 0xff);
- value = (value<<8) + (c2 & 0xff);
- value = (value<<8) + (c3 & 0xff);
- value = (value<<8) + (c4 & 0xff);
- return (value);
- }
- int Midifile_reader::to16bit(int c1, int c2)
- {
- return ((c1 & 0xff ) << 8) + (c2 & 0xff);
- }
- long Midifile_reader::read32bit()
- {
- int c1, c2, c3, c4;
- c1 = egetc(); if (midifile_error) return 0;
- c2 = egetc(); if (midifile_error) return 0;
- c3 = egetc(); if (midifile_error) return 0;
- c4 = egetc(); if (midifile_error) return 0;
- return to32bit(c1,c2,c3,c4);
- }
- int Midifile_reader::read16bit()
- {
- int c1, c2;
- c1 = egetc(); if (midifile_error) return 0;
- c2 = egetc(); if (midifile_error) return 0;
- return to16bit(c1,c2);
- }
- void Midifile_reader::mferror(char *s)
- {
- Mf_error(s);
- midifile_error = 1;
- }
- void Midifile_reader::mferror(const char *s)
- {
- mferror(const_cast<char *>(s));
- }
- /* 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
- Midifile_reader::Midifile_reader()
- {
- Mf_nomerge = 0;
- Mf_currtime = 0L;
- Mf_skipinit = 0;
- Mf_toberead = 0;
- Msgbuff = 0; /* message buffer */
- Msgsize = 0; /* Size of currently allocated Msg */
- Msgindex = 0; /* index of next available location in Msg */
- }
- void Midifile_reader::finalize()
- {
- if (Msgbuff) Mf_free(Msgbuff, Msgsize);
- Msgbuff = nullptr;
- }
- void Midifile_reader::msginit()
- {
- Msgindex = 0;
- }
- unsigned char *Midifile_reader::msg()
- {
- return(Msgbuff);
- }
- int Midifile_reader::msgleng()
- {
- return(Msgindex);
- }
- void Midifile_reader::msgadd(int c)
- {
- /* If necessary, allocate larger message buffer. */
- if ( Msgindex >= Msgsize )
- msgenlarge();
- Msgbuff[Msgindex++] = c;
- }
- void Midifile_reader::msgenlarge()
- {
- unsigned char *newmess;
- unsigned char *oldmess = Msgbuff;
- int oldleng = Msgsize;
- Msgsize += MSGINCREMENT;
- newmess = (unsigned char *) Mf_malloc((sizeof(unsigned char) * Msgsize) );
- /* copy old message into larger new one */
- if ( oldmess != 0 ) {
- memcpy(newmess, oldmess, oldleng);
- Mf_free(oldmess, oldleng);
- }
- Msgbuff = newmess;
- }
|