Example #1
0
        /// <summary>Creates a method to create the track.</summary>
        /// <param name="name">The name of the track method.</param>
        /// <param name="track">The track to translate.</param>
        /// <returns>The method that creates the track.</returns>
        private static CodeMemberMethod CreateTrackMethod(string name, MidiTrack track)
        {
            // Create the method
            CodeMemberMethod method = new CodeMemberMethod();
            method.Name = name;
            method.ReturnType = new CodeTypeReference(typeof(MidiTrack));

            // MidiTrack track = new MidiTrack();
            method.Statements.Add(
                new CodeVariableDeclarationStatement(typeof(MidiTrack), "track",
                new CodeObjectCreateExpression(typeof(MidiTrack),
                new CodeExpression[]{})));

            // Add all of the events!
            for(int i=0; i<track.Events.Count; i++)
            {
                CodeObjectCreateExpression createEvent = CreateEvent(track.Events[i]);

                // track.Events.Add(new "event"());
                if (createEvent != null)
                {
                    method.Statements.Add(
                        new CodeMethodInvokeExpression(
                        new CodePropertyReferenceExpression(
                        new CodeVariableReferenceExpression("track"), "Events"),
                        "Add", new CodeExpression[]{createEvent}));
                }
            }

            // return track;
            method.Statements.Add(new CodeMethodReturnStatement(
                new CodeVariableReferenceExpression("track")));

            // Return the newly created method
            return method;
        }
 public void RemoveTrack(MidiTrack track)
 {
     this._tracks.Remove(track);
 }
Example #3
0
        /// <summary>Parses a byte array into a track's worth of events.</summary>
        /// <param name="data">The data to be parsed.</param>
        /// <returns>The track containing the parsed events.</returns>
        public static MidiTrack ParseToTrack(byte [] data)
        {
            long pos           = 0;     // current position in data
            bool running       = false; // whether we're in running status
            int  status        = 0;     // the current status byte
            bool sysExContinue = false; // whether we're in a multi-segment system exclusive message

            byte [] sysExData = null;   // system exclusive data up to this point from a multi-segment message

            try
            {
                // Create the new track
                MidiTrack track = new MidiTrack();

                // Process all bytes, turning them into events
                while (pos < data.Length)
                {
                    // Read in the delta time
                    long deltaTime = ReadVariableLength(data, ref pos);

                    // Get the next character
                    byte nextValue = data[pos];

                    // Are we continuing a sys ex?  If so, the next value better be 0x7F
                    if (sysExContinue && (nextValue != 0x7f))
                    {
                        throw new MidiParserException("Expected to find a system exclusive continue byte.", pos);
                    }

                    // Are we in running status?  Determine whether we're running and
                    // what the current status byte is.
                    if ((nextValue & 0x80) == 0)
                    {
                        // We're now in running status... if the last status was 0, uh oh!
                        if (status == 0)
                        {
                            throw new MidiParserException("Status byte required for running status.", pos);
                        }

                        // Keep the last iteration's status byte, and now we're in running mode
                        running = true;
                    }
                    else
                    {
                        // Not running, so store the current status byte and mark running as false
                        status  = nextValue;
                        running = false;
                    }

                    // Grab the 4-bit identifier
                    byte messageType = (byte)((status >> 4) & 0xF);

                    MidiEvent tempEvent = null;

                    // Handle voice events
                    if (messageType >= 0x8 && messageType <= 0xE)
                    {
                        if (!running)
                        {
                            pos++;                                   // if we're running, we don't advance; if we're not running, we do
                        }
                        byte channel = (byte)(status & 0xF);         // grab the channel from the status byte
                        tempEvent = ParseVoiceEvent(deltaTime, messageType, channel, data, ref pos);
                    }
                    // Handle meta events
                    else if (status == 0xFF)
                    {
                        pos++;
                        byte eventType = data[pos];
                        pos++;
                        tempEvent = ParseMetaEvent(deltaTime, eventType, data, ref pos);
                    }
                    // Handle system exclusive events
                    else if (status == 0xF0)
                    {
                        pos++;
                        long length = ReadVariableLength(data, ref pos);                         // figure out how much data to read

                        // If this is single-segment message, process the whole thing
                        if (data[pos + length - 1] == 0xF7)
                        {
                            sysExData = new byte[length - 1];
                            Array.Copy(data, (int)pos, sysExData, 0, (int)length - 1);
                            tempEvent = new SystemExclusiveMidiEvent(deltaTime, sysExData);
                        }
                        // It's multi-segment, so add the new data to the previously aquired data
                        else
                        {
                            // Add to previously aquired sys ex data
                            int     oldLength    = (sysExData == null ? 0 : sysExData.Length);
                            byte [] newSysExData = new byte[oldLength + length];
                            if (sysExData != null)
                            {
                                sysExData.CopyTo(newSysExData, 0);
                            }
                            Array.Copy(data, (int)pos, newSysExData, oldLength, (int)length);
                            sysExData     = newSysExData;
                            sysExContinue = true;
                        }
                        pos += length;
                    }
                    // Handle system exclusive continuations
                    else if (status == 0xF7)
                    {
                        if (!sysExContinue)
                        {
                            sysExData = null;
                        }

                        // Figure out how much data there is
                        pos++;
                        long length = ReadVariableLength(data, ref pos);

                        // Add to previously aquired sys ex data
                        int     oldLength    = (sysExData == null ? 0 : sysExData.Length);
                        byte [] newSysExData = new byte[oldLength + length];
                        if (sysExData != null)
                        {
                            sysExData.CopyTo(newSysExData, 0);
                        }
                        Array.Copy(data, (int)pos, newSysExData, oldLength, (int)length);
                        sysExData = newSysExData;

                        // Make it a system message if necessary (i.e. if we find an end marker)
                        if (data[pos + length - 1] == 0xF7)
                        {
                            tempEvent     = new SystemExclusiveMidiEvent(deltaTime, sysExData);
                            sysExData     = null;
                            sysExContinue = false;
                        }
                    }
                    // Nothing we know about
                    else
                    {
                        throw new MidiParserException("Invalid status byte found.", pos);
                    }

                    // Add the newly parsed event if we got one
                    if (tempEvent != null)
                    {
                        track.Events.Add(tempEvent);
                    }
                }

                // Return the newly populated track
                return(track);
            }
            // Let MidiParserExceptions through
            catch (MidiParserException) { throw; }
            // Wrap all other exceptions in MidiParserExceptions
            catch (Exception exc) { throw new MidiParserException("Failed to parse MIDI file.", exc, pos); }
        }
 public MidiTrack AddTrack()
 {
     MidiTrack track = new MidiTrack();
     this.AddTrack(track);
     return track;
 }
 public void AddTrack(MidiTrack track)
 {
     if (track == null)
     {
         throw new ArgumentNullException("track");
     }
     if (this._tracks.Contains(track))
     {
         throw new ArgumentException("This track is already part of the sequence.");
     }
     if ((this._format == 0) && (this._tracks.Count >= 1))
     {
         throw new InvalidOperationException("Format 0 MIDI files can only have 1 track.");
     }
     this._tracks.Add(track);
 }
Example #6
0
 /// <summary>Removes a track that has been adding to the MIDI sequence.</summary>
 /// <param name="track">The track to be removed.</param>
 public void RemoveTrack(MidiTrack track)
 {
     // Remove the track
     _tracks.Remove(track);
 }
Example #7
0
        /// <summary>Converts a MIDI sequence from its current format to the specified format.</summary>
        /// <param name="sequence">The sequence to be converted.</param>
        /// <param name="format">The format to which we want to convert the sequence.</param>
        /// <param name="options">Options used when doing the conversion.</param>
        /// <returns>The converted sequence.</returns>
        /// <remarks>
        /// This may or may not return the same sequence as passed in.
        /// Regardless, the reference passed in should not be used after this call as the old
        /// sequence could be unusable if a different reference was returned.
        /// </remarks>
        public static MidiSequence Convert(MidiSequence sequence, int format, FormatConversionOptions options)
        {
            // Validate the parameters
            if (sequence == null)
            {
                throw new ArgumentNullException("sequence");
            }
            if (format < 0 || format > 2)
            {
                throw new ArgumentOutOfRangeException("format", format, "The format must be 0, 1, or 2.");
            }

            // Handle the simple cases
            if (sequence.Format == format)
            {
                return(sequence);                                       // already in requested format
            }
            if (format != 0 || sequence.NumberOfTracks == 1)            // only requires change in format #
            {
                // Change the format and return the same sequence
                sequence.SetFormat(format);
                return(sequence);
            }

            // Now the hard one, converting to format 0.
            // We need to combine all tracks into 1.
            MidiSequence newSequence = new MidiSequence(format, sequence.Division);
            MidiTrack    newTrack    = newSequence.AddTrack();

            // Iterate through all events in all tracks and change deltaTimes to actual times.
            // We'll then be able to sort based on time and change them back to deltas later
            foreach (MidiTrack track in sequence)
            {
                track.Events.ConvertDeltasToTotals();
            }

            // Add all events to new track (except for end of track markers!)
            int trackNumber = 0;

            foreach (MidiTrack track in sequence)
            {
                foreach (MidiEvent midiEvent in track.Events)
                {
                    // If this event has a channel, and if we're storing tracks as channels, copy to it
                    if ((options & FormatConversionOptions.CopyTrackToChannel) > 0 &&
                        (midiEvent is VoiceMidiEvent) &&
                        trackNumber >= 0 && trackNumber <= 0xF)
                    {
                        ((VoiceMidiEvent)midiEvent).Channel = (byte)trackNumber;
                    }

                    // Add all events, except for end of track markers (we'll add our own)
                    if (!(midiEvent is EndOfTrack))
                    {
                        newTrack.Events.Add(midiEvent);
                    }
                }
                trackNumber++;
            }

            // Sort the events
            newTrack.Events.SortByTime();

            // Now go back through all of the events and update the times to be deltas
            newTrack.Events.ConvertTotalsToDeltas();

            // Put an end of track on for good measure as we've already taken out
            // all of the ones that were in the original tracks.
            newTrack.Events.Add(new EndOfTrack(0));

            // Return the new sequence
            return(newSequence);
        }
Example #8
0
        /// <summary>
        /// Creates a notechart from the specified midi path and the actual charttype
        /// (i.e. ExpertSingle from notes.mid).  Due to the overhead necessary to
        /// parse a midi file.  I am going to cram all midi->chart operations into
        /// one function call.
        /// This function uses the Toub midi parser which is much faster than Sanford,
        /// but will throw an exception on certian midi files.
        /// </summary>
        /// <param name="chartSelection">
        /// The information on which particular notechart to use.
        /// </param>
        /// <param name="chartInfo">The metadata on the chart.</param>
        /// <param name="BPMChanges">The list of BPM changes for this chart.</param>
        /// <returns>
        /// A filled out Notechart containing the needed information from the *.mid file.
        /// </returns>
        public static Notes ParseMidiInformationToub(ChartSelection chartSelection,
            Info chartInfo,
            List<BPMChange> BPMChanges)
        {
            Notes notechartToReturn = new Notes();
            notechartToReturn.instrument = chartSelection.instrument;
            notechartToReturn.difficulty = chartSelection.difficulty;

            // The following two switch's are used to get the proper midi terminology for
            // the selected track and difficulty.
            string instrumentPart = null;
            string greenKey = null;
            string redKey = null;
            string yellowKey = null;
            string blueKey = null;
            string orangeKey = null;

            switch (chartSelection.instrument)
            {
                case "Single":
                    instrumentPart = "PART GUITAR";
                    break;
                case "DoubleGuitar":
                    instrumentPart = "PART GUITAR COOP";
                    break;
                case "DoubleBass":
                    instrumentPart = "PART BASS";
                    break;
                case "Drums":
                    instrumentPart = "PART DRUMS";
                    break;
                default:
                    instrumentPart = "PART GUITAR";
                    break;
            }

            switch (chartSelection.difficulty)
            {
                case "Expert":
                    greenKey = "C8";
                    redKey = "C#8";
                    yellowKey = "D8";
                    blueKey = "D#8";
                    orangeKey = "E8";
                    break;
                case "Hard":
                    greenKey = "C7";
                    redKey = "C#7";
                    yellowKey = "D7";
                    blueKey = "D#7";
                    orangeKey = "E7";
                    break;
                case "Medium":
                    greenKey = "C6";
                    redKey = "C#6";
                    yellowKey = "D6";
                    blueKey = "D#6";
                    orangeKey = "E6";
                    break;
                case "Easy":
                    greenKey = "C5";
                    redKey = "C#5";
                    yellowKey = "D5";
                    blueKey = "D#5";
                    orangeKey = "E5";
                    break;
                default:
                    greenKey = "C8";
                    redKey = "C#8";
                    yellowKey = "D8";
                    blueKey = "D#8";
                    orangeKey = "E8";
                    break;
            }

            MidiSequence mySequence = MidiSequence.Import(chartSelection.directory + "\\notes.mid");
            MidiTrack[] myTracks = mySequence.GetTracks();
            chartInfo.resolution = mySequence.Division;

            MidiTrack trackToUse = new MidiTrack();
            uint totalTickValue = 0;

            // Go through each event in the first track (which contains the BPM changes)
            // and parse the resulting string.
            for (int i = 0; i < myTracks[0].Events.Count; i++)
            {
                Toub.Sound.Midi.MidiEvent currEvent = myTracks[0].Events[i];
                string eventString = currEvent.ToString();
                string[] splitEventString = eventString.Split('\t');

                // Since ticks are stored relative to each other (e.g. 300 ticks
                // until next note), we must maintain the total tick amout.
                totalTickValue += Convert.ToUInt32(splitEventString[1]);
                if (splitEventString[0] == "Tempo")
                {
                    // In midi files, bpm chages are stored as "microseconds per quarter note"
                    // and must be converted to BPM, and then into the non decimal format the game
                    // uses.
                    double currBPMDouble = 60000000 / Convert.ToDouble(splitEventString[3]);
                    uint BPMToAdd = (uint)(currBPMDouble * 1000);
                    BPMChanges.Add(new BPMChange(totalTickValue, BPMToAdd));
                }
            }

            trackToUse = new MidiTrack();
            // Find the specified instrument's track
            foreach (MidiTrack currTrack in myTracks)
            {
                string trackHeader = currTrack.Events[0].ToString();
                string[] splitHeader = trackHeader.Split('\t');

                // -If we come across a "T1 GEMS" track, we're in GH1 territory.
                // -GH2/FoF has both PART BASS and PART RHYTHM (one or the other depending
                //  on the chart).
                if (((splitHeader[3] == instrumentPart) || (splitHeader[3] == "T1 GEMS")) ||
                    ((splitHeader[3] == "PART RHYTHM") && (instrumentPart == "PART BASS")))
                {
                    trackToUse = currTrack;
                }
            }

            totalTickValue = 0;
            uint currTickValue = 0;
            Note currNote = new Note();
            bool blankNote = true;
            // Scan through and record every note specific to the selected difficulty
            for (int i = 0; i < trackToUse.Events.Count; i++)
            {
                string currEvent = trackToUse.Events[i].ToString();
                string[] splitEvent = currEvent.Split('\t');
                currTickValue = Convert.ToUInt32(splitEvent[1]);
                totalTickValue += currTickValue;

                // We need to specify wether a note is blank or not so we don't add
                // blank notes from other difficulties into the chart, but if we have
                // a filled out note, any nonzero tick value means we are moving to a
                // new note, so we must cut our ties and add this note to the chart.
                if ((currTickValue != 0) && !blankNote)
                {
                    notechartToReturn.notes.Add(currNote);
                    currNote = new Note();
                    blankNote = true;
                }

                // The "0x64" I think means "not was hit."  There is another
                // set of notes that use "0x00" that all appear slightly after
                // the "0x64" notes.
                if ((splitEvent[0] == "NoteOn") && (splitEvent[4] != "0x00"))
                {
                    // Only consider notes within the octave our difficulty is in.
                    if ((splitEvent[3] == greenKey) || (splitEvent[3] == redKey) ||
                        (splitEvent[3] == yellowKey) || (splitEvent[3] == blueKey) ||
                        (splitEvent[3] == orangeKey))
                    {
                        // If it's a new note, we need to setup the tick value of it.
                        if (blankNote)
                        {
                            currNote.tickValue = totalTickValue;
                            blankNote = false;
                        }
                        if (splitEvent[3] == greenKey) { currNote.addNote(0); }
                        else if (splitEvent[3] == redKey) { currNote.addNote(1); }
                        else if (splitEvent[3] == yellowKey) { currNote.addNote(2); }
                        else if (splitEvent[3] == blueKey) { currNote.addNote(3); }
                        else if (splitEvent[3] == orangeKey) { currNote.addNote(4); }
                    }
                }
            }

            return notechartToReturn;
        }
 public static void Play(MidiTrack track, int division)
 {
     MidiSequence sequence = new MidiSequence(0, division);
     sequence.AddTrack(track);
     Play(sequence);
 }
Example #10
0
 /// <summary>Removes a track that has been adding to the MIDI sequence.</summary>
 /// <param name="track">The track to be removed.</param>
 public void RemoveTrack(MidiTrack track)
 {
     // Remove the track
     _tracks.Remove(track);
 }
Example #11
0
        /// <summary>Parses a byte array into a track's worth of events.</summary>
        /// <param name="data">The data to be parsed.</param>
        /// <returns>The track containing the parsed events.</returns>
        public static MidiTrack ParseToTrack(byte [] data)
        {
            long pos = 0; // current position in data
            bool running = false; // whether we're in running status
            int status = 0; // the current status byte
            bool sysExContinue = false; // whether we're in a multi-segment system exclusive message
            byte [] sysExData = null; // system exclusive data up to this point from a multi-segment message

            try
            {
                // Create the new track
                MidiTrack track = new MidiTrack();

                // Process all bytes, turning them into events
                while(pos < data.Length)
                {
                    // Read in the delta time
                    long deltaTime = ReadVariableLength(data, ref pos);

                    // Get the next character
                    byte nextValue = data[pos];

                    // Are we continuing a sys ex?  If so, the next value better be 0x7F
                    if (sysExContinue && (nextValue != 0x7f)) throw new MidiParserException("Expected to find a system exclusive continue byte.", pos);

                    // Are we in running status?  Determine whether we're running and
                    // what the current status byte is.
                    if ((nextValue & 0x80) == 0)
                    {
                        // We're now in running status... if the last status was 0, uh oh!
                        if (status == 0) throw new MidiParserException("Status byte required for running status.", pos);

                        // Keep the last iteration's status byte, and now we're in running mode
                        running = true;
                    }
                    else
                    {
                        // Not running, so store the current status byte and mark running as false
                        status = nextValue;
                        running = false;
                    }

                    // Grab the 4-bit identifier
                    byte messageType = (byte)((status >> 4) & 0xF);

                    MidiEvent tempEvent = null;

                    // Handle voice events
                    if (messageType >= 0x8 && messageType <= 0xE)
                    {
                        if (!running) pos++; // if we're running, we don't advance; if we're not running, we do
                        byte channel = (byte)(status & 0xF); // grab the channel from the status byte
                        tempEvent = ParseVoiceEvent(deltaTime, messageType, channel, data, ref pos);
                    }
                        // Handle meta events
                    else if (status == 0xFF)
                    {
                        pos++;
                        byte eventType = data[pos];
                        pos++;
                        tempEvent = ParseMetaEvent(deltaTime, eventType, data, ref pos);
                    }
                        // Handle system exclusive events
                    else if (status == 0xF0)
                    {
                        pos++;
                        long length = ReadVariableLength(data, ref pos); // figure out how much data to read

                        // If this is single-segment message, process the whole thing
                        if (data[pos+length-1] == 0xF7)
                        {
                            sysExData = new byte[length-1];
                            Array.Copy(data, (int)pos, sysExData, 0, (int)length-1);
                            tempEvent = new SystemExclusiveMidiEvent(deltaTime, sysExData);
                        }
                            // It's multi-segment, so add the new data to the previously aquired data
                        else
                        {
                            // Add to previously aquired sys ex data
                            int oldLength = (sysExData == null ? 0 : sysExData.Length);
                            byte [] newSysExData = new byte[oldLength + length];
                            if (sysExData != null) sysExData.CopyTo(newSysExData, 0);
                            Array.Copy(data, (int)pos, newSysExData, oldLength, (int)length);
                            sysExData = newSysExData;
                            sysExContinue = true;
                        }
                        pos += length;
                    }
                        // Handle system exclusive continuations
                    else if (status == 0xF7)
                    {
                        if (!sysExContinue) sysExData = null;

                        // Figure out how much data there is
                        pos++;
                        long length = ReadVariableLength(data, ref pos);

                        // Add to previously aquired sys ex data
                        int oldLength = (sysExData == null ? 0 : sysExData.Length);
                        byte [] newSysExData = new byte[oldLength + length];
                        if (sysExData != null) sysExData.CopyTo(newSysExData, 0);
                        Array.Copy(data, (int)pos, newSysExData, oldLength, (int)length);
                        sysExData = newSysExData;

                        // Make it a system message if necessary (i.e. if we find an end marker)
                        if (data[pos+length-1] == 0xF7)
                        {
                            tempEvent = new SystemExclusiveMidiEvent(deltaTime, sysExData);
                            sysExData = null;
                            sysExContinue = false;
                        }
                    }
                        // Nothing we know about
                    else throw new MidiParserException("Invalid status byte found.", pos);

                    // Add the newly parsed event if we got one
                    if (tempEvent != null) track.Events.Add(tempEvent);
                }

                // Return the newly populated track
                return track;
            }
                // Let MidiParserExceptions through
            catch(MidiParserException) { throw; }
                // Wrap all other exceptions in MidiParserExceptions
            catch(Exception exc) { throw new MidiParserException("Failed to parse MIDI file.", exc, pos); }
        }
Example #12
0
        /// <summary>Adds a track to the MIDI sequence.</summary>
        /// <param name="track">The complete track to be added.</param>
        public void AddTrack(MidiTrack track)
        {
            // Make sure the track is valid and that is hasn't already been added.
            if (track == null) throw new ArgumentNullException("track");
            if (_tracks.Contains(track)) throw new ArgumentException("This track is already part of the sequence.");

            // If this is format 0, we can only have 1 track
            if (_format == 0 && _tracks.Count >= 1) throw new InvalidOperationException("Format 0 MIDI files can only have 1 track.");

            // Add the track.
            _tracks.Add(track);
        }
Example #13
0
 /// <summary>Adds a track to the MIDI sequence.</summary>
 /// <returns>The new track as added to the sequence.  Modifications made to the track will be reflected.</returns>
 public MidiTrack AddTrack()
 {
     // Create a new track, add it, and return it
     MidiTrack track = new MidiTrack();
     AddTrack(track);
     return track;
 }
Example #14
0
 /// <summary>Plays an individual MIDI track.</summary>
 /// <param name="track">The track to be played.</param>
 /// <param name="division">The MIDI division to use for playing the track.</param>
 public static void Play(MidiTrack track, int division)
 {
     // Wrap the track in a sequence and play it
     MidiSequence tempSequence = new MidiSequence(0, division);
     tempSequence.AddTrack(track);
     Play(tempSequence);
 }
 private static CodeMemberMethod CreateTrackMethod(string name, MidiTrack track)
 {
     CodeMemberMethod method = new CodeMemberMethod {
         Name = name,
         ReturnType = new CodeTypeReference(typeof(MidiTrack))
     };
     CodeExpression[] parameters = new CodeExpression[0];
     method.Statements.Add(new CodeVariableDeclarationStatement(typeof(MidiTrack), "track", new CodeObjectCreateExpression(typeof(MidiTrack), parameters)));
     for (int i = 0; i < track.Events.Count; i++)
     {
         CodeObjectCreateExpression expression = CreateEvent(track.Events[i]);
         if (expression != null)
         {
             method.Statements.Add(new CodeMethodInvokeExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("track"), "Events"), "Add", new CodeExpression[] { expression }));
         }
     }
     method.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("track")));
     return method;
 }
 public static MidiTrack ParseToTrack(byte[] data)
 {
     MidiTrack track2;
     long pos = 0L;
     bool flag = false;
     int num2 = 0;
     bool flag2 = false;
     byte[] destinationArray = null;
     try
     {
         MidiTrack track = new MidiTrack();
         while (pos < data.Length)
         {
             long deltaTime = ReadVariableLength(data, ref pos);
             byte num4 = data[(int) ((IntPtr) pos)];
             if (flag2 && (num4 != 0x7f))
             {
                 throw new MidiParserException("Expected to find a system exclusive continue byte.", pos);
             }
             if ((num4 & 0x80) == 0)
             {
                 if (num2 == 0)
                 {
                     throw new MidiParserException("Status byte required for running status.", pos);
                 }
                 flag = true;
             }
             else
             {
                 num2 = num4;
                 flag = false;
             }
             byte messageType = (byte) ((num2 >> 4) & 15);
             MidiEvent message = null;
             if ((messageType >= 8) && (messageType <= 14))
             {
                 if (!flag)
                 {
                     pos += 1L;
                 }
                 byte channel = (byte) (num2 & 15);
                 message = ParseVoiceEvent(deltaTime, messageType, channel, data, ref pos);
             }
             else if (num2 == 0xff)
             {
                 pos += 1L;
                 byte eventType = data[(int) ((IntPtr) pos)];
                 pos += 1L;
                 message = ParseMetaEvent(deltaTime, eventType, data, ref pos);
             }
             else if (num2 == 240)
             {
                 pos += 1L;
                 long num8 = ReadVariableLength(data, ref pos);
                 if (data[(int) ((IntPtr) ((pos + num8) - 1L))] == 0xf7)
                 {
                     destinationArray = new byte[num8 - 1L];
                     Array.Copy(data, (int) pos, destinationArray, 0, ((int) num8) - 1);
                     message = new SystemExclusiveMidiEvent(deltaTime, destinationArray);
                 }
                 else
                 {
                     int destinationIndex = (destinationArray == null) ? 0 : destinationArray.Length;
                     byte[] array = new byte[destinationIndex + num8];
                     if (destinationArray != null)
                     {
                         destinationArray.CopyTo(array, 0);
                     }
                     Array.Copy(data, (int) pos, array, destinationIndex, (int) num8);
                     destinationArray = array;
                     flag2 = true;
                 }
                 pos += num8;
             }
             else
             {
                 if (num2 != 0xf7)
                 {
                     throw new MidiParserException("Invalid status byte found.", pos);
                 }
                 if (!flag2)
                 {
                     destinationArray = null;
                 }
                 pos += 1L;
                 long num10 = ReadVariableLength(data, ref pos);
                 int num11 = (destinationArray == null) ? 0 : destinationArray.Length;
                 byte[] buffer3 = new byte[num11 + num10];
                 if (destinationArray != null)
                 {
                     destinationArray.CopyTo(buffer3, 0);
                 }
                 Array.Copy(data, (int) pos, buffer3, num11, (int) num10);
                 destinationArray = buffer3;
                 if (data[(int) ((IntPtr) ((pos + num10) - 1L))] == 0xf7)
                 {
                     message = new SystemExclusiveMidiEvent(deltaTime, destinationArray);
                     destinationArray = null;
                     flag2 = false;
                 }
             }
             if (message != null)
             {
                 track.Events.Add(message);
             }
         }
         track2 = track;
     }
     catch (MidiParserException)
     {
         throw;
     }
     catch (Exception exception)
     {
         throw new MidiParserException("Failed to parse MIDI file.", exception, pos);
     }
     return track2;
 }