Opening a MIDI file and parsing the tracks.
Sample MIDI File
For most of these posts on parsing MIDI files I’ll be using this simple piece of music as an example. It is Bach’s “Two-Part Invention No. 1”. Here is a PDF of the sheet music and a MIDI file for the piece.
A MIDI file contains a series of chunks of data called tracks.
- The first track is always the MIDI header.
- In modern MIDI files there is always one special track called the Tempo Track. As its name says, events related to tempo will be placed in this track.
- After the Tempo Track we have the tracks with music events, such as notes. There can be many tracks in a MIDI file.
Tracks and Music Parts
After the Tempo Track, MIDI tracks are usually associated with a part in a piece of music. Our example piece is for piano and has a part for each hand.
One part is in the treble clef and the other in the bass clef in a grand staff. These parts are played simultaneously when the music is performed. However, the data for each part are in separate chunks written one after another in the MIDI file. The entire file must be loaded and parsed before the piece can be played.
Loading a MIDI file
To load a MIDI file and parse it with Core MIDI we start by importing the
Next we need to declare a variable for our
MusicSequence struct. Core MIDI is a C library and the following code is a common idiom that Apple will use for declaring and creating a struct.
Now we need to have a URL to a midi file. This code assumes the file is in the Desktop directory.
This code will load the midi file into the
kMusicSequenceLoadSMF_ChannelsToTracks flag will cause the file to be parsed into separate tracks for the different categories of MIDI events even if the file does not contain have those tracks.
Here’s what we have so far. This code combines the code in the above steps.
1 2 3 4 5 6 7
Extract the Tempo Track from the MIDI File
We’re now ready to split the file into its tracks. We know there is just one Tempo Track so that is an easy first step. We need to use an opaque struct called a
MusicTrack to hold each track. Then we use a C function to get the track.
Next we need an iterator that allows us to loop trough the events in the track. Following the familiar pattern, we declare a struct and then call a Core MIDI function.
We’ll need these variables next to work with the events as we loop through the track.
1 2 3 4 5
Now we’ll loop through all the events in this track. The way we loop is a little different than classic looping. Here’s the code and I’ll go into detail after it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
- Line 1: The
MusicEventIteratorHasCurrentEvent()function determines if there is an event to process at the current position. MIDI events can be different sizes so a normal indexed loop through an array won’t work. We need to run this function once before we start the loop in case the track is empty.
- Line 2: Loop until there are no more events.
- Line 3-7: The
MusicEventIteratorGetEventInfo()function will get all the data from the current event and populate our variables. We can test the
eventTypevariable to determine what event we have.
- Line 10: We’ll just output that we found an event and its type number.
- Line 12: Attempt to move to the next event with
- Line 13: Use the
MusicEventIteratorHasCurrentEvent()function to determine if there is a current event. If there isn’t one then the
hasNextvariable will be set to
NOand the loop will end.
When we run the above code against the bach-invention-01.mid file we see this output.
Event found! type: 5 Event found! type: 3
The types we found are identified in the following enum. Type 5 is
kMusicEventType_Meta, which is a MIDI Meta Event. Type 3 is
kMusicEventType_ExtendedTempo, which gives us the starting tempo of the piece. We’ll explore these types in more detail a later post in the series.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Full Code Sample
Here is the full code from the this post. It is all in the
main() function to simplify the code. We’ll add functions to improve the code in the next post.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
We are now looking at the events in a MIDI file. We’ll start adding to this in the next post. Until then,