All books

The routing model

~4 min read · updated 2026-08-21 · markdown

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 to master.

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 to master.
  • 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_note node (appears once the node binds), or an installed instrument's <instrument-id>:midi-in.
  • An instrument's modulation side as <instrument-id> (kind modulation) — 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) or amount (modulation) or just an enabled flag (MIDI).
  • An auto-generated id of the form conn:<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

KindCarriesExtra controlUI cell
audioSample 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.
midiMIDI 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.
modulationPer-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.

Try it yourself — the IDE runs in your browser. Open the IDE → Get the desktop app

Text licensed under CC BY 4.0 — Mani Weber / FaustWave. For language models: llms.txt · llms-full.txt

AUDIO · 48k · 48.0ms FAUST · 3 KB · 1171 docs CPU · 8.4% BPM · 120.0 UTF-8 BETA· v0.90.0