Example #1
0
        private static void UpdateTrackEnd(ICollection <MidiEvent> events, long?newTime = null)
        {
            var  endTrack = events.SingleOrDefault(MidiEvent.IsEndTrack);
            long absoluteTime;

            if (newTime == null)
            {
                var lastEvent = events.Where(e => !MidiEvent.IsEndTrack(e)).OrderBy(e => e.AbsoluteTime).LastOrDefault();
                absoluteTime = lastEvent?.AbsoluteTime ?? 0;
            }
            else
            {
                absoluteTime = newTime.Value;
            }

            if (endTrack == null)
            {
                events.Add(new MetaEvent(MetaEventType.EndTrack, 0, absoluteTime));
            }
            else
            {
                events.Remove(endTrack);
                endTrack.AbsoluteTime = absoluteTime;
                events.Add(endTrack);
            }
        }
Example #2
0
        private bool RemoveTrackIfEmpty(MidiEventCollection midi, int trackNumber)
        {
            // If a track only has an EndTrack (and optionally a name) then it is "empty"
            if (midi[trackNumber].All(e => e.IsSequenceTrackName() || MidiEvent.IsEndTrack(e)))
            {
                midi.RemoveTrack(trackNumber);
                return(true);
            }

            return(false);
        }
Example #3
0
        public static MemoryStream ExportMidiToStream(MidiEventCollection events)
        {
            //Taken from https://github.com/naudio/NAudio/blob/master/NAudio/Midi/MidiFile.cs#L256 and edited to support MemoryStream export(thanks Mettra)
            if (events.MidiFileType == 0 && events.Tracks > 1)
            {
                throw new ArgumentException("Can't export more than one track to a type 0 file");
            }


            using (MemoryStream stream = new MemoryStream())
            {
                using (var writer = new BinaryWriter(stream))
                {
                    writer.Write(Encoding.UTF8.GetBytes("MThd"));
                    writer.Write(SwapUInt32(6)); // chunk size
                    writer.Write(SwapUInt16((ushort)events.MidiFileType));
                    writer.Write(SwapUInt16((ushort)events.Tracks));
                    writer.Write(SwapUInt16((ushort)events.DeltaTicksPerQuarterNote));

                    for (int track = 0; track < events.Tracks; track++)
                    {
                        IList <MidiEvent> eventList = events[track];

                        writer.Write(Encoding.UTF8.GetBytes("MTrk"));
                        long trackSizePosition = writer.BaseStream.Position;
                        writer.Write(SwapUInt32(0));

                        long absoluteTime = events.StartAbsoluteTime;

                        // use a stable sort to preserve ordering of MIDI events whose
                        // absolute times are the same
                        MergeSort.Sort(eventList, new MidiEventComparer());
                        if (eventList.Count > 0)
                        {
                            System.Diagnostics.Debug.Assert(MidiEvent.IsEndTrack(eventList[eventList.Count - 1]), "Exporting a track with a missing end track");
                        }
                        foreach (var midiEvent in eventList)
                        {
                            midiEvent.Export(ref absoluteTime, writer);
                        }

                        uint trackChunkLength = (uint)(writer.BaseStream.Position - trackSizePosition) - 4;
                        writer.BaseStream.Position = trackSizePosition;
                        writer.Write(SwapUInt32(trackChunkLength));
                        writer.BaseStream.Position += trackChunkLength;
                    }
                }
                stream.Flush();
                byte[] bytes = stream.GetBuffer();
                return(new MemoryStream(bytes));
            }
        }
Example #4
0
        public void ConsolidateTracks(MidiEventCollection midi)
        {
            // find all of the names
            var nameCounts = new Dictionary <string, int>();

            for (var t = 0; t < midi.Tracks; t++)
            {
                var names = midi[t].
                            OfType <TextEvent>().
                            Where(e => e.MetaEventType == MetaEventType.SequenceTrackName).
                            ToList();

                if (names.Count > 1)
                {
                    var detail = string.Join(", ", names.Select(n => $"'{n.Text}'"));
                    throw new InvalidOperationException($"Multiple names {detail} on the same track.");
                }

                var name = names.FirstOrDefault()?.Text ?? "";

                int count;
                nameCounts.TryGetValue(name, out count);
                nameCounts[name] = count + 1;
            }

            /* For all of the names that appear on more than one track
             * find all the other tracks that have this name and consolidate them.
             * We iterate multiple times because the track numbers will change every
             * time tracks are consolidated. */
            foreach (var kvp in nameCounts.Where(kvp => kvp.Value > 1))
            {
                var name = kvp.Key;
                var list = new List <MidiEvent>();

                // iterate in reverse so track numbers don't change mid iteration
                for (var t = midi.Tracks - 1; t >= 0; t--)
                {
                    if (!midi[t].OfType <TextEvent>().Any(e => e.IsSequenceTrackName() && e.Text == name))
                    {
                        continue;
                    }

                    var events = midi[t].Where(e => !MidiEvent.IsEndTrack(e) && !e.IsSequenceTrackName());

                    list.AddRange(events);
                    midi.RemoveTrack(t);
                }

                midi.AddNamedTrack(name, list.OrderBy(e => e.AbsoluteTime));
            }
        }
Example #5
0
        /// <summary>
        /// Exports a MIDI file
        /// </summary>
        /// <param name="filename">Filename to export to</param>
        /// <param name="events">Events to export</param>
        public static void Export(string filename, MidiEventCollection events)
        {
            if (events.MidiFileType == 0 && events.Tracks > 1)
            {
                throw new ArgumentException("Can't export more than one track to a type 0 file");
            }
            using (var writer = new BinaryWriter(File.Create(filename)))
            {
                writer.Write(Encoding.UTF8.GetBytes("MThd"));
                writer.Write(SwapUInt32((uint)6));                 // chunk size
                writer.Write(SwapUInt16((ushort)events.MidiFileType));
                writer.Write(SwapUInt16((ushort)events.Tracks));
                writer.Write(SwapUInt16((ushort)events.DeltaTicksPerQuarterNote));

                for (int track = 0; track < events.Tracks; track++)
                {
                    IList <MidiEvent> eventList = events[track];

                    writer.Write(Encoding.UTF8.GetBytes("MTrk"));
                    long trackSizePosition = writer.BaseStream.Position;
                    writer.Write(SwapUInt32((uint)0));

                    long absoluteTime = events.StartAbsoluteTime;

                    // use a stable sort to preserve ordering of MIDI events whose
                    // absolute times are the same
                    MergeSort.Sort(eventList, new MidiEventComparer());
                    if (eventList.Count > 0)
                    {
                        System.Diagnostics.Debug.Assert(MidiEvent.IsEndTrack(eventList[eventList.Count - 1]), "Exporting a track with a missing end track");
                    }
                    foreach (MidiEvent midiEvent in eventList)
                    {
                        midiEvent.Export(ref absoluteTime, writer);
                    }

                    uint trackChunkLength = (uint)(writer.BaseStream.Position - trackSizePosition) - 4;
                    writer.BaseStream.Position = trackSizePosition;
                    writer.Write(SwapUInt32(trackChunkLength));
                    writer.BaseStream.Position += trackChunkLength;
                }
            }
        }
        public void TestType0()
        {
            MidiEventCollection collection = new MidiEventCollection(0, 120);

            collection.AddEvent(new TextEvent("Test", MetaEventType.TextEvent, 0), 0);
            collection.AddEvent(new NoteOnEvent(0, 1, 30, 100, 15), 1);
            collection.AddEvent(new NoteOnEvent(15, 1, 30, 100, 15), 1);
            collection.AddEvent(new NoteOnEvent(30, 1, 30, 100, 15), 1);
            collection.AddEvent(new NoteOnEvent(0, 10, 60, 100, 15), 10);
            collection.AddEvent(new NoteOnEvent(15, 10, 60, 100, 15), 10);
            collection.AddEvent(new NoteOnEvent(30, 10, 60, 100, 15), 10);
            Assert.AreEqual(collection.Tracks, 1);
            collection.PrepareForExport();
            Assert.AreEqual(collection.Tracks, 1);
            IList <MidiEvent> track0 = collection.GetTrackEvents(0);

            Assert.AreEqual(track0.Count, 8);
            Assert.IsTrue(MidiEvent.IsEndTrack(track0[track0.Count - 1]));
        }
        public void TestType0ToType1()
        {
            MidiEventCollection collection = new MidiEventCollection(0, 120);

            collection.AddEvent(new TextEvent("Test", MetaEventType.TextEvent, 0), 0);
            collection.AddEvent(new NoteOnEvent(0, 1, 30, 100, 15), 1);
            collection.AddEvent(new NoteOnEvent(15, 1, 30, 100, 15), 1);
            collection.AddEvent(new NoteOnEvent(30, 1, 30, 100, 15), 1);
            collection.AddEvent(new NoteOnEvent(0, 10, 60, 100, 15), 10);
            collection.AddEvent(new NoteOnEvent(15, 10, 60, 100, 15), 10);
            collection.AddEvent(new NoteOnEvent(30, 10, 60, 100, 15), 10);
            Assert.AreEqual(collection.Tracks, 1);
            collection.MidiFileType = 1;
            collection.PrepareForExport();
            Assert.AreEqual(3, collection.Tracks, "Wrong number of tracks");
            IList <MidiEvent> track0 = collection.GetTrackEvents(0);

            Assert.AreEqual(track0.Count, 2);
            Assert.AreEqual(collection.GetTrackEvents(1).Count, 4);
            Assert.AreEqual(collection.GetTrackEvents(2).Count, 4);
            Assert.IsTrue(MidiEvent.IsEndTrack(track0[track0.Count - 1]));
        }
Example #8
0
        private static void ExportBinary(BinaryWriter writer, MidiEventCollection events)
        {
            writer.Write(System.Text.Encoding.UTF8.GetBytes("MThd"));
            writer.Write(SwapUInt32(6)); // chunk size
            writer.Write(SwapUInt16((ushort)events.MidiFileType));
            writer.Write(SwapUInt16((ushort)events.Tracks));
            writer.Write(SwapUInt16((ushort)events.DeltaTicksPerQuarterNote));

            for (int track = 0; track < events.Tracks; track++)
            {
                IList <MidiEvent> eventList = events[track];

                writer.Write(System.Text.Encoding.UTF8.GetBytes("MTrk"));
                long trackSizePosition = writer.BaseStream.Position;
                writer.Write(SwapUInt32(0));

                long absoluteTime = events.StartAbsoluteTime;

                // use a stable sort to preserve ordering of MIDI events whose
                // absolute times are the same
                MergeSort.Sort(eventList, new MidiEventComparer());
                if (eventList.Count > 0 && !MidiEvent.IsEndTrack(eventList[eventList.Count - 1]))
                {
                    Memory.Log.WriteLine("Exporting a track with a missing end track");
                }
                foreach (var midiEvent in eventList)
                {
                    midiEvent.Export(ref absoluteTime, writer);
                }

                uint trackChunkLength = (uint)(writer.BaseStream.Position - trackSizePosition) - 4;
                writer.BaseStream.Position = trackSizePosition;
                writer.Write(SwapUInt32(trackChunkLength));
                writer.BaseStream.Position += trackChunkLength;
            }
        }