/// <summary>Removes from this file the MidiItem object at the specified index.</summary> /// <param name="index">The index in this file's list of the MidiItem object to remove.</param> public void RemoveItem(int index) { /* Determine how many bytes to remove from the array. If the item is chunk info, the entire * chunk will be removed. If it is a track (MTrk) chunk, decrement the number of tracks. */ MidiItem item = this.Items[index]; int i, n = item.Size; MidiChunkInfo chunkInfo = item as MidiChunkInfo; if (chunkInfo != null) { n += chunkInfo.Length; i = this.Bytes.Length - item.Offset; if (n > i) { n = i; } if (chunkInfo.Type == MidiChunkInfo.TrackType) { --this.Header.NumberOfTracks; } } /* If the item is an MTrk event with a nonzero delta-time, the total time of each subsequent * event in the track/chunk will need to be adjusted accordingly (after this item is removed). */ MidiEvent mtrkEvent = item as MidiEvent; int deltaTime = (mtrkEvent == null) ? 0 : mtrkEvent.DeltaTime; /* Remove from the list all items whose offsets are within the range of data being removed. */ for (i = item.Offset + n; index < this.ItemCount && this.Items[index].Offset < i; this.Items.RemoveAt(index)) { item = this.Items[index]; /* If the item being removed is a channel message/event that does not use running status, there * should be a corresponding entry in the map of running statuses that needs to be removed. */ MidiChannelEvent channelEvent = item as MidiChannelEvent; if (channelEvent != null && !channelEvent.RunningStatus) { this.RunningStatusMap.Remove(item.Offset); } /* If the item being removed is a meta-event representing a key signature, there should * be a corresponding entry in the key signature map that also needs to be removed. */ MidiMetaEvent metaEvent = MidiFile.ItemToKeySignatureEvent(item); if (metaEvent != null) { this.KeySignatureMap.Remove(metaEvent.TotalTime); } } /* If applicable, cascade total time changes through all subsequent events in the track. */ this.AdjustTotalTimes(index, -deltaTime); /* Remove the appropriate number of bytes from the array. */ this.Resize(-n, i, (chunkInfo == null) ? index : -1); }
private void SetTotalTime(int index) { MidiEvent mtrkEvent = this.Items[index] as MidiEvent, previousEvent = this.Items[index - 1] as MidiEvent; mtrkEvent.TotalTime = mtrkEvent.DeltaTime + ((previousEvent == null) ? 0 : previousEvent.TotalTime); /* If the event is a meta-event representing a key signature, * use it to set the key signature at the appropriate time. */ MidiMetaEvent metaEvent = MidiFile.ItemToKeySignatureEvent(mtrkEvent); if (metaEvent != null) { this.SetKeySignature(metaEvent); } }
/// <summary> /// Changes the total (cumulative) time of each MTrk event in a track chunk, starting /// at the specified index in this file's list, by the specified number of ticks. /// </summary> /// <param name="index">The index of the event in this file's list at which to start the change.</param> /// <param name="delta">The number of ticks by which to change the total time of each event.</param> public void AdjustTotalTimes(int index, int delta) { if (delta == 0) { return; } /* Starting at the specified index, change the total time of each event in the list (until end- * of-list or a non-event is encountered, presumably indicating the end of the track/chunk), * and collect a list of key signature map "keys" (i.e., total times) affected by the change. */ List <int> keys = new List <int>(); for (int i = index; i < this.ItemCount; ++i) { MidiEvent mtrkEvent = this.Items[i] as MidiEvent; if (mtrkEvent == null) { break; } mtrkEvent.TotalTime += delta; /* If this is a meta-event representing a key signature, add its former * total time to the list of keys (total times) affected by this change. */ MidiMetaEvent metaEvent = MidiFile.ItemToKeySignatureEvent(mtrkEvent); if (metaEvent != null) { keys.Add(metaEvent.TotalTime - delta); } } /* Since our key signature map is keyed by total time, it must be adjusted correspondingly. * Replace each affected entry with a new one having the updated "key" (total time). */ if (delta > 0) { keys.Reverse(); } foreach (int key in keys) { MidiKeySignature keySignature = this.KeySignatureMap[key]; this.KeySignatureMap.Remove(key); this.KeySignatureMap[key + delta] = keySignature; } }
private void ParseEvents(int offset, int length, int trackNumber) { string s; int n = offset + length, i, j = 0; MidiEvent mtrkEvent = null; MidiMetaEvent metaEvent; /* A track chunk is a stream of MIDI (MTrk) events. Process each such * event in this part of the byte array comprising the track chunk. */ for (i = offset; i < n; i += mtrkEvent.Size) { /* In order to know what kind of event to instantiate, we must read the event's status * byte, which requires skipping past the delta-time (stored as a variable-length quantity). */ for (j = i; j < this.Bytes.Length && (this.Bytes[j] & 0x80) > 0; ++j) { if (j - i > 3) { s = string.Format("{0} (@ {1} {2})", Properties.Resources.InvalidVLQ, Properties.Resources.Byte, i); this.AddErrorText(s, trackNumber); return; } } if (++j >= this.Bytes.Length) { break; } /* Instantiate an event object of the appropriate type (based on the status byte). */ switch (this.Bytes[j]) { case 0xFF: mtrkEvent = new MidiMetaEvent(this, i); break; case 0xF7: mtrkEvent = new MidiSysExEvent(this, i); break; case 0xF0: mtrkEvent = new MidiSysExEvent(this, i); break; default: mtrkEvent = new MidiChannelEvent(this, i); break; } this.Items.Add(mtrkEvent); this.SetTotalTime(this.ItemCount - 1); /* If the event is a MIDI channel message/event that does not use * running status, use it to set the running status at this byte offset. */ if (++j >= this.Bytes.Length) { break; } MidiChannelEvent channelEvent = mtrkEvent as MidiChannelEvent; if (channelEvent != null && !channelEvent.RunningStatus) { this.SetRunningStatus(i, channelEvent.Status); } /* If the event is a meta-event representing a key signature, * use it to set the key signature at the appropriate time. */ if (++j >= this.Bytes.Length) { break; } metaEvent = MidiFile.ItemToKeySignatureEvent(mtrkEvent); if (metaEvent != null) { this.SetKeySignature(metaEvent); } } /* If we ran out of data, add an error message. */ if (j >= this.Bytes.Length) { s = string.Format(Properties.Resources.MismatchFormat, Properties.Resources.Byte, length, i - offset); this.AddErrorText(s, trackNumber); } /* The last event in a track chunk should be an End of Track meta-event. */ metaEvent = mtrkEvent as MidiMetaEvent; if (metaEvent == null || (metaEvent != null && metaEvent.Type != MidiMetaEvent.EndOfTrackType)) { this.AddErrorText(Properties.Resources.NoEndOfTrack, trackNumber); } }