//read data from a single track chunk private static void loadTrackData(MidiInStream stream, Sequence seq, int trackNum) { //read track header String trackSig = stream.getString(4); uint trackDataLength = stream.getFour(); if (!trackSig.Equals("MTrk")) { throw new MidiFileException(stream.filename + " has an invalid track at ", stream.getDataPos() - 8); } Track track = seq.addTrack(); //get new track from sequence track.setName("Track " + trackNum.ToString()); int currentTime = 0; //event time in ticks runningStatus = 0; sysexCont = false; prevSysEx = null; Event evt = null; int startpos = stream.getDataPos(); while ((stream.getDataPos() - startpos) < trackDataLength) { currentTime += (int)stream.getVariableLengthVal(); //add delta time to current num of ticks evt = loadEventData(stream); if ((evt != null) && !(evt is EndofTrackEvent || evt is MarkerEvent || evt is CuePointEvent || //these are ignored in tracks other than track 0 evt is TempoEvent || evt is SMPTEOffsetEvent || evt is TimeSignatureEvent || evt is KeySignatureEvent)) { track.addEvent(evt, currentTime); } } //last event in track must be an "end of track" event if ((evt != null) && !(evt is EndofTrackEvent)) { throw new MidiFileException(stream.filename + ": track " + curTrackNum.ToString() + "missing end of track event at ", stream.getDataPos() - 8); } }
//build the tempo, meter and marker maps from tempo message from track 0 private static void loadTrackZeroData(MidiInStream stream, Sequence seq) { //read track header String trackSig = stream.getString(4); uint trackDataLength = stream.getFour(); if (!trackSig.Equals("MTrk")) { throw new MidiFileException(stream.filename + " has an invalid track 0 at ", stream.getDataPos() - 8); } int currentTime = 0; //event time in ticks runningStatus = 0; sysexCont = false; prevSysEx = null; Event evt; Meter prevMeter = null; int startpos = stream.getDataPos(); while ((stream.getDataPos() - startpos) < trackDataLength) { currentTime += (int)stream.getVariableLengthVal(); //add delta time to current num of ticks evt = loadEventData(stream); if (evt is TempoEvent) { Tempo tempo = new Tempo(currentTime, ((TempoEvent)evt).tempo); seq.tempoMap.addTempo(tempo); } else if (evt is SMPTEOffsetEvent) //not handling smpte timing yet { } else if (evt is TimeSignatureEvent) { int keysig = (prevMeter != null) ? prevMeter.keysig : 0; Meter meter = new Meter(currentTime, ((TimeSignatureEvent)evt).numer, ((TimeSignatureEvent)evt).denom, keysig); seq.meterMap.addMeter(meter); prevMeter = meter; } else if (evt is KeySignatureEvent) { int numer = 4; int denom = 4; if (prevMeter != null) { numer = prevMeter.numer; denom = prevMeter.denom; } Meter meter = new Meter(currentTime, numer, denom, ((KeySignatureEvent)evt).keySig); seq.meterMap.addMeter(meter); prevMeter = meter; } else if (evt is MarkerEvent) { } else if (evt is CuePointEvent) { } } }
//read data for a single event in a track's data, convert the event's delta time to an absolute track time private static Event loadEventData(MidiInStream stream) { Event evt = null; int status = stream.getOne(); if (status < 0x80) //running status { stream.pushBack(1); status = runningStatus; } if (status >= 0x80 && status < 0xff) //message event { Message msg = loadMessageData(stream, status); runningStatus = status; evt = new MessageEvent(msg); } else if (status == 0xff) //meta event { evt = loadMetaEventData(stream); //this may return null for unrecognized events } else { throw new MidiFileException(stream.filename + " has an invalid event at", stream.getDataPos() - 8); } return(evt); }