54 lines
2.5 KiB
Markdown
54 lines
2.5 KiB
Markdown
# 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.
|