Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
# 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 `RPNChangeEvent` on 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
```ts
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
```ts
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 RPNReducer`
- `consume(msg: ControlChangeMessage): RPNChangeEvent | undefined`
- `reset(): 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.