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."); }
/// <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)); } }
// 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); } }
/// <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); } } }
/// <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); } }
/// <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); }
/// <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()); }
/// <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); }
/// <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."); }
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(); }
// 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(); }
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>()); }
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(); }
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; } } }