MIDI BLE Tutorial

Contributors: MTaylor
Favorited Favorite 3

Encapsulating MIDI Data

Paraphrased from the MIDI Tutorial,

Bytes of MIDI messages are divided into 2 major categories, based on the setting of the most significant bit. If a byte's MSB is a 0, it is a data byte with 7 usable bits of data. If the MSB is a 1, it's a status byte (or could be a special case if SysEx messages are used, but they are ignored here).

A serial MIDI packet is started with a status byte, then contains a number of data bytes depending on what type of packet is indicated by the status byte (see Summary of MIDI Messages for more information).

In the world of BLE, data is thought of in terms of characteristics, which is just a size of data that can be written to, and is available on both ends of the connection by the unseen negotiations of the link.

The Bluetooth LE MIDI Specification serves as the source material for the next section. Go register with midi.org to download it for free, and it helps them see who's using the information. They're a great organization and are allowing direct reprinting of their copyrighted material for this tutorial.

The specification allows a few types of MIDI packets to exist within a characteristic.

BLE Packet with One Full MIDI Message

The most basic type of MIDI BLE packet, or characteristic value, is one containing a single MIDI message.

The first and second bytes are overhead to the actual midi payload.

  • The first byte describe the upper 6 bits of the timestamp and has the MSB set.
  • The second byte describes the lower 7 bits of the timestamp and also has the MSB set.
  • The remaining bytes are the payload. It's the original midi message that is being encapsulated.

BLE Packet with One Full MIDI Message

Shown here, a MIDI message of 3 bytes is appended to a timestamp to create a BLE packet. If the midi message was only 2 bytes long, the BLE packet size would be 4 bytes, and so on.

Also notice that the MSB is set for any byte that isn't data. This will be useful when parsing out MIDI messages of other forms.

BLE Packet with Multiple Full MIDI Messages

A BLE characteristic can be written to a variable size, and can contain more than 1 MIDI message. When two or more MIDI messages are concatenated, the upper 6 bits of timestamp can be omitted because it can't rollover twice per BLE packet. All contained messages share the same upper timestamp bits and have their own lower bits.

BLE Packet with Multiple Full MIDI Messages

Here two MIDI messages are contained in a single characteristic. The second message has the same header byte and so it's omitted to reduce overhead.

Look at the output from the basic BLE Peripheral example.


This has been hand decoded in the following table.

Offset Hex
Binary BLE Name
MIDI Decode
0xB9 10111001b Header
0x01 0xFD 11111101b Timestamp
0x02 0xB0 10110000b Status Control Change
Channel 0
0x03 0x62 01100010b Data Controller 98
0x04 0x48 01001000b Data 74
0x05 0xFD 11111101b Timestamp
0x06 0xB0 10110000b Status Control Change
Channel 0
0x07 0x06 00000110b Data Controller 6
0x08 0x00 00000000b Data 0
0x09 0xFD 11111101b Timestamp
0x0A 0xB0 10110000b Status Control Change
Channel 0
0x0B 0x26 00100110b Data Controller 38
0x0C 0x0A 00001010b Data 10

This is an NRPN message. It's three controller change commands sent sequentially. Just as the MIDI specification indicates, the packet is formed with single Header and unique timestamps for each message (although they all seemed to originate simultaneously).

BLE Packet with Running Status MIDI Messages

The last type of BLE packet that can exist is a running status message. In a message like this many MIDI messages all have the same timestamp and MIDI status, so the timestamp and status are only sent once followed by a block of data. The MIDI status indicates what size the data will be (by message type) and the data can be parsed accordingly.

BLE Packet with Running Status MIDI Messages

From the previous example, the MacBook Air sent 3 messages that did have the same timestamp and status. It's unusual that it was instructed to send these as full messages rather than running status messages.