/** Parse a single Midi track into a list of MidiEvents. * Entering this function, the file offset should be at the start of * the MTrk header. Upon exiting, the file offset should be at the * start of the next MTrk header. */ private List<MidiEvent> ReadTrack(MidiFileReader file) { List<MidiEvent> result = new List<MidiEvent>(20); int starttime = 0; string id = file.ReadAscii(4); if (id != "MTrk") { throw new MidiFileException("Bad MTrk header", file.GetOffset() - 4); } int tracklen = file.ReadInt(); int trackend = tracklen + file.GetOffset(); int eventflag = 0; while (file.GetOffset() < trackend) { // If the midi file is truncated here, we can still recover. // Just return what we've parsed so far. int startoffset, deltatime; byte peekevent; try { startoffset = file.GetOffset(); deltatime = file.ReadVarlen(); starttime += deltatime; peekevent = file.Peek(); } catch (MidiFileException e) { return result; } MidiEvent mevent = new MidiEvent(); result.Add(mevent); mevent.DeltaTime = deltatime; mevent.StartTime = starttime; if (peekevent >= EventNoteOff) { mevent.HasEventflag = true; eventflag = file.ReadByte(); } // Console.WriteLine("offset {0}: event {1} {2} start {3} delta {4}", // startoffset, eventflag, EventName(eventflag), // starttime, mevent.DeltaTime); if (eventflag >= EventNoteOn && eventflag < EventNoteOn + 16) { mevent.EventFlag = EventNoteOn; mevent.Channel = (byte)(eventflag - EventNoteOn); mevent.Notenumber = file.ReadByte(); mevent.Velocity = file.ReadByte(); } else if (eventflag >= EventNoteOff && eventflag < EventNoteOff + 16) { mevent.EventFlag = EventNoteOff; mevent.Channel = (byte)(eventflag - EventNoteOff); mevent.Notenumber = file.ReadByte(); mevent.Velocity = file.ReadByte(); } else if (eventflag >= EventKeyPressure && eventflag < EventKeyPressure + 16) { mevent.EventFlag = EventKeyPressure; mevent.Channel = (byte)(eventflag - EventKeyPressure); mevent.Notenumber = file.ReadByte(); mevent.KeyPressure = file.ReadByte(); } else if (eventflag >= EventControlChange && eventflag < EventControlChange + 16) { mevent.EventFlag = EventControlChange; mevent.Channel = (byte)(eventflag - EventControlChange); mevent.ControlNum = file.ReadByte(); mevent.ControlValue = file.ReadByte(); } else if (eventflag >= EventProgramChange && eventflag < EventProgramChange + 16) { mevent.EventFlag = EventProgramChange; mevent.Channel = (byte)(eventflag - EventProgramChange); mevent.Instrument = file.ReadByte(); } else if (eventflag >= EventChannelPressure && eventflag < EventChannelPressure + 16) { mevent.EventFlag = EventChannelPressure; mevent.Channel = (byte)(eventflag - EventChannelPressure); mevent.ChanPressure = file.ReadByte(); } else if (eventflag >= EventPitchBend && eventflag < EventPitchBend + 16) { mevent.EventFlag = EventPitchBend; mevent.Channel = (byte)(eventflag - EventPitchBend); mevent.PitchBend = file.ReadShort(); } else if (eventflag == SysexEvent1) { mevent.EventFlag = SysexEvent1; mevent.Metalength = file.ReadVarlen(); mevent.Value = file.ReadBytes(mevent.Metalength); } else if (eventflag == SysexEvent2) { mevent.EventFlag = SysexEvent2; mevent.Metalength = file.ReadVarlen(); mevent.Value = file.ReadBytes(mevent.Metalength); } else if (eventflag == MetaEvent) { mevent.EventFlag = MetaEvent; mevent.Metaevent = file.ReadByte(); mevent.Metalength = file.ReadVarlen(); mevent.Value = file.ReadBytes(mevent.Metalength); if (mevent.Metaevent == MetaEventTimeSignature) { if (mevent.Metalength < 2) { // throw new MidiFileException( // "Meta Event Time Signature len == " + mevent.Metalength + // " != 4", file.GetOffset()); mevent.Numerator = (byte)0; mevent.Denominator = (byte)4; } else if (mevent.Metalength >= 2 && mevent.Metalength < 4) { mevent.Numerator = (byte)mevent.Value[0]; mevent.Denominator = (byte)System.Math.Pow(2, mevent.Value[1]); } else { mevent.Numerator = (byte)mevent.Value[0]; mevent.Denominator = (byte)System.Math.Pow(2, mevent.Value[1]); } } else if (mevent.Metaevent == MetaEventTempo) { if (mevent.Metalength != 3) { throw new MidiFileException( "Meta Event Tempo len == " + mevent.Metalength + " != 3", file.GetOffset()); } mevent.Tempo = ( (mevent.Value[0] << 16) | (mevent.Value[1] << 8) | mevent.Value[2]); } else if (mevent.Metaevent == MetaEventEndOfTrack) { /* break; */ } } else { throw new MidiFileException("Unknown event " + mevent.EventFlag, file.GetOffset()-1); } } return result; }
public void TestVarlen() { byte[] data = new byte[12]; data[0] = 0x40; data[1] = 0x90; data[2] = 0x30; data[3] = 0x81; data[4] = 0xA5; data[5] = 0x10; data[6] = 0x81; data[7] = 0x84; data[8] = 0xBF; data[9] = 0x05; WriteTestFile(data); MidiFileReader reader = new MidiFileReader(testfile); int len = varlen(0, 0, 0, data[0]); Assert.AreEqual(reader.GetOffset(), 0); Assert.AreEqual(reader.ReadVarlen(), len); Assert.AreEqual(reader.GetOffset(), 1); len = varlen(0, 0, data[1], data[2]); Assert.AreEqual(reader.ReadVarlen(), len); Assert.AreEqual(reader.GetOffset(), 3); len = varlen(0, data[3], data[4], data[5]); Assert.AreEqual(reader.ReadVarlen(), len); Assert.AreEqual(reader.GetOffset(), 6); len = varlen(data[6], data[7], data[8], data[9]); Assert.AreEqual(reader.ReadVarlen(), len); Assert.AreEqual(reader.GetOffset(), 10); File.Delete(testfile); }