Updated:
Tested on: macOS 27.0 (measurements); a macOS 15 configuration file

On a Mac with no MIDI 2.0 hardware connected at all, the IAC Driver buses were the only ports speaking MIDI 2.0.

In other words, MIDI between apps connected through IAC already passes through MIDI 2.0, even without any MIDI 2.0 hardware. IAC buses report the MIDI 2.0 protocol, and when an app sends MIDI 1.0 through an IAC bus, Core MIDI converts it for each receiver: an app that reads MIDI 2.0 gets MIDI 2.0 messages (with 16-bit velocity, for example), and an app that reads MIDI 1.0 gets MIDI 1.0.

How we noticed

For MIDIFabric we had to decide where conversion between MIDI 1.0 and MIDI 2.0 should happen, so we checked the protocol of every port on our Mac. On macOS 27.0, Core MIDI’s protocol property said 1 (MIDI 1.0) for every USB, DIN and network device, and 2 (MIDI 2.0) for the IAC buses, on both their sources and destinations.

This did not start with macOS 27. A MIDI configuration file saved in July 2025, during the macOS 15 period, already recorded protocol = 2 for the IAC Driver, so it has been MIDI 2.0 at least since then. We have not looked into when this started.

We did not find this stated in the Apple documents we checked (the Core MIDI headers and API documentation, and the Audio MIDI Setup user guide). On the JUCE forum, a JUCE developer pointed out the same thing based on how it behaves.

As for the conversion itself, Apple’s documentation says Core MIDI adopts the MIDI 2 specification and converts transparently to the protocol each destination specifies. It does not say how each message is converted, so we sent messages and looked.

MIDI 1.0 into MIDI 2.0

We sent MIDI 1.0 into an IAC bus and received it as MIDI 2.0:

Sent as MIDI 1.0Received as MIDI 2.0
Note On C4, velocity 64Note On C4, 16-bit velocity 0x8000 (the center value)
CC 74 = 32CC 74 = 0x40000000 (32-bit value)
Bank select 1 / 2 + Program 5One Program Change message with the bank included

Values are scaled so that the minimum, center and maximum stay the same, which is the method the MIDI 2.0 specification describes.

MIDI 2.0 down to MIDI 1.0

Going the other way, we sent MIDI 2.0 messages to a MIDI 1.0 receiver:

Sent as MIDI 2.0Received as MIDI 1.0
Note On, velocity 0x8000Note On, velocity 64
Note On, velocity 0x0100 (very soft)Note On, velocity 1, not 0
Note On, velocity 0x0000Note On, velocity 1, so it does not turn into a Note Off
NRPN 1:2Four CCs (99, 98, 6, 38) with the value
RPN 0:0 = 2Three CCs. CC 38 was left out, probably because the low byte was 0 (not confirmed)
Program Change with bankCC 0, CC 32 and Program Change
Per-Note Pitch Bend, Relative controllers, note attributesDropped (MIDI 1.0 has no equivalent)

Details that change along the way

Most of it followed the specification, but a few things caught our eye:

  • The same message can arrive differently depending on how it was sent. If an app sends MIDI 1.0 messages but declares them as MIDI 2.0, Core MIDI does not convert them, and a MIDI 2.0 receiver gets MIDI 1.0 messages as they are. Declared as MIDI 1.0, they are converted.
  • An NRPN can arrive with an incomplete value first. Converting MIDI 1.0 to MIDI 2.0, Core MIDI produced the NRPN value twice: first when CC 6 arrived, with only the high part of the value, and again when CC 38 arrived with the full value. A receiver that acts on the first one briefly sees a wrong value. Details: Why does an NRPN arrive with the wrong value first?
  • RPN lets CC 101 through first. The raw CC 101 came through before the converted RPN message.
  • MIDI 1.0 receivers on IAC can see repeated messages. An NRPN sent through IAC reached a MIDI 1.0 receiver as CC 99, 98, 6, 6, 38, and an RPN brought CC 101 twice. Our guess is that IAC converts up to MIDI 2.0 internally and back down. The final values were correct, but a MIDI learn function or a log will see the extra messages.

What this means for you

  • If you connect apps with IAC, you are already using a MIDI 2.0 path, even with only MIDI 1.0 hardware.
  • Apps that read MIDI 2.0, such as Logic Pro 10.7.7 or later and Cubase/Nuendo 13 or later, can receive higher-resolution values from IAC.
  • If MIDI learn picks up a doubled CC or an odd value for an NRPN sent through IAC, suspect the conversion above.

Not yet verified

  • Whether a DAW in MIDI 2.0 mode accepts MIDI 1.0 messages that another app declared as MIDI 2.0 when sending through IAC.
  • Whether Core MIDI’s expanded sequences (RPN, NRPN, bank and program) can be interrupted when two apps send to the same destination at the same time.
  • Whether CC 38 is always left out when the RPN low byte is 0.
  • The behavior before macOS 15, and on Windows or Android.

We will update this page if we learn more.

About MIDIFabric

MIDIFabric shows which ports use MIDI 2.0, IAC buses included, and decodes MIDI 2.0 messages by name in its monitor. The conversion described above is done by macOS.

← All notes