As stated in earlier sections, the overarching goal of MIDI was to allow different devices to communicate, speaking a well-defined protocol. The protocol revolves around a stream of small messages. Most messages are between one and four bytes long, although some can be longer, and some information requires groups of messages.
MIDI messages fall into several categories, such as performance messages (“the performer pressed the middle C key”), information about the sounds being played (“change from the piano sound to the organ sound”), and other musical data, such as metronome or real-time clocks.
Status or Data?
In the previous section, we discussed the UART configuration for MIDI – 31,250 bits per second, 8 data bits per byte, and 1 stop bit. MIDI uses those 8 data bits to the fullest extent!
Within a byte, each bit may be either a 0 or 1.
One Eight-Bit Byte
Some bytes are further divided into nybbles, or 4-bit chunks. Each nybble is identified by its position within the byte. When written in hexidecimal, each nybble is represented by a character. The left hand character (made up of the higher-value bits) is known as the most-significant nybble, and the right-hand character is known as the least-significant nybble.
Byte divided into nybbles
Bytes of MIDI messages are divided into 2 major categories, based on the setting of the most significant bit.
Status Byte Dissection
If the first bit is high (values between 0x80 and 0xff), it denotes a status byte. Status bytes are the commands of the MIDI stream.
- Status bytes are further subdivided by nybble. The first nybble specifies the command type, and the second nybble specifies which the channel the command applies to.
- There are only eight bit combinations with the MSB set (0x8 to 0xf), therefore there are eight possible status commands.
- The 4 bits in the channel nybble are all usable, giving MIDI 16 possible channels.
If the first bit is low (values between 0x00 and 0x7f), it is a data byte, indicating parameters that correspond to a previous status byte. Because the MSB must be zero (otherwise they’d become status bytes), the data is limited to 7-bits, or the range from 0 to 127 (0x0 to 0x7f).
Statuses & Corresponding Data
Let’s look at the status bytes. We’ll start with a list, then explore each in the following sections.
|MIDI Status Messages|
|Message Type||MS Nybble||LS Nybble||Number of|
|Data Byte 1||Data Byte 2|
|Note Off||0x8||Channel||2||Note Number||Velocity|
|Note On||0x9||Channel||2||Note Number||Velocity|
|Polyphonic Pressure||0xA||Channel||2||Note Number||Pressure|
|Control Change||0xB||Channel||2||Controller Number||Value|
|Program Change||0xC||Channel||1||Program Number||-none-|
|Pitch Bend||0xE||Channel||2||Bend LSB |
|Bend MSB |
The messages with the channel number in the second nybble of the status byte are known as channel messages. Channels are often used to separate individual instruments – channel one could be a piano, two a bass, and so on. This allows a single MIDI connection to carry information for multiple destinations simultaneously. Each sound would be played by sending messages with the apprppriate value in the channel nybble.
Since they’re very useful, and easy to implement, we’re going to start with two of the most common types of messages: Note On/Off and System Realtime.
Note Off (0x80), Note On (0x90)
The meaning of Note On and Off messages is reasonably obvious. When a key on a keyboard is pressed, it sends a Note On message, and it sends a Note Off when the key is released. On and Off messages are also sent by other types of controllers, such as drum pads, and MIDI wind instruments.
When a synthesizer receives a Note On, it starts generating sound; the Note Off instructs it to stop.
Note On and Off are each comprised of three bytes
- n is the command (note on (0x9) or off(0x8))
- c is the channel (1 to 16)
- kk is the key number (0 to 127, where middle C is key number 60)
- vv is the striking velocity (0 to 127)
Velocity is most commonly measured using a pair of switches under each key, which are slightly offset from each other. As the key is pressed, one switch closes before the other. By measuring the time between the two switch closures, it can determine how quickly the key was moving. Some instruments don’t measure velocity, and instead transmit a fixed value for that byte, such as 0x40 (64).
We said these are simple on the surface, but there are a couple of tricky shortcuts that complicate the situation.
In order to transmit fewer bytes, and free up some bandwidth on the connection, MIDI uses a form of shorthand. When the same status byte would be transmitted repeatedly, the protocol uses the notion of Running Status, where only the first status byte is transmitted, and successive messages simply transmit new data bytes. For example, three Note On messages would usually be:
0x90, 0x3D, 0x40
0x90, 0x3E, 0x40
Using running status, the second and third status bytes are omitted:
Although we’re illustrating this in the context of Note On/Off messages, it can apply to any status. When a new status byte arrives, it replaces the previous running status.
Implicit Note Off
To make more effective use of running status, MIDI uses a Note On command with a velocity of zero as an alias for a Note Off. For example, starting with this on/off pair
0x80, 0x3C, 0x40
We can change the off byte (0x80) into an on (0x90) with zero velocity
0x90, 0x3C, 0x00
Since it is now two Note On messages in a row, we can apply running status, remove the second command, and save the transmission of one byte
When implicit note off is used, the implied Note Off doesn’t have a velocity value. While Note Off velocity is frequently not implemented, there are times that it is an important element of a performance. In those situations, the instruments need to support it, and it might need to be explicitly enabled, often in a configuration menu.
System Real Time
Another useful category is System Real Time messages. These are denoted when the MSB of the second nybble of the status byte is set (values from 0xF8 to 0xFF).
These messages are all one byte long. They’re mostly used to synchronize sequencers, acting as a high-resolution metromone.
|System Real Time Messages|
|Timing Clock||0xF8||These messages represent metronimic timing, transmitted at a rate of 24 per quarter note. The actual rate is dependent on the tempo of the song.|
|Start||0xFA||Start playing from the beginning|
|Continue||0xFB||Start playing from where last stopped|
|Active Sense||0xFE||An optional "keep alive" message that, when implemented, can be used to detect if a transmitter has been accidentally unplugged. A transmitter sends them every 300 milliseconds. If the messages stop flowing, the recipient times out, and cleans up by stopping sequences and turning off notes that would otherwise be stuck on.|
|System Reset||0xFF||A "panic switch" that instructs to receiver to reset to power-up condition.|
Because system real time messages are one byte long, they have no data bytes, and are therefore not status bytes. They can be transmitted without interrupting running status.