first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
# `hooks/use-nrpn`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`useNRPN(selector)` wraps the universal NRPN reducer from the
`midi-contract` module and exposes the consolidated `NRPNChange` as
React state. NRPN ("Non-Registered Parameter Number") edits arrive
over the wire as a four-CC dance — CC 99 (param MSB), CC 98 (param
LSB), CC 6 (data MSB), CC 38 (data LSB) — and consumers usually want
a single React-level event per completed dance rather than four
intermediate `controlChange` events. The hook performs that fold
internally and re-renders once per consolidated change.
The reducer is owned per port: when the underlying input identity
changes (plug/unplug, selector switch) the reducer is reset, so a
half-completed dance from a previous device does not leak into the
new one.
## Features
- Folds CC 99/98/6/38 into one `NRPNChange` per dance.
- Per-port reducer — no cross-device interference.
- Resets internal state on port identity change.
- Returns `null` until the first completed dance.
## Usage: Basic
```tsx
import { useNRPN } from "@suaveplan/react-midi";
function ParamPanel() {
const change = useNRPN("first");
if (!change) return <p>Move a knob…</p>;
return (
<p>
NRPN ({change.paramHigh}.{change.paramLow}) → {change.value}
</p>
);
}
```
## Usage: Advanced
```tsx
function NRPNLog() {
const change = useNRPN("first");
const log = useRef<NRPNChange[]>([]);
useEffect(() => {
if (change !== null) log.current.push(change);
}, [change]);
return <pre>{JSON.stringify(log.current, null, 2)}</pre>;
}
```
## API Reference
- `useNRPN(selector: string | "first"): NRPNChange | null`
## Implementation Notes
The hook keeps the reducer in a `useRef` (rather than a `useMemo` or
`useState`) because the reducer is conceptually an external store
attached to the lifetime of the port subscription, not a render
output. When the port becomes `null` the ref is cleared so a fresh
port gets a fresh reducer.