Exemple #1
0
        /// <summary>Trims a MIDI file to a specified length.</summary>
        /// <param name="sequence">The sequence to be copied and trimmed.</param>
        /// <param name="totalTime">The requested time length of the new MIDI sequence.</param>
        /// <returns>A MIDI sequence with only those events that fell before the requested time limit.</returns>
        public static Sequence Trim(this Sequence sequence, long totalTime)
        {
            // Create a new sequence to mimic the old
            var newSequence = new Sequence(sequence.DivisionType, sequence.Resolution, 0, sequence.MidiFileType);

            // Copy each track up to the specified time limit
            foreach (Track track in sequence.Tracks)
            {
                // Create a new track in the new sequence to match the old track in the old sequence
                var newTrack = newSequence.CreateTrack();

                // Copy over all events that fell before the specified time
                for (int i = 0; i < track.Events.Count && track.Events[i].Tick < totalTime; i++)
                {
                    newTrack.Events.Add(track.Events[i].DeepClone());                     // add at the end
                    //newTrack.Add(track.Events[i].DeepClone()); // insert at correct timing
                }

                // If the new track lacks an end of track, add one
                if (!newTrack.HasEndOfTrack)
                {
                    newTrack.Add(MetaEvent.CreateMetaEvent("EndOfTrack", "", newTrack.Ticks(), 0));
                }
            }

            // Return the new sequence
            return(newSequence);
        }
Exemple #2
0
        public void SaveMidiSequence(string filePath)
        {
            long ticks = midiTrack.Ticks();

            midiTrack.Add(MetaEvent.CreateMetaEvent((int)MidiHelper.MetaEventType.EndOfTrack, "", ticks, midiTickResolution));

            midiSequence.DumpMidi("output.mid.txt");
            new MidiFileWriter().Write(midiSequence, midiSequence.MidiFileType, new FileInfo(filePath));
        }
Exemple #3
0
        void InitMidiSequence()
        {
            // Generate midi file
            midiSequence = new Sequence(Sequence.PPQ, midiTickResolution, 0, (int)MidiHelper.MidiFormat.SingleTrack);
            midiTrack    = midiSequence.CreateTrack();

            midiTrack.Add(MetaEvent.CreateMetaEvent((int)MidiHelper.MetaEventType.SequenceOrTrackName, "Audio2Midi", 0, midiTickResolution));
            midiTrack.Add(MetaEvent.CreateMetaEvent((int)MidiHelper.MetaEventType.CopyrightNotice, "*****@*****.**", 0, midiTickResolution));
            midiTrack.Add(MetaEvent.CreateMetaEvent((int)MidiHelper.MetaEventType.Tempo, "" + midiBPM, 0, midiTickResolution));
            midiTrack.Add(MetaEvent.CreateMetaEvent((int)MidiHelper.MetaEventType.TimeSignature, "4/4", 0, midiTickResolution));

            // Convert from ticks to duration
            // Midi timings are explained here
            // http://sites.uci.edu/camp2014/2014/05/19/timing-in-midi-files/
            // http://stackoverflow.com/questions/2038313/midi-ticks-to-actual-playback-seconds-midi-music
            // The formula is 60000 / (BPM * PPQ) (milliseconds).
            // Where BPM is the tempo of the track (Beats Per Minute).
            // (i.e. a 120 BPM track would have a MIDI time of:
            // (60000 / (120 * 192)) or 2.604 ms for 1 tick.
            double timeBufferMs = ((double)bufferSize / sampleRate) * 1000;
            double timeTickMs   = (60000 / (double)(midiBPM * midiTickResolution));

            tickMultiplier = timeBufferMs / timeTickMs;
        }
Exemple #4
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);
        }