15 — Views & the control model
Status · reconciled 2026-08-30. The touch/null-profile views and parameter assignments are implemented. The abstract physical-control, profile, takeover and feedback path is the active M6 work.
The Yamaha S90 is a proof-of-concept chassis, not the product. The system must
adopt to other hardware — a future in-house control surface, a third-party MIDI
controller, or no hardware at all (a plain touch display). This doc fixes the
software architecture that makes the chassis disposable: an abstract control
surface that both the touch display and the physical hardware
(06) bind into. The app stays hardware-free; a thin
per-chassis profile does the binding.
This is the canonical model for the interaction layer. It refines the screen
list in 13 §3 (which named PERFORM/MIX/EDIT/BROWSE) into
the three views below, and builds on the three-layer parameter model in
13 §4.
1. Three views + a persistent header
Section titled “1. Three views + a persistent header”There are exactly three top-level views, chosen by hardware buttons and by an elegant switcher in the display header:
| View | Purpose | Driven by |
|---|---|---|
| PERFORM | The play surface — the control surface, rendered. Big, glanceable, distraction-free. What’s on screen most of a gig. | Touch and hardware (§2). |
| MIXER | Build & set up everything: per-slot plugin, level/pan/mute/solo, zone/split, inserts & sends, master/limiter, and arranging the PERFORM controls. | Touch. |
| SETTINGS | System: audio device, MIDI, the hardware profile, display, store/updates. | Touch. |
Header (every view): the view switcher (mirrors the hardware buttons + shows
the current view), the current patch name + setlist ◀ ▶, and a global readout
(voices / master meter). BROWSE — the full patch library + setlist editing — is
a header-invoked overlay, not a fourth view; patch navigation is always in
the header. A native plugin editor (16) is a
modal setup workbench, also not a core view.
One focused slot ties the views together: in MIXER you focus a strip to configure it; in PERFORM the surface’s controls follow that focused slot. Changing focus is the single gesture that changes “what am I playing/editing.”
2. The control-surface model — the core abstraction
Section titled “2. The control-surface model — the core abstraction”A control surface is an ordered, laid-out set of controls. Each control is:
Control = { kind: Knob | Fader | Button, target, label, behavior }- kind — how it renders and reads: a rotary Knob, a Fader, or a Button.
- target — the bindable destination (a deliberately small set):
PluginParam { slot, id }→Command::SetSlotParam(12);Strip { slot, Level | Pan | Mute | Solo }/Master { Level | Dim };Action { PatchNext | PatchPrev | Panic | FocusSlot(n) | View(v) }.
- behavior — range / taper / detents (continuous) or toggle / momentary (button).
PERFORM is a renderer of this model, and both touch and hardware are bindings
into it. The model lives in auvra-core (platform-free — it names no sdl3 /
i2c / plugin type). Turn a physical knob → its bound control moves → the target
updates → the on-screen control reflects it, and vice-versa.
Crucially, the physical hardware is wired to the PERFORM surface permanently. Selecting MIXER or SETTINGS changes what the display shows, not what the knobs do — so a performer can always reach the live controls even while glancing at the mixer. (Whether the knobs go inert when PERFORM is off-screen is a small policy choice — default: stay live.)
3. Two decoupled mappings
Section titled “3. Two decoupled mappings”The surface is defined by two mappings that are kept separate, which is what lets the same patch play on any chassis:
- Patch → controls — what is on the surface. Patch-owned
(
13§4, layer 1). Default: a consistent layout whose content follows the focused slot — knob 1 is always “focused slot, macro 1”, so muscle memory holds and only the values change per patch. A fully per-patch layout is a power-user override, not the default. - Hardware profile → physical bindings — which physical control drives which surface position. Per-chassis, lives in SETTINGS. Touch-only = the null profile (nothing bound; the screen is the whole surface) — which is also the Mac development path.
flowchart LR
subgraph SURFACE["Control surface (auvra-core)"]
direction TB
M["controls[]<br/>Knob / Fader / Button<br/>+ target + behavior"]
end
HW["Physical controls<br/>(06: pots, motor-faders, buttons)"]
CT["auvra-ctld<br/>(physical I/O only)"]
PROFILE["Hardware profile<br/>(app/core mapping)"]
TOUCH["Touch display<br/>(14: PERFORM renderer)"]
ENG["Engine<br/>(12: SetSlotParam, strip, master)"]
HW -->|"node state"| CT -->|"auvra-proto event"| PROFILE -->|"physical id -> position"| M
TOUCH <-->|"draw + touch"| M
M -->|"target -> command"| ENG
ENG -.->|"value / meter feedback"| M
M -.->|"target feedback"| PROFILE -.->|"auvra-proto output"| CT -.->|"LED ring, motor-fader"| HW
4. The three hard parts (design up front)
Section titled “4. The three hard parts (design up front)”- Pages / banks. A patch may expose more controls than the hardware has physical knobs. A button pages the physical controls across the surface; the touch display shows the whole surface at once. Build paging into the model, not as an afterthought.
- Feedback & takeover. Motor-faders (
06§5) track target values directly — no jump. Non-motor pots need soft takeover1 (the value doesn’t move until the physical position crosses it) plus LED-ring feedback to show the real value. Each control therefore tracks a physical-vs-actual delta. - Bidirectional sync. Touch → target → hardware (LED / motor-fader); hardware → target → touch. One model, pushed both ways, is the single source of truth.
5. Where it lives
Section titled “5. Where it lives”| Piece | Home |
|---|---|
| Control model + targets | auvra-core (platform-free). |
| Patch-owned control layout | extends the persist model (12 §3) — the patch stores its control assignments. |
| Hardware profile | SETTINGS / app config backed by the platform-free model. auvra-ctld reports physical ids and applies output feedback but knows no patch, surface position or engine target. |
| Physical I/O + IPC | auvra-ctld and auvra-proto (11). |
| PERFORM rendering | auvra-ui (14) draws the surface from the model. |
6. Phasing
Section titled “6. Phasing”| Phase | Scope | Status |
|---|---|---|
| Touch MVP (M3–M5) | Three views, focused-slot PERFORM controls, live audio/MIDI settings, parameter reassignment and MIDI learn | Done |
| Control transport (M6.1) | Versioned ctld/app IPC, physical ids/raw values, node status, LED/fader feedback and reconnect | Done |
| Target mapping (M6.2) | Abstract control state, hardware profiles, paging, takeover and bidirectional target mapping | Active |
| Power features | Fully custom per-patch layouts and an arrangement UI beyond the focused-slot default | Later |
M6 must reuse the existing semantic targets and engine commands. Hardware input
is another binding onto the same model, not a parallel UI or parameter path. The
end-to-end acceptance test is defined in 10.
7. Open questions [verify]
Section titled “7. Open questions [verify]”- Physical control inventory (knob / fader / button count) — unknown until the in-house hardware is designed. The whole point of this abstraction is to defer that decision without blocking the software.
- Scope of per-patch layout overrides, and the MIXER UI for arranging controls.
- Do the physical knobs go inert when PERFORM is off-screen, or stay live (default)? Decide alongside the physical inventory.
- Persistence format for the control layout — embedded in the patch, or a sidecar keyed by patch id?
Footnotes
Section titled “Footnotes”-
Soft takeover (a.k.a. pickup / catch): a non-motorized physical control does not change the target value until its position matches (crosses) the current value, preventing an audible jump when the hardware and the stored value disagree — after a patch switch, a page change, or a touch edit. ↩