This one's more of a cross-backend consistency thing than a hard crash, but it surprised us, so here it is. So, the MIDI spec lets a "note-on with velocity 0" stand in for a note-off — lots of gear sends it that way to keep running status going (as you well know). The question is what minimidio reports when it sees one, and right now the answer depends on which platform you're on.
The inconsistency
For the exact same incoming bytes 90 3C 00 (note-on, middle C, velocity 0),
you get a different mm_message.type depending on the backend:
- ALSA (Linux) folds it to a note-off — around
minimidio.h:1406:
msg.type = (ev->data.note.velocity > 0) ? MM_NOTE_ON : MM_NOTE_OFF;
- The UMP → MIDI-1 conversion also folds it — around
minimidio.h:665:
if (msg->type == MM_NOTE_ON && msg->data[1] == 0) msg->type = MM_NOTE_OFF;
- CoreMIDI (macOS) does not fold — it just takes the status nibble, so the
message stays MM_NOTE_ON with velocity 0 (around minimidio.h:786):
msg.type = (mm_message_type)((s >> 4) & 0x0F);
(We read the ALSA, UMP, and CoreMIDI paths directly. We didn't line-by-line re-verify WinMM and WebMIDI, so we're not claiming anything specific about those two — but the three above already disagree with each other.)
So the same physical event is MM_NOTE_OFF on Linux and MM_NOTE_ON on macOS. For something that's meant to be low-level transport, that's a tricky thing to build on — and the folding is lossy in one direction: once 90 3C 00 has been turned into MM_NOTE_OFF, a downstream consumer can't tell it actually arrived as a velocity-0 note-on.
How to see it
Feed 90 3C 00 into each backend's input and compare msg.type. Today ALSA and the UMP path report MM_NOTE_OFF; CoreMIDI reports MM_NOTE_ON.
Needs (preferences) as a NIF developer
The way that's friendliest to a low-level transport is "don't fold anywhere" — report exactly what came in and let the consumer decide whether to treat it as a note-off. That keeps all backends agreeing and keeps the information intact. Not the friendliest for other types of consumers, though ;-)
If you'd rather keep the fold as a convenience (I'd imagine this is a given, since this is a nice thing to provide for app developers who don't want to think about it), it would be really nice to have consistency on this across on all backends. It probably rates a mention in the docs, so people know to expect it.
On the NIF side, we can normalize a velocity-0 note-on into a note-off ourselves, but only if the backend hands it to us unfolded in the first place. However, we can't un-fold what's already been folded. That being said, this is going to go away once the raw-bytes mode is implemented :-) That's going to be what we'll want to use in the future!
This one's more of a cross-backend consistency thing than a hard crash, but it surprised us, so here it is. So, the MIDI spec lets a "note-on with velocity 0" stand in for a note-off — lots of gear sends it that way to keep running status going (as you well know). The question is what minimidio reports when it sees one, and right now the answer depends on which platform you're on.
The inconsistency
For the exact same incoming bytes
90 3C 00(note-on, middle C, velocity 0),you get a different
mm_message.typedepending on the backend:minimidio.h:1406:minimidio.h:665:message stays
MM_NOTE_ONwith velocity 0 (aroundminimidio.h:786):(We read the ALSA, UMP, and CoreMIDI paths directly. We didn't line-by-line re-verify WinMM and WebMIDI, so we're not claiming anything specific about those two — but the three above already disagree with each other.)
So the same physical event is
MM_NOTE_OFFon Linux andMM_NOTE_ONon macOS. For something that's meant to be low-level transport, that's a tricky thing to build on — and the folding is lossy in one direction: once90 3C 00has been turned intoMM_NOTE_OFF, a downstream consumer can't tell it actually arrived as a velocity-0 note-on.How to see it
Feed
90 3C 00into each backend's input and comparemsg.type. Today ALSA and the UMP path reportMM_NOTE_OFF; CoreMIDI reportsMM_NOTE_ON.Needs (preferences) as a NIF developer
The way that's friendliest to a low-level transport is "don't fold anywhere" — report exactly what came in and let the consumer decide whether to treat it as a note-off. That keeps all backends agreeing and keeps the information intact. Not the friendliest for other types of consumers, though ;-)
If you'd rather keep the fold as a convenience (I'd imagine this is a given, since this is a nice thing to provide for app developers who don't want to think about it), it would be really nice to have consistency on this across on all backends. It probably rates a mention in the docs, so people know to expect it.
On the NIF side, we can normalize a velocity-0 note-on into a note-off ourselves, but only if the backend hands it to us unfolded in the first place. However, we can't un-fold what's already been folded. That being said, this is going to go away once the raw-bytes mode is implemented :-) That's going to be what we'll want to use in the future!