/// <summary>Transposes a MIDI sequence up/down the specified number of half-steps.</summary>
        /// <param name="sequence">The sequence to be transposed.</param>
        /// <param name="steps">The number of steps up(+) or down(-) to transpose the sequence.</param>
        /// <param name="includeDrums">Whether drum tracks should also be transposed.</param>
        /// <remarks>If the step value is too large or too small, notes may wrap.</remarks>
        public static void Transpose(this MidiSequence sequence, int steps, bool includeDrums)
        {
            Validate.NonNull("sequence", sequence);

            // Modify each track
            foreach (MidiTrack track in sequence)
            {
                // Modify each event
                foreach (MidiEvent ev in track.Events)
                {
                    // If the event is not a voice MIDI event but the channel is the
                    // drum channel and the user has chosen not to include drums in the
                    // transposition (which makes sense), skip this event.
                    NoteVoiceMidiEvent nvme = ev as NoteVoiceMidiEvent;
                    if (nvme == null ||
                        (!includeDrums && nvme.Channel == (byte)SpecialChannel.Percussion))
                    {
                        continue;
                    }

                    // If the event is a NoteOn, NoteOff, or Aftertouch, shift the note
                    // according to the supplied number of steps.
                    nvme.Note = (byte)((nvme.Note + steps) % 128);
                }
            }
        }
Exemple #2
0
 /// <summary>Adds a collection of tracks to this collection.</summary>
 /// <param name="tracks">The tracks to add.</param>
 public void AddRange(IEnumerable <MidiTrack> tracks)
 {
     Validate.NonNull("tracks", tracks);
     foreach (MidiTrack track in tracks)
     {
         Add(track);
     }
 }
        }                                  // Prevent external instantiation

        /// <summary>Adds a collection of events to this collection.</summary>
        /// <param name="events">The events to add.</param>
        public void AddRange(IEnumerable <MidiEvent> events)
        {
            Validate.NonNull("events", events);
            foreach (MidiEvent ev in events)
            {
                Add(ev);
            }
        }
Exemple #4
0
 /// <summary>Dumps the MIDI track to the writer in human-readable form.</summary>
 /// <param name="writer">The writer to which the track should be written.</param>
 public void ToString(TextWriter writer)
 {
     Validate.NonNull("writer", writer);
     foreach (MidiEvent ev in Events)
     {
         writer.WriteLine(ev.ToString());
     }
 }
Exemple #5
0
 /// <summary>Initializes the track with a copy of the data in another track.</summary>
 /// <returns>The track to copy.</returns>
 public MidiTrack(MidiTrack source) : this()
 {
     Validate.NonNull("source", source);
     m_requireEndOfTrack = source.RequireEndOfTrack;
     foreach (var e in source.Events)
     {
         m_events.Add(e.DeepClone());
     }
 }
Exemple #6
0
        /// <summary>Writes a MIDI file header out to the stream.</summary>
        /// <param name="outputStream">The output stream to which the header should be written.</param>
        /// <param name="numTracks">The number of tracks that will be a part of this sequence.</param>
        /// <remarks>This functionality is automatically performed during a Save.</remarks>
        private void WriteHeader(Stream outputStream, int numTracks)
        {
            // Check parameters
            Validate.NonNull("outputStream", outputStream);
            Validate.InRange("numTracks", numTracks, 1, int.MaxValue);

            // Write out the main header for the sequence
            MThdChunkHeader mainHeader = new MThdChunkHeader(Format, numTracks, m_division);

            mainHeader.Write(outputStream);
        }
Exemple #7
0
        /// <summary>Initialize the MIDI sequence with a copy of the data from another sequence.</summary>
        /// <param name="source">The source sequence from which to copy.</param>
        public MidiSequence(MidiSequence source)
        {
            Validate.NonNull("source", source);

            Format   = source.Format;
            Division = source.Division;
            Tracks   = new MidiTrackCollection(this);

            foreach (MidiTrack t in source)
            {
                Tracks.Add(new MidiTrack(t));
            }
        }
Exemple #8
0
        /// <summary>Dumps the MIDI sequence to the writer in human-readable form.</summary>
        /// <param name="writer">The writer to which the sequence should be written.</param>
        public void ToString(TextWriter writer)
        {
            Validate.NonNull("writer", writer);

            // Info on sequence
            writer.WriteLine("MIDI Sequence");
            writer.WriteLine("Format: " + m_format);
            writer.WriteLine("Tracks: " + Tracks.Count);
            writer.WriteLine("Division: " + m_division);
            writer.WriteLine("");

            // Print out each track
            foreach (MidiTrack track in this)
            {
                track.ToString(writer);
            }
        }
Exemple #9
0
        /// <summary>Inserts an element into the collection at the specified index.</summary>
        /// <param name="index">The index at which to insert the element..</param>
        /// <param name="item">The item to insert.</param>
        protected override void InsertItem(int index, MidiTrack item)
        {
            // Make sure the track is valid and that is hasn't already been added.
            Validate.NonNull("item", item);
            if (Contains(item))
            {
                throw new ArgumentException("This track is already part of the sequence.");
            }

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

            base.InsertItem(index, item);
        }
        /// <summary>Trims a MIDI file to a specified length.</summary>
        /// <param name="sequence">The sequence to be copied and trimmed.</param>
        /// <param name="startTime"></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 MidiSequence Trim(this MidiSequence sequence, long startTime, long totalTime)
        {
            Validate.NonNull("sequence", sequence);
            Validate.InRange("startTime", startTime, 0, long.MaxValue);
            Validate.InRange("totalTime", totalTime, 0, long.MaxValue);

            // Create a new sequence to mimic the old
            MidiSequence newSequence = new MidiSequence(sequence.Format, sequence.Division);

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

                // Convert all times in the old track to deltas
                track.Events.ConvertDeltasToTotals();
                int i = 0;

                for (i = 0; track.Events[i].DeltaTime < startTime; i++)
                {
                }

                // Copy over all events that fell before the specified time
                for (int j = i; j < track.Events.Count && track.Events[j].DeltaTime < totalTime; j++)
                {
                    newTrack.Events.Add(track.Events[j].DeepClone());
                }

                // Convert all times back (on both new and old tracks; the new one inherited the totals)
                track.Events.ConvertTotalsToDeltas();
                newTrack.Events.ConvertTotalsToDeltas();

                newTrack.Events[0].DeltaTime = 0;

                // If the new track lacks an end of track, add one
                if (!newTrack.HasEndOfTrack)
                {
                    newTrack.Events.Add(new EndOfTrackMetaMidiEvent(0));
                }
            }

            // Return the new sequence
            return(newSequence);
        }
Exemple #11
0
        /// <summary>Writes the MIDI sequence to the output stream.</summary>
        /// <param name="outputStream">The stream to which the MIDI sequence should be written.</param>
        public void Save(Stream outputStream)
        {
            // Check parameters
            Validate.NonNull("outputStream", outputStream);

            // Check valid state (as best we can check)
            if (Tracks.Count < 1)
            {
                throw new InvalidOperationException("No tracks have been added.");
            }

            // Write out the main header for the sequence
            WriteHeader(outputStream, Tracks.Count);

            // Write out each track in the order it was added to the sequence
            for (int i = 0; i < Tracks.Count; i++)
            {
                // Write out the track to the stream
                Tracks[i].Write(outputStream);
            }
        }
Exemple #12
0
        /// <summary>Write the track to the output stream.</summary>
        /// <param name="outputStream">The output stream to which the track should be written.</param>
        internal void Write(Stream outputStream)
        {
            Validate.NonNull("outputStream", outputStream);

            // Make sure we have an end of track marker if we need one
            if (!HasEndOfTrack && m_requireEndOfTrack)
            {
                throw new InvalidOperationException("The track cannot be written until it has an end of track marker.");
            }

            using (MemoryStream memStream = new MemoryStream()) {
                // Get the event data and write it out
                for (int i = 0; i < m_events.Count; i++)
                {
                    m_events[i].Write(memStream);
                }

                // Tack on the header and write the whole thing out to the main stream.
                MTrkChunkHeader header = new MTrkChunkHeader(memStream.ToArray());
                header.Write(outputStream);
            }
        }
Exemple #13
0
        /// <summary>Reads a MIDI stream into a new MidiSequence.</summary>
        /// <param name="inputStream">The stream containing the MIDI data.</param>
        /// <returns>A MidiSequence containing the parsed MIDI data.</returns>
        public static MidiSequence Open(Stream inputStream)
        {
            Validate.NonNull("inputStream", inputStream);

            // Read in the main MIDI header
            MThdChunkHeader mainHeader = MThdChunkHeader.Read(inputStream);

            // Read in all of the tracks
            MTrkChunkHeader[] trackChunks = new MTrkChunkHeader[mainHeader.NumberOfTracks];
            for (int i = 0; i < mainHeader.NumberOfTracks; i++)
            {
                trackChunks[i] = MTrkChunkHeader.Read(inputStream);
            }

            // Create the MIDI sequence
            MidiSequence sequence = new MidiSequence(mainHeader.Format, mainHeader.Division);

            for (int i = 0; i < mainHeader.NumberOfTracks; i++)
            {
                sequence.Tracks.Add(MidiParser.ParseToTrack(trackChunks[i].Data));
            }
            return(sequence);
        }
Exemple #14
0
 /// <summary>Initializes the collection.</summary>
 /// <param name="sequence">The sequence with which this collection is associated.</param>
 internal MidiTrackCollection(MidiSequence sequence)
 {
     Validate.NonNull("sequence", sequence);
     m_owningSequence = sequence;
 }
 /// <summary>Gets whether the sequence contains any lyric events.</summary>
 /// <param name="sequence">The sequence to examine.</param>
 /// <returns>true if the sequence contains any lyric events; otherwise, false.</returns>
 public static bool HasLyrics(this MidiSequence sequence)
 {
     Validate.NonNull("sequence", sequence);
     return(sequence.SelectMany(t => t.Events).Any(e => e is LyricTextMetaMidiEvent));
 }
        /// <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>
        /// <returns>The new, converted sequence.</returns>
        public static MidiSequence Convert(this MidiSequence sequence, Format format, FormatConversionOption options)
        {
            Validate.NonNull("sequence", sequence);
            Validate.InRange("format", (int)format, (int)Format.Zero, (int)Format.Two);

            if (sequence.Format == format)
            {
                // If the desired format is the same as the original, just return a copy.
                // No transformation is necessary.
                sequence = new MidiSequence(sequence);
            }
            else if (format != Format.Zero || 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 MidiSequence(format, sequence.Division);
                foreach (MidiTrack t in sequence)
                {
                    newSequence.Tracks.Add(new MidiTrack(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.
                sequence = new MidiSequence(sequence);

                // 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;
                MidiTrack newTrack    = new MidiTrack();
                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 & FormatConversionOption.CopyTrackToChannel) > 0 && trackNumber >= 0 && trackNumber <= 0xF)
                        {
                            var vme = midiEvent as VoiceMidiEvent;
                            if (vme != null)
                            {
                                vme.Channel = (byte)trackNumber;
                            }
                        }

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

                // Sort the events by total time, then convert back to delta time,
                // and top things off with an end-of-track marker.
                newTrack.Events.Sort((x, y) => x.DeltaTime.CompareTo(y.DeltaTime));
                newTrack.Events.ConvertTotalsToDeltas();
                newTrack.Events.Add(new EndOfTrackMetaMidiEvent(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);
        }