Eric's Homemade Stuff

An infrequent blog of stuff I make, including music.

MIDI Notes

Part 5 of a Series

Exploring Notes in MIDI Files.

Sample MIDI File

I’m using Bach’s “Two-Part Invention No. 1” in this series. Here is a PDF of the sheet music and a MIDI file for the piece.

MIDI Notes

In Part 4 of this series we extracted the notes from the sample MIDI file. I listed them for the first measure like this for the treble clef.

First Measure Treble Clef
1
2
3
4
5
6
7
8
9
10
11
Note - timestamp:  0.250, channel: 0, note: 60, velocity: 90, release velocity: 0, duration: 0.250
Note - timestamp:  0.500, channel: 0, note: 62, velocity: 90, release velocity: 0, duration: 0.250
Note - timestamp:  0.750, channel: 0, note: 64, velocity: 90, release velocity: 0, duration: 0.250
Note - timestamp:  1.000, channel: 0, note: 65, velocity: 90, release velocity: 0, duration: 0.250
Note - timestamp:  1.250, channel: 0, note: 62, velocity: 90, release velocity: 0, duration: 0.250
Note - timestamp:  1.500, channel: 0, note: 64, velocity: 90, release velocity: 0, duration: 0.250
Note - timestamp:  1.750, channel: 0, note: 60, velocity: 90, release velocity: 0, duration: 0.250
Note - timestamp:  2.000, channel: 0, note: 67, velocity: 90, release velocity: 0, duration: 0.500
Note - timestamp:  2.500, channel: 0, note: 72, velocity: 90, release velocity: 0, duration: 0.500
Note - timestamp:  3.000, channel: 0, note: 71, velocity: 90, release velocity: 0, duration: 0.500
Note - timestamp:  3.500, channel: 0, note: 72, velocity: 90, release velocity: 0, duration: 0.500

Let’s look at what these notes are in more detail and at how Core MIDI lets us work with them. MIDI is a streaming control protocol. It was intended for playing music live and is action based. A MIDI device, like a keyboard controller, would send these events to a computer to play a quarter note that is middle C (C4).

  • Start playing C4 at timestamp 0.0
  • Stop playing C4 at timestamp 1.0

Play Middle C

Core MIDI simplifies this for us by combining the Note On and the balancing Note Off message into one MIDINoteMessage struct. When we get a note and cast the data into a note message we have all the information for one note.

Getting One Note
1
2
3
4
    MusicEventIteratorGetEventInfo(iterator, &timestamp, &eventType, &eventData, &eventDataSize);
    if (eventType == kMusicEventType_MIDINoteMessage) {
        MIDINoteMessage *noteMessage = (MIDINoteMessage*)eventData;
    }

Here’s the MIDINoteMessage struct.

MIDINoteMessage
1
2
3
4
5
6
7
typedef struct MIDINoteMessage {
   UInt8    channel;
   UInt8    note;
   UInt8    velocity;
   UInt8    releaseVelocity;
   Float32  duration;
} MIDINoteMessage;

The data for our note would look like this.

Middle C Quarter Note
1
Note - timestamp:  0.0, channel: 0, note: 60, velocity: 90, release velocity: 0, duration: 1.0

What does a duration of 1.0 mean?

MIDI timestamps are a relative time indicator, not an absolute time. The default value of a quarter note is 1.0. A MIDI file starts with a timestamp of 0.0. All the notes will have a timestamp relative to 0.0. Here is a list of some of the common notes and their default duration values.

  • Whole Note: 4.0
  • Half Note: 2.0
  • Quarter Note: 1.0
  • Eighth Note: 0.5
  • Sixteenth Note: 0.25

The duration is the only data we have in MIDI about the length of a note. We do not have a flag saying what type of note it is in the score.


What about tied or dotted notes?

It’s very common in music scores to have tied notes like this or dotted notes like this. How are these done in MIDI? They aren’t. There is no way in MIDI to do these notes. It is not a notation language, it is a streaming protocol. When a note is played all we hear is how long it was played, we have no information on how it was scored in the sheet music. The tied note would have this information in the MIDINoteMessage

  • duration: 2.25

The dotted note would be

  • duration: 0.75

What about notes that could be either tied or dotted? We don’t have enough information to distinguish between them unless the note extends over a measure boundary. We can’t make a fully accurate score from a MIDI file, there is not enough information.


What about rests?

There is no MIDI event for a rest in a music score. To MIDI, a rest is the absence of a note. Our sample piece gives a good example of this. Here is the first measure.

First Measure


The treble clef starts with a sixteenth note rest. The timestamp for the first note event is 0.25. This implies that there is a rest before the first note of 0.25, which is a sixteenth note rest.

First Three Notes - Treble Clef
1
2
3
Note - timestamp:  0.250, channel: 0, note: 60, velocity: 90, release velocity: 0, duration: 0.250
Note - timestamp:  0.500, channel: 0, note: 62, velocity: 90, release velocity: 0, duration: 0.250
Note - timestamp:  0.750, channel: 0, note: 64, velocity: 90, release velocity: 0, duration: 0.250

In the bass clef the first note starts at 2.25. That is a half note and a sixteenth note into the measure. We can assume there are rests in the score for that length, but we can only guess as to how it has been notated.

First Three Notes - Bass Clef
1
2
3
Note - timestamp:  2.250, channel: 1, note: 48, velocity: 90, release velocity: 0, duration: 0.250
Note - timestamp:  2.500, channel: 1, note: 50, velocity: 90, release velocity: 0, duration: 0.250
Note - timestamp:  2.750, channel: 1, note: 52, velocity: 90, release velocity: 0, duration: 0.250

Human Readable Notes?

Can we convert the MIDI data for a note into a human readable music note? We can finally answer yes to a question. The data for the note value can be from 0–127. Middle C (261.6 Hz) is 60. Every half step is a value of 1 away from the notes next to it. This gives us this chart for note translation.

MIDI Value to Music Note (sharps)
1
2
A0  A#0 B0  C1  C#1 D1  D#1 E1  F1  F#1 G1  G#1 A1  A#1 B1  C2  C#2 D2  D#2 E2  F2  F#2 G2  G#2 A2  A#2 B2  C3  C#3 D3  D#3 E3  F3  F#3 G3  G#3 A3  A#3 B3  C4  C#4 D4  D#4 E4  F4  F#4 G4  G#4 A4  A#4 B4  C5  C#5 D5  D#5 E5  F5  F#5 G5  G#5 A5  A#5 B5  C6  C#6 D6  D#6 E6  F6  F#6 G6  G#6 A6  A#6 B6  C7  C#7 D7  D#7 E7  F7  F#7 G7  G#7 A7  A#7 B7  C8
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  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99  100 101 102 103 104 105 106 107 108

The sharped notes can also be flatted notes depending on the key. We also know the key signature of each measure so we can accurately translate notes. You can use a simple array to translate as in this C function.

noteForMidiNumber()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const char * noteForMidiNumber(int midiNumber) {

    const char * const noteArraySharps[] = {"", "", "", "", "", "", "", "", "", "", "", "",
        "C0", "C#0", "D0", "D#0", "E0", "F0", "F#0", "G0", "G#0", "A0", "A#0", "B0",
        "C1", "C#1", "D1", "D#1", "E1", "F1", "F#1", "G1", "G#1", "A1", "A#1", "B1",
        "C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2",
        "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", "A3", "A#3", "B3",
        "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4",
        "C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5",
        "C6", "C#6", "D6", "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6",
        "C7", "C#7", "D7", "D#7", "E7", "F7", "F#7", "G7", "G#7", "A7", "A#7", "B7",
        "C8", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""};

    return noteArraySharps[midiNumber];
}

Wrap Up

That’s all we do in this post in the series. The next logical step is to combine notes into measures. That will be the next entry.

Just keep coding,

-Eric




Comments