//- static methods ------------------------------------------------------------ public static Message getMessage(byte[] data) { Message msg = null; int status = data[0]; if (status < 0xF0) //midi channel message { int msgtype = status / 16; int channel = status % 16; int b1 = data[1]; int b2 = 0; if ((msgtype != 0xC) && (msgtype != 0xD)) { b2 = data[2]; } msg = Message.getChannelMessage(msgtype, channel, b1, b2); } else if (status == 0xF0) //sys ex msg { List <byte> bytes = new List <byte>(data); msg = new SysExMessage(bytes); } else { //status msg int b1 = 0; int b2 = 0; int datalen = SystemMessage.SysMsgLen[status - 0xF0] - 1; if (datalen > 0) { b1 = data[1]; } if (datalen > 1) { b2 = data[2]; b1 = ((b1 % 128) * 128) + (b2 % 128); } msg = new SystemMessage(status, b1); } return(msg); }
//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 midi message (80 - ff), handle sysex continuation and escape sequences private static Message loadMessageData(MidiInStream stream, int status) { Message msg = null; if (status < 0xF0) //midi channel message { int msgtype = status / 16; int channel = status % 16; int b1 = stream.getOne(); int b2 = 0; if ((msgtype != 0xC) && (msgtype != 0xD)) { b2 = stream.getOne(); } msg = Message.getChannelMessage(msgtype, channel, b1, b2); } else if (status == 0xF0) //sysex message { int len = stream.getOne(); List <byte> sysExData = stream.getRange(len); sysexCont = (sysExData[sysExData.Count - 1] != 0xf7); //is the last byte of sysex data a F7? msg = new SysExMessage(sysExData); prevSysEx = (SysExMessage)msg; runningStatus = 0; //sysex msg cancel running status } else if (status == 0xF7) { if (sysexCont) //sysex continuation - append this data to prev sysex message { int len = stream.getOne(); List <byte> contData = stream.getRange(len); sysexCont = (contData[contData.Count - 1] != 0xf7); //is the last byte of sysex data a F7? prevSysEx.sysExData.AddRange(contData); } else { //escape sequence int len = stream.getOne(); List <byte> escData = stream.getRange(len); msg = new EscapeMessage(escData); } runningStatus = 0; } else { //system common msgs shouldn't occur here, but if they do, we need to skip them int b1 = 0; int b2 = 0; int datalen = SystemMessage.SysMsgLen[status - 0xF0] - 1; if (datalen > 0) { b1 = stream.getOne(); } if (datalen > 1) { b2 = stream.getOne(); b1 = ((b1 % 128) * 128) + (b2 % 128); } msg = new SystemMessage(status, b1); runningStatus = 0; } return(msg); }