On a MIDIFabric input port set up to receive MIDI 2.0, each NRPN value arrived twice, and the first one was wrong.
The cause turned out to be Core MIDI converting the NRPN before its value is complete. An NRPN in MIDI 1.0 is four messages. CC 99 and 98 select the parameter, CC 6 sends the high part of the value and CC 38 the low part. When an app receives MIDI with the MIDI 2.0 protocol, Core MIDI turns these four messages into one MIDI 2.0 controller message, but it does so already when CC 6 arrives, using only the high part of the value, and then once more when CC 38 arrives with the full value. So the app briefly sees an incomplete value, then the correct one. Apps that receive MIDI 1.0 are not affected.
How we noticed
An earlier version of MIDIFabric created its Core MIDI input ports with the MIDI 2.0 protocol. That is where we saw MIDI 1.0 NRPNs arrive once before the low part of the value was in, and we switched to creating input ports with the MIDI 1.0 protocol.
Later, when we found that IAC Driver buses are MIDI 2.0 (Is the IAC Driver MIDI 2.0?), we checked the same thing again through IAC.
What we observed
We sent an NRPN in MIDI 1.0 to an IAC bus and received it with an app that reads MIDI 2.0. The parameter was 1:2 and the value 0x1234:
| MIDI 1.0 sent | MIDI 2.0 received |
|---|---|
| CC 99 = 1, CC 98 = 2 | nothing yet |
| CC 6 = 0x24 (high part) | Assignable Controller 1:2 with 0x48000000, the value with only its high part |
| CC 38 = 0x34 (low part) | Assignable Controller 1:2 again, with 0x48D00000, the full value |
The first message is not a duplicate of the second: it carries a different, incomplete value.
This is the same behavior we had seen in the earlier version of MIDIFabric. We believe it is not specific to IAC, and happens wherever Core MIDI converts MIDI 1.0 into MIDI 2.0 for an app.
Related details
- RPN lets CC 101 through first. An RPN in MIDI 1.0 (CC 101, 100, 6) arrived as the raw CC 101, followed by the converted RPN message.
- MIDI 1.0 receivers on IAC can see repeats. Because IAC converts to MIDI 2.0 internally and back, an app receiving MIDI 1.0 from IAC got the NRPN as CC 99, 98, 6, 6, 38, and the RPN with CC 101 twice. See Is the IAC Driver MIDI 2.0?.
What it affects
- A control in the app jumps to a wrong value for a moment before settling.
- Recorded automation can contain an extra point at the incomplete value.
- MIDI learn or a logger may record the incomplete value, or two messages for one change.
- For parameters that react immediately (pitch, filter cutoff), the jump can be audible.
The final value is correct, so in many cases nobody notices. It matters most for fast or precise NRPN changes.
How to avoid it
For users:
- If the app lets you choose how it receives MIDI, receiving MIDI 1.0 avoids this.
- Between apps on the same Mac, the IAC Driver itself is MIDI 2.0, so an app that reads MIDI 2.0 from IAC is affected even if everything else is MIDI 1.0.
For app developers:
- Create your input port with the MIDI 1.0 protocol and assemble NRPN yourself from CC 99, 98, 6 and 38, waiting for CC 38 (or a short timeout) before using the value.
- If you need MIDI 2.0 input, treat an Assignable Controller that is quickly followed by another for the same parameter as one change, or ignore the low part until it arrives.
- If you send to a MIDI 2.0 destination, send the MIDI 2.0 controller message directly instead of an NRPN in MIDI 1.0.
Not yet verified
- Whether the same happens on every macOS version since MIDI 2.0 support was added.
- How common MIDI 2.0 apps handle the incomplete value.
We will update this page if we learn more.
About MIDIFabric
MIDIFabric receives MIDI 1.0 devices with the MIDI 1.0 protocol and groups CC 99, 98, 6 and 38 into one NRPN entry itself, so its monitor shows one change with the final value and lets you expand it to the four messages.