Convert MIDI file to list of notes with length and starting time

brendon-ai picture brendon-ai · Jan 20, 2017 · Viewed 11.7k times · Source

I am working on a game in Unity that will generate levels from music. I am planning to include simple text files (that don't have to be in a standard format) with the game and parse them to generate the levels. The problem is, I need to be able to convert MIDI files to a text format (preferably not something as complicated as MusicXML). The text files that I will include with the game would ideally consist of a list of notes, each with a length and a starting time (in arbitrary time units). I cannot simply include a music file, as my bullet-hell game will have to precisely time the bullets with the notes.

I do not care what programming language this is in, as this code will not be included with the game. Also, I have never worked with MIDI before in any form. I am happy to use any library and/or free program for this.

Thank you in advance for your help!

Answer

Maxim picture Maxim · May 26, 2017

You can use any open-source library for parsing MIDI file and convert its data to text file in your desired format. For example, with the DryWetMIDI you can use this code:

public static void ConvertMidiToText(string midiFilePath, string textFilePath)
{
    var midiFile = MidiFile.Read(midiFilePath);

    File.WriteAllLines(textFilePath,
                       midiFile.GetNotes()
                               .Select(n => $"{n.NoteNumber} {n.Time} {n.Length}"));
}

ConvertMidiToText method will produce text files like this:

37 0 480
37 960 480
37 1920 480
37 2400 480
70 2640 192

where first number is note number (60 = C4), second one is starting time in MIDI ticks and the third one is length in MIDI ticks.

You can even write time and length in hours:minutes:seconds format. This code

public static void ConvertMidiToText(string midiFilePath, string textFilePath)
{
    var midiFile = MidiFile.Read(midiFilePath);
    TempoMap tempoMap = midiFile.GetTempoMap();

    File.WriteAllLines(textFilePath,
                        midiFile.GetNotes()
                                .Select(n => $"{n.NoteNumber} {n.TimeAs<MetricTimeSpan>(tempoMap)} {n.LengthAs<MetricTimeSpan>(tempoMap)}"));
}

will produce text like this:

37 00:00:00 00:00:00.4800000
37 00:00:00.9600000 00:00:00.4800000
37 00:00:01.9200000 00:00:00.4800000
37 00:00:02.4000000 00:00:00.4800000
70 00:00:02.6400000 00:00:00.1920000