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,53 @@
# dispatch/cc14
## Purpose
`CC14Reducer` accumulates paired MIDI 1.0 control-change messages into 14-bit values. The wire convention is: CCs 0..31 carry the most-significant byte of a controller, CCs 32..63 carry the least-significant byte of the *same* logical controller (`lsbController = msbController + 32`). Sending MSB then LSB lets the receiver follow value changes smoothly across a 14-bit range — far more useful than the 7-bit single-CC form for parameters like volume, expression, and pan.
The reducer maintains per-channel, per-controller state and emits one `CC14ChangeEvent` on every LSB arrival, regardless of whether the MSB has been seen yet (controllers that send LSB-only updates simply use the last seen MSB, or zero on the first message).
## Features
- Per-channel, per-MSB-controller partial state.
- Emits a consolidated `CC14ChangeEvent` on every CC 32..63 arrival.
- Ignores controllers outside 0..63 — they belong to NRPN/RPN dispatch or are 7-bit-only.
- `reset()` to clear state on port reset.
## Usage: Basic
```ts
import { CC14Reducer } from "@suaveplan/midi";
const r = new CC14Reducer();
r.consume({ type: "controlChange", channel: 0, controller: 7, value: 0x40 }); // MSB
const evt = r.consume({ type: "controlChange", channel: 0, controller: 39, value: 0x05 }); // LSB
// evt = { type: "cc14Change", channel: 0, controller: 7, value: (0x40 << 7) | 0x05 }
```
## Usage: Advanced
```ts
import { CC14Reducer } from "@suaveplan/midi";
const reducer = new CC14Reducer();
// Combine with the running-status decoder to extract smooth volume curves.
decoder.onMessage((m) => {
if (m.type !== "controlChange") return;
const evt = reducer.consume(m);
if (evt && evt.controller === 7) {
mixer.setVolume(evt.channel, evt.value / 16383);
}
});
```
## API Reference
- `class CC14Reducer`
- `consume(msg: ControlChangeMessage): CC14ChangeEvent | undefined`
- `reset(): void`
- `CC14ChangeEvent { type: "cc14Change"; channel; controller; value }` — `controller` is the MSB CC number (0..31).
## Implementation Notes
State is keyed by a composite `${channel}:${msbController}` string so a single `Map` covers all 16 channels × 32 controllers without nested maps. The reducer treats an LSB without a preceding MSB as `msb = 0`, matching the behaviour of every consumer-grade synth tested against the spec. Out-of-range controllers (≥ 64) are silently ignored rather than throwing — they have legitimate uses (sustain pedal, sostenuto, NRPN parameter MSB) that this reducer should not interfere with.