docs(slp-workshop): fold in operator review decisions

- HW = exactly 2 ratio sequences (memlcelium verbatim); browser = arbitrary N
  (each gate = own 7-param track), net reshapes; note 126-output WASM cap.
- Gates OPTIONAL in CV mode: slider 0-14 (was wrongly floored at 3); first 3
  use the free gate-only digital jacks, CV = gates<=3 ? 11 : 14-gates.
- Split-net input = per-input-channel engine routing (pad->2nd pad,
  gamepad->double-stick, MIDI->per-CC Continuous|Rhythm tag).
- Internal BPM clock; external-MIDI-clock sync + pulse-width + continuous
  velocity recorded as deferred niceties.
- Cross-ref the existing slp_workshop mode (workshop/synth-fw-audit).
This commit is contained in:
monkey-w1n5t0n 2026-06-28 22:29:03 +02:00
parent 653ea0dbe5
commit f822e4231f

View file

@ -1,14 +1,25 @@
# SLP-Workshop — output modes, gate sequences & config UX
**Status:** DRAFT / spec-only (2026-06-28). The SLP-Workshop firmware (a fork of
`memlcelium`) is being built by another agent and is WIP. This document specs the
mode model, the Manifold-side gate-sequence configuration, and a proposed UI so
the firmware and the browser app stay aligned. Nothing here is implemented yet.
**Status:** DRAFT / spec-only (2026-06-28). The SLP-Workshop firmware exists as
the `slp_workshop` mode (Synth Library Portland workshop; reuses the MEMLCelium
engine + MLP shape verbatim, foregrounds the Jolt / OU-noise learning gestures —
branch `workshop/synth-fw-audit`). The **three output modes** below are a planned
evolution: compile-time *slices* of that mode. This document specs the mode model,
the Manifold-side gate-sequence configuration, and a proposed UI so the firmware
and the browser app stay aligned. The output-mode slicing + Manifold config are
NOT implemented yet.
Companion docs: `docs/useq-celium/protocol.md` (the CV wire protocol), the
manifold backends (`manifold/src/backends/`), the input layer
(`manifold/src/inputs/`).
**Decisions locked (2026-06-28, operator):** (1) hardware keeps exactly 2 ratio
sequences (memlcelium verbatim); the browser may instantiate as many as the user
wants. (2) The 3 uSEQ gate-only jacks make gates *optional* — pure CV is valid;
if doing gates at all, use those 3 first, then convert CV jacks. (3) Split-net
input is per-input-channel engine routing (each pad/stick/CC tagged Continuous or
Rhythm). (4) Internal BPM clock now; external-MIDI-clock sync is a later nicety.
---
## 1. Ground truth: what `memlcelium` actually is
@ -51,7 +62,8 @@ A **mode** is just *which streams are active* and *whether they share a network*
Per gate-sequence track, each control tick (`nisps/engines/*` `ratio_seq_*`):
1. A shared **clock** advances a bar phasor (BPM-driven).
1. A shared **internal-BPM clock** advances a bar phasor. (External-MIDI-clock
sync is a desirable later addition, not a launch requirement — operator.)
2. `seq_phasor = (bar_phasor × phasor_mul + phase_off) mod 1`.
3. `ratio_seq_3(seq_phasor, ratios, pw=0.5)`**boolean gate** (the 3 ratios
carve the cycle into proportional segments; the phasor's segment + pulse-width
@ -64,6 +76,12 @@ So a gate is **clock + learned pattern**, not a threshold on a continuous value
this is what makes "Rhythm" a distinct stream, and why it needs its own 7-params
per track rather than one output per gate.
**Defaults = firmware parity** (pulse width fixed at 0.5; 2-level velocity). Two
deferred extensions, not blocking: a per-track **pulse-width** (the 8th param →
controllable gate length, matters for envelope vs trigger) and **continuous
velocity/accent** instead of the 127/64 two-level. Add later if the workshop
wants them.
> Where RatioSeq runs in the browser: when the active engine *is* the memlcelium /
> SLP WASM engine, consume `pop_events()`. For CV/MIDI modes that don't run that
> audio engine, a small **TS RatioSeq** fed by the Rhythm MLP's 7-params/track
@ -75,18 +93,24 @@ per track rather than one output per gate.
### Firmware (SLP-Workshop) — compile-time, one chosen at build
| Mode | Streams | Net | MLP output_size (≈) |
Always **exactly 2 ratio sequences** (memlcelium verbatim) — the firmware does not
vary sequence count.
| Mode | Streams | Net | MLP output_size |
|-----------------------|----------------------|-----|----------------------------|
| **Continuous only** | Continuous | 1 | continuous params only |
| **Continuous & Rhythm** (= memlcelium) | both, **shared** | 1 | 14 seq + 42 synth = 56 |
| **Rhythm only** | Rhythm | 1 | 7 × n_tracks (e.g. 14) |
| **Rhythm only** | Rhythm | 1 | 2 × 7 = 14 seq params |
### Browser (Manifold) — dynamic, switchable live
Same three **plus** a fourth:
Same three **plus** a fourth, and with **as many ratio sequences as the user wants**
(each gate sequence = its own independent 7-param track; the net reshapes to suit):
- **Continuous & Rhythm (split nets)** — Continuous MLP + a separate Rhythm MLP.
Browser-only (two MLP heads; hardware uses one).
Browser-only (two MLP heads; hardware uses one). `output_size`: shared net =
`continuous + 7 × n_gates` (mind the 126-output WASM cap → ~16 gates max shared);
split net = the Rhythm net is sized independently, so it scales further.
The browser mode is implied by the Outputs config (§4), not a separate picker:
choosing continuous-count > 0 and gate-sequences = 0 ⇒ Continuous-only;
@ -113,16 +137,20 @@ Gate sequences: [ 2 ] each → a rhythm track → note on/off
```
**CV mode** — hardware is fixed (11 PWM/CV-capable + 3 digital/gate-only), so the
two counts are *linked*. One control:
two counts are *linked*. Gates are **optional** — pure CV (0 gates) is valid. One
control:
```
uSEQ jacks CV 8 · Gate 6
Gate sequences 3 ●───────────── 14
3 are free (digital pins); each extra converts a CV jack
uSEQ jacks CV 11 · Gate 0
Gate sequences 0 ●───────────── 14
first 3 are FREE (the gate-only digital jacks); the 4th+ convert a CV jack
```
Rule: `gates ∈ [3, 14]`, `CV = 14 gates` (so CV ∈ [0, 11]; gates=3 ⇒ CV=11).
"Add more gates by swapping CV outs to gate outs" = dragging this up past 3.
Rule: `gates ∈ [0, 14]`; `CV = gates ≤ 3 ? 11 : 14 gates`. So the first 3 gate
sequences land on the dedicated gate-only jacks and cost no CV (gates 0→11 CV;
3→11 CV; 6→8 CV; 14→0 CV). "Add more gates by swapping CV outs to gate outs" only
kicks in past 3. (If you want gates at all, those 3 gate-only jacks are there to
use; if you don't, they sit idle and you keep all 11 CV.)
> Wire-protocol impact: **none**. A CV jack acting as a gate just carries 0/full
> (or the 2-level velocity) in its `u16` slot; the 3 dedicated gate bits stay the
@ -136,20 +164,25 @@ Rule: `gates ∈ [3, 14]`, `CV = 14 gates` (so CV ∈ [0, 11]; gates=3 ⇒ C
Rhythm network: ( Shared ) ( Separate ◀ default )
```
- **Separate** (default): the Rhythm stream gets its **own MLP**. The input is
then automatic, following the active input source:
- **XY pad (internal)** → a **second on-screen XY pad** appears (the Rhythm
pad); the first drives Continuous.
- **Gamepad** → the controller switches to **double-stick**: **left stick →
Continuous**, **right stick → Rhythm**.
- **Shared**: one MLP drives both streams; a **single** input source feeds it
(one pad / one stick). This is the hardware-parity hybrid.
- **Separate** (default): the Rhythm stream gets its **own MLP**, so each input
channel is **routed to one of the two engines** (Continuous or Rhythm). The
routing is automatic per source kind:
- **XY pad (internal)** → a **second on-screen XY pad** appears; pad 1 →
Continuous, pad 2 → Rhythm.
- **Gamepad****double-stick**: left stick → Continuous, right stick → Rhythm.
- **MIDI controller****per-CC routing**: each learned CC has a small
`Continuous | Rhythm` toggle, so the user assigns which knobs drive which
engine. (The pad/gamepad cases are just the pre-grouped 2-axis versions of
this same per-channel routing.)
- **Shared**: one MLP drives both streams; **all** input channels feed the single
net (one pad / one stick / all CCs). This is the hardware-parity hybrid.
That is the whole decision surface: **two numbers + one toggle.** The second
pad / double-stick is a consequence shown as an explanatory line, not a separate
control. It reuses the existing input layer (`InputSource.axisCount()` already
supports gamepad single↔double-stick; "Separate" composes a second `xy-pad`
source or flips the gamepad to 4-axis).
pad / double-stick / per-CC tag is a *consequence* of Separate, surfaced inline,
not a separate mode control. It reuses the existing input layer
(`InputSource.axisCount()` already supports gamepad single↔double-stick; per-axis
labels exist via `axisLabels()`) — "Separate" adds an engine tag per input axis
and composes a second `xy-pad` source / flips the gamepad to 4-axis as needed.
### 4.3 Per-output detail (advanced, optional)
@ -165,9 +198,9 @@ polarity. Defaults (identity assignment) make this unnecessary for the common ca
Outputs panel, top to bottom:
1. **Output kinds**`Continuous [n]` + `Gate sequences [n]` (MIDI), or the
linked `Gate sequences 314` slider with the live `CV n · Gate n` readout (CV).
2. **Rhythm network** toggle `( Shared | Separate )` + the one-line input
explainer — only when gate sequences > 0.
linked `Gate sequences 014` slider with the live `CV n · Gate n` readout (CV).
2. **Rhythm network** toggle `( Shared | Separate )` + the inline input surface
(2nd pad / double-stick / per-CC engine tags) — only when gate sequences > 0.
3. The existing per-output rows (off/fixed/live, mute, arm, min/max/curve) +
the per-backend specifics (CC#, CV jack, note#) as today.
@ -178,14 +211,21 @@ mode.
## 6. Open questions / deltas to build later
Build deltas (when we proceed):
- **Manifold engine**: today one MLP head on the spine. "Separate" needs a second
Rhythm MLP head + a second input slice. Scope: engine + input-layer change.
Rhythm MLP head + per-input-axis engine routing. Scope: engine + input-layer.
- **TS RatioSeq**: a browser-side RatioSeq (or a `pop_events()` bridge) to turn
Rhythm-MLP params into gate/note events for the CV & MIDI backends.
Rhythm-MLP params into gate/note events for the CV & MIDI backends. 2 sequences
on firmware; arbitrary count in the browser.
- **MIDI backend**: gate sequences → note on/off (it currently only sends CC).
- **CvSpec**: allow PWM jacks (cv1..cv11) to be gate targets (velocity gate),
with the digital pins still the always-on 3.
- **Reshape**: the dynamic browser net's `output_size` = continuous_count +
7 × gate_sequences (shared) — confirm against the reshape/reset-on-reshape flow.
- **Firmware output_size** for "Continuous only" / "Rhythm only" — the other
agent's call; this doc only fixes the *shapes*, not exact counts.
- **CvSpec**: allow PWM jacks (cv1..cv11) to be gate targets (incl. velocity-CV),
with the 3 digital pins as the first-used gate jacks.
- **Reshape**: shared-net `output_size` = continuous + 7 × gate_sequences (≤126
WASM cap) — confirm against the reshape/reset-on-reshape flow.
- **Firmware output_size** for "Continuous only" / "Rhythm only" — slices of the
`slp_workshop` mode (`workshop/synth-fw-audit`); the firmware agent's call. This
doc fixes the *shapes*, not exact counts.
Deferred niceties (explicitly not blocking): external-MIDI-clock sync; per-track
pulse-width (gate length); continuous velocity/accent beyond 127/64.