# parameters _(no submodules)_ ## Purpose Declares the parameter surface the `SpessaSynthPlugin` exposes to the host chain: 16 GM channels × 5 controls (`volume`, `pan`, `program`, `reverbSend`, `chorusSend`) plus a master gain, for a total of 81 parameters. Each is described as a `PluginParameter` from `@suaveplan/audio-fx-plugin-base` so the host chain's automation lanes, parameter-curve scheduler, and preset snapshot machinery treat them uniformly with every other plugin in the chain. Parameter ids follow the deterministic `channel//` pattern (and the singleton `master/gain`) so external systems — control surfaces, OSC bridges, MIDI-learn UIs — can address any control by literal string without consulting a runtime registry. ## Features - `buildParameterDeclarations()` — emits 81 `PluginParameter` records in deterministic order. - `channelParameterId(channel, kind)` / `parseChannelParameterId(id)` — symmetric id encode / decode. - `assertChannel(channel)` — throws `SPESSASYNTH.INVALID_CHANNEL` for non-integer or out-of-range channels. - `CHANNEL_COUNT`, `CHANNEL_PARAM_KINDS`, `MASTER_GAIN_ID` — closed-enum constants. - All ratios use `[0, 1]` for sends and volume, `[-1, 1]` for pan, `[0, 127]` for program selection, `[0, 4]` for master gain. - Every parameter except `program` is marked `automatable: true` (program-change is a discrete enum jump and shouldn't be interpolated). ## Usage: Basic ```ts import { buildParameterDeclarations, channelParameterId, } from "@suaveplan/audio-fx-plugin-spessasynth/parameters"; const parameters = buildParameterDeclarations(); console.log(channelParameterId(0, "volume")); // "channel/0/volume" ``` ## Usage: Advanced ```ts import { parseChannelParameterId, MASTER_GAIN_ID, assertChannel, } from "@suaveplan/audio-fx-plugin-spessasynth/parameters"; function applyAutomation(id: string, value: number) { if (id === MASTER_GAIN_ID) { processor.setMasterGain(value); return; } const decoded = parseChannelParameterId(id); if (!decoded) return; assertChannel(decoded.channel); routeToChannel(decoded.channel, decoded.kind, value); } ``` ## API Reference - `CHANNEL_COUNT: 16`, `MAX_CHANNEL_INDEX: 15`. - `CHANNEL_PARAM_KINDS: ReadonlyArray<"volume" | "pan" | "program" | "reverbSend" | "chorusSend">`. - `channelParameterId(channel: number, kind: ChannelParamKind): string`. - `parseChannelParameterId(id: string): { channel, kind } | undefined`. - `buildParameterDeclarations(): ReadonlyArray`. - `assertChannel(channel: number): void`. - `MASTER_GAIN_ID: "master/gain"`. ## Implementation Notes `program` is declared `automatable: false` because the host chain's parameter-schedule evaluator interpolates linearly; an in-flight ramp between two program numbers would emit non-integer program values to the underlying synth, which has no defined semantics. Treating program-change as a discrete `setParameter` write keeps the contract clean. `volume` is `[0, 1]` to match MIDI CC #7 semantics — callers that need pre-master headroom should drive `master/gain` (which goes to 4×) rather than over-driving channel volume. The parameter order — channels in numeric order, kinds in declaration order, master last — is deterministic so snapshots and UI iteration produce stable byte-for-byte output.