# The Keyboard

```yaml
actions:
  - action_id: surface.open
    input: { surface: keyboard-default }
    label: "▶ 0. Open the Keyboard panel"
  - action_id: keyboard.channel.set
    input: { channel: 1 }
    label: "▶ 1. Send on MIDI channel 1"
  - action_id: keyboard.octave.set
    input: { base: 60 }
    label: "▶ 2. Octave base = C4 (middle C)"
  - action_id: keyboard.note.on
    input: { note: 60, velocity: 100 }
    label: "▶ 3. Play C4 (loud)"
  - action_id: keyboard.note.on
    input: { note: 64, velocity: 100 }
    label: "▶ 4. Add E4 (major third)"
  - action_id: keyboard.note.on
    input: { note: 67, velocity: 100 }
    label: "▶ 5. Add G4 (C major chord)"
  - action_id: keyboard.notes.clear
    input: {}
    label: "■ Release all"
```

The Keyboard is FaustWave's virtual MIDI input — a clickable on-screen keyboard registered as a routing-table source (`keyboard-default`). Useful for testing a DSP ("does my Builder make a sound when I press a key?"), for performing without a hardware controller, and — as the action chain above shows — for driving notes programmatically from the assistant.

> 🟦 **HANDS-FREE PATH**: the buttons above send a real chord. Step 1 picks channel 1, step 2 plants the octave at middle C, steps 3–5 stack a C major triad, the last button releases everything. The chord runs through the same source-layer wrapper a click on the on-screen keys would — receivers can't tell the difference.

## Opening + anatomy

Open the **Keyboard** rail panel from the left rail (piano-keys icon under the MIDI sources area). The panel shows:

- A two-octave keyboard layout (default: leftmost key = MIDI 60 / C4 / middle C).
- Octave shift buttons — move the visible range up / down by one octave.
- A MIDI channel-picker chip (Omni / 1–16).
- A hold-mode toggle (see below).
- An **ALL OFF** button for the stuck-notes recovery.

## Playing notes

Click a key to send a `note-on` MIDI event; release (or pointer-out) sends `note-off`. The event flows through the routing table to every connected MIDI sink — instruments, Builders, sequencer record-armed tracks, hardware MIDI outs, etc.

Holding multiple pointers (or stacking with hold-mode) plays a chord. The Keyboard itself has no polyphony cap; the receiving consumer decides what it can voice.

> 🟦 **NEW TO MIDI?** A MIDI keyboard sends an event per gesture: `note-on { pitch, velocity }` when you press, `note-off { pitch }` when you release. The keyboard carries no sound — it just describes the gesture. The receiving instrument (your Builder, or any instrument you've installed) decides what that gesture sounds like.

## Hold mode — stacking chords with one pointer

The hold toggle changes the click semantics:

- **OFF (default, physical-key feel)**: pointer-down plays, pointer-up releases. Mono unless you have multi-touch.
- **ON (toggle / latch feel)**: click ONCE to start a note, click AGAIN to release. Stack chords by clicking multiple keys one after another, without holding the pointer down.

Hold-mode does NOT auto-clear when flipped — if you turn it OFF with notes still latched, they stay on until you hit ALL OFF or play and release them with a fresh pointer press.

> 🔘 **MCP**: `keyboard.hold.set { enabled: true | false }`.

## MIDI channel

The Keyboard sends on **channel 1** by default. Change it via the panel's channel selector or:

> 🔘 **MCP**: `keyboard.channel.set { channel: 0..16 }`. `0` = **Omni** broadcast (status-byte channel bits = 0, equivalent to channel 1 on the wire; receivers configured Omni accept any channel). `1..16` = specific channel.

Receiving consumers (Builder `midi_note` nodes, sequencer tracks) filter by channel — if they listen on a different channel than you send, they ignore the event. **Heads-up**: changing channel mid-held-note strands the note on the OLD channel. Call `keyboard.notes.clear` before the channel switch if any notes are held.

## Octave shift

The Keyboard's visible range moves in semitones by setting the **octave base** — the MIDI number of the leftmost visible key. Default is 60 (C4). Range is clamped to 0–103 so the topmost visible key stays inside MIDI 0–127.

> 🔘 **MCP**: `keyboard.octave.set { base: 0..103 }`. Examples: 48 = C3 (one octave down), 72 = C5 (one octave up). The panel's octave buttons step in 12-semitone increments and call `keyboard.notes.clear` first to avoid hung notes — do the same when scripting if any notes are held.

## Stuck notes — ALL OFF / `keyboard.notes.clear`

Long sessions can leave notes hanging — a channel switch mid-held-note, a hold-mode flip, an MCP call that fired `keyboard.note.on` without a matching `keyboard.note.off`. The **ALL OFF** button (and its MCP twin `keyboard.notes.clear`) is the recovery: it sends a `note-off` for every entry in the held-set on the current channel, then clears the set.

Idempotent — calling it with nothing held is a no-op. Always safe to fire.

> 🔘 **MCP**: `keyboard.notes.clear {}` — releases what THIS keyboard holds; useful as an action chain's epilogue. It is not the panic: for a note stuck on an instrument or on external hardware, use `midi.panic {}` (or the Panic button at the right end of the transport strip), which tells every MIDI sink and every hardware output to let go.

## Routing

The Keyboard registers as the source `keyboard-default`. A fresh project has **no seeded MIDI connections** — there's no bundled instrument to wire to, so you draw the Keyboard's routing yourself.

Wire it via the **Patchbay** rail panel (click the cell at the row for `keyboard-default` and the column of any compatible MIDI sink). Common targets: your own Builders' `midi_note` nodes, sequencer record-armed tracks, hardware MIDI-out devices.

> 🔘 **MCP**: connection management lives under `routing.connect` / `routing.disconnect` / `routing.list`. The Keyboard's source id is the stable `keyboard-default`.

## Physical MIDI integration

Plug in a hardware MIDI controller (USB / Bluetooth / DIN-USB adapter). FaustWave's MIDI subsystem picks it up via WebMIDI — the OS prompts for permission on first use.

Physical controllers appear as separate routing sources (e.g. `midi-input-input-0`) alongside `keyboard-default`. Wire them into sinks via the Patchbay the same way you'd wire any source. The on-screen Keyboard and physical controllers can coexist — both feed the same sinks if both are wired up.

> 🔘 **MCP**: list everything currently MIDI-audible with `system.state { sections: ["midi_consumers"] }`. For full topology hit `routing.list` and filter by `kind: "midi"`.

## The audible-consumer hint

A quality-of-life affordance in the Mixer: if you press a key and NOTHING is currently set to be audibly driven by MIDI, the Mixer panel shows a **Smart Mute Hint** — a soft "hey, your input isn't reaching anything that makes sound". Common causes: nothing is wired from the Keyboard to a sound source yet, every MIDI consumer is muted, or the Builder you're driving isn't on a MixerTrack (not "+ ADD AUDIO"-ed).

The hint clears as soon as an audible MIDI consumer becomes reachable again.

## MCP surface — the real catalog

| Action | Purpose |
|---|---|
| `keyboard.note.on` | Trigger `note-on` (note, velocity). Velocity defaults to 100. |
| `keyboard.note.off` | Trigger `note-off`. Idempotent re: stuck-note recovery. |
| `keyboard.channel.set` | Set output channel (0 = Omni, 1–16 specific). |
| `keyboard.octave.set` | Set the leftmost visible MIDI number (0–103). |
| `keyboard.hold.set` | Toggle hold-mode (latch vs press-and-release). |
| `keyboard.notes.clear` | Release every note THIS keyboard holds. Not the global panic — that is `midi.panic`. |
| `keyboard.state` | Snapshot: held notes, channel, hold flag, octave base. |

All Keyboard MCP actions are `palette.run`-only (no hot-path tools) — they're low-frequency compared to the Sequencer's surface, which earns dedicated tools. See **FaustWave — AI Assistant** § *Why some actions are hot-path and others aren't* for the discipline.

## Where to go from here

- **FaustWave — Sequencer** for the other big MIDI source. Both route through the Patchbay; both share the master transport.
- **FaustWave — Builder** for `midi_note` nodes so your DSP responds to Keyboard input.
- **FaustWave — Getting Started** § *Your first pattern* — a Keyboard-driven walk-through.
