/// <summary> /// Converts CSV representation of a MIDI file to <see cref="MidiFile"/> reading CSV data from a file. /// </summary> /// <remarks> /// Note that CSV representation of a MIDI file can be different. You can specify expected CSV layout /// via <paramref name="settings"/> using <see cref="MidiFileCsvConversionSettings.CsvLayout"/> property. /// </remarks> /// <param name="filePath">Path of the file with CSV representation of a MIDI file.</param> /// <param name="settings">Settings according to which CSV data must be converted.</param> /// <returns>An instance of the <see cref="MidiFile"/> representing a MIDI file written in CSV format.</returns> /// <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="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 reading 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> public MidiFile ConvertCsvToMidiFile(string filePath, MidiFileCsvConversionSettings settings = null) { using (var fileStream = FileUtilities.OpenFileForRead(filePath)) { return(ConvertCsvToMidiFile(fileStream, settings)); } }
private static void WriteNote(Note note, CsvWriter csvWriter, int trackNumber, long time, MidiFileCsvConversionSettings settings, TempoMap tempoMap) { var formattedNote = settings.NoteNumberFormat == NoteNumberFormat.NoteNumber ? (object)note.NoteNumber : note; var formattedLength = TimeConverter.ConvertTo(note.Length, settings.NoteLengthType, tempoMap); WriteRecord(csvWriter, trackNumber, time, DryWetMidiRecordTypes.Note, settings, tempoMap, note.Channel, formattedNote, formattedLength, note.Velocity, note.OffVelocity); }
private static object FormatNoteNumber(SevenBitNumber noteNumber, MidiFileCsvConversionSettings settings) { if (settings.CsvLayout == MidiFileCsvLayout.MidiCsv) { return(noteNumber); } return(NoteCsvConversionUtilities.FormatNoteNumber(noteNumber, settings.NoteNumberFormat)); }
private static Record ReadRecord( StreamReader streamReader, int lineNumber, MidiFileCsvConversionSettings settings) { var line = Enumerable.Range(0, int.MaxValue) .Select(i => new { LineNumber = lineNumber + i, Line = streamReader.ReadLine()?.Trim() }) .FirstOrDefault(l => l.Line != string.Empty); if (string.IsNullOrEmpty(line.Line)) { return(null); } var lineNumberOffset = 0; var parts = CsvUtilities.SplitCsvValues(line.Line, settings.CsvDelimiter, () => { var nextLine = streamReader.ReadLine(); if (nextLine != null) { lineNumberOffset++; } return(nextLine); }); if (parts.Length < 3) { ThrowBadFormat(line.LineNumber, "Missing required parameters."); } int parsedTrackNumber; var trackNumber = int.TryParse(parts[0], out parsedTrackNumber) ? (int?)parsedTrackNumber : null; ITimeSpan time = null; TimeSpanUtilities.TryParse(parts[1], settings.TimeType, out time); var recordType = parts[2]; if (string.IsNullOrEmpty(recordType)) { ThrowBadFormat(line.LineNumber, "Record type isn't specified."); } var parameters = parts.Skip(3).ToArray(); lineNumber = line.LineNumber + lineNumberOffset; return(new Record(line.LineNumber, trackNumber, time, recordType, parameters)); }
private static HeaderChunk ParseHeader(Record record, MidiFileCsvConversionSettings settings) { var parameters = record.Parameters; var format = default(MidiFileFormat?); var timeDivision = default(short); switch (settings.CsvLayout) { case MidiFileCsvLayout.DryWetMidi: { if (parameters.Length < 2) { CsvError.ThrowBadFormat(record.LineNumber, "Parameters count is invalid."); } MidiFileFormat formatValue; if (Enum.TryParse(parameters[0], true, out formatValue)) { format = formatValue; } if (!short.TryParse(parameters[1], out timeDivision)) { CsvError.ThrowBadFormat(record.LineNumber, "Invalid time division."); } } break; case MidiFileCsvLayout.MidiCsv: { if (parameters.Length < 3) { CsvError.ThrowBadFormat(record.LineNumber, "Parameters count is invalid."); } ushort formatValue; if (ushort.TryParse(parameters[0], out formatValue) && Enum.IsDefined(typeof(MidiFileFormat), formatValue)) { format = (MidiFileFormat)formatValue; } if (!short.TryParse(parameters[2], out timeDivision)) { CsvError.ThrowBadFormat(record.LineNumber, "Invalid time division."); } } break; } return(new HeaderChunk { FileFormat = format != null ? (ushort)format.Value : ushort.MaxValue, TimeDivision = TimeDivisionFactory.GetTimeDivision(timeDivision) }); }
/// <summary> /// Converts CSV representation of a MIDI file to <see cref="MidiFile"/> readong CSV data from a stream. /// </summary> /// <param name="stream">Stream to read MIDI file from.</param> /// <param name="settings">Settings according to which CSV data must be converted.</param> /// <returns>An instance of the <see cref="MidiFile"/> representing a MIDI file written in CSV format.</returns> /// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception> /// <exception cref="ArgumentException"><paramref name="stream"/> doesn't support reading.</exception> /// <exception cref="IOException">An I/O error occurred while reading from the stream.</exception> /// <exception cref="ObjectDisposedException"><paramref name="stream"/> is disposed.</exception> public MidiFile ConvertCsvToMidiFile(Stream stream, MidiFileCsvConversionSettings settings = null) { ThrowIfArgument.IsNull(nameof(stream), stream); if (!stream.CanRead) { throw new ArgumentException("Stream doesn't support reading.", nameof(stream)); } return(CsvToMidiFileConverter.ConvertToMidiFile(stream, settings ?? new MidiFileCsvConversionSettings())); }
/// <summary> /// Converts the specified <see cref="MidiFile"/> to CSV represenattion and writes it to a stream. /// </summary> /// <remarks> /// Note that <see cref="MidiFile"/> can be converted to different CSV representations. You can specify desired /// CSV layout via <paramref name="settings"/> using <see cref="MidiFileCsvConversionSettings.CsvLayout"/> property. /// </remarks> /// <param name="midiFile"><see cref="MidiFile"/> to convert to CSV.</param> /// <param name="stream">Stream to write CSV representation to.</param> /// <param name="settings">Settings according to which <paramref name="midiFile"/> must be converted.</param> /// <exception cref="ArgumentNullException"><paramref name="midiFile"/> is null. -or- /// <paramref name="stream"/> is null.</exception> /// <exception cref="ArgumentException"><paramref name="stream"/> doesn't support writing.</exception> /// <exception cref="IOException">An I/O error occurred while writing to the stream.</exception> /// <exception cref="ObjectDisposedException"><paramref name="stream"/> is disposed.</exception> public void ConvertMidiFileToCsv(MidiFile midiFile, Stream stream, MidiFileCsvConversionSettings settings = null) { ThrowIfArgument.IsNull(nameof(midiFile), midiFile); ThrowIfArgument.IsNull(nameof(stream), stream); if (!stream.CanWrite) { throw new ArgumentException("Stream doesn't support writing.", nameof(stream)); } MidiFileToCsvConverter.ConvertToCsv(midiFile, stream, settings ?? new MidiFileCsvConversionSettings()); }
private static TimedMidiEvent[] ParseNote(Record record, MidiFileCsvConversionSettings settings) { if (record.TrackNumber == null) { CsvError.ThrowBadFormat(record.LineNumber, "Invalid track number."); } if (record.Time == null) { CsvError.ThrowBadFormat(record.LineNumber, "Invalid time."); } var parameters = record.Parameters; if (parameters.Length < 5) { CsvError.ThrowBadFormat(record.LineNumber, "Invalid number of parameters provided."); } var i = -1; try { var channel = (FourBitNumber)TypeParser.FourBitNumber(parameters[++i], settings); var noteNumber = (SevenBitNumber)TypeParser.NoteNumber(parameters[++i], settings); ITimeSpan length = null; TimeSpanUtilities.TryParse(parameters[++i], settings.NoteLengthType, out length); var velocity = (SevenBitNumber)TypeParser.SevenBitNumber(parameters[++i], settings); var offVelocity = (SevenBitNumber)TypeParser.SevenBitNumber(parameters[++i], settings); return(new[] { new TimedMidiEvent(record.Time, new NoteOnEvent(noteNumber, velocity) { Channel = channel }), new TimedMidiEvent(record.Time.Add(length, TimeSpanMode.TimeLength), new NoteOffEvent(noteNumber, offVelocity) { Channel = channel }), }); } catch { CsvError.ThrowBadFormat(record.LineNumber, $"Parameter ({i}) is invalid."); } return(null); }
private static void WriteFileEnd(CsvWriter csvWriter, MidiFileCsvConversionSettings settings, TempoMap tempoMap) { switch (settings.CsvLayout) { case MidiFileCsvLayout.DryWetMidi: return; case MidiFileCsvLayout.MidiCsv: WriteRecord(csvWriter, 0, 0, MidiCsvRecordTypes.File.FileEnd, settings, tempoMap); break; } }
private static void WriteTrackChunkStart(CsvWriter csvWriter, int trackNumber, MidiFileCsvConversionSettings settings, TempoMap tempoMap) { switch (settings.CsvLayout) { case MidiFileCsvLayout.DryWetMidi: break; case MidiFileCsvLayout.MidiCsv: WriteRecord(csvWriter, trackNumber, 0, MidiCsvRecordTypes.File.TrackChunkStart, settings, tempoMap); break; } }
private static void WriteRecord(CsvWriter csvWriter, int?trackNumber, long?time, string type, MidiFileCsvConversionSettings settings, TempoMap tempoMap, params object[] parameters) { var convertedTime = time == null ? null : TimeConverter.ConvertTo(time.Value, settings.TimeType, tempoMap); var processedParameters = parameters.SelectMany(ProcessParameter); csvWriter.WriteRecord(new object[] { trackNumber, convertedTime, type }.Concat(processedParameters)); }
private static void WriteTrackChunkEnd( StreamWriter streamWriter, int trackNumber, long time, MidiFileCsvConversionSettings settings, TempoMap tempoMap) { switch (settings.CsvLayout) { case MidiFileCsvLayout.DryWetMidi: return; case MidiFileCsvLayout.MidiCsv: WriteRecord(streamWriter, trackNumber, time, MidiCsvRecordTypes.File.TrackChunkEnd, settings, tempoMap); break; } }
public static void ConvertToCsv(MidiFile midiFile, Stream stream, MidiFileCsvConversionSettings settings) { using (var csvWriter = new CsvWriter(stream, settings.CsvSettings)) { var trackNumber = 0; var tempoMap = midiFile.GetTempoMap(); WriteHeader(csvWriter, midiFile, settings, tempoMap); foreach (var trackChunk in midiFile.GetTrackChunks()) { WriteTrackChunkStart(csvWriter, trackNumber, settings, tempoMap); var time = 0L; var timedEvents = trackChunk.GetTimedEvents(); var timedObjects = settings.CsvLayout == MidiFileCsvLayout.MidiCsv || settings.NoteFormat == NoteFormat.Events ? timedEvents : timedEvents.GetTimedEventsAndNotes(); foreach (var timedObject in timedObjects) { time = timedObject.Time; var timedEvent = timedObject as TimedEvent; if (timedEvent != null) { WriteTimedEvent(timedEvent, csvWriter, trackNumber, time, settings, tempoMap); } else { var note = timedObject as Note; if (note != null) { WriteNote(note, csvWriter, trackNumber, time, settings, tempoMap); } } } WriteTrackChunkEnd(csvWriter, trackNumber, time, settings, tempoMap); trackNumber++; } WriteFileEnd(csvWriter, settings, tempoMap); } }
public static void ConvertToCsv(MidiFile midiFile, Stream stream, MidiFileCsvConversionSettings settings) { using (var streamWriter = new StreamWriter(stream)) { var trackNumber = 0; var tempoMap = midiFile.GetTempoMap(); WriteHeader(streamWriter, midiFile, settings, tempoMap); foreach (var trackChunk in midiFile.GetTrackChunks()) { WriteTrackChunkStart(streamWriter, trackNumber, settings, tempoMap); var time = 0L; foreach (var timedEvent in trackChunk.GetTimedEvents()) { time = timedEvent.Time; var midiEvent = timedEvent.Event; var eventType = midiEvent.GetType(); var eventNameGetter = EventNameGetterProvider.Get(eventType, settings.CsvLayout); var recordType = eventNameGetter(midiEvent); var eventParametersGetter = EventParametersGetterProvider.Get(eventType); var recordParameters = eventParametersGetter(midiEvent, settings); WriteRecord(streamWriter, trackNumber, time, recordType, settings, tempoMap, recordParameters); } WriteTrackChunkEnd(streamWriter, trackNumber, time, settings, tempoMap); trackNumber++; } WriteFileEnd(streamWriter, settings, tempoMap); } }
private static void WriteRecord( StreamWriter streamWriter, int?trackNumber, long?time, string type, MidiFileCsvConversionSettings settings, TempoMap tempoMap, params object[] parameters) { var convertedTime = time == null ? null : TimeConverter.ConvertTo(time.Value, settings.TimeType, tempoMap); var processedParameters = parameters.SelectMany(ProcessParameter); streamWriter.WriteLine(CsvUtilities.MergeCsvValues( settings.CsvDelimiter, new object[] { trackNumber, convertedTime, type }.Concat(processedParameters))); }
private static void WriteHeader( StreamWriter streamWriter, MidiFile midiFile, MidiFileCsvConversionSettings settings, TempoMap tempoMap) { MidiFileFormat?format = null; try { format = midiFile.OriginalFormat; } catch { } var trackChunksCount = midiFile.GetTrackChunks().Count(); switch (settings.CsvLayout) { case MidiFileCsvLayout.DryWetMidi: WriteRecord(streamWriter, null, null, DryWetMidiRecordTypes.File.Header, settings, tempoMap, format, midiFile.TimeDivision.ToInt16()); break; case MidiFileCsvLayout.MidiCsv: WriteRecord(streamWriter, 0, 0, MidiCsvRecordTypes.File.Header, settings, tempoMap, format != null ? (ushort)format.Value : (trackChunksCount > 1 ? 1 : 0), trackChunksCount, midiFile.TimeDivision.ToInt16()); break; } }
private static RecordType?GetRecordType(string recordType, MidiFileCsvConversionSettings settings) { var csvLayout = settings.CsvLayout; var recordTypes = csvLayout == MidiFileCsvLayout.DryWetMidi ? RecordTypes_DryWetMidi : RecordTypes_MidiCsv; var eventsNames = EventsNamesProvider.Get(csvLayout); RecordType result; if (recordTypes.TryGetValue(recordType, out result)) { return(result); } if (eventsNames.Contains(recordType, StringComparer.OrdinalIgnoreCase)) { return(RecordType.Event); } return(null); }
private static void WriteTimedEvent(TimedEvent timedEvent, CsvWriter csvWriter, int trackNumber, long time, MidiFileCsvConversionSettings settings, TempoMap tempoMap) { var midiEvent = timedEvent.Event; var eventType = midiEvent.GetType(); var eventNameGetter = EventNameGetterProvider.Get(eventType, settings.CsvLayout); var recordType = eventNameGetter(midiEvent); var eventParametersGetter = EventParametersGetterProvider.Get(eventType); var recordParameters = eventParametersGetter(midiEvent, settings); WriteRecord(csvWriter, trackNumber, time, recordType, settings, tempoMap, recordParameters); }
private static MidiEvent ParseEvent(Record record, MidiFileCsvConversionSettings settings) { if (record.TrackNumber == null) { CsvError.ThrowBadFormat(record.LineNumber, "Invalid track number."); } if (record.Time == null) { CsvError.ThrowBadFormat(record.LineNumber, "Invalid time."); } var eventParser = EventParserProvider.Get(record.RecordType, settings.CsvLayout); try { return(eventParser(record.Parameters, settings)); } catch (FormatException ex) { CsvError.ThrowBadFormat(record.LineNumber, "Invalid format of event record.", ex); return(null); } }
private static Record ReadRecord(CsvReader csvReader, int lineNumber, MidiFileCsvConversionSettings settings) { var record = csvReader.ReadRecord(); if (record == null) { return(null); } var values = record.Values; if (values.Length < 3) { CsvError.ThrowBadFormat(record.LineNumber, "Missing required parameters."); } int parsedTrackNumber; var trackNumber = int.TryParse(values[0], out parsedTrackNumber) ? (int?)parsedTrackNumber : null; ITimeSpan time = null; TimeSpanUtilities.TryParse(values[1], settings.TimeType, out time); var recordType = values[2]; if (string.IsNullOrEmpty(recordType)) { CsvError.ThrowBadFormat(record.LineNumber, "Record type isn't specified."); } var parameters = values.Skip(3).ToArray(); return(new Record(record.LineNumber, trackNumber, time, recordType, parameters)); }
/// <summary> /// Converts the specified <see cref="MidiFile"/> to CSV represenattion and writes it to a file. /// </summary> /// <remarks> /// Note that <see cref="MidiFile"/> can be converted to different CSV representations. You can specify desired /// CSV layout via <paramref name="settings"/> using <see cref="MidiFileCsvConversionSettings.CsvLayout"/> property. /// </remarks> /// <param name="midiFile"><see cref="MidiFile"/> to convert to CSV.</param> /// <param name="filePath">Path of the output CSV file.</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="settings">Settings according to which <paramref name="midiFile"/> must be converted.</param> /// <exception cref="ArgumentNullException"><paramref name="midiFile"/> is null.</exception> /// <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="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> public void ConvertMidiFileToCsv(MidiFile midiFile, string filePath, bool overwriteFile = false, MidiFileCsvConversionSettings settings = null) { ThrowIfArgument.IsNull(nameof(midiFile), midiFile); using (var fileStream = FileUtilities.OpenFileForWrite(filePath, overwriteFile)) { ConvertMidiFileToCsv(midiFile, fileStream, settings); } }
public static MidiFile ConvertToMidiFile(Stream stream, MidiFileCsvConversionSettings settings) { var midiFile = new MidiFile(); var events = new Dictionary <int, List <TimedMidiEvent> >(); using (var csvReader = new CsvReader(stream, settings.CsvDelimiter)) { var lineNumber = 0; Record record; while ((record = ReadRecord(csvReader, lineNumber, settings)) != null) { var recordType = GetRecordType(record.RecordType, settings); if (recordType == null) { CsvError.ThrowBadFormat(lineNumber, "Unknown record."); } switch (recordType) { case RecordType.Header: { var headerChunk = ParseHeader(record, settings); midiFile.TimeDivision = headerChunk.TimeDivision; midiFile.OriginalFormat = (MidiFileFormat)headerChunk.FileFormat; } break; case RecordType.TrackChunkStart: case RecordType.TrackChunkEnd: case RecordType.FileEnd: break; case RecordType.Event: { var midiEvent = ParseEvent(record, settings); var trackChunkNumber = record.TrackNumber.Value; AddTimedEvents(events, trackChunkNumber, new TimedMidiEvent(record.Time, midiEvent)); } break; case RecordType.Note: { var noteEvents = ParseNote(record, settings); var trackChunkNumber = record.TrackNumber.Value; AddTimedEvents(events, trackChunkNumber, noteEvents); } break; } lineNumber = record.LineNumber + 1; } } if (!events.Keys.Any()) { return(midiFile); } var tempoMap = GetTempoMap(events.Values.SelectMany(e => e), midiFile.TimeDivision); var trackChunks = new TrackChunk[events.Keys.Max() + 1]; for (int i = 0; i < trackChunks.Length; i++) { List <TimedMidiEvent> timedMidiEvents; trackChunks[i] = events.TryGetValue(i, out timedMidiEvents) ? timedMidiEvents.Select(e => new TimedEvent(e.Event, TimeConverter.ConvertFrom(e.Time, tempoMap))).ToTrackChunk() : new TrackChunk(); } midiFile.Chunks.AddRange(trackChunks); return(midiFile); }