# `midi-out` handler ## Purpose The `midiOut` handler is the binding-registry slot that converts a control's value into an outbound MIDI Control Change. The default export is the "unwired" implementation that throws `REACT_CONTROL_SURFACE.BINDING_HANDLER_MISSING`; consumers replace it with a closure that captures a `MidiOutputHandle` from `useMidiOutput(binding.portId)` and emits CC traffic on the bound `channel`/`cc` pair, scaled into the binding's optional `range`. Throwing at the default keeps a partially-wired surface from masquerading as live. Once `useMidiOutput` is bridged the runtime forwards every drag, press, or keyboard increment as a single CC message. ## Features - Strongly-typed signature `(binding: MidiOutBinding, value: number) => never`. - Error context preserves `portId`, `channel`, `cc`, and the dropped value. - Encoding/range scaling are deferred to the production handler — this default is value-agnostic. ## Usage: Basic ```ts import { BindingRegistry } from "@suaveplan/react-control-surface/runtime/binding-registry"; const registry = new BindingRegistry({ midiOut: (binding, value) => output.sendMessage({ kind: "controlChange", channel: binding.channel, controller: binding.cc, value: Math.round(value * 127), }), }); ``` ## Usage: Advanced ```ts import { midiOutHandler } from "@suaveplan/react-control-surface/runtime/binding-registry"; expect(() => midiOutHandler( { kind: "midiOut", portId: "p1", channel: 1, cc: 7 }, 0.5 ) ).toThrow(); ``` ## API Reference - `midiOutHandler(binding: MidiOutBinding, value: number): never` — always throws with code `REACT_CONTROL_SURFACE.BINDING_HANDLER_MISSING`. ## Implementation Notes Range scaling is the consumer's responsibility because the schema's optional `range` can flip min/max, and the production handler may want to interpret it differently (linear vs. log) per surface. Keeping the default value-agnostic ensures the consumer never inherits an opinionated mapping they can't override.