예제 #1
0
        /// <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);
        }
예제 #2
0
 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();
 }
예제 #3
0
        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);
        }
예제 #4
0
 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();
 }
예제 #5
0
        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());
        }
예제 #6
0
        private static MidiMetaEvent ItemToKeySignatureEvent(MidiItem item)
        {
            MidiMetaEvent metaEvent = item as MidiMetaEvent;

            if (metaEvent == null)
            {
                return(null);
            }
            return((metaEvent.Type == MidiMetaEvent.KeySignatureType) ? metaEvent : null);
        }
예제 #7
0
        /// <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);
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        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));
        }
예제 #10
0
        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));
        }
예제 #11
0
        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"));
        }
예제 #12
0
        /// <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;
        }
예제 #13
0
        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);
            }
        }
예제 #14
0
        /// <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;
            }
        }
예제 #15
0
        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);
        }
예제 #16
0
        /****************
        * 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;
        }
예제 #17
0
 private static string DescribeChannelPrefix(byte[] bytes)
 {
     return(MidiMetaEvent.DescribeNumber(MidiMetaEvent.MidiChannelPrefixType, bytes));
 }
예제 #18
0
 private static string DescribeSequenceNumber(byte[] bytes)
 {
     return(MidiMetaEvent.DescribeNumber(MidiMetaEvent.SequenceNumberType, bytes));
 }
예제 #19
0
        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);
            }
        }