memlnaut-nisps/manifold/src/backends/README.md

3.9 KiB
Raw Blame History

Output Backends (manifold/src/backends/)

Real output transports for the Manifold app. Exactly one backend is active at a time, chosen by the dock Mode (Particle / MIDI / OSC / VCV / Built-in Synth / Editor → BackendId). The BackendManager consumes the engine spine and forwards each routed output vector to the active backend's send().

See docs/redesign/backends-spec.md for the authoritative design.

Files

File Role
backend.ts The OutputBackend interface + BackendContext / OutputMapping / BackendStatus.
mapping.ts Universal per-output baseline mapping (applyCurve, mapOutput) — shared by all backends, input-clamped.
manager.ts BackendManager — single spine consumer; switches/teardowns backends; gates synth audio.
midi-backend.ts WebMidiBackend — real Web MIDI CC out (per-output CC#/channel/range/name, throttled + dead-zone).
osc-client.ts NispsOscClient — WS transport to the Deno OSC bridge (JSON protocol, auto-reconnect).
osc-backend.ts OscBridgeBackend — OSC out over WS; per-output address path + physical range.
vcv-backend.ts VcvBackend — drives + trains the VCV Rack NISPS module over the OSC↔WS bridge (streams /nisps/input, forwards the verdict loop to /nisps/feedback, receives /nisps/output + /nisps/state).
passthrough-backend.ts No-op sink for synth (plays in-engine) / particles (rAF consumer) / editor.
presets.ts Named per-backend output-config presets (localStorage, per-backend namespace).
useBackendManager.ts Thin React binding: builds BackendContext from the store, switches Mode, surfaces status.

Audio gating

The Built-in Synth plays inside the engine (EngineHost pushes params to the worklet on every spine tick). So the manager mutes audio on every non-synth Mode via engine.audio.setMuted(true) and unmutes on the synth Mode. This is the documented gate — cleanly suppressing the worklet push would need an engine change that this workstream doesn't make.

OSC bridge — the process must be running

Browsers cannot send UDP, so the OSC backend connects over WebSocket to the Deno bridge, which encodes OSC and forwards over UDP. Start the bridge locally or the OSC backend shows "bridge not running" (it auto-reconnects):

cd manifold/osc-bridge
deno run --allow-net bridge.ts
#   --osc-host 127.0.0.1 --osc-port 9000 --ws-port 8765 --listen-port 9001
# or, without Deno:
node bridge.mjs

Default bridge URL: ws://localhost:8765 (configurable in the OSC config panel). WS protocol (browser → bridge): { type:'params', payload:[[path,value],…] }.

VCV bridge — module and bridge must both be running

When Mode = VCV, the browser is authoritative in bridged mode: the VcvBackend connects over the same Deno bridge and

  • streams the current input vector continuously to /nisps/input (the browser drives the module's inputs),
  • streams the routed per-output CV (uni 010 V / bipolar ±5 V), and
  • forwards the verdict loop — thumbs-up/down + explore-and-place — to /nisps/feedback ({ op:'up'|'down'|'rand'|'clear', spread, input[], output[] }), so the module's embedded net trains in lock-step with the browser session.

It receives /nisps/output + /nisps/state back for status / visualisation. The verdict forwarding is gated in BackendManager.forwardFeedback — a no-op unless VCV is the active backend (otherwise the browser engine is the learner). Both processes are required:

# 1) the VCV Rack NISPS module (vcv/) — default UDP listen port 7001
# 2) the Deno bridge, pointed at the module's UDP port:
cd manifold/osc-bridge
deno run --allow-net bridge.ts --osc-port 7001

Default VCV bridge URL: ws://localhost:8765 (configurable in the VCV config panel). Until the WS connects the backend shows "bridge not running"; until the module replies it shows "waiting for module…".