2.5 KiB
dispatch/rpn
Purpose
RPNReducer is the sister of NRPNReducer for Registered Parameter Numbers — the parameter MSB and LSB CC numbers move from 99/98 to 101/100, but the rest of the four-CC dance is identical. RPN is the MIDI Manufacturers Association's way of standardising a handful of common parameters (pitch-bend range, fine/coarse tuning, modulation-depth range, MPE configuration) that every spec-compliant synth interprets identically.
Separating RPN from NRPN avoids cross-contamination: an RPNReducer will never accidentally interpret CC 99 as a parameter MSB, and vice versa. Consumers typically run both reducers in parallel on the same control-change stream.
Features
- Per-channel partial state, exactly mirroring
NRPNReducer. - Emits one
RPNChangeEventon CC 38 (data LSB) once the parameter pair is set. - Throws
MidiError("MIDI.NRPN_INCOMPLETE")when data arrives before the parameter MSB/LSB. - Ignores unrelated CCs cleanly — composes with other CC-aware reducers without interference.
Usage: Basic
import { RPNReducer } from "@suaveplan/midi";
const r = new RPNReducer();
r.consume({ type: "controlChange", channel: 0, controller: 101, value: 0 });
r.consume({ type: "controlChange", channel: 0, controller: 100, value: 0 });
r.consume({ type: "controlChange", channel: 0, controller: 6, value: 2 });
const evt = r.consume({ type: "controlChange", channel: 0, controller: 38, value: 0 });
// evt.value = 0x100 (pitch-bend range = 2 semitones, coarse)
Usage: Advanced
import { NRPNReducer, RPNReducer } from "@suaveplan/midi";
const nrpn = new NRPNReducer();
const rpn = new RPNReducer();
decoder.onMessage((m) => {
if (m.type !== "controlChange") return;
const rpnEvt = rpn.consume(m);
if (rpnEvt) {
handleRpn(rpnEvt);
return; // RPN takes priority
}
const nrpnEvt = nrpn.consume(m);
if (nrpnEvt) handleNrpn(nrpnEvt);
});
API Reference
class RPNReducerconsume(msg: ControlChangeMessage): RPNChangeEvent | undefinedreset(): void
RPNChangeEvent { type: "rpnChange"; channel; paramHigh; paramLow; value }.
Implementation Notes
Because RPN does not specify increment/decrement shortcuts in the same canonical form as NRPN (the MMA's recommendation is that vendors generally avoid them for RPN), this reducer omits CC 96/97 handling — adding them would invite confusion when both reducers run on the same stream. Vendors that do support RPN inc/dec can layer their own behaviour on top by inspecting CC 96/97 outside this class.