External clock sync & controller actions
FaustWave can follow an external MIDI clock — a hardware sequencer, groovebox, or another DAW acts as the tempo master, and FaustWave's whole transport (BPM, play/stop, every sequencer, every transport-linked Builder clock) rides along. This is how FaustWave joins a live set.
Wiring it up — routing IS the switch
The routing table has a permanent MIDI Sink called Transport Clock In (transport-clock:midi-in). There is no settings toggle and no mode switch: connecting a MIDI input to this Sink enables clock follow; disconnecting it stops it. Same doctrine as everything else in the Patchbay.
- Connect your clock-sending device (USB-MIDI, or a virtual port like loopMIDI on Windows / an IAC bus on macOS when the master is software).
- In the Patchbay matrix, connect that device's input Source to Transport Clock In.
For AI & automation · MCP
routing.connect { kind: "midi", from_source: "midi-input-<deviceId>", from_port: "out", to_sink: "transport-clock:midi-in", to_port: "in" }.
The persisted routing survives restarts and unplug/replug like any other hardware routing.
What "following" means
MIDI clock is a stream of tick messages (24 per quarter note) plus Start/Stop commands. FaustWave does not replace its own clock with those ticks — the sample-accurate Faust master clock (see FaustWave — Sequencer § The shared Faust master clock) stays the only timebase that triggers notes. Instead, the tick stream is treated as a measurement: a windowed tempo estimate (about two beats of ticks, first lock after half a beat) continuously nudges the transport BPM, the same way a cruise control holds speed. Tick jitter — real hardware clocks wobble by several milliseconds — is absorbed by the estimator; your groove stays sample-locked.
While ticks are arriving, an EXT tag appears next to the BPM readout in the topbar transport strip (hover it for the source device and current external tempo).
- Start (0xFA) — the transport (re)starts from the top, even if it was already playing. This mirrors what the master does: pressing Start on the master realigns everyone to bar 1. The start is phase-locked: the grid is anchored on the master's own step raster rather than on FaustWave's reaction time, so the first beat lands with the master instead of ~110 ms behind it (measured ±2–5 ms against a deterministic software master). This is the START only — see below for what happens over the following minutes.
- Stop (0xFC) — the transport stops.
- Continue (0xFB) — followed as play, but FaustWave restarts from the top rather than resuming mid-song, and deliberately does not snap to the master's raster (there is no song position to snap to). Song-position resume is on the roadmap.
- Ticks alone never start playback. Many masters send clock continuously even while stopped — FaustWave uses that to keep its tempo estimate warm, so pressing Start on the master drops you in at the right BPM immediately.
Rules and gotchas
- One clock source at a time. If two devices are routed to Transport Clock In, the one that ticks first wins; the other is ignored until the active one goes silent for over a second (then the survivor takes over). Two merged clock streams would read as double tempo — don't route two masters.
- Manual BPM edits are overridden while EXT is active. The external master owns the tempo; edit BPM on the master, not in FaustWave.
- Out-of-range masters clamp. FaustWave's transport range is 30–300 BPM; a master outside that range pins FaustWave to the nearer limit.
- Clock lost (device unplugged, master powered off): after ~1 second the EXT tag disappears and FaustWave keeps the last followed BPM and play state — the set doesn't stop just because the cable did. The Logs panel records the hand-off.
- No hardware handy? Any DAW can be the master over a virtual MIDI port (loopMIDI on Windows, IAC on macOS) — e.g. Reaper's "Send clock/SPP to output" per MIDI output device. On Windows, create the virtual port before launching FaustWave (see the warning above).
Controller actions (pads & buttons run FaustWave)
The second controller sink is Controller Actions (controller-actions:midi-in) — route a controller's MIDI input onto it and its pads/buttons can fire any action in the IDE: launch a scene, stop the transport, toggle recording. Same doctrine: routing is the enable switch, and the mapping persists per project.
Bindings are authored via commands (ask the assistant, or palette.run):
For AI & automation · MCP
controller.bind.add { type: "note", number: 36, channel: 16, action_id: "sequencer.scene.launch", input: { scene_id: "..." } } — pad note 36 on channel 16 launches that scene. type: "cc" fires button-style on the rising edge across value 64. channel is required: 1..16, or 0 = any channel — only when nothing else on that port uses the number (a hardware sequencer sharing the port fires an omni binding on every step). controller.bind.list shows the map; controller.bind.remove { binding_id } drops one.
Or Learn it in the Controller panel: choose Action or Param, pick Note or CC (one kind at a time — a hardware sequencer sharing the port sends notes that look like pad presses), arm Learn, press the pad or turn the knob; a param widget's right-click menu has a "MIDI Learn…" row that lands there pre-selected.
Every fired binding lands in the Activity log like a UI click — a live set stays auditable. A binding to a mistyped action id is rejected at add time; a binding whose action breaks later warns in the Logs panel instead of silently doing nothing.
Encoders → sound parameters (midi_cc node)
Turning a hardware encoder into a sound control is not a binding — it's a Builder node. Add a MIDI CC node (Sources category) to the instrument's graph, set its CC number / channel / min–max range, and wire its value output into any control input (cutoff_in, res_in, a gain). The CC-to-value mapping runs inside the Faust worklet ([midi:ctrl] metadata — no JS hop, smoothed against zipper noise). Route the controller's MIDI input onto the instrument's :midi-in sink for this — the module, not the Controller Actions sink.
Rule of thumb: buttons/pads → Controller Actions sink + bindings; knobs/encoders → midi_cc node in the instrument.
What FaustWave does not do (yet)
- Running phase lock: the START is snapped onto the master's raster (see above), but from then on the ticks correct the rate only, never the position — there is no control loop nudging the downbeats back together. Against a deterministic master the residual drift is ~0.04 ms/s; against real hardware it is whatever that device's tempo quantisation costs — a BeatStep Pro running "120" actually sends 119.9, which works out to ~0.6 ms/s, i.e. roughly 36 ms per minute. For a song-length set that is inaudible; over a long DJ-style set it is not, and re-pressing Start on the master re-aligns everyone.
- Clock master: FaustWave sending clock OUT to hardware is planned but not wired yet.
- Ableton Link: on the roadmap after the MIDI path is proven.