/// <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 && 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 < Events.Count; i++) { 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); } }
/// <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 MidiSequence Trim(this MidiSequence sequence, long totalTime) { Validate.NonNull("sequence", sequence); 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(); // Copy over all events that fell before the specified time for (int i = 0; i < track.Events.Count && track.Events[i].DeltaTime < totalTime; i++) { newTrack.Events.Add(track.Events[i].DeepClone()); } // Convert all times back (on both new and old tracks; the new one inherited the totals) track.Events.ConvertTotalsToDeltas(); newTrack.Events.ConvertTotalsToDeltas(); // 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); }
/// <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); }