# Controller bindings, pads, and live effects

Modulation moves a parameter from inside the machine. A **controller binding** moves it from a hand — a pad, a knob, a hardware controller — and the two compose: the hand writes the **base**, the modulator swings around it.

## Two kinds of binding

`controller.bind.add` binds one MIDI trigger to ONE target:

| Target | Input | Fires |
|---|---|---|
| **Action** | `action_id` + `input` | a note-on (`type: "note"`) or a CC crossing 64 upwards (`type: "cc"`) runs the action — `sequencer.scene.launch { scene_id }`, `transport.stop`, anything in the palette |
| **Parameter** | `sink_id` + `path` (+ `min`, `max`), CC only | the CC is followed continuously, 0..127 mapped into `min..max` (default: the parameter's own range), and written to the parameter's **base** |

`channel` is required — 1..16, or 0 = any channel. The demo puts its whole control map on channel 16 so it never collides with the instruments on 1–5; use 0 only when nothing else on that port sends the same number, since a hardware sequencer sharing the port would fire the binding on every step. The trigger must reach the **Controller Actions** sink (`controller-actions:midi-in`) — route your controller (or the Pads) onto it in the Patchbay; the cable is the enable switch.

```yaml
actions:
  - action_id: controller.bind.list
    input: {}
    label: "▶ Every binding in this project — action or parameter"
  - action_id: midi.cc.map
    input: {}
    label: "▶ Who already listens to which CC (before you pick a number)"
```

## Pads are an instrument

The **Pads** surface is a virtual controller on the bus — pads send notes, knobs and faders send CCs, buttons send a momentary max/min. It is a MIDI *source* like a hardware device, so everything above applies to it, and a hardware controller that sends the same numbers plays the same map. The demo's surface, 21 widgets:

- pads on channel 1/2 that play the Sample Kit and the 808 directly (a note into an instrument's `:midi-in`);
- knobs on channel 16 bound to **parameters**: CC 71 → the Earthquake macro's `value`, CC 74 → the Hook Lead `cutoff` (base — the LFO keeps breathing around wherever the knob is);
- buttons on channel 16 bound to **parameters of a master insert**: CC 80/81/82 → `stutter`, `brake`, `throw` of the Live Pads slot;
- pads on channel 16 bound to **actions**: five scenes and STOP.

```yaml
actions:
  - action_id: surface.open
    input: { surface: faustwave-bundled/pads }
    label: "▶ Open the Pads"
  - action_id: pads.state
    input: {}
    label: "▶ The widget list with ids, numbers and channels"
```

## The live-effects pattern: parameters, not MIDI

`FX Live Pads.dsp` in the demo is a master insert with a DJ filter, a beat-repeat stutter, a tape-stop brake, a bit crusher and a reverb throw — and it contains **no MIDI at all**. Every control is a plain slider:

```faust
filter  = hslider("[0]filter", 0.5, 0, 1, 0.001) : si.smooth(ba.tau2pole(0.02));
stutter = hslider("[1]stutter", 0, 0, 1, 1);
brake   = hslider("[2]brake", 0, 0, 1, 1);
bpm     = hslider("bpm[bpm]", 130, 30, 300, 0.1);   // the live tempo, for a stutter of exactly one eighth
```

Because they are parameters, the same DSP works from the Inspector, from a pads button through a binding, from a hardware controller through the same binding, and from a modulator through an edge — four hands on one knob, no code per hand. The binding for a button is one call:

```yaml
actions:
  - action_id: master.fx.slots.list
    input: {}
    label: "▶ Find the slot id and its parameter names"
```

then `controller.bind.add { type: "cc", number: 80, channel: 16, sink_id: "pattern-live-pads", path: "/Live_Pads/stutter" }` — the sink id is the slot's `pattern-<slot_id>`, the path the full Faust address (`controller.bind.list` shows the demo's thirteen). Three details that make it click-free: every gate inside the DSP is ramped (`si.smooth`) so a button press is not a step; the brake releases through a crossfade instead of snapping the delay line back; and the filter is a bit-exact bypass at its centre so "hands off" costs nothing.

## What to try

1. Open Perform, launch TRAP, hold **Stutter** on the pads for a beat, let go on the one.
2. Turn **Erdbeben** to 0.6 — three 808 parameters move together; look at the Modulators pane while you do.
3. Turn **Hook Cutoff** while the LFO breathes — you moved the base, the breath continues around it. That is the whole model.

- **FaustWave — Pads** — the widget types and EDIT mode.
- **FaustWave — Patchbay & Routing** § *External clock sync* and § *MIDI Monitor* — when a controller "does nothing".
- **FaustWave — Mixer & Master** § *The Master FX rack* — where an insert like Live Pads lives.
