/// <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 TypeTextBox_TextChanged(object sender, TextChangedEventArgs e) { this.ValidateNumericInput(this.TypeLabel, ref this._Type, 0, sbyte.MaxValue); this.TypeTextBlock.Text = (this.Type < 0) ? null : MidiMetaEvent.GetTypeComment(this.Type); this.DataLength = MidiMetaEvent.GetDataLength(this.Type); this.DataCommentTextBox.IsReadOnly = (this.DataLength >= 0); this.UpdateDataComment(); }
private MidiMetaEvent CreateMetaEvent(int offset, int deltaTime, int type, byte[] bytes) { MidiMetaEvent metaEvent = new MidiMetaEvent(this, offset); this.Bytes[metaEvent.StatusOffset] = 0xFF; metaEvent.DeltaTime = deltaTime; metaEvent.Type = type; metaEvent.Data = bytes; return(metaEvent); }
private void UpdateDataComment() { this.IsDataLengthValid = (this.DataLength < 0) ? true : (((this.Data == null) ? 0 : this.Data.Length) >= this.DataLength); this.NoValidation = true; this.DataCommentTextBox.Text = (this.Type < 0 || this.Data == null) ? null : MidiMetaEvent.GetDataComment(this.Type, this.Data); this.NoValidation = false; this.EnableOkButton(); }
private static string DescribeNumber(int type, byte[] bytes) { int n = MidiMetaEvent.GetDataLength(type); if (bytes.Length < n) { return(null); } return(Midi.ReadNumber(bytes, 0, n).ToString()); }
private static MidiMetaEvent ItemToKeySignatureEvent(MidiItem item) { MidiMetaEvent metaEvent = item as MidiMetaEvent; if (metaEvent == null) { return(null); } return((metaEvent.Type == MidiMetaEvent.KeySignatureType) ? metaEvent : null); }
/// <summary>Inserts a new meta-event into this file at the specified index.</summary> /// <param name="index">The index in this file's list at which the event should be inserted.</param> /// <param name="deltaTime">The amount of time (in ticks) between the previous event in the track and this one.</param> /// <param name="type">Meta-event type (always less than 128).</param> /// <param name="bytes"> /// Array of bytes containing the event data (not including the delta-time, status byte, or type byte). /// </param> /// <returns>The new MidiMetaEvent object that is inserted.</returns> public MidiMetaEvent InsertMetaEvent(int index, int deltaTime, int type, byte[] bytes) { int n = MidiMetaEvent.SizeItem(0, 0), offset = this.Items[index].Offset; this.Resize(n, offset, index); MidiMetaEvent metaEvent = this.CreateMetaEvent(offset, deltaTime, type, bytes); this.Items.Insert(index, metaEvent); this.SetTotalTime(index); return(metaEvent); }
/// <summary>Adds a new meta-event to the end of this file.</summary> /// <param name="deltaTime">The amount of time (in ticks) between the previous event in the track and this one.</param> /// <param name="type">Meta-event type (always less than 128).</param> /// <param name="bytes"> /// Array of bytes containing the event data (not including the delta-time, status byte, or type byte). /// </param> /// <returns>The new MidiMetaEvent object that is added.</returns> public MidiMetaEvent AddMetaEvent(int deltaTime, int type, byte[] bytes) { int n = MidiMetaEvent.SizeItem(0, 0), offset = this.Bytes.Length; this.Resize(n, offset, this.ItemCount); MidiMetaEvent metaEvent = this.CreateMetaEvent(offset, deltaTime, type, bytes); this.Items.Add(metaEvent); this.SetTotalTime(this.ItemCount - 1); return(metaEvent); }
private static string DescribeTimeSignature(byte[] bytes) { int n = MidiMetaEvent.GetDataLength(MidiMetaEvent.TimeSignatureType); if (bytes.Length < n) { return(null); } return(string.Format("{0}/{1} {2}, {3} {4}, {5} {6}", bytes[0], Math.Pow(2, bytes[1]), Properties.Resources.Time, bytes[2], Properties.Resources.ClocksPerClick, bytes[3], Properties.Resources.NotesPerQuarterNote)); }
private static string DescribeTempo(byte[] bytes) { int n = MidiMetaEvent.GetDataLength(MidiMetaEvent.SetTempoType); if (bytes.Length < n) { return(null); } n = Midi.ReadNumber(bytes, 0, n); return(string.Format("{0} {1} ({2} {3})", n, Properties.Resources.MicrosecondsPerQuarterNote, (n == 0) ? double.PositiveInfinity : (60000000 / n), Properties.Resources.BeatsPerMinute)); }
private static string DescribeKeySignature(byte[] bytes) { int n = MidiMetaEvent.GetDataLength(MidiMetaEvent.KeySignatureType); if (bytes.Length < n) { return(null); } MidiKeySignature k = MidiMetaEvent.DataToKeySignature(bytes); return((k == MidiKeySignature.NA) ? null : k.ToString().Replace("Flat", "b").Replace("Sharp", "#").Replace("Major", " major").Replace("Minor", " minor")); }
/// <summary>Adds an entry to the key signature map, or updates the entry if it already exists.</summary> /// <param name="metaEvent">A MidiMetaEvent object representing a key signature.</param> public void SetKeySignature(MidiMetaEvent metaEvent) { if (metaEvent.TotalTime < 0) { return; } MidiKeySignature keySignature = MidiMetaEvent.DataToKeySignature(metaEvent.Data); if (keySignature == MidiKeySignature.NA) { return; } this.KeySignatureMap[metaEvent.TotalTime] = keySignature; }
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 static string DescribeSmpteOffset(byte[] bytes) { int n = MidiMetaEvent.GetDataLength(MidiMetaEvent.SmpteOffsetType); if (bytes.Length < n) { return(null); } string s = string.Empty; foreach (byte b in bytes) { if (s.Length > 0) { s += (s.Length > 9) ? "." : ":"; } s += b.ToString("00"); } return(s); }
/**************** * Constructors * ****************/ #region Public Constructors /// <summary>Initializes a new instance of the MidiMetaEventDialog class.</summary> /// <param name="metaEvent"> /// MidiMetaEvent object representing the MIDI meta-event to edit, or null to create a new one. /// </param> public MidiMetaEventDialog(MidiMetaEvent metaEvent) : base(metaEvent) { int i = this.ControlCount; DockPanel typePanel, dataPanel; /* Initialize the "Type" controls. */ this.TypeLabel = UI.CreateLabel(MarginType.Standard, Properties.Resources.Type, true); typePanel = MidiEventDialog.CreateDataBytePanel(ref this.TypeTextBox, ref this.TypeTextBlock, ++i); this.TypeTextBox.TextChanged += this.TypeTextBox_TextChanged; this.TypeLabel.Target = this.TypeTextBox; /* Initialize the "Data" controls. */ Label label = UI.CreateLabel(MarginType.Standard, Properties.Resources.Data, true); dataPanel = MidiEventDialog.CreateDataPanel(ref this.DataHexTextBox, ref this.DataCommentTextBox, ++i); this.DataHexTextBox.TextChanged += this.DataHexTextBox_TextChanged; this.DataCommentTextBox.TextChanged += this.DataCommentTextBox_TextChanged; label.Target = this.DataHexTextBox; /* Build out the window and its content. */ this.AddUIElement(this.TypeLabel); this.AddUIElement(typePanel); this.AddUIElement(label); this.AddUIElement(dataPanel); this.BuildOut(UI.ClientWidth, MidiMetaEventDialog.TitleString); /* The OK button should start out disabled and stay that way until all required input is entered. */ this.OkButton.IsEnabled = false; /* If a MidiMetaEvent object was supplied, use it to set initial values. */ if (this.ForNewItem) { return; } this.DeltaTime = metaEvent.DeltaTime; this.TypeTextBox.Text = metaEvent.Type.ToString(); this.NoValidation = true; this.DataHexTextBox.Text = this.Hex = Midi.FormatHex(metaEvent.Data, 0, metaEvent.Data.Length); this.NoValidation = false; }
private static string DescribeChannelPrefix(byte[] bytes) { return(MidiMetaEvent.DescribeNumber(MidiMetaEvent.MidiChannelPrefixType, bytes)); }
private static string DescribeSequenceNumber(byte[] bytes) { return(MidiMetaEvent.DescribeNumber(MidiMetaEvent.SequenceNumberType, bytes)); }
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); } }