2.4 KiB
midi-contract/reducers
Purpose
Three small reducers that fold the four-CC NRPN, RPN, and 14-bit CC
"dances" into single consolidated events. The Web MIDI runtime delivers
each CC as an isolated message; a knob that maps to a 14-bit parameter
therefore arrives as two CC messages (MSB then LSB), and an NRPN edit
arrives as four (CC 99/98 for parameter MSB/LSB then CC 6/38 for value
MSB/LSB). The reducers track per-channel partial state and emit one
NRPNChange / RPNChange / CC14Change per completed dance.
This logic lives here so the useNRPN / useRPN / useCC14 hooks
have a deterministic, pure target to wrap with useSyncExternalStore.
Once @suaveplan/midi ships its canonical reducers, these classes
SHALL be replaced one-for-one without behavioural change.
Features
NRPNReducer— CC 99/98 parameter MSB/LSB, CC 6/38 data MSB/LSB.RPNReducer— CC 101/100 parameter MSB/LSB, CC 6/38 data MSB/LSB.CC14Reducer— pairs CC 0-31 (MSB) with CC 32-63 (LSB).- Per-channel partial state — concurrent edits on different channels do not interleave.
Usage: Basic
import { NRPNReducer } from "@suaveplan/react-midi/midi-contract";
const reducer = new NRPNReducer();
const out = reducer.feed({
type: "controlChange",
channel: 0,
controller: 99,
value: 0x20,
});
// out === null (waiting for CC 98 / 6 / 38)
Usage: Advanced
import { CC14Reducer } from "@suaveplan/react-midi/midi-contract";
const r = new CC14Reducer();
// 14-bit "bank select" on channel 0:
const a = r.feed({ type: "controlChange", channel: 0, controller: 0, value: 1 });
const b = r.feed({ type: "controlChange", channel: 0, controller: 32, value: 64 });
// b === { type: "cc14Change", channel: 0, controller: 0, value: (1<<7)|64 }
API Reference
class NRPNReducer { feed(msg: ControlChangeMessage): NRPNChange | null }class RPNReducer { feed(msg: ControlChangeMessage): RPNChange | null }class CC14Reducer { feed(msg: ControlChangeMessage): CC14Change | null }
Implementation Notes
Reception of a fresh paramHigh (CC 99 / CC 101) resets all four
partial slots for that channel — devices commonly start a fresh NRPN
edit without an explicit "null param" sentinel, so the reducer treats
a new paramHigh as the implicit "start of new dance" marker. The
14-bit CC reducer keys per (channel, baseController) so two
simultaneous 14-bit edits on different channels do not corrupt each
other's pending MSB.