CSV serializer
With the CsvSerializer you can either serialize objects to CSV or deserialize them back from CSV.
In this article the comma (,
) used as a values delimiter. You can change it with the Delimiter
property of the CsvSerializationSettings (for serialization) and CsvDeserializationSettings (for deserialization).
Example
Here is a quick example of CSV:
0,"MThd",0,"Header",96
1,"MTrk",0,"Text",0,"A"
1,"MTrk",1,"Text",100,"B"
2,"MTrk",0,"Note",0,100,4,E7,127,0
2,"MTrk",1,"Note",100,100,3,D3,127,0
2,"MTrk",1,"Note",110,100,3,E2,127,0
The meaning of these lines:
- The file has the time division of 96 ticks per quarter note.
- Track chunk #1 contains two events:
- #0: Text event with text of A;
- #1: Text event with text of B.
- Track chunk #2 contains two objects:
- #0: note E7;
- #1: chord with two notes: D3 and E2.
See detailed description of the format in the next section.
Format
Each record in CSV representation is in the following form:
ChunkIndex,ChunkId,ObjectIndex,ObjectName,ObjectData
ChunkIndex
is a number of a MIDI chunk with an object. ChunkId
is the ID of the chunk, for example, "MTrk"
for a track chunk or "MThd"
for a header one.
ObjectIndex
is a number of an object within the chunk. Also note that if two notes have the same ObjectIndex
, it means they form a chord. For example:
2,"MTrk",0,"Note",0,100,4,E7,127,0
2,"MTrk",1,"Note",100,100,3,D3,127,0
2,"MTrk",1,"Note",110,100,3,E2,127,0
Here we have a single note E7 (the object #0) and a chord (the object #1) with two notes: D3 and E2.
Note that when you're deserializing a MIDI file, you can put any number to ObjectIndex
. This property is irrelevant for MIDI file deserialization, but required to correctly deserialize objects (DeserializeObjectsFromCsv). The property exists to have single CSV schema for both file and objects deserialization.
If ObjectName
is "Header"
then ObjectData
will contain a single number – time division.
If ObjectName
is "Note"
then ObjectData
will be in the following format:
Time,Length,Channel,Note,Velocity,OffVelocity
where Time
, Length
, Channel
, Velocity
and OffVelocity
hold values of the corresponding properties of a Note object. You can change Time
and Length
representations via TimeType and LengthType properties of the CsvSerializationSettings correspondingly (there are also the same properties in CsvDeserializationSettings for deserialization). Note
can be either the note number or letter (C4, for example) depending on the NoteFormat
property value.
In other cases of ObjectName
it's assumed that this component represents the type of a MIDI event. For example, you can see "NoteOn"
or "SequenceTrackName"
as a value for ObjectName
. ObjectData
for such records will be in the following format:
Time,EventData
As for the Time
, it holds the time of a TimedEvent object. You can change Time
representation used for serialization via the TimeType property of the CsvSerializationSettings (and the same property of the CsvDeserializationSettings for deserialization). EventData
depends on the type of an event:
ObjectName | EventData | Modifiers |
---|---|---|
"SequenceTrackName" | Text | |
"CopyrightNotice" | Text | |
"InstrumentName" | Text | |
"Marker" | Text | |
"CuePoint" | Text | |
"Lyric" | Text | |
"Text" | Text | |
"ProgramName" | Text | |
"DeviceName" | Text | |
"SequenceNumber" | Number | |
"PortPrefix" | Port | |
"ChannelPrefix" | Channel | |
"TimeSignature" | Numerator,Denominator,ClocksPerClick,ThirtySecondNotesPerBeat | |
"KeySignature" | Key,Scale | |
"SetTempo" | MicrosecondsPerQuarterNote | |
"SmpteOffset" | Format,Hours,Minutes,Seconds,Frames,SubFrames | |
"SequencerSpecific" | Data | BytesArray |
"UnknownMeta" | StatusByte,Data | BytesArray |
"NoteOn" | Channel,Note,Velocity | Note |
"NoteOff" | Channel,Note,Velocity | Note |
"PitchBend" | Channel,PitchValue | |
"ControlChange" | Channel,ControlNumber,ControlValue | |
"ProgramChange" | Channel,ProgramNumber | |
"ChannelAftertouch" | Channel,AftertouchValue | |
"NoteAftertouch" | Channel,Note,AftertouchValue | Note |
"NormalSysEx" | Data | BytesArray |
"EscapeSysEx" | Data | BytesArray |
"ActiveSensing" | ||
"Start" | ||
"Stop" | ||
"Reset" | ||
"Continue" | ||
"TimingClock" | ||
"TuneRequest" | ||
"MidiTimeCode" | Component,ComponentValue | |
"SongSelect" | Number | |
"SongPositionPointer" | PointerValue |
where Modifiers are:
- BytesArray –
Data
will be serialized as"B1 B2 B3 ..."
where format of the string controlled by theBytesArrayFormat
andBytesArrayDelimiter
properties of the CsvSerializationSettings and CsvDeserializationSettings; - Note –
Note
property is serialized as either the note number or letter (C4, for example) depending on theNoteFormat
property of the CsvSerializationSettings and CsvDeserializationSettings.
By the way, when you're deserializing CSV data, you can set TimeType, LengthType and NoteFormat properties of the CsvDeserializationSettings to null
to let the library detect formats automatically (which is the default behavior). Of course, you can tell the engine exact formats to simplify its work.
Note that if you deserialize a MIDI file from CSV, you don't need to manually sort records by time in text representation before deserialization. DryWetMIDI will do it for you. So, for example, these lines
0,"MThd",0,"Header",96
1,"MTrk",0,"NoteOff",6/4,4,100,0
1,"MTrk",1,"Text",1/4,"A"
1,"MTrk",2,"NoteOn",3/4,4,100,127
1,"MTrk",3,"Text",2/4,"B"
will be correctly deserialized to the file with single track chunk containing following events:
- Text event with text of A at time of
1/4
. - Text event with text of B at time of
2/4
. - Note On event with note number
100
and velocity127
at time of3/4
. - Note Off event with note number
100
and velocity0
at time of6/4
.