Part 6 of a Series
How does MIDI handle measures in music?
Sample MIDI Files
I’ll be using some new MIDI example files in this part of the series in addition to the Bach Invention I have been using.
Core MIDI Measures
The answer to the question, "How does MIDI handle measures in music?" is that it doesn’t. There is no MIDI event for a measure. The MIDI protocol was not meant for music notation. It was only intended as a streaming protocol from one electronic device to another. When we hear a note for a duration of time we have no idea how it was notated in a score or how it should be notated. MIDI mimics playing a musical instrument.
Let’s look at some notation examples to get a better understanding of this. Here are three fragments from an example music score. They are in different time signatures, however they all sound identical when heard.
Sample 1 in 2/4

Sample 2 in 4/4

Sample 3 in 6/8

Here are the first four notes from the treble clef of each sample.
Sample 1 – 2/4
timestamp: 0.000 channel: 0, note: 60–C4, duration: 1.000
timestamp: 1.000 channel: 0, note: 62–D4, duration: 1.000
timestamp: 2.000 channel: 0, note: 64–E4, duration: 1.000 <-- Start of new measure
timestamp: 3.000 channel: 0, note: 65–F4, duration: 1.000
Sample 2 – 4/4
timestamp: 0.000 channel: 0, note: 60–C4, duration: 1.000
timestamp: 1.000 channel: 0, note: 62–D4, duration: 1.000
timestamp: 2.000 channel: 0, note: 64–E4, duration: 1.000
timestamp: 3.000 channel: 0, note: 65–F4, duration: 1.000
... <-- Start of new measure
Sample 3 – 6/8
timestamp: 0.000 channel: 0, note: 60–C4, duration: 1.000
timestamp: 1.000 channel: 0, note: 62–D4, duration: 1.000
timestamp: 2.000 channel: 0, note: 64–E4, duration: 1.000
timestamp: 3.000 channel: 0, note: 65–F4, duration: 1.000 <-- Start of new measure
As you can see, they are all the same. There are no other events around these note events with measure information. The first sample has a measure break after two notes. The second is just one measure. The third sample has a measure break after three notes. There must be something that we can use to determine the measures, and there is. Core MIDI provides us with functions to calculate the measure, beat, and note value of each note. We just need to understand some things about MIDI, C, and Core MIDI to use them.
Pulses Per Quarter note
The first piece of the puzzle is a value associated with a MIDI file, the PPQ. This stands for Pulses Per Quarter note and is sometimes referred to as PPQN. However, Core MIDI uses the term time resolution. This can be confusing when reading the MIDI specs.
Notice that every note in the above samples has a duration of 1.0. This is because these MIDI files were created from notation software. The default nominal value for a quarter note in MIDI is 1.0. It’s easy for notation software to set this since I created these notes as quarter notes.
MIDI is also a live streaming protocol. A musician playing the samples live would not be able play the notes as precisely. Even if the musician had a very steady hand the durations would vary a little. These durations could easily vary from something like 0.9 to 1.1. That’s one of the things that makes music played by a person sound better. Part of making music expressive is varying the timing of notes very slightly for effect. MIDI started as a very light-weight live protocol with very small and predictable data. PPQ is a setting that determines how fine the resolution of this variation will be in a MIDI file. This value is a 2-byte int and frequently ranges from small numbers like 96 up to 960. Core MIDI recommends the value of 480 when creating a MIDI file with the library.
A value of 480 means a quarter note has a resolution of 480 parts. An eighth note will be 240, a sixteenth 120, and so forth. It gets more interesting with things like triplets. With a higher value of 480 for the PPQ, odd time divisions can be more accurate. When a very expressive musician with strong phrasing is playing, the resulting music will sound very natural.
Obtaining the PPQ from a MIDI File
Core MIDI uses this function to determine the PPQ time resolution for a MIDI file. The MusicTrack we use has to be the Tempo Track detailed in part 3. Core MIDI and Core Audio have the concept of properties using functions similar to this. If you’ve done some coding with Core Audio this will seem very familiar.
The MusicTrackGetProperty
function
OSStatus MusicTrackGetProperty (
MusicTrack inTrack,
UInt32 inPropertyID,
void *outData,
UInt32 *ioLength
);
The function needs to be used twice to get the property. The first call will retrieve the length of the property. The second call uses the retrieved property length to get the actual property.
Variables
We will need the following two variables for these function calls.
Time Resolution Variables
UInt32 timeResolution = 0;
UInt32 propertyLength = 0;
First call
The property ID for the time resolution is kSequenceTrackProperty_TimeResolution
. It is part of this enum
which is found in the MusicPlayer.h file.
Track Properties
enum {
kSequenceTrackProperty_LoopInfo = 0,
kSequenceTrackProperty_OffsetTime = 1,
kSequenceTrackProperty_MuteStatus = 2,
kSequenceTrackProperty_SoloStatus = 3,
kSequenceTrackProperty_AutomatedParameters = 4,
kSequenceTrackProperty_TrackLength = 5,
kSequenceTrackProperty_TimeResolution = 6
};
For the first function call the outData
argument must be set to NULL
. This causes the function to assign the length of the property to the propertyLength
variable.
Retrieve the Length of the Property
MusicTrackGetProperty(tempoTrack,
kSequenceTrackProperty_TimeResolution,
NULL,
&propertyLength);
Second Call
With the length retrieved we call the function again replacing NULL
with the address of the timeResolution
variable.
Retrieve the Time Resolution Property
MusicTrackGetProperty(tempoTrack,
kSequenceTrackProperty_TimeResolution,
&timeResolution,
&propertyLength);
Here is a typical result of this process.
Output
printf("propertyLength: %d\n", propertyLength);
printf("timeResolution: %d\n", timeResolution);
//Output:
// propertyLength: 2
// timeResolution: 384
The time resolution needs to be saved for future use.
self.timeResolution = timeResolution;
Full method
Here’s all the above code in one method.
determineTimeResolutionWithTempoTrack
:
- (void)determineTimeResolutionWithTempoTrack:(MusicTrack)tempoTrack
{
UInt32 timeResolution = 0;
UInt32 propertyLength = 0;
MusicTrackGetProperty(tempoTrack,
kSequenceTrackProperty_TimeResolution,
NULL,
&propertyLength);
MusicTrackGetProperty(tempoTrack,
kSequenceTrackProperty_TimeResolution,
&timeResolution,
&propertyLength);
printf("propertyLength: %d\n", propertyLength);
printf("timeResolution: %d\n", timeResolution);
self.timeResolution = timeResolution;
}
With the fabled PPQ retrieved we can now calculate measures, beats, and subbeats. This is good.
Bar, Beat, and Subbeat
Next, we need to understand what is meant by the terms bar, beat, and subbeat.
- Bar means measure. They are numbered in a midi file starting with 1.
- Beat means the beats in a measure. In music the number of beats in a measure is determined by the time signature. With a time signature of 2/4 there are two beats per measure. For 4/4 there are four beats.
- Subbeat is a term that is not part of common music terminology. In MIDI it refers to where the note occurs within a beat. The length of a beat is the number we calculated in the previous section, the PPQ.
These terms are illustrated in this annotated score. We’ll focus on measure 2.

The first note in the treble clef has these parts:
- Bar: 2 (the second measure)
- Beat: 1 (the first beat)
- Subbeat: 0 (the beginning of the beat)
A common way to show this data in MIDI apps is with this notation.
- 002:01:000 (bar:beat:subbeat)
The second note we’ll mention is note 2 of the bass clef in this measure. It is an eighth note that takes up the second half of the first beat. Therefore, it’s subbeats will be half of the PPQ, or 192. Here are it’s details.
- Bar: 2 (the second measure)
- Beat: 1 (the first beat)
- Subbeat: 192 (the beginning of the second half of the beat)
And it’s condensed notation:
- 002:01:192 (bar:beat:subbeat)
Calculating Bar, Beat, and Subbeat
Now we finally have enough information to programmatically determine the bar:beat:subbeat
for notes in a midi file. This is not a trivial thing to do and we can be very grateful that Core MIDI does it for us.
The first piece is this struct which is used by several MIDI functions. It is found in CoreAudioClock.h.
CABarBeatTime
struct CABarBeatTime {
SInt32 bar;
UInt16 beat;
UInt16 subbeat;
UInt16 subbeatDivisor;
UInt16 reserved;
};
typedef struct CABarBeatTime CABarBeatTime;
Here’s the function we use:
MusicSequenceBeatsToBarBeatTime
OSStatus MusicSequenceBeatsToBarBeatTime(
MusicSequence inSequence,
MusicTimeStamp inBeats,
UInt32 inSubbeatDivisor,
CABarBeatTime *outBarBeatTime
);
Let’s look at the parameters in more detail.
MusicSequence inSequence
– the MIDI sequence we have open and are working with.MusicTimeStamp inBeats
– the timestamp of the note we’re inspecting.UInt32 inSubbeatDivisor
– the PPQ or time resolution we determined in the previous section. We will use that value a lot in MIDI apps.CABarBeatTime *outBarBeatTime
– the address of a copy of the struct.
Finally, here’s how we use the function.
Using the MusicSequenceBeatsToBarBeatTime()
function
CABarBeatTime barBeatTime;
MusicSequenceBeatsToBarBeatTime(self.sequence, timestamp, self.timeResolution, &barBeatTime);
Put it all into a method and we’re ready to go.
showNoteInformationWithNote:timestamp:
- (void)showNoteInformationWithNote:(MIDINoteMessage *)noteMessage
timestamp:(MusicTimeStamp)timestamp
{
CABarBeatTime barBeatTime;
MusicSequenceBeatsToBarBeatTime(_sequence, timestamp, self.timeResolution, &barBeatTime);
printf("%03d:%02d:%03d, timestamp: %5.3f, channel: %d, note: %s, duration: %.3f\n",
barBeatTime.bar,
barBeatTime.beat,
barBeatTime.subbeat,
timestamp,
noteMessage->channel,
noteForMidiNumber(noteMessage->note),
noteMessage->duration
);
}
The output for our test measure 2 of the sample in 2/4. First the treble clef.
Measure 2 – Treble Clef
002:01:000, timestamp: 2.000, channel: 0, note: E4, duration: 1.000
002:02:000, timestamp: 3.000, channel: 0, note: F4, duration: 1.000
hen the bass clef.
Measure 2 – Bass Clef
002:01:000, timestamp: 2.000, channel: 1, note: G3, duration: 0.500
002:01:192, timestamp: 2.500, channel: 1, note: A3, duration: 0.500
002:02:000, timestamp: 3.000, channel: 1, note: B3, duration: 0.500
002:02:192, timestamp: 3.500, channel: 1, note: C4, duration: 0.500
Full Output
Here is the full output for all three of the sample files that we started with. The notes are all the same, however the time signatures, number of measures, and beat counts are different.
Sample 1 in 2/4
Treble Clef:
001:01:000, timestamp: 0.000, channel: 0, note: C4, duration: 1.000
001:02:000, timestamp: 1.000, channel: 0, note: D4, duration: 1.000
002:01:000, timestamp: 2.000, channel: 0, note: E4, duration: 1.000
002:02:000, timestamp: 3.000, channel: 0, note: F4, duration: 1.000
003:01:000, timestamp: 4.000, channel: 0, note: G4, duration: 1.000
003:02:000, timestamp: 5.000, channel: 0, note: A4, duration: 1.000
004:01:000, timestamp: 6.000, channel: 0, note: B4, duration: 1.000
004:02:000, timestamp: 7.000, channel: 0, note: C5, duration: 1.000
005:01:000, timestamp: 8.000, channel: 0, note: C5, duration: 4.000
Bass Clef:
001:01:000, timestamp: 0.000, channel: 1, note: C3, duration: 0.500
001:01:192, timestamp: 0.500, channel: 1, note: D3, duration: 0.500
001:02:000, timestamp: 1.000, channel: 1, note: E3, duration: 0.500
001:02:192, timestamp: 1.500, channel: 1, note: F3, duration: 0.500
002:01:000, timestamp: 2.000, channel: 1, note: G3, duration: 0.500
002:01:192, timestamp: 2.500, channel: 1, note: A3, duration: 0.500
002:02:000, timestamp: 3.000, channel: 1, note: B3, duration: 0.500
002:02:192, timestamp: 3.500, channel: 1, note: C4, duration: 0.500
003:01:000, timestamp: 4.000, channel: 1, note: B3, duration: 0.500
003:01:192, timestamp: 4.500, channel: 1, note: A3, duration: 0.500
003:02:000, timestamp: 5.000, channel: 1, note: G3, duration: 0.500
003:02:192, timestamp: 5.500, channel: 1, note: F3, duration: 0.500
004:01:000, timestamp: 6.000, channel: 1, note: E3, duration: 0.500
004:01:192, timestamp: 6.500, channel: 1, note: D3, duration: 0.500
004:02:000, timestamp: 7.000, channel: 1, note: C3, duration: 0.500
004:02:192, timestamp: 7.500, channel: 1, note: B2, duration: 0.500
005:01:000, timestamp: 8.000, channel: 1, note: C3, duration: 4.000
Sample 2 in 4/4
Treble Clef:
001:01:000, timestamp: 0.000, channel: 0, note: C4, duration: 1.000
001:02:000, timestamp: 1.000, channel: 0, note: D4, duration: 1.000
001:03:000, timestamp: 2.000, channel: 0, note: E4, duration: 1.000
001:04:000, timestamp: 3.000, channel: 0, note: F4, duration: 1.000
002:01:000, timestamp: 4.000, channel: 0, note: G4, duration: 1.000
002:02:000, timestamp: 5.000, channel: 0, note: A4, duration: 1.000
002:03:000, timestamp: 6.000, channel: 0, note: B4, duration: 1.000
002:04:000, timestamp: 7.000, channel: 0, note: C5, duration: 1.000
003:01:000, timestamp: 8.000, channel: 0, note: C5, duration: 4.000
Bass Clef:
001:01:000, timestamp: 0.000, channel: 1, note: C3, duration: 0.500
001:01:192, timestamp: 0.500, channel: 1, note: D3, duration: 0.500
001:02:000, timestamp: 1.000, channel: 1, note: E3, duration: 0.500
001:02:192, timestamp: 1.500, channel: 1, note: F3, duration: 0.500
001:03:000, timestamp: 2.000, channel: 1, note: G3, duration: 0.500
001:03:192, timestamp: 2.500, channel: 1, note: A3, duration: 0.500
001:04:000, timestamp: 3.000, channel: 1, note: B3, duration: 0.500
001:04:192, timestamp: 3.500, channel: 1, note: C4, duration: 0.500
002:01:000, timestamp: 4.000, channel: 1, note: B3, duration: 0.500
002:01:192, timestamp: 4.500, channel: 1, note: A3, duration: 0.500
002:02:000, timestamp: 5.000, channel: 1, note: G3, duration: 0.500
002:02:192, timestamp: 5.500, channel: 1, note: F3, duration: 0.500
002:03:000, timestamp: 6.000, channel: 1, note: E3, duration: 0.500
002:03:192, timestamp: 6.500, channel: 1, note: D3, duration: 0.500
002:04:000, timestamp: 7.000, channel: 1, note: C3, duration: 0.500
002:04:192, timestamp: 7.500, channel: 1, note: B2, duration: 0.500
003:01:000, timestamp: 8.000, channel: 1, note: C3, duration: 4.000
Sample 3 in 6/8
Treble Clef:
001:01:000, timestamp: 0.000, channel: 0, note: C4, duration: 1.000
001:03:000, timestamp: 1.000, channel: 0, note: D4, duration: 1.000
001:05:000, timestamp: 2.000, channel: 0, note: E4, duration: 1.000
002:01:000, timestamp: 3.000, channel: 0, note: F4, duration: 1.000
002:03:000, timestamp: 4.000, channel: 0, note: G4, duration: 1.000
002:05:000, timestamp: 5.000, channel: 0, note: A4, duration: 1.000
003:01:000, timestamp: 6.000, channel: 0, note: B4, duration: 1.000
003:03:000, timestamp: 7.000, channel: 0, note: C5, duration: 1.000
003:05:000, timestamp: 8.000, channel: 0, note: C5, duration: 4.000
Bass Clef:
001:01:000, timestamp: 0.000, channel: 1, note: C3, duration: 0.500
001:02:000, timestamp: 0.500, channel: 1, note: D3, duration: 0.500
001:03:000, timestamp: 1.000, channel: 1, note: E3, duration: 0.500
001:04:000, timestamp: 1.500, channel: 1, note: F3, duration: 0.500
001:05:000, timestamp: 2.000, channel: 1, note: G3, duration: 0.500
001:06:000, timestamp: 2.500, channel: 1, note: A3, duration: 0.500
002:01:000, timestamp: 3.000, channel: 1, note: B3, duration: 0.500
002:02:000, timestamp: 3.500, channel: 1, note: C4, duration: 0.500
002:03:000, timestamp: 4.000, channel: 1, note: B3, duration: 0.500
002:04:000, timestamp: 4.500, channel: 1, note: A3, duration: 0.500
002:05:000, timestamp: 5.000, channel: 1, note: G3, duration: 0.500
002:06:000, timestamp: 5.500, channel: 1, note: F3, duration: 0.500
003:01:000, timestamp: 6.000, channel: 1, note: E3, duration: 0.500
003:02:000, timestamp: 6.500, channel: 1, note: D3, duration: 0.500
003:03:000, timestamp: 7.000, channel: 1, note: C3, duration: 0.500
003:04:000, timestamp: 7.500, channel: 1, note: B2, duration: 0.500
003:05:000, timestamp: 8.000, channel: 1, note: C3, duration: 4.000
Wrap Up
Parsing measures from a MIDI file is a big part of whole MIDI file puzzle. We can get a lot done with what we have so far, but we haven’t started on the more subtle parts of the modern MIDI spec.
Just keep coding,
-Eric