MIDI file splitting
You can split a MIDI file in different ways using extension methods from the Splitter class. Available methods are described below. Please note that the article doesn't cover all possible methods and their settings. Please read API documentation on Splitter to get complete information.
SplitByChannel
SplitByChannel method splits a MIDI file by channel so all channel events will be separated by channel and copied to corresponding new files. All meta and system exclusive events will be copied to all the new files (that's default behavior that can be turned off). Thus each new file will contain all meta and sysex events and channel ones for a single channel. The image below illustrates this process:
SplitByObjects
SplitByObjects method splits a MIDI file by objects. The process can be adjusted via SplitByObjectsSettings passed to the second parameter of the method.
For example, to split a file by notes copying all MIDI events to each new file:
var newFiles = midiFile.SplitByObjects(
ObjectType.Note | ObjectType.TimedEvent,
new SplitByObjectsSettings
{
WriteToAllFilesPredicate = obj => obj is TimedEvent
});
Here we specify that we need to split the file by notes and timed events, but every timed event must be written to all result files. So in fact we're splitting the file by notes having all non-notes timed events presented in each new file. The image below illustrates the process:
To split a file by objects the tool needs to determine the key of each object. Objects with the same key will be outputted in a separate file. In the example above default logic of key calculation is used. The following table shows what a default key is for each type of an object:
Object type | Key |
---|---|
TimedEvent | The type of the underlying event (EventType of TimedEvent.Event). |
Note | Pair of the channel and note number of a note. |
Chord | Collection of keys of the underlying notes. |
You can alter key calculation logic by providing custom key selector. For example, to separate notes by only note number ignoring a note's channel:
var newFiles = midiFile.SplitByObjects(
ObjectType.Note | ObjectType.TimedEvent,
new SplitByObjectsSettings
{
KeySelector = obj => (obj as Note)?.NoteNumber,
WriteToAllFilesPredicate = obj => obj is TimedEvent
});
Here a key will be resolved to null
if an object is not an instance of the Note
. null
key means to use the default key calculation logic shown above.
If key selection is complex, you may decide to implement a class for such a key. Just for example, let's create the key class that identifies a chord by its shortest name:
private sealed class ChordNameId
{
private readonly string _name;
public ChordNameId(Chord chord)
{
_name = chord.GetMusicTheoryChord().GetNames().FirstOrDefault();
}
public override bool Equals(object obj) =>
obj is ChordNameId chordNameId &&
chordNameId._name == _name;
public override int GetHashCode() =>
_name.GetHashCode();
}
And now we can use it to split a file by chords of the same name:
var newFiles = midiFile.SplitByObjects(
ObjectType.Chord,
new SplitByObjectsSettings
{
KeySelector = obj => new ChordNameId((Chord)obj)
});
Please see documentation on SplitByObjectsSettings to learn more about how you can adjust the process of splitting.
SplitByGrid
SplitByGrid method splits a MIDI file by the specified grid. Each file will preserve the original tempo map and all parameters changes (like a control value or program changes). The image below shows general case of splitting a MIDI file by grid:
Splitting can be adjusted via the settings
parameter of the SliceMidiFileSettings type. SplitNotes and PreserveTimes properties described below. Please see all available properties in documentation for SliceMidiFileSettings.
SplitNotes
SplitNotes indicates whether notes should be split at points of grid intersection or not. The default value is true
. If false
used, notes treated as just Note On / Note Off events rather than note objects. Splitting notes produces new Note On / Note Off events at points where grid intersects notes if the property set to true
. The following image shows splitting by grid if SplitNotes
set to false
:
PreserveTimes
PreserveTimes indicates whether original times of events should be preserved or not. The default value is false
. If false
used, events will be moved to the start of a new file. If true
used, events will be placed in new files at the same times as in the input file. The following image shows splitting in case of PreserveTimes
set to true
:
SkipPart
SkipPart method skips part of the specified length of a MIDI file and returns the remaining part as an instance of MidiFile. The image below shows general case of skipping a part of a MIDI file:
TakePart
TakePart methods take part of the specified length of a MIDI file (starting at the start or at the specified time within the file) and return it as an instance of MidiFile. The image below shows both cases of taking a part of a MIDI file:
CutPart
CutPart method cuts a part of the specified length from a MIDI file (starting at the specified time within the file) and returns a new instance of MidiFile which is the original file without the part. The image below shows how the method works: