Initial Commit
This commit is contained in:
67
packages/ui/react/react-midi/src/midi-contract/reducers.md
Normal file
67
packages/ui/react/react-midi/src/midi-contract/reducers.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# `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
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user