MIDI Tutorial

Contributors: Byron J.
Favorited Favorite 13


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.

A quick word about written numbers: In the following sections, we'll be using both decimal and hexadecimal notation to represent numbers. Decimal numbers will be written normally, and the hex will be prefixed with `0x`. If you're new to hexadecimal, you can learn more about it in this tutorial .

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.

Bits in a Byte

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.

Nybbles in a Byte

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 Determination

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 TypeMS NybbleLS NybbleNumber of
Data Bytes
Data Byte 1 Data Byte 2
Note Off0x8Channel2Note NumberVelocity
Note On 0x9Channel2Note NumberVelocity
Polyphonic Pressure0xAChannel2Note NumberPressure
Control Change0xBChannel2Controller NumberValue
Program Change0xCChannel1Program Number-none-
Channel Pressure0xDChannel1Pressure-none-
Pitch Bend0xEChannel2Bend LSB
Bend MSB
System0xFfurther specificationvariablevariablevariable

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.

Channel numbering leads to some confusion. With 4 bits, there are 16 possible binary values, 0 through 15 (0x0 through 0xF). Since most people start counting at one rather than zero, MIDI devices commonly (but not always) internally add an offset of one to the binary value, resulting in the range 1 to 16. If you're having trouble getting a system to communicate, you might try adjusting channels up or down by one.

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

0xnc, 0xkk, 0xvv


  • 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.

Running Status

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, 0x3C, 0x40
0x90, 0x3D, 0x40
0x90, 0x3E, 0x40

Using running status, the second and third status bytes are omitted:

0x90, 0x3C, 0x40, 0x3D, 0x40, 0x3E, 0x40

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

0x90, 0x3C, 0x40
0x80, 0x3C, 0x40

We can change the off byte (0x80) into an on (0x90) with zero velocity

0x90, 0x3C, 0x40
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

0x90, 0x3C, 0x40, 0x3C, 0x00

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).

System Realtime Bit

These messages are all one byte long. They’re mostly used to synchronize sequencers, acting as a high-resolution metromone.

System Real Time Messages
TypeStatus ByteUsage
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.
Start0xFA Start playing from the beginning
Continue0xFB Start playing from where last stopped
Stop 0xFC Stop playing
Undefined 0xFD
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.