// This code written mainly from information provided at // http://www.borg.com/~jglatt/tech/midifile/table.htm // The program has accreted by exploring a few MIDI files. // if(zz) {It reports details as it reads files.} // I expect it to accrete more mods. // In text output the construction "at 0X..." gives a hexadecimal location // within the input file where something is located. #include #include #define zz 0 #include "h.h" note * freenote = 0; static int cc = 0; static uchar h[16]; static FILE * in; void ex(int d){printf("Bye %d, at 0X%X\n", d, cc); {int j=16; while(j--) printf("%02X ", h[(cc-1-j)&15]); // report the last 16 characters that were read. printf("\n"); j=16; while(j--) printf("%02X ", getc(in));} // and upcomming 16 too. printf("\n"); exit(4);} static uchar nc(){char x = getc(in); if(x == EOF && feof(in)) {printf("Unexpected end of file at 0X%X\n", cc); ex(6);} h[cc++&15] = x; return x;} static ui gvl(){ui x = 0; uchar c; do {c = nc(); x = (x<<7) | (c & 0x7f);} while(c & 0x80); return x;} static void expect(char * s){while (*s) if(*(s++) != nc()) ex(1);} static void * mal(int k, int p){void * y = malloc(k); if(y) return y; printf("Memory allocation failure; wanted %d bytes at %d\n", k, p); ex(65); return 0;} static ui gn(int l) {ui k = 0; while(l--) k = (k<<8) | nc(); return k;} static note* nf(){while(1){note * n = freenote; if(n) {freenote = n->next; return n;} {note* ar = mal (2000*sizeof(note), 1); {int j = 2000; while(j--) {ar[j].next = freenote; freenote = ar+j;}}}}} note* Read(char * fn, BP* bp){note * music = 0, * playing = 0; in = *fn?fopen(fn, "r"):stdin; expect("MThd"); if(gn(4) != 6) ex(3); // Needs a MThd . {int format = gn(2), tc = gn(2); bp->trks = tc; bp->den = gn(2); bp->type = format; if(0) printf("%d tracks, %d ticks/quarternote\n", tc, bp->den); if(!(format == 1 || (format == 0 && tc == 1))) ex(2); while(tc--) { int Time=0; // computed by accumulating timing bytes in track. expect("MTrk"); {int ts = gn(4), hstrt = cc; uchar cst=-1, cchan=0x80; void ex2(int j){printf("cst=%d, qn = %10.4f, ", cst, Time/(float)bp->den); ex(10000+j);} if(zz) printf("Beginning track %d with %d bytes at 0X%X\n", bp->trks-tc, ts, cc); while(1){Time += gvl(); zx: { // We return to zx when we have seen a status byte // which is sort of a message prefix. uchar sc = nc(); if(sc & 0x80) { if(sc == 0xFF) {uchar sbtp = nc(); void Opaque(ui rq){ note * nn = nf(); nn->start = Time; if(0) nn->chan = (sc&15)+1; // perhaps someone depends on this bogus value. nn->trk = bp->trks-tc; nn->next = music; music = nn; nn->dur = OpaqueStuff; //nn->o.tempo = gn(3); {ui size = gvl(); uchar * stuff = (uchar*)mal(size+1, 2); if(size != rq && rq) ex2(11); {int j; for(j=0; j< size; ++j) stuff[j] = nc(); stuff[j] = 0; nn->o.opaque.odp = stuff; nn->o.opaque.cc = size; nn->o.opaque.subtype = sbtp;}}} if(0) printf("0x%02x at 0X%X -> ", sbtp, cc); switch(sbtp){ case 1: // Text case 2: // Copyright case 3: // Track Name case 4: // Instrument name case 7: // Cue Point Opaque(0); break; case 33: Opaque(1); break; // MIDI Port? case 47: if(zz) printf("End of Track\n\n"); if(gvl()) ex2(13); if(cc != hstrt+ts) ex2(14); goto endTrk; case 81: Opaque(3); break; // Tempo case 84: Opaque(5); break; // SMPTE Offset case 88: Opaque(4); break; // Time Signature case 89: Opaque(2); break; // Key Signature default: ex2(9);}} else {cst = sc>>4; // if(cst == 11) ex2(53); {note* gen(int code){ note * nn = nf(); cst=-1; nn->start = Time; nn->chan = (sc&15)+1; nn->trk = bp->trks-tc; nn->next = music; music = nn; nn->dur = code; cst=-1; return nn;} switch(cst){ case 9: case 8: cchan = sc&15; goto zx; case 11: {int cn = nc(), v=nc(); printf("Channel %d controller %d to %d", sc&15, cn, v); if(cn) printf(" Unknown effet\n"); else printf(": Bank Select to %d\n", v);} break; case 12: // printf("Channel %d changes to program %d\n", 1+(sc&15), nc()); {note * nn = gen(ProgramChange); nn->o.prog = nc();} break; case 14: //printf("Channel %d reports pitch wheel at %04X\n", sc&15, nc() | (nc()<<7)); {note * nn = gen(PitchWheel); nn->o.pitch = nc() | (nc()<<7);} break; // See // for meanings. default: printf("Status byte = %02X!\n", sc); ex2(17); }}}} else {// channel message char sb = nc(); // get second byte: message is sc:sb if(cchan & 0x80) { if(sc == 10 && sb == 0x40 && Time == 0) { printf("Ignoring mysterious 0x0A40 in track %d at 0x%x\n", bp->trks-tc, cc); continue;} ex2(21);} // Note but no running status. if(!(cst == 9 || cst == 8 || cst == 10)) ex2(39); if(sb && cst==9){note * nn = nf(); nn->start = Time; nn->o.n.freq = sc; nn->o.n.loud = sb; nn->chan = cchan+1; nn-> trk = bp->trks-tc; nn->next = playing; playing = nn;} else if(cst == 8 || !sb) { note * sch = playing; note * * tp = &playing; while(sch){ if(sc == sch->o.n.freq && cchan+1 == sch->chan){ int dur = Time - sch->start; if (dur > maxdur) ex2(97); sch->dur = dur; sch->o.n.quit = cst==8?sb:64; *tp = sch-> next; sch->next = music; music = sch; break;} else{tp = &sch->next; sch = sch->next;}} }}}} endTrk: ;}} if(getc(in) != EOF || !feof(in)) ex(99); if(0) {note * m = music; while(m) {printf("note %d from %d for %d, c=%d, t=%d\n", m->o.n.freq, m->start, m->dur, m->chan, m->trk); m = m-> next;}} if(playing) {printf("Stuck notes\n"); ex(57);} fclose(in); return music;}} void Free(note ** m) {while(*m){note* n = (*m)->next; (*m)->next = freenote; freenote = *m; *m=n;}} void aug(note n, note** m){note * nn = nf(); n.next = *m; *nn = n; *m = nn;} void scan(seeNote feed, note* mus, int consume){ while(mus) {note nte = *mus; note * nx = nte.next; nte.next = 0; feed(nte); if(consume){mus->next = freenote; freenote = mus;} mus=nx;}}