/// <summary> /// Initializes a new instance of the Track class with another instance /// of the Track class. /// </summary> /// <param name="trk"> /// The Track instance to use for initialization. /// </param> public Track(Track trk) { // Copy events from the existing track into this one. foreach(MidiEvent e in trk.midiEvents) { this.midiEvents.Add(e.Clone()); } }
/// <summary> /// Initializes a new instance of the TrackPlayer class with the /// specified MIDI sender, tick generator, and track. /// </summary> /// <param name="midiSender"> /// The MIDI sender to use for sending MIDI messages. /// </param> /// <param name="tickGen"> /// The tick generator used for timing the playback of MIDI messages. /// </param> /// <param name="trk"> /// The track to play back. /// </param> public TrackPlayer(IMidiSender midiSender, TickGenerator tickGen, Track trk) { this._midiSender = midiSender; this.tickGen = tickGen; this.trk = trk; tickGen.Tick += new EventHandler(TickHandler); // Set playback position at the beginning of the track. Seek(0); }
/// <summary> /// Reads the number of tracks in the Midi file. /// </summary> /// <returns> /// <b>MidiFileResult.Succes</b> if the track count and format type /// are valid; otherwise, <b>MidiFileResult.InvalidFormat</b>. /// </returns> private MidiFileResult ReadTrackCount() { MidiFileResult result = MidiFileResult.Success; byte[] trackCount = binReader.ReadBytes(TrackCountByteCount); // Convert array to the same byte order as this platform. ConvertByteArray(trackCount); // Convert array to number. tracks = new Track[BitConverter.ToInt16(trackCount, 0)]; // If there are more than one track and the Midi file is type 0. if(tracks.Length > 1 && Format == 0) { // Indicate that the format is invalid. A type 0 Midi file can // only have one track. result = MidiFileResult.InvalidFormat; } // If the format is valid. if(result == MidiFileResult.Success) { // Allocate tracks. for(int i = 0; i < tracks.Length; i++) { tracks[i] = new Track(); } } return result; }
/// <summary> /// Initializes a new instance of the TrackEnumerator class with /// the specified track to iterate over. /// </summary> /// <param name="owner"> /// The track to iterate over. /// </param> public TrackEnumerator(Track owner) { this.owner = owner; version = owner.Version; }
/// <summary> /// /// </summary> /// <param name="tracks"></param> /// <returns></returns> public static Track Merge(ArrayList tracks) { Track mergedTrack = new Track(); Track currentTrack; ArrayList trackList = new ArrayList(); ArrayList events = new ArrayList(); ArrayList trackIndexes = new ArrayList(); for(int i = 0; i < tracks.Count; i++) { currentTrack = (Track)tracks[i]; if(currentTrack.Count > 1) { trackList.Add(currentTrack); trackIndexes.Add(0); events.Add(currentTrack[0]); } } while(events.Count > 0) { int n = 0; MidiEvent e1 = (MidiEvent)events[0]; MidiEvent e2; int ticks = e1.Ticks; for(int i = 1; i < events.Count; i++) { e1 = (MidiEvent)events[i]; if(e1.Ticks < ticks) { ticks = e1.Ticks; n = i; } } e1 = (MidiEvent)events[n]; mergedTrack.Add(e1); for(int i = 0; i < events.Count; i++) { e2 = (MidiEvent)events[i]; e2.Ticks -= e1.Ticks; events[i] = e2; } int counter = (int)trackIndexes[n] + 1; currentTrack = (Track)trackList[n]; if(counter < currentTrack.Count - 1) { events[n] = currentTrack[counter]; trackIndexes[n] = counter; } else { trackList.RemoveAt(n); trackIndexes.RemoveAt(n); events.RemoveAt(n); } } return mergedTrack; }
/// <summary> /// Merges two tracks together. /// </summary> /// <param name="trackA"> /// The first of two tracks to merge. /// </param> /// <param name="trackB"> /// The second of two tracks to merge. /// </param> /// <returns> /// The merged track. /// </returns> public static Track Merge(Track trackA, Track trackB) { Track trkA = new Track(trackA); Track trkB = new Track(trackB); Track mergedTrack = new Track(); int a = 0, b = 0; // // The following algorithm merges two Midi tracks together. It // assumes that both tracks are valid in that both end with a // end of track meta message. // // While neither the end of track A or track B has been reached. while(a < trkA.Count - 1 && b < trkB.Count - 1) { // While the end of track A has not been reached and the // current Midi event in track A comes before the current Midi // event in track B. while(a < trkA.Count - 1 && trkA[a].Ticks <= trkB[b].Ticks) { // Slide the events in track B backwards by the amount of // ticks in the current event in track A. This keeps both // tracks in sync. trkB.Slide(b, -trkA[a].Ticks); // Add the current event in track A to the merged track. mergedTrack.Add(trkA[a]); // Move to the next Midi event in track A. a++; } // If the end of track A has not yet been reached. if(a < trkA.Count - 1) { // While the end of track B has not been reached and the // current Midi event in track B comes before the current Midi // event in track A. while(b < trkB.Count - 1 && trkB[b].Ticks < trkA[a].Ticks) { // Slide the events in track A backwards by the amount of // ticks in the current event in track B. This keeps both // tracks in sync. trkA.Slide(a, -trkB[b].Ticks); // Add the current event in track B to the merged track. mergedTrack.Add(trkB[b]); // Move forward to the next Midi event in track B. b++; } } } // If the end of track A has not yet been reached. if(a < trkA.Count - 1) { // Add the rest of the events in track A to the merged track. while(a < trkA.Count - 1) { mergedTrack.Add(trkA[a]); a++; } } // Else if the end of track B has not yet been reached. else if(b < trkB.Count - 1) { // Add the rest of the events in track B to the merged track. while(b < trkB.Count - 1) { mergedTrack.Add(trkB[b]); b++; } } return mergedTrack; }
/// <summary> /// Removes the specified track from the sequence. /// </summary> /// <param name="trk"> /// The track to remove. /// </param> /// <exception cref="InvalidOperationException"> /// Thrown if the sequence is currently locked. /// </exception> public void Remove(Track trk) { // Enforce preconditions. if(IsLocked()) throw new InvalidOperationException( "Cannot modify sequence. It is currently locked."); int i = 0; while(i < tracks.Count) { DictionaryEntry de = (DictionaryEntry)tracks[i]; if((Track)de.Key == trk) { tracks.RemoveAt(i); break; } else { i++; } } version++; }
/// <summary> /// Adds a track to the Sequence. /// </summary> /// <param name="trk"> /// The track to add to the Sequence. /// </param> /// <exception cref="InvalidOperationException"> /// Thrown if the sequence is currently locked. /// </exception> public void Add(Track trk) { // Enforce preconditions. if(IsLocked()) throw new InvalidOperationException( "Cannot modify sequence. It is currently locked."); tracks.Add(new DictionaryEntry(trk, trk.Version)); version++; }
/// <summary> /// Writes the specified track to MIDI file. /// </summary> /// <param name="trk"> /// The track to write to the MIDI file. /// </param> private void WriteTrack(Track trk) { writer.Write(TrackHeaderID); // // The length of the track in bytes will be written to the // MIDI file first. However, at this point, the length is unknown. // // The track is written to a memory stream first. After this is // done, the length of the track can be retrieved from the stream. // This value is written to the MIDI file followed by the contents // of the stream. // midiStream = new MemoryStream(); MessageWriter msgWriter = new MessageWriter(midiStream); // Write each MIDI event to the stream via the message writer. for(int i = 0; i < trk.Count; i++) { msgWriter.WriteNextEvent(trk[i]); } // Get the length of the track. int trackLength = (int)midiStream.Length; // Convert value if this platform uses the little endian format. if(BitConverter.IsLittleEndian) { byte[] b = BitConverter.GetBytes(trackLength); Array.Reverse(b); trackLength = BitConverter.ToInt32(b, 0); } // Write track length. writer.Write(trackLength); midiStream.Position = 0; // Write the contents of the stream to the MIDI file. for(int i = 0; i < midiStream.Length; i++) { writer.Write((byte)midiStream.ReadByte()); } }