Files
skills/docs/media/audio/midi/src/dispatch/cc14.md
2026-05-29 08:20:56 +01:00

54 lines
2.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.