/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2005 Stepan Moskovchenko * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "plugin.h" #include "midiutil.h" extern struct plugin_api * rb; struct Track * readTrack(int file); int readID(int file); struct MIDIfile midi_file IBSS_ATTR; struct MIDIfile * loadFile(char * filename) { struct MIDIfile * mfload; int file = rb->open (filename, O_RDONLY); if(file==-1) { printf("Could not open file"); return NULL; } mfload = &midi_file; rb->memset(mfload, 0, sizeof(struct MIDIfile)); int fileID = readID(file); if(fileID != ID_MTHD) { if(fileID == ID_RIFF) { printf("Detected RMID file"); printf("Looking for MThd header"); char dummy[17]; rb->read(file, &dummy, 16); if(readID(file) != ID_MTHD) { rb->close(file); printf("Invalid MIDI header within RIFF."); return NULL; } } else { rb->close(file); printf("Invalid file header chunk."); return NULL; } } if(readFourBytes(file)!=6) { rb->close(file); printf("Header chunk size invalid."); return NULL; } if(readTwoBytes(file)==2) { rb->close(file); printf("MIDI file type 2 not supported"); return NULL; } mfload->numTracks = readTwoBytes(file); mfload->div = readTwoBytes(file); int track=0; printf("File has %d tracks.", mfload->numTracks); while(! eof(file) && track < mfload->numTracks) { unsigned char id = readID(file); if(id == ID_EOF) { if(mfload->numTracks != track) { printf("Error: file claims to have %d tracks. I only see %d here.", mfload->numTracks, track); mfload->numTracks = track; } return mfload; } if(id == ID_MTRK) { mfload->tracks[track] = readTrack(file); track++; } else { printf("SKIPPING TRACK"); int len = readFourBytes(file); while(--len) readChar(file); } } rb->close(file); return mfload; } /* Global again. Not static. What if track 1 ends on a running status event * and then track 2 starts loading */ int rStatus = 0; /* Returns 0 if done, 1 if keep going */ int readEvent(int file, void * dest) { struct Event dummy; struct Event * ev = (struct Event *) dest; if(ev == NULL) ev = &dummy; /* If we are just counting events instead of loading them */ ev->delta = readVarData(file); int t=readChar(file); if((t&0x80) == 0x80) /* if not a running status event */ { ev->status = t; if(t == 0xFF) { ev->d1 = readChar(file); ev->len = readVarData(file); /* Allocate and read in the data block */ if(dest != NULL) { ev->evData = readData(file, ev->len); /* printf("\nDATA: <%s>", ev->evData); */ } else { /* * Don't allocate anything, just see how much it would take * To make memory usage efficient */ unsigned int a=0; for(a=0; alen; a++) readChar(file); //Skip skip } if(ev->d1 == 0x2F) { return 0; /* Termination meta-event */ } } else /* If part of a running status event */ { rStatus = t; ev->status = t; ev->d1 = readChar(file); if ( ((t & 0xF0) != 0xD0) && ((t & 0xF0) != 0xC0) && ((t & 0xF0) > 0x40) ) { ev->d2 = readChar(file); } else ev->d2 = 127; } } else /* Running Status */ { ev->status = rStatus; ev->d1 = t; if ( ((rStatus & 0xF0) != 0xD0) && ((rStatus & 0xF0) != 0xC0) && ((rStatus & 0xF0) > 0x40) ) { ev->d2 = readChar(file); } else ev->d2 = 127; } return 1; } struct Track * readTrack(int file) { struct Track * trk = (struct Track *)malloc(sizeof(struct Track)); rb->memset(trk, 0, sizeof(struct Track)); trk->size = readFourBytes(file); trk->pos = 0; trk->delta = 0; int numEvents=0; int pos = rb->lseek(file, 0, SEEK_CUR); while(readEvent(file, NULL)) /* Memory saving technique */ numEvents++; /* Attempt to read in events, count how many */ /* THEN allocate memory and read them in */ rb->lseek(file, pos, SEEK_SET); int trackSize = (numEvents+1) * sizeof(struct Event); void * dataPtr = malloc(trackSize); trk->dataBlock = dataPtr; numEvents=0; while(readEvent(file, dataPtr)) { if(trackSize < dataPtr-trk->dataBlock) { printf("Track parser memory out of bounds"); exit(1); } dataPtr+=sizeof(struct Event); numEvents++; } trk->numEvents = numEvents; return trk; } int readID(int file) { char id[5]; id[4]=0; BYTE a; for(a=0; a<4; a++) id[a]=readChar(file); if(eof(file)) { printf("End of file reached."); return ID_EOF; } if(rb->strcmp(id, "MThd")==0) return ID_MTHD; if(rb->strcmp(id, "MTrk")==0) return ID_MTRK; if(rb->strcmp(id, "RIFF")==0) return ID_RIFF; return ID_UNKNOWN; } int readFourBytes(int file) { int data=0; BYTE a=0; for(a=0; a<4; a++) data=(data<<8)+readChar(file); return data; } int readTwoBytes(int file) { int data=(readChar(file)<<8)+readChar(file); return data; } /* This came from the MIDI file format guide */ int readVarData(int file) { unsigned int value; char c; if ( (value = readChar(file)) & 0x80 ) { value &= 0x7F; do { value = (value << 7) + ((c = readChar(file)) & 0x7F); } while (c & 0x80); } return(value); } /* void unloadFile(struct MIDIfile * mf) { if(mf == NULL) return; int a=0; //Unload each track for(a=0; anumTracks; a++) { int b=0; if(mf->tracks[a] != NULL) for(b=0; btracks[a]->numEvents; b++) { if(((struct Event*)((mf->tracks[a]->dataBlock)+b*sizeof(struct Event)))->evData!=NULL) free(((struct Event*)((mf->tracks[a]->dataBlock)+b*sizeof(struct Event)))->evData); } if(mf->tracks[a]!=NULL && mf->tracks[a]->dataBlock != NULL) free(mf->tracks[a]->dataBlock); //Unload the event block if(mf->tracks[a]!=NULL) free(mf->tracks[a]); //Unload the track structure itself } free(mf); //Unload the main struct } */