memlnaut-nisps/docs/slp-workshop/SPEC.md
monkey-w1n5t0n 653ea0dbe5 docs(slp-workshop): spec output modes, gate sequences & config UX
Spec-only (firmware fork is WIP, built by another agent). Captures: memlcelium
is a single shared-net hybrid (14 ratio-seq + 42 synth outputs); the two-stream
model (Continuous + Rhythm); firmware 3 compile-time modes + browser's 4th
split-net hybrid; gate-sequence config for CV & MIDI (counts + shared/separate
net toggle; XY-pad→2nd pad / gamepad→double-stick); CV jack allocation
(11 CV + 3 gate, swap CV→gate); RatioSeq gate generation; no wire-protocol change.
2026-06-28 22:30:54 +02:00

191 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
Companion docs: `docs/useq-celium/protocol.md` (the CV wire protocol), the
manifold backends (`manifold/src/backends/`), the input layer
(`manifold/src/inputs/`).
---
## 1. Ground truth: what `memlcelium` actually is
`memlcelium` is the **hybrid**, and it is **one MLP**, not two:
- Net: `MLP<4, [10,14,18], 56>` (`nisps/engines/memlcelium.hpp`).
- The 56 outputs are split:
- `[0..13]`**sequencer**: 2 sequences × **7 ratio-seq params** each.
- `[14..55]`**synthesis**: 42 continuous params (Voice 0 + Voice 1).
- The single net therefore produces **both** the continuous values **and** the
RatioSeq parameters. An internal RatioSeq tick turns the seq params into note
triggers; the engine already exposes `pop_events()` so those triggers can be
consumed externally (MIDI / CV / gate) — that is the hook for gate outputs.
So the **two-separate-networks** hybrid is a genuinely different topology (two MLP
heads), which is why the hardware does the shared-net version and only the browser
(more compute, dynamic) does the split one.
### 7 ratio-seq params per track
`ratios[0..2]` (3), `phasor_mul` (1), `phase_off` (1), `amp_ratios[0..1]` (2) = 7.
(The April browser uSEQ-Celium used 8 — it added a pulse-width param. Use **7**
for firmware parity; pulse width can be an optional 8th later.)
---
## 2. The unifying model: two output STREAMS
Everything below collapses to two output streams, each driven by an MLP head:
| Stream | MLP outputs | Generates | Routes to |
|----------------|----------------------------------|----------------------|----------------------|
| **Continuous** | 1 value per channel | smooth 0..1 values | MIDI CC / CV jack |
| **Rhythm** | 7 params per gate-sequence track | clock-driven gates | MIDI note / gate jack|
A **mode** is just *which streams are active* and *whether they share a network*.
### How the Rhythm stream generates gates (RatioSeq)
Per gate-sequence track, each control tick (`nisps/engines/*` `ratio_seq_*`):
1. A shared **clock** advances a bar phasor (BPM-driven).
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
decide high/low).
4. `ratio_seq_2(amp_ratios)`**2-level velocity** (127 / 64).
5. Rising edge → note-on / gate-high (with velocity); falling edge → note-off /
gate-low.
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.
> 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
> drives the gates. (Implementation note — not built yet.)
---
## 3. Modes
### Firmware (SLP-Workshop) — compile-time, one chosen at build
| 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) |
### Browser (Manifold) — dynamic, switchable live
Same three **plus** a fourth:
- **Continuous & Rhythm (split nets)** — Continuous MLP + a separate Rhythm MLP.
Browser-only (two MLP heads; hardware uses one).
The browser mode is implied by the Outputs config (§4), not a separate picker:
choosing continuous-count > 0 and gate-sequences = 0 ⇒ Continuous-only;
gates > 0 with Shared net ⇒ hybrid-shared; gates > 0 with Separate net ⇒
hybrid-split; continuous-count = 0 ⇒ Rhythm-only.
---
## 4. Configuring gate sequences (Manifold, CV **and** MIDI modes)
The user can add **gate-sequence outputs** in both CV and MIDI modes. Two new
controls only; everything else is automatic.
### 4.1 The two output kinds
- **Continuous** → CC (MIDI) / CV jack (CV).
- **Gate sequence** → note (MIDI) / gate jack (CV). Each = one RatioSeq track.
**MIDI mode** — two independent steppers (capped by the model output budget):
```
Continuous (CC): [ 8 ]
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:
```
uSEQ jacks CV 8 · Gate 6
Gate sequences 3 ●───────────── 14
└ 3 are free (digital pins); each extra converts 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.
> 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
> digital pins (`docs/useq-celium/protocol.md`). The CV backend's `CvSpec` already
> lets any output target a CV *or* a gate channel — this extends it so PWM jacks
> can be gate targets too.
### 4.2 The Rhythm network (shown only when gate sequences > 0)
```
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.
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).
### 4.3 Per-output detail (advanced, optional)
In `BackendAdvanced` (full-depth modal), each output row can override the
defaults: which model output drives each CC/CV; which rhythm track drives each
note/gate; gate threshold (for continuous-derived gates); MIDI note#/channel; CV
polarity. Defaults (identity assignment) make this unnecessary for the common case.
---
## 5. Proposed UI summary (the "simple" target)
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.
3. The existing per-output rows (off/fixed/live, mute, arm, min/max/curve) +
the per-backend specifics (CC#, CV jack, note#) as today.
No mode dropdown for the rhythm/continuous split — the counts + toggle *are* the
mode.
---
## 6. Open questions / deltas to build later
- **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.
- **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.
- **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.