Skip to content

MIDI Messages

With the foundation from the previous chapters, it is time to move into the more animated part of our journey.

Until now, we have talked about MIDI messages in very general sense, we touched bits, bytes, number systems, and how MIDI data is structured. You now understand why MIDI uses 7-bit values, how status and data bytes are distinguished, and how multi-byte values are assembled. In this chapter, we will put that knowledge to work.

If you have an Electra One controller, open the web editor and create a new, empty project:

[ IMAGE OF EMPTY PROJECT ]

Do not worry if you are completely new to this. We will move step by step.

Earlier in this book, we described MIDI messages conceptually. Note On, Control Change, Program Change, Pitch Bend, and so on. Now we will look at them from a more technical perspective. For each message type, we will:

  • Identify the status byte
  • Decode the data bytes
  • Examine their binary and hexadecimal forms
  • Send them manually
  • Observe what happens

Instead of only reading about MIDI messages, you will see the exact bytes being transmitted and interpreted. To do so we will use two tools:

  • The Electra One hardware controller
  • The MIDI Console in the Electra One web editor

The MIDI Console allows you to type and send raw MIDI data directly and observe incoming messages in real time.

[ MIDI Console ]

How MIDI Data Is Sent

MIDI data is transmitted as a sequence of bytes, each consisting of 8 bits. Every MIDI message is composed of a Status byte, which identifies the type of message and, in most cases, the MIDI channel, followed by zero or more Data bytes, which carry the actual parameters of that message.

A Status byte is easy to recognize: its msb is always set to 1. This means its numeric value falls within the range 128 to 255 (0x80 to 0xFF in hexadecimal). Because the most significant bit is 1, the receiving device immediately understands that a new message is beginning. For channel messages, the Status byte encodes both the message type, such as Note On or Control Change, and the channel number. We will decode this structure in detail shortly.

Data bytes, on the other hand, always have their msb set to 0. This limits their numeric range to 0 – 127 (0x00–0x7F in hexadecimal). The remaining seven bits carry the actual MIDI value, whether that is a note number, a velocity, a controller number, or a pressure value. Since data bytes can never have their msb set to 1, they can never be confused with a Status byte. This is precisely what makes the MIDI protocol self-synchronizing: the receiver can always detect where a new message begins.

The number of Data bytes depends entirely on the type of message being sent. Some messages consist of only a single Status byte and carry no Data bytes at all. Real-Time messages such as Start, Stop, or Clock are examples of this. Other messages contain one or two Data bytes. A Note On message, for instance, carries two: the first indicates which note (which key) was pressed, and the second specifies the velocity—how strongly it was struck.

SysEx messages are a special case. They begin with a Status byte but can contain a variable number of Data bytes, sometimes forming quite long messages. These are used for device-specific communication, and we will explore them in detail later in this chapter.

MIDI Channels in Status Bytes

Status byte format for channel messages:

[ message type ][ channel ]

Example:

  • Channel 1 → 0
  • Channel 16 → 15 (0x0F)

Note On

Purpose: Start playing a note Status byte: Decimal: 144–159 Hex: 0x90–0x9F Byte sequence:

[ Status ] [ Note Number ] [ Velocity ]

Example (Note On, channel 1):

144 60 100
0x90 0x3C 0x64

Description: Plays note 60 (Middle C) with velocity 100.

Note Off

Purpose: Stop playing a note Status byte: Decimal: 128–143 Hex: 0x80–0x8F Byte sequence:

[ Status ] [ Note Number ] [ Velocity ]

Example:

128 60 64
0x80 0x3C 0x40

Note On with Velocity 0 (Alternate Note Off)

Status byte: same as Note On Velocity: 0

144 60 0
0x90 0x3C 0x00

Description: Treated identically to Note Off.

Control Change

Purpose: Change a continuous or switch parameter Status byte: Decimal: 176–191 Hex: 0xB0–0xBF Byte sequence:

[ Status ] [ Controller Number ] [ Value ]

Example (CC 7 Volume):

176 7 100
0xB0 0x07 0x64

Program Change

Purpose: Select preset or instrument Status byte: Decimal: 192–207 Hex: 0xC0–0xCF Byte sequence:

[ Status ] [ Program Number ]

Example:

192 10
0xC0 0x0A

Note: Only 2 bytes, no value byte.

Pitch Bend

Purpose: Smooth pitch control Status byte: Decimal: 224–239 Hex: 0xE0–0xEF Byte sequence:

[ Status ] [ LSB ] [ MSB ]

Value range: 14-bit (0–16383), center = 8192 Example (center):

224 0 64
0xE0 0x00 0x40

Channel Pressure (Channel Aftertouch)

Purpose: Pressure affecting entire channel Status byte: Decimal: 208–223 Hex: 0xD0–0xDF Byte sequence:

[ Status ] [ Pressure ]

Example:

208 80
0xD0 0x50

Polyphonic Key Pressure

Purpose: Pressure per individual note Status byte: Decimal: 160–175 Hex: 0xA0–0xAF Byte sequence:

[ Status ] [ Note Number ] [ Pressure ]

Example:

160 60 90
0xA0 0x3C 0x5A

System Exclusive (SysEx)

Purpose: Manufacturer-specific data Status bytes: Start: 240 (0xF0) End: 247 (0xF7) Byte sequence:

0xF0 [ Manufacturer ID ] [ Data... ] 0xF7

Example:

240 67 16 76 0 247
0xF0 0x43 0x10 0x4C 0x00 0xF7

MIDI Clock

Purpose: Tempo synchronization Status byte: Decimal: 248 Hex: 0xF8 Byte sequence:

248
0xF8

Sent 24 times per quarter note.

Start

Purpose: Start playback Status byte: Decimal: 250 Hex: 0xFA

250
0xFA

Continue

Purpose: Resume playback Status byte: Decimal: 251 Hex: 0xFB

251
0xFB

Stop

Purpose: Stop playback Status byte: Decimal: 252 Hex: 0xFC

252
0xFC

Active Sensing

Purpose: Connection monitoring Status byte: Decimal: 254 Hex: 0xFE

254
0xFE

System Reset

Purpose: Reset all devices Status byte: Decimal: 255 Hex: 0xFF

255
0xFF

Summary

MIDI communication is based on small byte sequences where the status byte defines the message type and channel, followed by one or two data bytes carrying parameters. Understanding the decimal and hexadecimal values of MIDI messages — and their byte order — is essential for low-level MIDI programming, debugging, and working with MIDI APIs such as Electra One’s Lua Extension.

Made with 🩶 in the EU · © 2019–2026 Electra One