Beispiel #1
0
        public void DontCompareOriginalFormat_Read(MidiFileFormat firstFormat, MidiFileFormat secondFormat)
        {
            var midiFile1 = MidiFileTestUtilities.Read(
                new MidiFile(new TrackChunk(new NoteOnEvent {
                DeltaTime = 100
            })),
                null,
                null,
                firstFormat);
            var midiFile2 = MidiFileTestUtilities.Read(
                new MidiFile(new TrackChunk(new NoteOnEvent {
                DeltaTime = 100
            })),
                null,
                null,
                secondFormat);

            var areEqual = MidiFile.Equals(
                midiFile1,
                midiFile2,
                new MidiFileEqualityCheckSettings {
                CompareOriginalFormat = false
            },
                out var message);

            Assert.IsTrue(areEqual, "Files aren't equal.");
            Assert.IsNull(message, "Message isn't null.");
        }
Beispiel #2
0
        /// <summary>
        /// Writes current <see cref="MidiFile"/> to the stream.
        /// </summary>
        /// <param name="stream">Stream to write file's data to.</param>
        /// <param name="format">Format of the file to be written.</param>
        /// <param name="settings">Settings according to which the file must be written. Specify <c>null</c> to use
        /// default settings.</param>
        /// <exception cref="ArgumentNullException"><paramref name="stream"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException"><paramref name="stream"/> doesn't support writing.</exception>
        /// <exception cref="InvalidEnumArgumentException"><paramref name="format"/> specified an invalid value.</exception>
        /// <exception cref="InvalidOperationException">Time division is <c>null</c>.</exception>
        /// <exception cref="IOException">An I/O error occurred while writing to the stream.</exception>
        /// <exception cref="ObjectDisposedException"><paramref name="stream"/> is disposed.</exception>
        /// <exception cref="TooManyTrackChunksException">Count of track chunks presented in the file
        /// exceeds maximum value allowed for MIDI file.</exception>
        public void Write(Stream stream, MidiFileFormat format = MidiFileFormat.MultiTrack, WritingSettings settings = null)
        {
            ThrowIfArgument.IsNull(nameof(stream), stream);
            ThrowIfArgument.IsInvalidEnumValue(nameof(format), format);

            if (TimeDivision == null)
            {
                throw new InvalidOperationException("Time division is null.");
            }

            if (!stream.CanWrite)
            {
                throw new ArgumentException("Stream doesn't support writing.", nameof(stream));
            }

            //

            if (settings == null)
            {
                settings = new WritingSettings();
            }

            if (settings.WriterSettings == null)
            {
                settings.WriterSettings = new WriterSettings();
            }

            using (var writer = new MidiWriter(stream, settings.WriterSettings))
            {
                var chunksConverter = ChunksConverterFactory.GetConverter(format);
                var chunks          = chunksConverter.Convert(Chunks);

                if (settings.WriteHeaderChunk)
                {
                    var trackChunksCount = chunks.Count(c => c is TrackChunk);
                    if (trackChunksCount > ushort.MaxValue)
                    {
                        throw new TooManyTrackChunksException(trackChunksCount);
                    }

                    var headerChunk = new HeaderChunk
                    {
                        FileFormat   = (ushort)format,
                        TimeDivision = TimeDivision,
                        TracksNumber = (ushort)trackChunksCount
                    };
                    headerChunk.Write(writer, settings);
                }

                foreach (var chunk in chunks)
                {
                    if (chunk is UnknownChunk && settings.DeleteUnknownChunks)
                    {
                        continue;
                    }

                    chunk.Write(writer, settings);
                }
            }
        }
 private static IEnumerable <MidiFile> GetFiles(MidiFileFormat midiFileFormat, MidiFileSize midiFileSize, ReadingSettings settings)
 {
     foreach (var filePath in Directory.GetFiles(Path.Combine(TestFilesProvider.ValidFilesPath, midiFileFormat.ToString(), midiFileSize.ToString())))
     {
         yield return(MidiFile.Read(filePath, settings));
     }
 }
 private static IEnumerable <MidiFile> GetFiles(MidiFileFormat midiFileFormat, MidiFileSize midiFileSize)
 {
     foreach (var filePath in Directory.GetFiles(Path.Combine(FilesPath, midiFileFormat.ToString(), midiFileSize.ToString())))
     {
         yield return(MidiFile.Read(filePath));
     }
 }
Beispiel #5
0
 // Token: 0x0600340D RID: 13325 RVA: 0x00148864 File Offset: 0x00146A64
 public void Write(string filePath, bool overwriteFile = false, MidiFileFormat format = MidiFileFormat.MultiTrack, WritingSettings settings = null)
 {
     using (FileStream fileStream = FileUtilities.OpenFileForWrite(filePath, overwriteFile))
     {
         this.Write(fileStream, format, settings);
     }
 }
Beispiel #6
0
        /// <summary>
        /// Writes current <see cref="MidiFile"/> to the stream.
        /// </summary>
        /// <param name="stream">Stream to write file's data to.</param>
        /// <param name="format">Format of the file to be written.</param>
        /// <param name="settings">Settings according to which the file must be written.</param>
        /// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception>
        /// <exception cref="ArgumentException"><paramref name="stream"/> doesn't support writing.</exception>
        /// <exception cref="InvalidEnumArgumentException"><paramref name="format"/> specified an invalid value.</exception>
        /// <exception cref="InvalidOperationException">Time division is null.</exception>
        /// <exception cref="IOException">An I/O error occurred while writing to the stream.</exception>
        /// <exception cref="ObjectDisposedException"><paramref name="stream"/> is disposed. -or-
        /// Underlying stream writer is disposed.</exception>
        /// <exception cref="TooManyTrackChunksException">Count of track chunks presented in the file
        /// exceeds maximum value allowed for MIDI file.</exception>
        public void Write(Stream stream, MidiFileFormat format = MidiFileFormat.MultiTrack, WritingSettings settings = null)
        {
            ThrowIfArgument.IsNull(nameof(stream), stream);
            ThrowIfArgument.IsInvalidEnumValue(nameof(format), format);

            if (TimeDivision == null)
            {
                throw new InvalidOperationException("Time division is null.");
            }

            if (!stream.CanWrite)
            {
                throw new ArgumentException("Stream doesn't support writing.", nameof(stream));
            }

            //

            if (settings == null)
            {
                settings = new WritingSettings();
            }

            ValidateCustomMetaEventsStatusBytes(settings.CustomMetaEventTypes);
            ValidateCustomChunksIds(Chunks.Where(c => !(c is TrackChunk) && !(c is HeaderChunk)));

            using (var writer = new MidiWriter(stream))
            {
                var chunksConverter = ChunksConverterFactory.GetConverter(format);
                var chunks          = chunksConverter.Convert(Chunks);

                var trackChunksCount = chunks.Count(c => c is TrackChunk);
                if (trackChunksCount > ushort.MaxValue)
                {
                    throw new TooManyTrackChunksException(
                              $"Count of track chunks to be written ({trackChunksCount}) is greater than the valid maximum ({ushort.MaxValue}).",
                              trackChunksCount);
                }

                var headerChunk = new HeaderChunk
                {
                    FileFormat   = (ushort)format,
                    TimeDivision = TimeDivision,
                    TracksNumber = (ushort)trackChunksCount
                };
                headerChunk.Write(writer, settings);

                foreach (var chunk in chunks)
                {
                    if (settings.CompressionPolicy.HasFlag(CompressionPolicy.DeleteUnknownChunks) && chunk is UnknownChunk)
                    {
                        continue;
                    }

                    chunk.Write(writer, settings);
                }
            }
        }
Beispiel #7
0
        /// <summary>
        /// Writes the MIDI file to location specified by full path.
        /// </summary>
        /// <param name="filePath">Full path of the file to write to.</param>
        /// <param name="overwriteFile">If true and file specified by <paramref name="filePath"/> already
        /// exists it will be overwritten; if false and the file exists exception will be thrown.</param>
        /// <param name="format">MIDI file format to write in.</param>
        /// <param name="settings">Settings according to which the file must be written.</param>
        /// <exception cref="ArgumentException"><paramref name="filePath"/> is a zero-length string,
        /// contains only white space, or contains one or more invalid characters as defined by
        /// <see cref="Path.InvalidPathChars"/>.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="filePath"/> is null.</exception>
        /// <exception cref="InvalidEnumArgumentException"><paramref name="format"/> specified an invalid value.</exception>
        /// <exception cref="PathTooLongException">The specified path, file name, or both exceed the system-defined
        /// maximum length. For example, on Windows-based platforms, paths must be less than 248 characters,
        /// and file names must be less than 260 characters.</exception>
        /// <exception cref="DirectoryNotFoundException">The specified path is invalid, (for example,
        /// it is on an unmapped drive).</exception>
        /// <exception cref="IOException">An I/O error occurred while writing the file.</exception>
        /// <exception cref="NotSupportedException"><paramref name="filePath"/> is in an invalid format.</exception>
        /// <exception cref="UnauthorizedAccessException">This operation is not supported on the current platform.-or-
        /// <paramref name="filePath"/> specified a directory.-or- The caller does not have the required permission.</exception>
        /// <exception cref="InvalidOperationException">Time division is null.</exception>
        /// <exception cref="TooManyTrackChunksException">Count of track chunks presented in the file
        /// exceeds maximum value allowed for MIDI file.</exception>
        public void Write(string filePath, bool overwriteFile = false, MidiFileFormat format = MidiFileFormat.MultiTrack, WritingSettings settings = null)
        {
            ThrowIfArgument.IsInvalidEnumValue(nameof(format), format);

            using (var fileStream = FileUtilities.OpenFileForWrite(filePath, overwriteFile))
            {
                Write(fileStream, format, settings);
            }
        }
Beispiel #8
0
        /// <summary>
        /// Writes current <see cref="MidiFile"/> to the stream.
        /// </summary>
        /// <param name="stream">Stream to write file's data to.</param>
        /// <param name="format">Format of the file to be written.</param>
        /// <param name="settings">Settings according to which the file must be written.</param>
        /// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception>
        /// <exception cref="ArgumentException"><paramref name="stream"/> does not support writing,
        /// or is already closed.</exception>
        /// <exception cref="InvalidEnumArgumentException"><paramref name="format"/> specified an invalid value.</exception>
        /// <exception cref="InvalidOperationException">Time division is null.</exception>
        /// <exception cref="TooManyTrackChunksException">Count of track chunks presented in the file
        /// exceeds maximum value allowed for MIDI file.</exception>
        private void Write(Stream stream, MidiFileFormat format = MidiFileFormat.MultiTrack, WritingSettings settings = null)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            if (!Enum.IsDefined(typeof(MidiFileFormat), format))
            {
                throw new InvalidEnumArgumentException(nameof(format), (int)format, typeof(MidiFileFormat));
            }

            if (TimeDivision == null)
            {
                throw new InvalidOperationException("Time division is null.");
            }

            //

            if (settings == null)
            {
                settings = new WritingSettings();
            }

            using (var writer = new MidiWriter(stream))
            {
                var chunksConverter = ChunksConverterFactory.GetConverter(format);
                var chunks          = chunksConverter.Convert(Chunks);

                var trackChunksCount = chunks.Count(c => c is TrackChunk);
                if (trackChunksCount > ushort.MaxValue)
                {
                    throw new TooManyTrackChunksException(
                              $"Count of track chunks to be written ({trackChunksCount}) is greater than the valid maximum ({ushort.MaxValue}).",
                              trackChunksCount);
                }

                var headerChunk = new HeaderChunk
                {
                    FileFormat   = (ushort)format,
                    TimeDivision = TimeDivision,
                    TracksNumber = (ushort)trackChunksCount
                };
                headerChunk.Write(writer, settings);

                foreach (var chunk in chunks)
                {
                    if (settings.CompressionPolicy.HasFlag(CompressionPolicy.DeleteUnknownChunks) && chunk is UnknownChunk)
                    {
                        continue;
                    }

                    chunk.Write(writer, settings);
                }
            }
        }
        // Token: 0x06003243 RID: 12867 RVA: 0x0014740C File Offset: 0x0014560C
        public static IChunksConverter GetConverter(MidiFileFormat format)
        {
            IChunksConverter result;

            if (!ChunksConverterFactory._converters.TryGetValue(format, out result))
            {
                throw new NotSupportedException(string.Format("Converter for the {0} format is not supported.", format));
            }
            return(result);
        }
Beispiel #10
0
        /// <summary>
        /// Writes the MIDI file to location specified by full path.
        /// </summary>
        /// <param name="filePath">Full path of the file to write to.</param>
        /// <param name="overwriteFile">If true and file specified by <paramref name="filePath"/> already
        /// exists it will be overwritten; if false and the file exists exception will be thrown.</param>
        /// <param name="format">MIDI file format to write in.</param>
        /// <param name="settings">Settings according to which the file must be written.</param>
        /// <exception cref="ArgumentException"><paramref name="filePath"/> is a zero-length string,
        /// contains only white space, or contains one or more invalid characters as defined by
        /// <see cref="Path.InvalidPathChars"/>.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="filePath"/> is null.</exception>
        /// <exception cref="InvalidEnumArgumentException"><paramref name="format"/> specified an invalid value.</exception>
        /// <exception cref="PathTooLongException">The specified path, file name, or both exceed the system-defined
        /// maximum length. For example, on Windows-based platforms, paths must be less than 248 characters,
        /// and file names must be less than 260 characters.</exception>
        /// <exception cref="DirectoryNotFoundException">The specified path is invalid, (for example,
        /// it is on an unmapped drive).</exception>
        /// <exception cref="IOException">An I/O error occurred while writing the file.</exception>
        /// <exception cref="NotSupportedException"><paramref name="filePath"/> is in an invalid format.</exception>
        /// <exception cref="UnauthorizedAccessException">This operation is not supported on the current platform.-or-
        /// <paramref name="filePath"/> specified a directory.-or- The caller does not have the required permission.</exception>
        /// <exception cref="InvalidOperationException">Time division is null.</exception>
        /// <exception cref="TooManyTrackChunksException">Count of track chunks presented in the file
        /// exceeds maximum value allowed for MIDI file.</exception>
        public void Write(string filePath, bool overwriteFile = false, MidiFileFormat format = MidiFileFormat.MultiTrack, WritingSettings settings = null)
        {
            if (!Enum.IsDefined(typeof(MidiFileFormat), format))
            {
                throw new InvalidEnumArgumentException(nameof(format), (int)format, typeof(MidiFileFormat));
            }

            using (var fileStream = FileUtilities.OpenFileForWrite(filePath, overwriteFile))
            {
                Write(fileStream, format, settings);
            }
        }
        private int[] GetEventsCount(MidiFileFormat midiFileFormat, MidiFileSize midiFileSize)
        {
            var result = new List <int>();

            foreach (var midiFile in GetFiles(midiFileFormat, midiFileSize))
            {
                var events = midiFile.GetTrackChunks().SelectMany(c => c.Events).ToList();
                result.Add(events.Count);
            }

            return(result.ToArray());
        }
Beispiel #12
0
        /// <summary>
        /// Gets chunks converter which is appropriate for a passed MIDI file format.
        /// </summary>
        /// <param name="format">MIDI file format to get <see cref="IChunksConverter"/> for.</param>
        /// <returns>An instance of the <see cref="IChunksConverter"/> appropriate for
        /// <paramref name="format"/>.</returns>
        /// <exception cref="InvalidEnumArgumentException"><paramref name="format"/> specified an invalid value.</exception>
        /// <exception cref="NotSupportedException"><paramref name="format"/> is not supported.</exception>
        public static IChunksConverter GetConverter(MidiFileFormat format)
        {
            ThrowIfArgument.IsInvalidEnumValue(nameof(format), format);

            IChunksConverter converter;

            if (_converters.TryGetValue(format, out converter))
            {
                return(converter);
            }

            throw new NotSupportedException($"Converter for the {format} format is not supported.");
        }
        protected static Dictionary <MidiFile, string> GetFiles(MidiFileFormat midiFileFormat, MidiFileSize midiFileSize)
        {
            var result = new Dictionary <MidiFile, string>();

            foreach (var filePath in Directory.GetFiles(Path.Combine(FilesPath, midiFileFormat.ToString(), midiFileSize.ToString())))
            {
                var midiFile    = MidiFile.Read(filePath);
                var newFilePath = Path.GetTempFileName();
                result.Add(midiFile, newFilePath);
            }

            return(result);
        }
Beispiel #14
0
        /// <summary>
        /// Gets chunks converter which is appropriate for a passed MIDI file format.
        /// </summary>
        /// <param name="format">MIDI file format to get <see cref="IChunksConverter"/> for.</param>
        /// <returns>An instance of the <see cref="IChunksConverter"/> appropriate for
        /// <paramref name="format"/>.</returns>
        /// <exception cref="InvalidEnumArgumentException"><paramref name="format"/> specified an invalid value.</exception>
        /// <exception cref="NotSupportedException"><paramref name="format"/> is not supported yet.</exception>
        public static IChunksConverter GetConverter(MidiFileFormat format)
        {
            if (!Enum.IsDefined(typeof(MidiFileFormat), format))
            {
                throw new InvalidEnumArgumentException(nameof(format), (int)format, typeof(MidiFileFormat));
            }

            IChunksConverter converter;

            if (_converters.TryGetValue(format, out converter))
            {
                return(converter);
            }

            throw new NotImplementedException($"Converter for the {format} format is not implemented.");
        }
        /// <summary>
        /// Gets chunks converter which is appropriate for a passed MIDI file format.
        /// </summary>
        /// <param name="format">MIDI file format to get <see cref="IChunksConverter"/> for.</param>
        /// <returns>An instance of the <see cref="IChunksConverter"/> appropriate for
        /// <paramref name="format"/>.</returns>
        /// <exception cref="InvalidEnumArgumentException"><paramref name="format"/> specified an invalid value.</exception>
        /// <exception cref="NotSupportedException"><paramref name="format"/> is not supported.</exception>
        public static IChunksConverter GetConverter(MidiFileFormat format)
        {
            ThrowIfArgument.IsInvalidEnumValue(nameof(format), format);

            switch (format)
            {
            case MidiFileFormat.SingleTrack:
                return(new SingleTrackChunksConverter());

            case MidiFileFormat.MultiTrack:
                return(new MultiTrackChunksConverter());

            case MidiFileFormat.MultiSequence:
                return(new MultiSequenceChunksConverter());
            }

            throw new NotSupportedException($"Converter for the {format} format is not supported.");
        }
Beispiel #16
0
        public MidiFile(Stream stream)
        {
            // header_chunk = "MThd" + <header_length> + <format> + <n> + <division>
            var chunk = new Chunk(stream);

            if (chunk.Id != "MThd")
            {
                throw new FileFormatException("MidiFile.chunk.Id", chunk.Id, "MThd");
            }

            using (var header = chunk.GetStream()) {
                Format         = (MidiFileFormat)StreamHelperBe.ReadInt16(header);
                NumberOfTracks = StreamHelperBe.ReadUInt16(header);

                var division = StreamHelperBe.ReadInt16(header);
                if (division > 0)
                {
                    TicksPerBeat = division;
                }
                else
                {
                    throw new Exception("STMPE based time is not supported");
                }
            }

            // track_chunk = "MTrk" + <track_length> [+ <delta_time> + <event> ...]
            for (int track = 0; track < NumberOfTracks; track++)
            {
                chunk = new Chunk(stream);
                if (chunk.Id != "MTrk")
                {
                    throw new FileFormatException("MidiFile.chunk.Id", chunk.Id, "MTrk");
                }

                using (var trackStream = chunk.GetStream()) {
                    BuildTrack(trackStream, track);
                }
            }

            Sort();
            Rebase();
            Trim();
        }
Beispiel #17
0
 // Token: 0x0600340F RID: 13327 RVA: 0x00148AE4 File Offset: 0x00146CE4
 public void Write(Stream stream, MidiFileFormat format = MidiFileFormat.MultiTrack, WritingSettings settings = null)
 {
     if (this.TimeDivision == null)
     {
         throw new InvalidOperationException("Time division is null.");
     }
     if (!stream.CanWrite)
     {
         throw new ArgumentException("Stream doesn't support writing.", "stream");
     }
     if (settings == null)
     {
         settings = new WritingSettings();
     }
     using (MidiWriter midiWriter = new MidiWriter(stream))
     {
         IEnumerable <MidiChunk> enumerable = ChunksConverterFactory.GetConverter(format).Convert(this.Chunks);
         int num = enumerable.Count((MidiChunk c) => c is TrackChunk);
         if (num > 65535)
         {
             throw new TooManyTrackChunksException(string.Format("Count of track chunks to be written ({0}) is greater than the valid maximum ({1}).", num, ushort.MaxValue), num);
         }
         new HeaderChunk
         {
             FileFormat   = (ushort)format,
             TimeDivision = this.TimeDivision,
             TracksNumber = (ushort)num
         }.Write(midiWriter, settings);
         foreach (MidiChunk midiChunk in enumerable)
         {
             if (!settings.CompressionPolicy.HasFlag(CompressionPolicy.DeleteUnknownChunks) || !(midiChunk is UnknownChunk))
             {
                 midiChunk.Write(midiWriter, settings);
             }
         }
     }
 }
 protected static void Read(MidiFileFormat midiFileFormat, MidiFileSize midiFileSize, ReadingSettings settings)
 {
     GetFiles(midiFileFormat, midiFileSize, settings).ToList();
 }
Beispiel #19
0
 private void Read(MidiFileFormat midiFileFormat, string directoryName)
 {
     Read(Path.Combine(FilesPath, midiFileFormat.ToString(), directoryName));
 }
        private static IEnumerable <TrackChunk> ConvertTrackChunks(IEnumerable <TrackChunk> trackChunks, MidiFileFormat format)
        {
            var chunksConverter = ChunksConverterFactory.GetConverter(format);

            return(chunksConverter.Convert(trackChunks)
                   .OfType <TrackChunk>());
        }
Beispiel #21
0
        public static MemoryStream ScrubFile(string filePath)
        {
            MidiFile midiFile;
            IEnumerable <TrackChunk> originalTrackChunks;
            TempoMap tempoMap;

            MidiFile newMidiFile;
            ConcurrentDictionary <int, TrackChunk> newTrackChunks;

            try
            {
                string md5 = CalculateMD5(filePath);
                if (lastMD5.Equals(md5) && lastFile != null)
                {
                    var oldfile = new MemoryStream();
                    lastFile.Write(oldfile, MidiFileFormat.MultiTrack, new WritingSettings {
                        CompressionPolicy = CompressionPolicy.NoCompression
                    });
                    oldfile.Flush();
                    oldfile.Position = 0;
                    return(oldfile);
                }

                if (Path.GetExtension(filePath).ToLower().Equals(".mmsong"))
                {
                    var mmSongStream = MMSong.Open(filePath).GetMidiFile(false, true);
                    lastFile = MidiFile.Read(mmSongStream);
                    lastMD5  = md5;
                    mmSongStream.Position = 0;
                    return(mmSongStream);
                }

                midiFile = MidiFile.Read(filePath, new ReadingSettings
                {
                    ReaderSettings = new ReaderSettings
                    {
                        ReadFromMemory = true
                    },
                    InvalidChunkSizePolicy = InvalidChunkSizePolicy.Ignore,
                    InvalidMetaEventParameterValuePolicy         = InvalidMetaEventParameterValuePolicy.SnapToLimits,
                    InvalidChannelEventParameterValuePolicy      = InvalidChannelEventParameterValuePolicy.SnapToLimits,
                    InvalidSystemCommonEventParameterValuePolicy = InvalidSystemCommonEventParameterValuePolicy.SnapToLimits,
                    MissedEndOfTrackPolicy           = MissedEndOfTrackPolicy.Ignore,
                    NotEnoughBytesPolicy             = NotEnoughBytesPolicy.Ignore,
                    UnexpectedTrackChunksCountPolicy = UnexpectedTrackChunksCountPolicy.Ignore,
                    UnknownChannelEventPolicy        = UnknownChannelEventPolicy.SkipStatusByteAndOneDataByte,
                    UnknownChunkIdPolicy             = UnknownChunkIdPolicy.ReadAsUnknownChunk
                });

                #region Require

                if (midiFile == null)
                {
                    throw new ArgumentNullException();
                }
                else
                {
                    try
                    {
                        if (midiFile.Chunks.Count < 1)
                        {
                            throw new NotSupportedException();
                        }

                        MidiFileFormat fileFormat = midiFile.OriginalFormat;

                        if (fileFormat == MidiFileFormat.MultiSequence)
                        {
                            throw new NotSupportedException();
                        }
                    }
                    catch (Exception exception) when(exception is UnknownFileFormatException || exception is InvalidOperationException)
                    {
                        throw exception;
                    }
                }
                #endregion

                var trackZeroName = midiFile.GetTrackChunks().First().Events.OfType <SequenceTrackNameEvent>().FirstOrDefault()?.Text;

                if (!string.IsNullOrEmpty(trackZeroName) && (trackZeroName.ToLower().Contains("mogamp") || trackZeroName.ToLower().Contains("mognotate")))
                {
                    var notateConfig = NotateConfig.GenerateConfigFromMidiFile(filePath);
                    var mmSongStream = notateConfig.Transmogrify().GetMidiFile(false, true);
                    lastFile = MidiFile.Read(mmSongStream);
                    lastMD5  = md5;
                    mmSongStream.Position = 0;
                    return(mmSongStream);
                }

                Console.WriteLine("Scrubbing " + filePath);
                var loaderWatch = Stopwatch.StartNew();

                originalTrackChunks = midiFile.GetTrackChunks();

                tempoMap       = midiFile.GetTempoMap();
                newTrackChunks = new ConcurrentDictionary <int, TrackChunk>();

                long firstNote = originalTrackChunks.GetNotes().First().GetTimedNoteOnEvent().TimeAs <MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000;

                TrackChunk allTracks = new TrackChunk();
                allTracks.AddNotes(originalTrackChunks.GetNotes());
                midiFile.Chunks.Add(allTracks);
                originalTrackChunks = midiFile.GetTrackChunks();

                Parallel.ForEach(originalTrackChunks.Where(x => x.GetNotes().Count() > 0), (originalChunk, loopState, index) =>
                {
                    var watch = Stopwatch.StartNew();

                    int noteVelocity = int.Parse(index.ToString()) + 1;

                    Dictionary <int, Dictionary <long, Note> > allNoteEvents = new Dictionary <int, Dictionary <long, Note> >();
                    for (int i = 0; i < 127; i++)
                    {
                        allNoteEvents.Add(i, new Dictionary <long, Note>());
                    }

                    foreach (Note note in originalChunk.GetNotes())
                    {
                        long noteOnMS = 0;

                        long noteOffMS = 0;

                        try
                        {
                            noteOnMS  = 5000 + (note.GetTimedNoteOnEvent().TimeAs <MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000) - firstNote;
                            noteOffMS = 5000 + (note.GetTimedNoteOffEvent().TimeAs <MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000) - firstNote;
                        }
                        catch (Exception) { continue; }
                        int noteNumber = note.NoteNumber;

                        Note newNote = new Note(noteNumber: (SevenBitNumber)noteNumber,
                                                time: noteOnMS,
                                                length: noteOffMS - noteOnMS
                                                )
                        {
                            Channel     = (FourBitNumber)0,
                            Velocity    = (SevenBitNumber)noteVelocity,
                            OffVelocity = (SevenBitNumber)noteVelocity
                        };

                        if (allNoteEvents[noteNumber].ContainsKey(noteOnMS))
                        {
                            Note previousNote = allNoteEvents[noteNumber][noteOnMS];
                            if (previousNote.Length < note.Length)
                            {
                                allNoteEvents[noteNumber][noteOnMS] = newNote;
                            }
                        }
                        else
                        {
                            allNoteEvents[noteNumber].Add(noteOnMS, newNote);
                        }
                    }

                    watch.Stop();
                    Debug.WriteLine("step 1: " + noteVelocity + ": " + watch.ElapsedMilliseconds);
                    watch = Stopwatch.StartNew();

                    TrackChunk newChunk = new TrackChunk();
                    for (int i = 0; i < 127; i++)
                    {
                        long lastNoteTimeStamp = -1;
                        foreach (var noteEvent in allNoteEvents[i])
                        {
                            if (lastNoteTimeStamp >= 0 && allNoteEvents[i][lastNoteTimeStamp].Length + lastNoteTimeStamp >= noteEvent.Key)
                            {
                                allNoteEvents[i][lastNoteTimeStamp].Length = allNoteEvents[i][lastNoteTimeStamp].Length - (allNoteEvents[i][lastNoteTimeStamp].Length + lastNoteTimeStamp + 1 - noteEvent.Key);
                            }

                            lastNoteTimeStamp = noteEvent.Key;
                        }
                    }
                    newChunk.AddNotes(allNoteEvents.SelectMany(s => s.Value).Select(s => s.Value).ToArray());
                    allNoteEvents = null;

                    watch.Stop();
                    Debug.WriteLine("step 2: " + noteVelocity + ": " + watch.ElapsedMilliseconds);
                    watch = Stopwatch.StartNew();

                    Note[] notesToFix = newChunk.GetNotes().Reverse().ToArray();
                    for (int i = 1; i < notesToFix.Count(); i++)
                    {
                        int noteNum  = notesToFix[i].NoteNumber;
                        long time    = (notesToFix[i].GetTimedNoteOnEvent().Time);
                        long dur     = notesToFix[i].Length;
                        int velocity = notesToFix[i].Velocity;

                        long lowestParent = notesToFix[0].GetTimedNoteOnEvent().Time;
                        for (int k = i - 1; k >= 0; k--)
                        {
                            long lastOn = notesToFix[k].GetTimedNoteOnEvent().Time;
                            if (lastOn < lowestParent)
                            {
                                lowestParent = lastOn;
                            }
                        }
                        if (lowestParent <= time + 50)
                        {
                            time = lowestParent - 50;
                            if (time < 0)
                            {
                                continue;
                            }
                            notesToFix[i].Time = time;
                            dur = 25;
                            notesToFix[i].Length = dur;
                        }
                    }

                    watch.Stop();
                    Debug.WriteLine("step 3: " + noteVelocity + ": " + watch.ElapsedMilliseconds);
                    watch = Stopwatch.StartNew();

                    notesToFix             = notesToFix.Reverse().ToArray();
                    List <Note> fixedNotes = new List <Note>();
                    for (int j = 0; j < notesToFix.Count(); j++)
                    {
                        var noteNum  = notesToFix[j].NoteNumber;
                        var time     = notesToFix[j].Time;
                        var dur      = notesToFix[j].Length;
                        var channel  = notesToFix[j].Channel;
                        var velocity = notesToFix[j].Velocity;

                        if (j + 1 < notesToFix.Count())
                        {
                            if (notesToFix[j + 1].Time <= notesToFix[j].Time + notesToFix[j].Length + 25)
                            {
                                dur = notesToFix[j + 1].Time - notesToFix[j].Time - 25;
                                dur = dur < 25 ? 1 : dur;
                            }
                        }
                        fixedNotes.Add(new Note(noteNum, dur, time)
                        {
                            Channel     = channel,
                            Velocity    = velocity,
                            OffVelocity = velocity
                        });
                    }
                    notesToFix = null;

                    watch.Stop();
                    Debug.WriteLine("step 4: " + noteVelocity + ": " + watch.ElapsedMilliseconds);
                    watch = Stopwatch.StartNew();

                    int octaveShift  = 0;
                    string trackName = originalChunk.Events.OfType <SequenceTrackNameEvent>().FirstOrDefault()?.Text;
                    if (trackName == null)
                    {
                        trackName = "";
                    }
                    trackName = trackName.ToLower().Trim().Replace(" ", String.Empty);
                    Regex rex = new Regex(@"^([A-Za-z]+)([-+]\d)?");
                    if (rex.Match(trackName) is Match match)
                    {
                        if (!string.IsNullOrEmpty(match.Groups[1].Value))
                        {
                            trackName = match.Groups[1].Value;
                            if (!string.IsNullOrEmpty(match.Groups[2].Value))
                            {
                                if (int.TryParse(match.Groups[2].Value, out int os))
                                {
                                    octaveShift = os;
                                }
                            }

                            (bool success, string parsedTrackName) = TrackNameToEnumInstrumentName(trackName);

                            if (success)
                            {
                                trackName = parsedTrackName;
                            }
                            else
                            {
                                (success, parsedTrackName) = TrackNameToStringInstrumentName(trackName);

                                if (success)
                                {
                                    trackName = parsedTrackName;
                                }
                                else
                                {
                                    var originalInstrument = originalChunk.Events.OfType <ProgramChangeEvent>().FirstOrDefault()?.ProgramNumber;
                                    if (!(originalInstrument is null) && originalInstrument.Equals(typeof(SevenBitNumber)))
                                    {
                                        (success, parsedTrackName) = ProgramToStringInstrumentName((SevenBitNumber)originalInstrument);
                                    }
                                    if (success)
                                    {
                                        trackName = parsedTrackName;
                                    }
                                }
                            }
 protected static void Read(MidiFileFormat midiFileFormat, MidiFileSize midiFileSize)
 {
     GetFiles(midiFileFormat, midiFileSize).ToList();
 }
Beispiel #23
0
        public static Sequence ScrubFile(string filePath)
        {
            MidiFile midiFile;
            IEnumerable <TrackChunk> originalTrackChunks;
            TempoMap tempoMap;

            MidiFile newMidiFile;
            ConcurrentDictionary <int, TrackChunk> newTrackChunks;

            Sequence sequence = null;

            try
            {
                midiFile = MidiFile.Read(filePath, new ReadingSettings
                {
                    ReaderSettings = new ReaderSettings
                    {
                        ReadFromMemory = true
                    }
                });

                bool explode = false;

                #region Require

                if (midiFile == null)
                {
                    throw new ArgumentNullException();
                }
                else
                {
                    try
                    {
                        if (midiFile.Chunks.Count < 1)
                        {
                            throw new NotSupportedException();
                        }

                        MidiFileFormat fileFormat = midiFile.OriginalFormat;

                        if (fileFormat == MidiFileFormat.MultiSequence)
                        {
                            throw new NotSupportedException();
                        }
                        else if (fileFormat == MidiFileFormat.SingleTrack)
                        {
                            explode = true;
                        }
                    }
                    catch (Exception exception) when(exception is UnknownFileFormatException || exception is InvalidOperationException)
                    {
                        throw exception;
                    }
                }
                #endregion

                Console.WriteLine("Scrubbing " + filePath);
                var loaderWatch = Stopwatch.StartNew();

                if (explode || midiFile.Chunks.Count == 1)
                {
                    originalTrackChunks = midiFile.GetTrackChunks().First().Explode();
                }
                else
                {
                    originalTrackChunks = midiFile.GetTrackChunks();
                }

                tempoMap       = midiFile.GetTempoMap();
                newTrackChunks = new ConcurrentDictionary <int, TrackChunk>();

                long firstNote = originalTrackChunks.GetNotes().First().GetTimedNoteOnEvent().TimeAs <MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000;

                Parallel.ForEach(originalTrackChunks.Where(x => x.GetNotes().Count() > 0), (originalChunk, loopState, index) =>
                {
                    var watch = Stopwatch.StartNew();

                    int noteVelocity = int.Parse(index.ToString()) + 1;

                    Dictionary <int, Dictionary <long, Note> > allNoteEvents = new Dictionary <int, Dictionary <long, Note> >();
                    for (int i = 0; i < 127; i++)
                    {
                        allNoteEvents.Add(i, new Dictionary <long, Note>());
                    }

                    // Fill the track dictionary and remove duplicate notes
                    foreach (Note note in originalChunk.GetNotes())
                    {
                        long noteOnMS = 0;

                        long noteOffMS = 0;

                        try
                        {
                            noteOnMS  = note.GetTimedNoteOnEvent().TimeAs <MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000 - firstNote;
                            noteOffMS = note.GetTimedNoteOffEvent().TimeAs <MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000 - firstNote;
                        }
                        catch (Exception) { continue; } // malformed note, most common is a note on missing a note off.
                        int noteNumber = note.NoteNumber;

                        Note newNote = new Note(noteNumber: (SevenBitNumber)noteNumber,
                                                time: noteOnMS,
                                                length: noteOffMS - noteOnMS
                                                )
                        {
                            Channel     = (FourBitNumber)0,
                            Velocity    = (SevenBitNumber)noteVelocity,
                            OffVelocity = (SevenBitNumber)noteVelocity
                        };

                        if (allNoteEvents[noteNumber].ContainsKey(noteOnMS))
                        {
                            Note previousNote = allNoteEvents[noteNumber][noteOnMS];
                            if (previousNote.Length < note.Length)
                            {
                                allNoteEvents[noteNumber][noteOnMS] = newNote;                                    // keep the longest of all duplicates
                            }
                        }
                        else
                        {
                            allNoteEvents[noteNumber].Add(noteOnMS, newNote);
                        }
                    }

                    watch.Stop();
                    Debug.WriteLine("step 1: " + noteVelocity + ": " + watch.ElapsedMilliseconds);
                    watch = Stopwatch.StartNew();

                    // Merge all the dictionaries into one collection
                    TrackChunk newChunk = new TrackChunk();
                    for (int i = 0; i < 127; i++)
                    {
                        long lastNoteTimeStamp = -1;
                        foreach (var noteEvent in allNoteEvents[i])
                        {
                            if (lastNoteTimeStamp >= 0 && allNoteEvents[i][lastNoteTimeStamp].Length + lastNoteTimeStamp >= noteEvent.Key)
                            {
                                allNoteEvents[i][lastNoteTimeStamp].Length = allNoteEvents[i][lastNoteTimeStamp].Length - (allNoteEvents[i][lastNoteTimeStamp].Length + lastNoteTimeStamp + 1 - noteEvent.Key);
                            }

                            lastNoteTimeStamp = noteEvent.Key;
                        }
                    }
                    newChunk.AddNotes(allNoteEvents.SelectMany(s => s.Value).Select(s => s.Value).ToArray());
                    allNoteEvents = null;

                    watch.Stop();
                    Debug.WriteLine("step 2: " + noteVelocity + ": " + watch.ElapsedMilliseconds);
                    watch = Stopwatch.StartNew();

                    // auto arpeggiate
                    Note[] notesToFix = newChunk.GetNotes().Reverse().ToArray();
                    for (int i = 1; i < notesToFix.Count(); i++)
                    {
                        int noteNum  = notesToFix[i].NoteNumber;
                        long time    = (notesToFix[i].GetTimedNoteOnEvent().Time);
                        long dur     = notesToFix[i].Length;
                        int velocity = notesToFix[i].Velocity;

                        long lowestParent = notesToFix[0].GetTimedNoteOnEvent().Time;
                        for (int k = i - 1; k >= 0; k--)
                        {
                            long lastOn = notesToFix[k].GetTimedNoteOnEvent().Time;
                            if (lastOn < lowestParent)
                            {
                                lowestParent = lastOn;
                            }
                        }
                        if (lowestParent <= time + 50)
                        {
                            time = lowestParent - 50;
                            if (time < 0)
                            {
                                continue;
                            }
                            notesToFix[i].Time = time;
                            dur = 25;
                            notesToFix[i].Length = dur;
                        }
                    }

                    watch.Stop();
                    Debug.WriteLine("step 3: " + noteVelocity + ": " + watch.ElapsedMilliseconds);
                    watch = Stopwatch.StartNew();

                    notesToFix             = notesToFix.Reverse().ToArray();
                    List <Note> fixedNotes = new List <Note>();
                    for (int j = 0; j < notesToFix.Count(); j++)
                    {
                        var noteNum  = notesToFix[j].NoteNumber;
                        var time     = notesToFix[j].Time;
                        var dur      = notesToFix[j].Length;
                        var channel  = notesToFix[j].Channel;
                        var velocity = notesToFix[j].Velocity;

                        if (j + 1 < notesToFix.Count())
                        {
                            if (notesToFix[j + 1].Time <= notesToFix[j].Time + notesToFix[j].Length + 25)
                            {
                                dur = notesToFix[j + 1].Time - notesToFix[j].Time - 25;
                                dur = dur < 25 ? 1 : dur;
                            }
                        }
                        fixedNotes.Add(new Note(noteNum, dur, time)
                        {
                            Channel     = channel,
                            Velocity    = velocity,
                            OffVelocity = velocity
                        });
                    }
                    notesToFix = null;

                    watch.Stop();
                    Debug.WriteLine("step 4: " + noteVelocity + ": " + watch.ElapsedMilliseconds);
                    watch = Stopwatch.StartNew();

                    // Discover the instrument name from the track title, and from program changes if that fails
                    int octaveShift  = 0;
                    string trackName = originalChunk.Events.OfType <SequenceTrackNameEvent>().FirstOrDefault()?.Text;
                    if (trackName == null)
                    {
                        trackName = "";
                    }
                    trackName = trackName.ToLower().Trim().Replace(" ", String.Empty);
                    Regex rex = new Regex(@"^([A-Za-z]+)([-+]\d)?");
                    if (rex.Match(trackName) is Match match)
                    {
                        if (!string.IsNullOrEmpty(match.Groups[1].Value))
                        {
                            trackName = match.Groups[1].Value;
                            if (!string.IsNullOrEmpty(match.Groups[2].Value))
                            {
                                if (int.TryParse(match.Groups[2].Value, out int os))
                                {
                                    octaveShift = os;
                                }
                            }

                            (bool success, string parsedTrackName) = TrackNameToEnumInstrumentName(trackName);

                            if (success)
                            {
                                trackName = parsedTrackName;
                            }
                            else
                            {
                                (success, parsedTrackName) = TrackNameToStringInstrumentName(trackName);

                                if (success)
                                {
                                    trackName = parsedTrackName;
                                }
                                else
                                {
                                    var originalInstrument = originalChunk.Events.OfType <ProgramChangeEvent>().FirstOrDefault()?.ProgramNumber;
                                    if (!(originalInstrument is null) && originalInstrument.Equals(typeof(SevenBitNumber)))
                                    {
                                        (success, parsedTrackName) = ProgramToStringInstrumentName((SevenBitNumber)originalInstrument);
                                    }
                                    if (success)
                                    {
                                        trackName = parsedTrackName;
                                    }
                                }
                            }