/* MIDI to Pov-Ray code ©Chris Johnson 2003 chris@chris-j.co.uk This program reads MIDI file data and outputs a file containing calls to Pov-Ray macros which control animations. The program is not a general-purpose animation system: it outputs macros specifically designed for my xylophone animation, though it should be easy to modify this for other animations. The whole MIDI reading section is a straight copy and paste from another of my programs, so the code is slightly odd - the NoteArray class can be trimmed a bit for this program. */ #include #include #include #include #include class Note { public: unsigned char pitch,vel; unsigned long time; unsigned char type; // 1 is note on, 2 is note off unsigned char track; }; class NoteArray { public: NoteArray(unsigned long n); NoteArray(NoteArray *n); ~NoteArray(); bool AddNote(Note *n); bool AddNotes(NoteArray *na); bool AddNotes(NoteArray *na, double timeOffset); bool OutputMidi(char *filename); bool InputMidi(char *filename); void Sort(); void Clear(); Note *data; unsigned long noteMax, noteInd; protected: unsigned char NoteArray::PutTime(unsigned long dt, FILE *out); void NoteArray::SaveNote(Note note, FILE *out); bool ReadEvent(FILE *in, unsigned long *lasttime, unsigned char trk); int oldTime; unsigned long mitime; unsigned long bytes; unsigned char lastid; char buffer[255]; }; NoteArray::NoteArray(unsigned long n) { noteMax=n; data=0; data=new Note[noteMax]; oldTime=bytes=noteInd=mitime=0; } NoteArray::NoteArray(NoteArray *n) { noteMax=n->noteMax; data=new Note[noteMax]; AddNotes(n); oldTime=bytes=noteInd=0; } NoteArray::~NoteArray() { if (data) delete [] data; } bool NoteArray::AddNote(Note *n) { if (noteInd==noteMax) return true; data[noteInd].pitch=n->pitch; data[noteInd].time=n->time; data[noteInd].vel=n->vel; data[noteInd].type=n->type; data[noteInd].track=n->track; noteInd++; return false; } bool NoteArray::AddNotes(NoteArray *na) { for (unsigned long i=0; inoteMax; i++) if (AddNote(&na->data[i])) return true; // &(na->data[i]) return false; } bool NoteArray::AddNotes(NoteArray *na, double timeOffset) { for (unsigned long i=0; inoteInd; i++) { if (AddNote(&na->data[i])) return true; // &(na->data[i]) data[noteInd-1].time+=timeOffset; } return false; } void NoteArray::Clear() { noteInd=0; } int Comp(const void *arg1, const void *arg2) // n.b. not class member { Note *note1, *note2; note1=(Note *)arg1; note2=(Note *)arg2; if (note1->time > note2->time) return 1; if (note1->time < note2->time) return -1; return 0; } void NoteArray::Sort() { qsort((void *)data,(size_t)noteInd,sizeof(Note),Comp); } bool NoteArray::ReadEvent(FILE *in, unsigned long *lasttime,unsigned char trk) { Note note; unsigned char t1=255,t2=255,t3=255,t4=255,id,pitch,vel,c; unsigned long time; if ((t1=fgetc(in))&0x80) { t1&=0x7F; if ((t2=fgetc(in))&0x80) { t2&=0x7F; if ((t3=fgetc(in))&0x80) { t3&=0x7F; t4=fgetc(in); time=(t1<<21)+(t2<<14)+(t3<<7)+t4; } else time=(t1<<14)+(t2<<7)+t3; printf("%d-%d-%d %d\n",t1,t2,t3,time); } else time=(t1<<7)+t2; } else time=t1; //printf("%02X-%02X-%02X-%02X-time=%d\n",t1,t2,t3,t4,time); getch(); *lasttime+=time; // increment delta-time for any message id=fgetc(in); if (id<0x80) // must be running status { ungetc(id,in); // unget the data we just read id=lastid; } else lastid=id; switch ((id&0xF0)>>4) { case 0xA: case 0xB: case 0xE: // two-byte messages fgetc(in); fgetc(in); break; case 0xC: case 0xD: // one-byte messages fgetc(in); break; case 0xF: // system messages switch (id&0xF) { case 0xF: // covers 0xFF messages if (fgetc(in)==0x2F) // if end of track { fgetc(in); return 1; } else // else ignore message { c=fgetc(in); if (c>127) {printf("Error - >1 byte data in FF event.\n"); getch();} fseek(in,c,SEEK_CUR); } break; default: // covers 0xF# messages (global sync etc.) break; } break; case 0x8: // Note Off pitch=fgetc(in); vel=fgetc(in); note.pitch=pitch; note.vel=vel; note.time=*lasttime; note.type=0; note.track=trk; AddNote(¬e); //printf("Added note off: pitch %d vel %d time %d\n",pitch,vel,*lasttime); break; case 0x9: // Note On pitch=fgetc(in); vel=fgetc(in); note.pitch=pitch; note.vel=vel; note.time=*lasttime; note.type=1; note.track=trk; AddNote(¬e); //printf("Added note on: pitch %d vel %d time %d\n",pitch,vel,*lasttime); break; default: printf("MIDI In error - unrecognised message - %02X at %d\n",id,ftell(in)); getch(); break; } // printf("\n"); return 0; } bool NoteArray::InputMidi(char *filename) { FILE *in=fopen(filename,"rb"); unsigned long dw; unsigned char trk=0; // trk 0 is 'conductor' track if (!in) {printf("Can't open %s for input\n",filename); return 1;} fgets(buffer,5,in); if (strcmp(buffer,"MThd")) {printf("Invalid MThd - found \'%s\'\n",buffer); fclose(in); return true;} dw=(fgetc(in)<<24)+(fgetc(in)<<16)+(fgetc(in)<<8)+fgetc(in); if (fseek(in,dw,SEEK_CUR)) {printf("Failed to seek past header\n"); fclose(in); return true;} while (fgets(buffer,5,in)) { if (strcmp(buffer,"MTrk")) {printf("Invalid MTrk - found \'%s\'\n",buffer); fclose(in); return true;} dw=(fgetc(in)<<24)+(fgetc(in)<<16)+(fgetc(in)<<8)+fgetc(in); mitime=0; // reset absolute time for each track while (!ReadEvent(in, &mitime,trk)); // read events + add to array until track end trk++; } fclose(in); return 0; } unsigned long GetTickVal(int br, int bt, int tk) { return (br*4+bt)*15360+tk*640; // (24 ticks per crotchet beat) } int main(int argc, char *argv[]) { int n; FILE *out; if (argc<2) {printf("Usage: midipov \n"); return 1;} NoteArray *nArray = new NoteArray(10000); if (nArray->InputMidi(argv[1])) return 1; strcpy(argv[1]+strlen(argv[1])-3,"pov"); out=fopen(argv[1],"wt"); float time,pitch,ext,vel,lasttime,lastpitch,lastext; int iPitch; for (n=0; nnoteInd; n++) { if (nArray->data[n].vel) { time=(float)nArray->data[n].time/15360.0; vel=(float)nArray->data[n].vel/127.0; iPitch=nArray->data[n].pitch-48; if (iPitch%12!=1 && iPitch%12!=3 && iPitch%12!=6 && iPitch%12!=8 && iPitch%12!=10) { pitch=1.1*floor((7.0/12.0)*(float)iPitch+.5); ext=1; // white note } else { pitch=1.1*(int)((7.0/12.0)*(float)iPitch)+.55; ext=0; // black note } fprintf(out,"Move(hand%dpos,%f,%f,%f,%f)\n",nArray->data[n].track,lasttime,time,lastpitch,pitch); fprintf(out,"Move(hand%dext,%f,%f,%f,%f)\n",nArray->data[n].track,lasttime,time,lastext,ext); fprintf(out,"Hit(hand%dang,%f,%f)\n",nArray->data[n].track,time,vel); lastpitch=pitch; lasttime=time; lastext=ext; } } fclose(out); delete nArray; return 0; }