Exemplo n.º 1
0
        /// <summary>Converts the MIDI sequence into a new one with the desired 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>
        /// <param name="newResolution">the new resolution (disable using 0 or -1)</param>
        /// <returns>The new, converted sequence.</returns>
        /// <remarks>This is based on the excellent MidiSharp package by Stephen Toub.</remarks>
        public static Sequence Convert(this Sequence sequence, int format, FormatConversionOption options, int newResolution, string trackName)
        {
            if (sequence.MidiFileType == format)
            {
                // If the desired format is the same as the original, just return a copy.
                // No transformation is necessary.
                sequence = new Sequence(sequence);
            }
            else if (format != 0 || sequence.Tracks.Count == 1)
            {
                // If the desired format is is not 0 or there's only one track, just copy the sequence with a different format number.
                // If it's not zero, then multiple tracks are acceptable, so no transformation is necessary.
                // Or if there's only one track, then there's no possible transformation to be done.
                var newSequence = new Sequence(sequence.DivisionType, sequence.Resolution, 0, format);
                foreach (Track t in sequence.Tracks)
                {
                    newSequence.Tracks.Add(new Track(t));
                }
                sequence = newSequence;
            }
            else
            {
                // Now the harder cases, converting to format 0.  We need to combine all tracks into 1,
                // as format 0 requires that there only be a single track with all of the events for the song.
                int originalResolution = sequence.Resolution;
                if (newResolution <= 0)
                {
                    newResolution = originalResolution;
                }

                sequence = new Sequence(sequence);
                sequence.MidiFileType = (int)MidiHelper.MidiFormat.SingleTrack;

                // Add all events to new track (except for end of track markers and SequenceOrTrackName events)
                int trackNumber = 0;
                var newTrack    = new Track();

                foreach (Track track in sequence.Tracks)
                {
                    foreach (MidiEvent midiEvent in track.Events)
                    {
                        bool doAddEvent = true;

                        var msg = midiEvent.Message;

                        // check if this is a meta message
                        var mm = msg as MetaMessage;
                        if (mm != null)
                        {
                            // we have a meta message
                            // add all meta messages except the end of track markers (we'll add our own)
                            int type = mm.GetMetaMessageType();

                            if (type == (int)MidiHelper.MetaEventType.EndOfTrack)
                            {
                                doAddEvent = false;
                            }
                            else if (type == (int)MidiHelper.MetaEventType.SequenceOrTrackName)
                            {
                                doAddEvent = false;

                                // store track name, will be used later
                                if (string.IsNullOrEmpty(trackName))
                                {
                                    byte[] data = mm.GetMetaMessageData();
                                    string text = MidiHelper.GetString(data);
                                    trackName = MidiHelper.TextString(text);
                                }
                            }
                        }

                        // check if this is a short message
                        var sm = msg as ShortMessage;
                        if (sm != null)
                        {
                            // get the data
                            var channel = sm.GetChannel();
                            var cmd     = sm.GetCommand();
                            var data1   = sm.GetData1();
                            var data2   = sm.GetData2();

                            // If this event has a channel, and if we're storing tracks as channels, copy to it
                            if ((options & FormatConversionOption.CopyTrackToChannel) > 0 &&
                                trackNumber >= MidiHelper.MIN_CHANNEL && trackNumber <= MidiHelper.MAX_CHANNEL)
                            {
                                if (sm.IsChannelMessage())
                                {
                                    // store the track number as the channel
                                    sm.SetMessage(cmd, trackNumber, data1, data2);
                                }
                            }

                            if ((options & FormatConversionOption.NoteOffZero2NoteOnZero) > 0)
                            {
                                // If the event is a NoteOff with Volume 0
                                if (cmd == (int)MidiHelper.MidiEventType.NoteOff && data2 == 0)
                                {
                                    // convert to a NoteOn instead
                                    sm.SetMessage((int)MidiHelper.MidiEventType.NoteOn, channel, data1, data2);
                                }
                            }
                        }

                        // convert ticks if resolution has changed
                        if (originalResolution != newResolution)
                        {
                            if (midiEvent.Tick != 0)
                            {
                                double fraction = (double)midiEvent.Tick / (double)originalResolution;
                                int    tick     = (int)(fraction * newResolution);
                                midiEvent.Tick = tick;
                            }
                        }

                        // Add all events, except for end of track markers (we'll add our own)
                        if (doAddEvent)
                        {
                            //newTrack.Events.Add(midiEvent); // add to end of list
                            newTrack.Add(midiEvent);                             // add in the right position based on the tick
                        }
                    }
                    trackNumber++;
                }

                if (originalResolution != newResolution)
                {
                    sequence.Resolution = newResolution;
                }

                // Sort the events by total time
                // newTrack.Events.Sort((x, y) => x.Tick.CompareTo(y.Tick));
                // Note! using newTrack.Add instead of newTrack.Events.Add, already ensures a correct sort order

                // Top things off with an end-of-track marker.
                newTrack.Add(MetaEvent.CreateMetaEvent("EndOfTrack", "", newTrack.Ticks(), 0));

                // add a new track name as the very first event
                newTrack.Events.Insert(0, MetaEvent.CreateMetaEvent((int)MidiHelper.MetaEventType.SequenceOrTrackName, trackName, 0, 0));

                // We now have all of the combined events in newTrack.  Clear out the sequence, replacing all the tracks
                // with this new one.
                sequence.Tracks.Clear();
                sequence.Tracks.Add(newTrack);
            }

            return(sequence);
        }