Example #1
0
        /* Create a new MIDI file (represented by this instance), including the header chunk and appropriate
         * number of track chunks (with End of Track events).  This does not set delta-time division.
         */
        private MidiHeader Create(int format, int numberOfTracks)
        {
            /* Make sure we start with a clean slate. */
            this.Clear();
            int n = MidiChunkInfo.SizeItem();

            this.Bytes = new byte[n + MidiHeader.SizeItem()];

            /* Start the header chunk. */
            MidiChunkInfo chunkInfo = this.CreateChunkInfo(0, MidiChunkInfo.HeaderType);

            this.Items.Add(chunkInfo);

            /* Finish the header chunk.  (Don't set NumberOfTracks here; AddTrack will set it.) */
            MidiHeader header = new MidiHeader(this, n);

            header.Format = format;
            this.Items.Add(header);

            /* Add each track chunk (with an End of Track meta-event). */
            for (int i = 0; i < numberOfTracks; ++i)
            {
                this.AddTrack();
                this.AddMetaEvent(0, MidiMetaEvent.EndOfTrackType, null);
            }

            /* Delta-time division still needs to be set. */
            return(header);
        }
Example #2
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);
        }
Example #3
0
        /// <summary>Inserts a new track (MTrk) chunk into this file at the specified index.</summary>
        /// <param name="index">The index in this file's list at which the track chunk should be inserted.</param>
        /// <returns>The new MidiChunkInfo object that is inserted.</returns>
        public MidiChunkInfo InsertTrack(int index)
        {
            int n = MidiChunkInfo.SizeItem(), offset = this.Items[index].Offset;

            this.Resize(n, offset, -1);
            MidiChunkInfo chunkInfo = this.CreateChunkInfo(offset, MidiChunkInfo.TrackType);

            this.Items.Insert(index, chunkInfo);
            return(chunkInfo);
        }
Example #4
0
        /// <summary>Adds a new track (MTrk) chunk to the end of this file.</summary>
        /// <returns>The new MidiChunkInfo object that is added.</returns>
        public MidiChunkInfo AddTrack()
        {
            int n = MidiChunkInfo.SizeItem(), offset = this.Bytes.Length;

            this.Resize(n, offset, -1);
            MidiChunkInfo chunkInfo = this.CreateChunkInfo(offset, MidiChunkInfo.TrackType);

            this.Items.Add(chunkInfo);
            return(chunkInfo);
        }
Example #5
0
        private MidiChunkInfo CreateChunkInfo(int offset, string type)
        {
            MidiChunkInfo chunkInfo = new MidiChunkInfo(this, offset);

            chunkInfo.Type   = type;
            chunkInfo.Length = (type == MidiChunkInfo.HeaderType) ? MidiHeader.SizeItem() : 0;
            if (type == MidiChunkInfo.TrackType)
            {
                ++this.Header.NumberOfTracks;
            }
            return(chunkInfo);
        }
Example #6
0
        /// <summary>Loads an existing MIDI file from disk (into this instance).</summary>
        /// <param name="path">The path of the MIDI file to load.</param>
        public void Load(string path)
        {
            int           n, i, j = 0;
            MidiChunkInfo chunkInfo;
            MidiHeader    header = null;

            /* Make sure we start with a clean slate. */
            this.Clear();
            this.Bytes = File.ReadAllBytes(path);

            /* Process each MidiItem object from the byte array. */
            for (i = 0; i < this.Bytes.Length; i += chunkInfo.Length)
            {
                /* A chunk should begin here. */
                chunkInfo = new MidiChunkInfo(this, i);
                this.Items.Add(chunkInfo);
                i += MidiChunkInfo.SizeItem();

                /* What comes next depends on the chunk type. */
                switch (chunkInfo.Type)
                {
                case MidiChunkInfo.HeaderType:
                    if (header == null)
                    {
                        header = new MidiHeader(this, i);
                        this.Items.Add(header);
                    }
                    else
                    {
                        this.AddErrorText(Properties.Resources.MultipleHeaders, 0);
                    }
                    break;

                case MidiChunkInfo.TrackType: this.ParseEvents(i, chunkInfo.Length, ++j); break;
                }
            }

            /* Check for track number mismatch. */
            n = (header == null) ? 0 : header.NumberOfTracks;
            if (n != j)
            {
                string s = UI.ParseLabel(Properties.Resources.Track).ToLower();
                s = string.Format(Properties.Resources.MismatchFormat, s, n, j);
                this.AddErrorText(s, 0);
            }
        }
Example #7
0
        /// <summary>Resizes this file's byte array, preserving the data from a given offset to the end.</summary>
        /// <param name="delta">The number of bytes by which to resize the array.</param>
        /// <param name="offset">Offset into the byte array at which to begin preservation.</param>
        /// <param name="index">
        /// Optional (non-negative) index of the affected MidiItem object in this file's list (or zero to
        /// determine the index dynamatically), so that the length of the containing chunk can be adjusted.
        /// </param>
        public void Resize(int delta, int offset, int index)
        {
            if (delta == 0)
            {
                return;
            }

            int i, j, m, n;

            if (delta < 0)
            {
                m = offset;
                n = this.Bytes.Length;
            }
            else
            {
                m = this.Bytes.Length - 1;
                n = offset - 1;
                Array.Resize(ref this.Bytes, this.Bytes.Length + delta);
            }
            for (j = delta / Math.Abs(delta), i = m; i != n; i -= j)
            {
                this.Bytes[i + delta] = this.Bytes[i];
                this.Bytes[i]         = 0;
            }
            if (delta < 0)
            {
                Array.Resize(ref this.Bytes, this.Bytes.Length + delta);
            }

            /* If applicable, cascade offset changes through all subsequent items. */
            j = offset + delta;
            if (j < this.Bytes.Length)
            {
                for (i = this.GetItemIndex(offset); i < this.ItemCount; ++i)
                {
                    this.Items[i].Offset += delta;
                }

                /* Since our map of running statuses is keyed by offset, it must also be adjusted correspondingly. */
                List <int> keys = new List <int>();

                /* Collect a list of keys (offsets) affected by this change. */
                foreach (int key in this.RunningStatusMap.Keys)
                {
                    if (key >= offset)
                    {
                        keys.Add(key);
                    }
                }
                if (delta > 0)
                {
                    keys.Reverse();
                }

                /* Replace each affected entry in the map with a new one having the updated key (offset). */
                foreach (int key in keys)
                {
                    int status = this.RunningStatusMap[key];
                    this.RunningStatusMap.Remove(key);
                    if (delta < 0 && key < j)
                    {
                        continue;
                    }
                    this.RunningStatusMap[key + delta] = status;
                }
            }

            /* If specified, adjust the length of the containing chunk. */
            if (index < 0)
            {
                return;
            }
            MidiChunkInfo chunkInfo = null;

            for (i = (index > 0) ? index : this.GetItemIndex((delta < 0) ? j : offset);
                 chunkInfo == null; chunkInfo = this.Items[--i] as MidiChunkInfo)
            {
                ;
            }
            chunkInfo.Length += delta;
        }