/** * BackendAdvanced — the FULL-depth advanced backend modal bodies (dock-spec §4). * One editor per backend. All backends share the §3.1 baseline (rendered as * OutputControlRow elsewhere); these add the backend-specific fields. * * The backend transport (backends-spec workstream E) is now LIVE: editing these * fields writes the shared MFParam store, which the BackendManager reads to send * real Web MIDI CC / OSC-over-WS. This modal is the full-depth duplicate of the * inline config in OutputsBackendConfig; both write the same store. */ import type { MFParam } from '../console/model'; import type { BackendId } from './output-state'; import { defaultMidiSpec, defaultOscSpec } from './output-state'; function num(s: string, fallback: number): number { const v = parseFloat(s); return Number.isFinite(v) ? v : fallback; } const cellInput: React.CSSProperties = { width: '100%', background: 'var(--bg-1)', border: '1px solid var(--line)', borderRadius: 'var(--r-1)', color: 'var(--fg)', fontFamily: 'var(--font-mono)', fontSize: 'var(--fs-xs)', padding: '3px 6px', }; function Th({ children }: { children: React.ReactNode }) { return ( {children} ); } export interface BackendAdvancedProps { backend: BackendId; params: MFParam[]; setParam: (i: number, patch: Partial) => void; } export function BackendAdvanced({ backend, params, setParam }: BackendAdvancedProps) { switch (backend) { case 'midi': return ; case 'osc': return ; case 'vcv': case 'cvgate': return ; default: return ; } } // ---- MIDI (dock-spec §4.1) ------------------------------------------------- function MidiCcEditor({ params, setParam, }: { params: MFParam[]; setParam: (i: number, patch: Partial) => void; }) { return (

{params.length} CCs · live Web MIDI out (backends-spec §2.3). Editing the CC map here sends in real time once a MIDI port is selected in the Outputs panel.

{params.map((p, i) => { const m = p.midi ?? defaultMidiSpec(i); return ( ); })}
Name CC# Ch State
setParam(i, { midi: { ...m, name: e.target.value } })} /> setParam(i, { midi: { ...m, cc: Math.max(0, Math.min(127, num(e.target.value, m.cc))) }, }) } /> setParam(i, { midi: { ...m, channel: Math.max(1, Math.min(16, num(e.target.value, m.channel))), }, }) } /> {p.status} {p.muted ? ' · muted' : ''}
); } // ---- OSC (dock-spec §4.2) -------------------------------------------------- function OscPathEditor({ params, setParam, }: { params: MFParam[]; setParam: (i: number, patch: Partial) => void; }) { return (

Live OSC over the WebSocket bridge (backends-spec §2.4). Set the bridge URL + per-output paths in the Outputs panel; emits only while the bridge process is connected.

{params.map((p, i) => { const o = p.osc ?? defaultOscSpec(p.name); return ( ); })}
OSC path range min range max
setParam(i, { osc: { ...o, path: e.target.value } })} /> setParam(i, { osc: { ...o, rangeMin: num(e.target.value, o.rangeMin) } })} /> setParam(i, { osc: { ...o, rangeMax: num(e.target.value, o.rangeMax) } })} />
); } // ---- VCV / CV (dock-spec §4.3) --------------------------------------------- function VcvChannelEditor({ params, setParam, }: { params: MFParam[]; setParam: (i: number, patch: Partial) => void; }) { return (

VCV adds nothing beyond the baseline (min/max = range, fixed = freeze) plus per-channel polarity. {/* TODO(backends-spec §2.6): the VCV browser↔module bridge transport is not yet wired here. */}

{params.map((p, i) => { const bipolar = p.vcv?.bipolar ?? false; return (
{p.name} {p.min.toFixed(2)}–{p.max.toFixed(2)} · {p.status === 'fixed' ? 'frozen' : 'live'}
); })}
); } function SynthGroupNote({ params }: { params: MFParam[] }) { const groups = Array.from(new Set(params.map((p) => p.group))); return (

The synth backend's advanced surface is the group-override matrix — see the Powerful Synth Engine drawer's full depth (dock-spec §4.4 / §5). Groups: {groups.join(' · ')}.

); }