The routing model
FaustWave's audio + MIDI topology is a single declarative routing table, JACK-style. Every signal-producing entity is a Source, every signal-consuming entity is a Sink, and a Connection wires one Source's output port to one Sink's input port. The Patchbay is the matrix editor for that table; the rest of this book is about what you can do with it.
Three entity types
Sources — things that PRODUCE
Anything that emits an audio buffer, a MIDI event, or a control value is a Source. From a fresh project you have:
keyboard-default(MIDI) — the on-screen Keyboard.sequencer-default(MIDI) — the step grid.- Hardware MIDI inputs (one Source per device, e.g.
midi-input-input-0). master(audio) — the master bus's pre-fade tap, so you can route the mix into the Recorder or back into a Builder.- Every Builder / Faust-DSP module you open registers itself:
builder-<id>(audio). - Every running instrument you've installed exposes its audio out (
<instrument-id>, audio). - Each MixerTrack is an audio Source too (
<track-id>) — it routes the track's post-fader signal on tomaster.
Each Source has one or more output ports. Most have a single port called out; multi-output entities (a few specialised modules) carry more.
Sinks — things that CONSUME
Anything that wants to receive audio, MIDI, or modulation values is a Sink:
master(audio) — the master bus input.- Each MixerTrack as an audio Sink (
<track-id>:in) — Sources route INTO a track here; the track applies its fader / mute / solo / FX / sends, then routes on tomaster. recorder-default(audio) — the bundled Recorder.- Hardware MIDI outputs (one Sink per device, e.g.
midi-output-output-1). - Every MIDI-accepting module's MIDI side — a Builder with a
midi_notenode (appears once the node binds), or an installed instrument's<instrument-id>:midi-in. - An instrument's modulation side as
<instrument-id>(kindmodulation) — one input port per exposed Faust param (e.g.<instrument-id>/cutoff).
The modulation Sink is the special one: a single entity exposes a whole bank of per-parameter ports, and you wire modulator Sources (LFOs, envelope followers) to specific params.
Connections — the wires
A Connection has:
- A
from{ sourceId, portId } pair (the producer side). - A
to{ sinkId, portId } pair (the consumer side). - A
kind—"audio","midi", or"modulation". - A
gain(audio) oramount(modulation) or just anenabledflag (MIDI). - An auto-generated
idof the formconn:<sourceId>:<sourcePort>-><sinkId>:<sinkPort>.
The id is deterministic — calling routing.connect twice for the same endpoints doesn't create two wires, it returns the same id (idempotent).
The three cable kinds
| Kind | Carries | Extra control | UI cell |
|---|---|---|---|
audio | Sample buffers (stereo by default, channel count from the port). | gain (linear 0..1, 5 ms ramp on changes; enabled suspends with a ramp to 0). | Cyan cell in the matrix. |
midi | MIDI events (note-on / note-off / CC / clock / sysex). | enabled only — a Boolean dispatch gate. No gain ("50 % MIDI" makes no sense). | Purple cell in the matrix. |
modulation | Per-param control values (LFO output, envelope follower, slow CCs converted to param-rate). | amount (signed scalar; modulator output scaled before reaching the param). | Reachable via routing.connect + the Modulators panel; not in the matrix V1. |
Kind-mismatch is rejected: you can't wire an audio Source to a MIDI Sink. The matrix greys those cells out; routing.connect returns an error if you try via MCP.
The audio path
A fresh project ships with nothing pre-wired — there's no bundled instrument and no seeded connections. You build the topology explicitly. The audio path for a DSP reads:
builder-<id> → <track>:in (audio — created by the mixer's "+ ADD AUDIO")
<track> → master (audio — the MixerTrack's own output)
i.e. a Source routes INTO a MixerTrack (which carries the fader / mute / solo / FX / sends), and the track routes on to master. Opening + running a Builder registers builder-<id> as a Source but does NOT wire it anywhere on its own — you add it to a track via the Master panel's "+ ADD AUDIO" picker (or mixer.tracks.input.set). The route then persists, so re-opening the DSP restores it.
MIDI is wired the same explicit way — keyboard-default / sequencer-default → <your Builder's midi-in>.
Multiple Sources can route into one track, and multiple tracks sum into master. The right-panel mixer shows one strip per MixerTrack, with the track's gain as the fader.
Permanent vs ephemeral
Every entry in routing.list carries a permanent flag:
permanent: true— the entity is built into FaustWave's posture (master, Keyboard, Sequencer, Recorder, hardware MIDI devices). Removing the underlying module is either impossible or auto-respawns.permanent: false— user-created entities (Builders + Faust DSPs you opened, MixerTracks you added). Closing the module / deleting the track unregisters its Source / Sink and disconnects its wires.
When you delete a Builder, its wires don't "leak" — the routing engine reconciles on the next diff cycle and tears down anything the closed module owned.
What this gets you
Because routing is observable + addressable + MCP-driven, every routing decision is:
- Visible — the Patchbay matrix + the Activity log show every connect / disconnect.
- Scriptable — the AI assistant (or your own MCP client) can build complex wirings end-to-end. See § 4 in this pack.
- Survives restart — connections persist in the project. Re-opening the project re-binds them once each Source / Sink rebinds.
Next section: opening the Patchbay matrix and using it.