예제 #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 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);
            }
        }
예제 #3
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;
            }
        }
예제 #4
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);
            }
        }