public static void Parse(Model midi, string filename, int frameRateMillis = 50)
 {
     using (MidiBigEndianWriter writer = new MidiBigEndianWriter(File.Open(filename, FileMode.Create)))
     {
         ParseHeaderChunk(writer, frameRateMillis);
         ParseTrackChunk(midi, writer, frameRateMillis);
     }
 }
        private static void ParseTrackChunk(Model midi, MidiBigEndianWriter writer, int frameRateMillis)
        {
            // track chunk signature
            writer.Write((uint)0x4D54726B);

            var notes = midi.Where((MidiControlEvent e) => e is NoteOn || e is NoteOff).OrderBy(e => e.AbsoluteRealTime).ToArray();

            // notes + instrument + end of track
            var length = notes.Length * 7 + 6 * (Instrument.NumberOfClusters - 1) + 4;

            // write the length of the track
            writer.Write((uint)length);

            // write instrument changes
            for (var i = 0; i < Instrument.NumberOfClusters; i++)
            {
                if (i == Channel.PercussionChannelNumber)
                {
                    continue;
                }

                writer.WriteSemiVariableLengthValue(0);
                writer.Write((byte)(0xC0 | i));
                writer.Write((byte)Instrument.TypicalInstrument((InstrumentCluster)i).Id());
            }


            var lastTime = 0.0;

            foreach (var note in notes)
            {
                // delta time
                writer.WriteSemiVariableLengthValue((uint)((note.AbsoluteRealTime.TotalMilliseconds - lastTime)));

                if (note is NoteOn)
                {
                    var noteOn = (NoteOn)note;

                    // event type
                    writer.Write((byte)(0x90 | note.ChannelNumber));

                    writer.Write((byte)noteOn.NoteNumber);
                    writer.Write((byte)noteOn.Volume);
                }
                else if (note is NoteOff)
                {
                    var noteOff = (NoteOff)note;

                    // event type
                    writer.Write((byte)(0x80 | note.ChannelNumber));

                    writer.Write((byte)noteOff.NoteNumber);
                    writer.Write((byte)noteOff.Velocity);
                }

                lastTime = note.AbsoluteRealTime.TotalMilliseconds;
            }


            // end of track

            // delta time
            writer.WriteVariableLengthValue(0);

            // meta event
            writer.Write((byte)0xFF);

            // end of track event
            writer.Write((byte)0x2F);

            // length
            writer.WriteVariableLengthValue(0);
        }