/* Modified to read SysEx and text messages > 64K */ /* Super fast extraction by reading the whole MIDI file into memory */ /* before processing */ #define NULLFUNC 0 #include #include #include #include #include #include char *strcpy(), *strcat(); void exit(), free(); extern unsigned int midf; /* public stuff */ /* Functions to be called while processing the MIDI file. */ int (*mf_error)() = NULLFUNC; int (*mf_noteon)() = NULLFUNC; int (*mf_sysex)() = NULLFUNC; int (*mf_arbitrary)() = 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 */ unsigned long mf_toberead = 0L; int lastbyte; static long readvarinum(); static long read32bit(); static long to32bit(); static int read16bit(); static int to16bit(); unsigned char huge *mid_buff = NULL; unsigned char huge *buff_ptr = NULL; unsigned char huge *end_of_mf = NULL; unsigned char far *msg_start = NULL; unsigned long msg_len = 0L; unsigned int bufsegp; unsigned long mf_size; mfread() { unsigned int max_para; mf_size = filelength(midf); if(allocmem((unsigned)((mf_size + 15) >> 4), &bufsegp) != -1) (*mf_error)("not enough memory to load MIDI file"); mid_buff = (unsigned char huge*) MK_FP(bufsegp, 0); mid_read(mid_buff, mf_size); /* Read the whole MIDI file */ buff_ptr = mid_buff; /* Buffer position pointer */ end_of_mf = mid_buff + mf_size; /* End of file pointer */ readheader(); while (readtrack()); freemem(bufsegp); } 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 = virt_getc()) != EOF ) { if ( c != *p++ ) { char buff[32]; (void) strcpy(buff,"expecting "); (void) strcat(buff,s); (*mf_error)(buff); } } return(c); } static egetc() /* read a single character and abort on EOF */ { int c = virt_getc(); if ( c == EOF ) (*mf_error)("premature EOF"); mf_toberead--; return(c); } static readheader() /* read a header chunk */ { int format, ntrks, division; if ( readmt("MThd") == EOF ) return; mf_toberead = read32bit(); format = read16bit(); ntrks = read16bit(); division = read16bit(); /* flush any extra stuff, in case the length of header is not 6 */ while ( mf_toberead > 0 ) (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 */ }; unsigned 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; while ( mf_toberead > 0L ) { mf_currtime += readvarinum(); /* delta time */ c = egetc(); if ( sysexcontinue && c != 0xf7 ) (*mf_error)("didn't find expected continuation of a sysex"); if ( (c & 0x80) == 0 ) { /* running status? */ if ( status == 0 ) (*mf_error)("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 = readvarinum(); msg_start = (unsigned char far*) buff_ptr; buff_ptr += lookfor; mf_toberead -= lookfor; msg_len = lookfor; metaevent(type); break; case 0xf0: /* start of system exclusive */ lookfor = readvarinum(); msg_start = (unsigned char far*) buff_ptr; buff_ptr += (lookfor - 1L); lastbyte = virt_getc(); mf_toberead -= lookfor; msg_len = lookfor; if ( lastbyte == 0xf7 || mf_nomerge == 0 ) (*mf_sysex)(msg_len, msg_start); else sysexcontinue = 1; /* merge into next msg */ break; case 0xf7: /* sysex continuation or arbitrary stuff */ lookfor = readvarinum(); if ( ! sysexcontinue ) { msg_len = 0L; msg_start = (unsigned char far*) buff_ptr; } buff_ptr += (lookfor - 1L); lastbyte = virt_getc(); mf_toberead -= lookfor; msg_len += lookfor; if ( ! sysexcontinue ) { if ( mf_arbitrary ) (*mf_arbitrary)(msg_len, msg_start); } else if ( lastbyte == 0xf7 ) { (*mf_sysex)(msg_len, msg_start); sysexcontinue = 0; } break; default: badbyte(c); break; } } return(1); } static badbyte(c) int c; { char buff[32]; (void) sprintf(buff,"unexpected byte: 0x%02x",c); (*mf_error)(buff); } static metaevent(type) { switch ( type ) { 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: (*mf_text)(type, msg_len, msg_start); } } chanmessage(int status, int c1, int c2) { int chan = status & 0xf; switch ( status & 0xf0 ) { case 0x90: (*mf_noteon)(chan,c1,c2); } } /* 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); } mid_read(unsigned char huge *buf, unsigned long len) { union REGS regs; struct SREGS segregs; unsigned int bytes_to_read; segregs.ds = FP_SEG(buf); while(1) { regs.h.ah = 0x3f ; regs.x.bx = midf; regs.x.dx = 0; if(len >= 0x8000) bytes_to_read = 0x8000; else bytes_to_read = (unsigned int)len; regs.x.cx = bytes_to_read; intdosx(®s, ®s, &segregs); if(bytes_to_read < 0x8000) break; len -= 0x8000L; if(len == 0L) break; segregs.ds += 0x0800; } } virt_getc() { if(buff_ptr >= end_of_mf) return EOF; return (int) (*buff_ptr++); } .