Skip to content

Instantly share code, notes, and snippets.

@melanchall
Last active February 24, 2021 12:22
Show Gist options
  • Save melanchall/d4142f5f0fb36ab86e46110d69966fed to your computer and use it in GitHub Desktop.
Save melanchall/d4142f5f0fb36ab86e46110d69966fed to your computer and use it in GitHub Desktop.
Creating a MIDI file with the DryWetMIDI
public static void CreateMidiFile()
{
// Create an empty MIDI file
var midiFile = new MidiFile();
// Set tempo map of the file using tempo of 110 BPM. See
// https://github.com/melanchall/drywetmidi/wiki/Tempo-map to learn more about managing
// tempo map in DryWetMIDI
var tempoMap = TempoMap.Create(Tempo.FromBeatsPerMinute(110));
midiFile.ReplaceTempoMap(tempoMap);
// Add first track chunk to the file
var trackChunk1 = BuildFirstTrackChunk(tempoMap);
midiFile.Chunks.Add(trackChunk1);
// Add second track chunk to the file
var trackChunk2 = BuildSecondTrackChunk(tempoMap);
midiFile.Chunks.Add(trackChunk2);
// Write MIDI data to file. See https://github.com/melanchall/drywetmidi/wiki/Writing-a-MIDI-file
// to learn more
midiFile.Write("Path to the Greatest MIDI File ever.mid");
}
private static TrackChunk BuildFirstTrackChunk(TempoMap tempoMap)
{
// We are going to create the first track chunk using PatternBuilder
// class which provides fluent interface to program music. See
// https://github.com/melanchall/drywetmidi/wiki/Pattern to learn more about features
// provided by pattern building API
var trackChunk = new PatternBuilder()
// All following notes will have eighth length (if length is not
// specified explicitly as an argument of corresponding methods)
.SetNoteLength(MusicalTimeSpan.Eighth)
// Insert A2 and A3 and repeat them one time so four notes totally
// will be inserted (A2 A3 A2 A3)
.Note(OctaveDefinition.Get(2).A)
.Note(OctaveDefinition.Get(3).A)
.Repeat(2, 1)
// All following notes will have length of 2 seconds (if length is not
// specified explicitly as an argument of corresponding methods)
.SetNoteLength(new MetricTimeSpan(0, 0, 2))
// All following intervals will be calculated relative to C4
.SetRootNote(NoteDefinition.Get(NoteName.C, 4)) // or OctaveDefinition.Get(4).C
// Insert notes by intervals relative to the root note set before
.Note(IntervalDefinition.Nine) // C4 + 9
.Note(IntervalDefinition.Seven) // C4 + 7
.Note(-IntervalDefinition.One) // C4 - 1
.Note(IntervalDefinition.Three) // C4 + 3
// Insert a pause with length of 2 bars
.StepForward(new BarBeatTimeSpan(2, 0))
// Insert a chord defined by intervals relative to the root note specified
// as an argument of Chord method (B2)
.Chord(new[]
{
IntervalDefinition.Two, // B2 + 2
IntervalDefinition.Three, // B2 + 3
-IntervalDefinition.Twelve, // B2 - 12
},
OctaveDefinition.Get(2).B)
// Insert a pause of single dotted triplet half length
.StepForward(MusicalTimeSpan.Half.Triplet().SingleDotted())
// Insert a chord defined by the specified notes
.Chord(new[]
{
NoteDefinition.Get(NoteName.B, 3), // B3
NoteDefinition.Get(NoteName.C, 4), // C4
})
// Build pattern into an instance of the Pattern class
.Build()
// Export the pattern to track chunk
.ToTrackChunk(tempoMap);
// Add Program Change event to specify an instrument used to play the track chunk.
// See https://github.com/melanchall/drywetmidi/wiki/Events-absolute-time to learn more
// about managing events by absolute time
using (var timedEventsManager = trackChunk.ManageTimedEvents())
{
timedEventsManager.Events.AddEvent(
new ProgramChangeEvent((SevenBitNumber)26), // 'Acoustic Guitar (steel)' in GM
time: 0);
}
return trackChunk;
}
private static TrackChunk BuildSecondTrackChunk(TempoMap tempoMap)
{
// We can create a track chunk and put events in it via its constructor
var trackChunk = new TrackChunk(
new ProgramChangeEvent((SevenBitNumber)1)); // 'Acoustic Grand Piano' in GM
// Insert notes via NotesManager class. See https://github.com/melanchall/drywetmidi/wiki/Notes
// to learn more about managing notes
using (var notesManager = trackChunk.ManageNotes())
{
var notes = notesManager.Notes;
// Convert time span of 1 minute and 30 seconds to MIDI ticks. See
// https://github.com/melanchall/drywetmidi/wiki/Time-and-length to learn more
// about time and length representations and conversion between them
var oneAndHalfMinute = TimeConverter.ConvertFrom(new MetricTimeSpan(0, 1, 30), tempoMap);
// Insert two notes:
// - A2 with length of 4/15 at 1 minute and 30 seconds from a file start
// - B4 with length of 4 beats (1 beat = 1 quarter length at this case) at the start of a file
notes.Add(new Note(noteName: NoteName.A,
octave: 2,
length: LengthConverter.ConvertFrom(new MusicalTimeSpan(4, 15),
time: oneAndHalfMinute,
tempoMap: tempoMap),
time: oneAndHalfMinute),
new Note(noteName: NoteName.B,
octave: 4,
length: LengthConverter.ConvertFrom(new BarBeatTimeSpan(0, 4),
time: 0,
tempoMap: tempoMap),
time: 0));
}
// Insert chords via ChordsManager class. See https://github.com/melanchall/drywetmidi/wiki/Chords
// to learn more about managing chords
using (var chordsManager = trackChunk.ManageChords())
{
var chords = chordsManager.Chords;
// Define notes for a chord:
// - C2 with length of 30 seconds and 600 milliseconds
// - C#3 with length of 300 milliseconds
var notes = new[]
{
new Note(noteName: NoteName.C,
octave: 2,
length: LengthConverter.ConvertFrom(new MetricTimeSpan(0, 0, 30, 600),
time: 0,
tempoMap: tempoMap)),
new Note(noteName: NoteName.CSharp,
octave: 3,
length: LengthConverter.ConvertFrom(new MetricTimeSpan(0, 0, 0, 300),
time: 0,
tempoMap: tempoMap))
};
// Insert the chord at different times:
// - at the start of a file
// - at 10 bars and 2 beats from a file start
// - at 10 hours from a file start
chords.Add(new Chord(notes,
time: 0),
new Chord(notes,
time: TimeConverter.ConvertFrom(new BarBeatTimeSpan(10, 2),
tempoMap)),
new Chord(notes,
time: TimeConverter.ConvertFrom(new MetricTimeSpan(10, 0, 0),
tempoMap)));
}
return trackChunk;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment