docs(phase-s): spec three-view editor redesign (S-VIEW) — Sample/Browse/Zone

This commit is contained in:
2026-07-27 12:46:43 -04:00
parent c22a78bcba
commit 13b23801a0
3 changed files with 530 additions and 147 deletions
+290 -27
View File
@@ -2139,6 +2139,274 @@ not a raw literal). What S18 adds is only the missing *plugin identity* layer.
headers before use — several §1a claims are experienced estimates until the spike
confirms them.
## Editor view-model redesign — three views: Sample / Browse / Zone (S-VIEW; Daniel, 2026-07-27, r9)
> **Additive sub-phase of Phase S — an *editor* redesign, not an engine change.** Re-partitions
> the ReaSampler 9000 editor from today's two-view toggle (Browser + Zones) into a **three-view
> model where the loaded sample is the home**, adds two new performance parameters (key-tracking,
> preview velocity) and two visual components (envelope overlay, real piano-key pattern), and
> frames two engineering prerequisites (drop-to-FX bug, default window size). The S3 voice
> engine, keymap resolution, and read-only-over-bank contract are **unchanged**; the
> component-state format extends additively for key-tracking. Product framing: `docs/product/
> midi-playback.md` §Addendum r9. Same standing discipline: **LICE/SWELL drawing only,
> all layout/hit-test in pure geometry modules, RT-safe, VST3 class UID unchanged, verify every
> API name/signature against the vendored headers before use.**
### The view model — Sample is home; Browse is modal; Zone is a dedicated surface
The three views are **not a flat three-way toggle** (today's Browser|Zones segmented switch is
retired). The model is **document-with-modal-picker** (mirror: Ableton Simpler — the device face
*is* the loaded sample; loading a new one is a distinct act):
- **Sample (the home / default face).** What the editor shows on open with a capture loaded. The
hero waveform, the envelope overlay, all per-sample tuning controls, the preview-trigger. This
is where the user lives.
- **Browse (a modal page layered over Sample).** Summoned by a **Browse button** (and, when
nothing is loaded, the empty-state's primary affordance — Browse must be *very* easy to open
when there is no capture selected). It renders as a **full-window overlay** over the Sample
face — filters + captures grid + **select-then-confirm** to change the loaded sample, then
dismisses back to Sample. It is a picker sheet, not a peer tab.
- **Zone (a dedicated editing surface).** Opened by its own **Zone button** when the user wants
to map the capture(s) across the keyboard. Not shown by default ("most of the time zones won't
be used" — the S10 reframe). Returns to Sample on close.
**Navigation contract.** From Sample: a Browse button opens the Browse overlay; a Zone button
opens the Zone surface. From Browse: select a card + confirm (or cancel) returns to Sample with
(or without) a new loaded capture. From Zone: a close/back affordance returns to Sample. The
empty state (no capture loaded) surfaces Browse as its dominant call-to-action. **The fresh
instance stays silent with a "pick a capture" empty state (S10 reversal, unchanged).**
### View 1 — Sample (the new main view)
The home face. Composition follows the reference devices (Simpler / Phase Plant): a **hero
waveform up top with the envelope drawn over it at accurate time**, a **dense labelled
value-strip beneath**, **root fenced as its own affordance**, and a **preview cluster**. Bands,
top to bottom:
- **Title band.** Plugin name (channel-derived `vstPluginName()`) + live readout (loaded capture
name / `[pick a capture]` / `[bank empty]` / `[host: no bridge]`) + the **Browse** and **Zone**
view buttons. (Inherits today's title-band text logic.)
- **Hero waveform band (enlarged) with the ENVELOPE OVERLAY (new).** The picked capture's
full-resolution envelope (from `peaks` over the cached mono PCM — no new decode), given real
vertical space (the Simpler/Phase-Plant hero, materially taller than today's 72px strip). Drawn
over it, at **accurate wall-clock time**: (a) the **S11 markers** — start (teal/secondary),
loop start/end (purple/tertiary), the faint loop-region fill — moved here from Browse
unchanged; and (b) the **NEW amplitude-envelope overlay** — the AHDSR shape (Gate) or the
fade-in/%-length/fade-out shape (Trigger) traced as a curve across the sample at the same time
base the voice engine uses, in an accent hue. This is the "reads like a real sampler, not a
spreadsheet" move: the envelope becomes a *shape over the sound*, not four abstract sliders.
- **Root + preview cluster (fenced, new preview-trigger).** Root note shown as a first-class,
always-visible control (fenced like Phase Plant's "Root" box), draggable/typeable — this is the
keyboard-strip root-drag from today's Browse setup, promoted to a fenced control. Adjacent: the
**NEW preview-trigger button** (fires the sampler at the loaded capture's root note through the
live voice engine, off the audio-thread commit path — no MIDI controller needed) with an
**adjacent velocity knob** setting the preview velocity level. The **Mono/Stereo toggle** moves
here from Browse (it is a per-capture output-mode tuning concern, not a choosing concern).
- **The "Modes-and-down" control strip (moved from Zone).** Every per-sample control that lives
on today's Zones param panel for the single-capture case — **Mode** (Gate/Trigger), **Pitch
engine** (Varispeed/Preserve), the **AHDSR** sliders (Gate) or **Length%/Fade-in/Fade-out**
(Trigger), and the **AD pitch envelope** (Off/On + P.Attack/P.Decay/P.Depth) — renders here as
the Sample view's control strip, laid out as a dense labelled value-row (bold micro-caps over a
value/slider, the reference grammar). For a single loaded capture this is the same one-zone
storage site the S15-F2 lean already established (the empty-map single-capture face reads/writes
the same `PerformanceZone` defaults) — no new storage.
**Browse elements that MOVE to Sample (inventory — nothing silently dropped):** the large
waveform preview, the S11 start/loop markers + loop fill, the keyboard-strip root affordance (now
the fenced root control), and the Mono/Stereo toggle.
### View 2 — Browse (reduced to *choosing*, laid over Sample as a modal)
Browse's only job is **pick a capture**. Today's Browser view is close but overloaded; it is cut
to the choosing essentials and rendered as a modal overlay:
- **Kept (the excellent core, unchanged):** the **type-to-filter search box**, the **bank filter
tabs** (All + one per named bank), the **captures grid** (cards: peak thumbnail + name +
root/key badge), **scroll** (wheel + thumb), and card **selection**.
- **Added:** a **confirm/cancel** affordance (select a card, confirm to load it into Sample and
dismiss; cancel to dismiss unchanged) — the modal-picker close semantics. Double-click-to-load
is the natural accelerator.
- **Removed from Browse (moved to Sample or cut):** the **large waveform preview** (redundant —
the grid thumbnails already show every capture's waveform; moved to Sample as the hero); the
**Mono/Stereo toggle** (moved to Sample); the **root-note keyboard-strip** in the setup band
(moved to Sample as the fenced root control); the **loop-point labels** and the **track-root
message** (cut — they waste space and add nothing to *choosing*).
**Rationale (why cut, not keep-and-hide):** Browse is a picker. Every tuning affordance on it is
a mode error — you tune what you *have*, you browse for what you *want*. Concentrating tuning on
Sample and choosing on Browse makes each view do one job, and makes the modal overlay light
enough to summon and dismiss without ceremony.
### View 3 — Zone (dedicated keyboard-mapping surface + key-tracking + real piano pattern)
Zone is the multi-zone keymap editor — RS5K structurally lacks a multi-zone-in-one-instance view,
so this is a genuine capability, kept as the deliberate secondary surface. Retains today's Zones
view wholesale, with two changes:
- **Kept (inventory — nothing silently dropped):** **+ Add Zone** / **Delete** buttons, the
**keyboard strip with one bar per zone** (selected zone lit accent-primary + static glow, others
categorical), the **selected-zone legend** with the three **Low/High/Root numeric-entry fields**
(click-to-type, `parseNoteEntry`), and the **per-zone param control panel** (Mode, Pitch engine,
AHDSR/Trigger, AD pitch envelope) — the same panel Sample now also hosts for the single-capture
case (one storage site, two surfaces).
- **NEW — key-tracking parameter (0%200%, default 100%).** A per-`PerformanceZone` scalar on how
sample pitch tracks the keyboard around the root note. **100% = standard 12-tone-ET tracking**
(today's behavior, bit-identical); **0% = no tracking** (the sample plays at root pitch on every
key — a fixed one-shot); **200% = double-rate tracking**. Additive on `PerformanceZone`,
version-bumped, defaulting to 100% so pre-existing zones are unchanged. Applies inside the
repitch math in **both** engines: under **Varispeed** it scales the semitone offset feeding
`pitchRatio(note, root)` (`effectiveSemis = (note root) · keyTrack`); under **Preserve** it
scales the semitone offset feeding `set_shift(2^(effectiveSemis/12))`. Surfaces as a control in
the Zone param panel (and, for the single capture, on the Sample control strip). **The pure
sampler core owns the key-track math** (unit-tested: a known note/root/keyTrack triple asserts
the expected ratio); the shell only maps the 0200% control to the scalar.
- **NEW — real piano-key pattern on the keyboard strip.** Today's spectral strip is pretty but
does not read as a keyboard (Daniel's note). Keep the pastel spectral hue as the backdrop, but
**overlay the actual alternating white/black key pattern** — bright cells for naturals, dark
cells for accidentals (C#/D#/F#/G#/A#), per the palette (bright ≈ a light neutral, dark ≈
`bg/base`/hairline) — so the strip is instantly identifiable as a keyboard. It need not be
*shaped* like a keyboard (no protruding black keys); it carries the **pattern** as an overlay,
so a glance reads pitch position without counting. The pure `keyboard_strip` geometry gains a
`isBlackKey(note)`/per-key-natural query (12-tone pattern, pure + unit-tested); the shell draws
the two-tone overlay over the spectral fill. This same strip serves the Sample view's fenced
root affordance (one keyboard grammar everywhere).
### New parameters — ownership and persistence (D-B; instrument-owned, never bank facts)
- **Key-tracking** — per-`PerformanceZone`, additive/version-bumped component state, default 100%.
A **performance choice**, never written to `Sample` or the bank. Back-compat: an absent field on
an older blob → 100% (bit-identical playback).
- **Preview velocity** — a **utility** setting for the Sample view's preview-trigger button, not a
musical parameter of the capture. **Lean: transient editor state** (like a metronome level) —
it does not need to persist in component state, so it adds no version bump and no bank-fact risk.
*(Fork S-VIEW-F1, Daniel's call: transient vs. a small persisted editor convenience. Lean
transient — simplest, no compat surface, and a preview level is not something users expect to
survive a reload.)*
### The envelope overlay — visual component (new)
The amp envelope drawn as a curve over the Sample view's hero waveform at accurate wall-clock
time (the Simpler/Phase-Plant grammar). Gate → the AHDSR shape (attack ramp, hold, decay to
sustain, release tail); Trigger → the fade-in/unity/%-length/fade-out shape anchored to `playEnd`.
The time base is the same the voice engine resolves (seconds → frames at the live rate), so the
drawn shape lines up with the waveform under it. **Pure geometry:** an `envelope_overlay` module
(mirror of `waveform_view` / `param_slider`) maps the AHDSR/Trigger params + the sample's
frame-length to a polyline in the waveform rect (`param↔pixel` at the shared time base),
unit-tested against known param sets; the shell traces it via kit line draws in an accent hue.
Decorative + informative — it never captures input (the sliders remain the edit surface); dragging
the envelope *nodes* directly is a deferred stretch (fork S-VIEW-F2, lean defer — sliders first,
node-drag is polish once the overlay reads).
### The real piano-key pattern — visual component (new)
Covered under View 3 above. Pure: `keyboard_strip` gains the natural/accidental predicate; shell
draws the bright/dark overlay over the existing pastel spectral fill. Shared by the Zone strip and
the Sample root affordance.
### Engineering prerequisite 1 — drop-to-FX bug (routed to implementation, NOT a design call)
**Symptom (Daniel):** dropping a capture onto a track's FX chain does not instantiate + init
ReaSampler 9000 — the audio-to-arrange drop works, but the "instrument init never fires."
**SDK sweep (done this pass — no false ceiling asserted):** the S17 drop-to-FX machinery is
**present and SDK-correct**. `src/instrument_drop_win.cpp` calls `TrackFX_AddByName(track,
"VST3:" + vstPluginName(), /*recFX*/false, /*instantiate*/-1)` then injects component state via
`TrackFX_SetNamedConfigParm(track, fx, "vst_chunk", <base64>)`; both signatures + semantics are
**verified present** in `reaper_plugin_functions.h` (the `VST3:` prefix, negative-instantiate =
always-new, and the `vst_chunk` base64 write-parm are all documented). The gesture is wired
(`src/bank_panel.cpp`: `decideGesture``InstrumentDrop` → on release `buildInstrumentDropChunk`
`performInstrumentDrop`), FX-hotspot detection uses `GetThingFromPoint` (`fx_*` prefix), and the
factory registers `kInstrumentSynthSampler` correctly. **So the platform CAN do this — the
capability is not in question; the wiring is present.** This is therefore a **runtime diagnosis
task, not a design decision or a platform-ceiling problem.** The likely fault domains for
staff-engineer to investigate (in order):
1. **FX-name resolution:** whether REAPER's scanned name for the plugin exactly matches `"VST3:" +
vstPluginName()` (a display-name/scan-name mismatch would make `TrackFX_AddByName` return 1).
2. **The gesture reaching `performInstrumentDrop`:** whether `decideGesture` actually resolves
`InstrumentDrop` over an FX button (vs. falling through to `OsDrag`, which produces exactly the
observed "drops as audio into arrange" symptom) — check the `overFxHotspot`/`GetThingFromPoint`
path and the single-capture arming guard.
3. **The `vst_chunk` write / blob format:** whether `TrackFX_SetNamedConfigParm` returns true and
the injected blob matches what `setState` accepts (a format drift would create-then-play-nothing
rather than fail to instantiate).
**Routing:** flagged for **staff-engineer** as a bug diagnosis. No product fork; no redesign
gates on it, but it is a **functional prerequisite** for the drop-to-FX gesture to feel finished.
**Verify in DAW:** drag a capture onto a track's FX button → a ReaSampler 9000 instance appears on
that track already playing that capture (one Ctrl-Z removes it).
### Engineering prerequisite 2 — default window size for 1080p (routed to implementation)
**Symptom (Daniel):** the editor window is too small by default; assume a 1080p minimum screen.
**SDK sweep (done this pass — mechanism verified):** the VST3 editor size is set by the
`IPlugView`/`CPluginView` contract. `getSize()` returns the view's `rect` (set via `setRect` — the
default the host opens at); `checkSizeConstraint()` is where a minimum is enforced; `onSize()`
handles host resizes; `canResize()` already returns `kResultTrue`. **Verified in
`vendor/vst3sdk/public.sdk/source/common/pluginview.h`.** Today `ReaSamplerEditor`'s constructor
sets `ViewRect(0, 0, 560, 400)` — the undersized default. **The fix is a one-line default change**
(a larger initial `ViewRect` sized for the new three-band Sample face on a 1080p display) **plus
an optional `checkSizeConstraint` minimum** so the host cannot shrink the window below a usable
floor. **This is not a platform limitation — the mechanism exists and is trivial.** The exact
default dimensions are a build-time value to set against the Sample face's band heights (hero
waveform + control strip want materially more than 400px tall; a ~840×560 or larger default is the
starting point, tuned at build). **Routing:** flagged for **staff-engineer**; no product fork.
**Verify in DAW:** the editor opens at the new default on a 1080p screen showing the full Sample
face without scrolling, and cannot be resized below the constraint floor.
### Module architecture (preserve the pure/shell split)
- **Pure (new/extended):** `envelope_overlay` (AHDSR/Trigger params + frame-length → polyline in a
rect; unit-tested); `keyboard_strip` extended with the natural/accidental predicate; the sampler
core extended with the **key-track scalar** in the repitch math (unit-tested against known
note/root/keyTrack → ratio); `sample_map` (`PerformanceZone`) extended with the additive
`keyTrack` field + component-state version bump + back-compat default.
- **Shell (`reasampler_editor.cpp`):** re-partition the paint/hit-test into the three views
(Sample face, Browse modal overlay, Zone surface) replacing the two-view toggle; add the
preview-trigger button + velocity knob wired to an off-audio-thread preview note through the
voice engine; draw the envelope overlay + piano-key overlay via the kit; set the larger default
`ViewRect` + `checkSizeConstraint`. All layout/hit-test math stays in the pure geometry modules.
### Precision / invariant implications
- **Read-only bank consumer (unchanged).** Key-tracking, preview velocity, and every marker/mode
control are the instrument's **performance map** (D-B) — never written to `Sample` or the bank.
- **Additive, back-compat component state.** `keyTrack` is a new optional field defaulting to 100%;
an older blob (absent field) restores to 100% → **playback bit-identical** under the same engine.
No existing field changes.
- **RT discipline (unchanged).** The preview-trigger fires a note through the existing voice engine
via the off-audio-thread commit path (`commitMapAndReload` idiom); no new `process`-thread work,
no allocation on the audio thread.
- **VST3 class UID unchanged.** A view reorganization + additive param is **not** a compat event;
saved instances rebind and restore. The UID stays the S-NAME-1/S18 forever-locked identity.
- **Capture ≠ placement ≠ playback (unchanged).** The preview-trigger plays; it never captures,
never inserts a timeline item. The three acts stay distinct.
### Open questions / forks (Daniel / Phase S team)
- **S-VIEW-F1 — preview velocity persistence.** Transient editor state (lean) vs. a small persisted
editor convenience. Lean transient — no compat surface, no version bump.
- **S-VIEW-F2 — envelope-overlay interactivity.** Read-only informative overlay + sliders as the
edit surface (lean, ship first) vs. draggable envelope nodes on the overlay (deferred stretch —
polish once the overlay reads).
- **S-VIEW-F3 — Browse modal presentation.** Full-window overlay (lean — the modal-picker feel) vs.
a large centered sheet with a dimmed Sample behind. Presentation detail; either satisfies "easy
to summon and dismiss."
### Must-verify before build (S-VIEW)
- **Editor size mechanism** — `getSize`/`setRect`/`checkSizeConstraint`/`onSize`/`canResize`
**verified present** in `vendor/vst3sdk/public.sdk/source/common/pluginview.h`; confirm the exact
min-size enforcement point (`checkSizeConstraint`) behaves under REAPER's host at build.
- **Drop-to-FX** — `TrackFX_AddByName` / `TrackFX_SetNamedConfigParm` / `GetThingFromPoint` all
**verified present** in `reaper_plugin_functions.h`; the remaining work is **runtime diagnosis**
(name match, gesture routing, blob format), not an SDK question.
- **Preview note through the voice engine off-thread** — confirm the existing `commitMapAndReload`
/ off-thread reload idiom is the right seam to fire a one-shot preview note without touching
`process` on the UI thread; no torn state on the atomic voice-engine pointer.
- **Envelope-overlay time base** — confirm the seconds→frames resolution the overlay draws against
matches the voice engine's live-rate resolution so the drawn shape lines up with the waveform.
---
# Look-and-feel — visual design language (Phase L)
@@ -2520,12 +2788,10 @@ slots that persist independent of their occupant, no slot actions, no MIDI-binda
no capture-to-slot-N. A slot is just a display position a sample occupies; dragging cards
rearranges which sample sits where.
**M9 overlap (awareness note — unchanged intent).** M9 "slots" (capture-to-slot-N /
insert-slot-N, MIDI-bindable, MPC-style) remains **explicitly deferred (Daniel, 2026-07-26).**
The interchangeable substrate L7 builds still *eases* a future M9 revival — it lays the
addressable-position groundwork M9 would sit on — but L7 adds **no** slot-numbered capture/insert
actions and **no** MIDI bindings. The "plain vs. M9-shaped" sub-fork is closed: plain
gap-preserving substrate, per F2 above.
**M9 note.** M9 "slots" (capture-to-slot-N / insert-slot-N, MIDI-bindable, MPC-style) is
**abandoned (Daniel, 2026-07-27)** — will not be built. L7 adds **no** slot-numbered
capture/insert actions and **no** MIDI bindings. The "plain vs. M9-shaped" sub-fork is closed:
plain gap-preserving substrate, per F2 above.
### 2. Decorative metadata overlay (bars.beats · s.ms)
@@ -2656,20 +2922,18 @@ overlay/selection-border draw. The L1 kit draws; no palette/font decision re-ope
## The L3 gate + Phase S coordination contract
**L3 (VST editor + embed-strip restyle) is GATED on Phase S landing on dev.** The VST
editor (`IPlugView` LICE surface), the S6 embed strip, and the keyboard strip live in
Phase S, which is **not on dev yet** (it exists on the phase-s worktree). L3 cannot be
built on dev until Phase S's editor/embed surfaces (≈ S1 / S6 / S10) merge to dev — **the
Phase L team must not chase these files on dev; they are not there.** Until then L3 is a
planned, blocked point; L1, L2, L4, L5, L6, and **L7 have all landed** — see `COMPLETED.md`.
L4, L5, L6, and L7 are independent of the L3 gate.
**L3 (VST editor + embed-strip restyle) has landed (merged `c53683e`, 2026-07-27).** The
Phase S surfaces (`reasampler_editor.cpp` + `reasampler_embed.cpp`) arrived on dev drawing
flat `LICE_FillRect` blocks + raw GDI `DrawTextA`, off a local pre-L1 forest-green palette
— the coordination contract's "born in the kit" branch did not occur — so L3 performed a
full restyle of both draw shells through the L1 kit. **L1, L2, L3, L4, L5, L6, and L7
have all landed — see `COMPLETED.md`. Phase L is complete.** The Phase Q gate condition
"Phase S + L3 merged to dev" is now satisfied.
**Coordination contract (load-bearing):** Phase S's S10S13 build their interaction UX with
the **current** drawing and **adopt the L1 kit when it is available — they are NOT gated on
Phase L.** Whichever lands first (the L1 kit or the S10S13 UX), the kit is the **one source
of drawing**: if S10S13 reach dev before L1, they draw in the current language and L3
restyles them; if L1 lands first, they are born in the kit. Either way there is one kit and
one look; L3 completes the VST/embed adoption and applies the settled-and-revised **B +
**Coordination contract (load-bearing — resolved):** Phase S's S10S13 built their
interaction UX with the current drawing and adopted the L1 kit via L3 (the coordination
contract's "L3 restyles them" branch). There is now one kit and one look across both
artifacts; L3 completed the VST/embed adoption and applied the settled-and-revised **B +
three-accent pastel** treatment (with C's **pastel** spectral keyboard strip as the
signature surface), routing text through the kit's cached-font `text()` (§3.1 — the kit's
current face; no font change). The VST3 class UID is unchanged — a visual refresh is not a
@@ -2925,8 +3189,9 @@ and **(2) Phase L L3** merged to dev (the VST restyle, itself gated on Phase S).
have already landed** (see `COMPLETED.md`) — the earlier "L2 + L3" wording was stale and is
corrected to **L3 only**. **D2** is functionally complete (D2-W1..W3-B landed; its lone open item,
a per-track lane-split panel indicator, is *explicitly deferred*, not a blocking residual). **M9**
(slots) is *explicitly deferred* (Daniel, 2026-07-26), not scheduled work. D2 and M9 are named here
only so that *reactivating* either re-arms the quiescence condition; neither blocks the gate today.
(slots) is **abandoned** (Daniel, 2026-07-27) — will not be built. D2 is named here only so that
*reactivating* its deferred panel indicator re-arms the quiescence condition; neither D2 nor M9
blocks the gate today.
*Why:* Phase Q touches **nearly every file in `src/`** (relocate, re-namespace, split the four
largest TUs, plus the §2b renames); every large in-flight branch (Phase S on its worktree, L3 once
it lands) is diffed against the *current flat layout*, so landing a rename-and-relocate-everything
@@ -2935,10 +3200,8 @@ re-resolution, not a linear one. Phase Q is *last* precisely because it reshapes
other pillar stands on: landing it early taxes every subsequent phase; landing it last taxes
nothing. Re-confirm quiescence against dev before Q-W1.
> **M9 disposition (Daniel-decision note).** "Deferred indefinitely" ≠ "abandoned." Immaterial to
> the gate (both clear it); matters only if M9 is reactivated — *before* Phase Q it lands cheaply on
> the flat layout, *after* it is authored against the reorganized tree. Surfaced, not silently
> resolved (full note: `docs/product/code-organization.md` §4).
> **M9 disposition — resolved (Daniel, 2026-07-27): abandoned.** M9 is out; it will not be
> reactivated. The gate remains satisfied; no re-arm condition applies.
## Wave sequencing (each independently landable, CTest-green at every step)
@@ -2991,7 +3254,7 @@ every commit), a property only an *incremental* reorg uses. Risk-ordered:
the thumbnail path is noted but **not touched** — reworking it risks the hot path.
- **No big-bang commit.** Every wave is independently landable and CTest-green; reject a change set
that cannot be verified at each step.
- **Do not begin before the GATE.** Re-confirm the tree is quiescent (Phase S + L + D2 + M9
merged/closed) before any Q point.
- **Do not begin before the GATE.** Re-confirm the tree is quiescent (Phase S + L + D2 merged/closed;
M9 abandoned) before any Q point.
- **Verify** the CMake `src/` path updates and the SWELL/LICE surfaces still resolve after
relocation, as the existing build already requires.
+127 -120
View File
@@ -16,8 +16,7 @@ it here and appends it to `COMPLETED.md`.
## Milestone 9 — slots (MPC-style)
> **Explicitly deferred (Daniel, 2026-07-26) — not silently dropped. Can be picked
> up later if wanted.** An extra, not scheduled work.
> **Abandoned (Daniel, 2026-07-27) — will not be built.**
**Goal:** "Capture to slot N" / "insert slot N", MIDI-bindable. CONTEXT.md
Build order 9.
@@ -177,6 +176,99 @@ is **ReaSampler 9000**. Framing + propagation surfaces:
*cut* (choke) on note-off or on a same-group re-trigger (hi-hat open/closed). Deliberately
out of S15 scope (Trigger ignores note-off entirely there); a Tier-3 choke-group direction.
## Phase S — editor view-model redesign (three views: Sample / Browse / Zone)
> **Additive Phase S sub-phase (S-VIEW; Daniel, 2026-07-27, r9).** Re-partitions the ReaSampler
> 9000 editor from a two-view toggle into a **three-view model where the loaded sample is the
> home** — Sample (default face), Browse (modal picker over Sample), Zone (dedicated keymap
> surface). Adds two performance parameters (key-tracking, preview velocity) and two visual
> components (envelope overlay, real piano-key pattern), and frames two engineering prerequisites
> (drop-to-FX bug, default window size). An **editor** redesign — the S3 voice engine, keymap
> resolution, and read-only-over-bank contract are **unchanged**; component state extends
> additively for key-tracking; VST3 class UID unchanged. Authoritative spec: **CONTEXT.md §Phase
> S — editor view-model redesign (S-VIEW)**. Product framing: `docs/product/midi-playback.md`
> §Addendum r9. When a point lands, doc-keeper moves it to `COMPLETED.md`.
>
> **Depends on Phase S being on dev** (this redesigns Phase S editor shells —
> `reasampler_editor.cpp` + the pure `src/vst/` geometry modules). The Phase L L3 restyle and
> this S-VIEW redesign both touch the editor; sequence so they do not collide (S-VIEW is a
> layout/param change, L3 is a palette/kit change — best landed as one editor pass or S-VIEW then
> L3-restyle-the-new-layout).
**Goal:** A three-view editor that makes "pick a capture, tune it, play it" fast, easy, and fun —
Sample is home, Browse is an easy-to-summon modal picker, Zone is the deliberate keymap surface.
**Verify (in DAW):** open on a 1080p screen → full Sample face (hero waveform + envelope overlay +
fenced root + preview-trigger + control strip) with no scroll; Browse opens as a modal over
Sample, select+confirm loads a new capture; Zone opens on its own button, key-tracking + the
piano-key pattern work; drop-a-capture-onto-FX instantiates a playing instance.
- [ ] **S-VIEW-1 — three-view navigation model.** Retire the flat Browser|Zones toggle; introduce
Sample (home/default), Browse (modal overlay over Sample, select+confirm), Zone (dedicated
surface, own button). Empty state surfaces Browse as the dominant call-to-action. Fresh instance
stays silent (S10 reversal). See CONTEXT.md §S-VIEW navigation contract.
- [ ] **S-VIEW-2 — Sample view (the new main face).** Compose the home face: enlarged **hero
waveform** with the S11 markers (moved from Browse), a **fenced root affordance**, the
**Mono/Stereo toggle** (moved from Browse), and the **"Modes-and-down" control strip** (Mode /
Pitch engine / AHDSR|Trigger / AD pitch env — moved from Zone's param panel, single-capture
one-zone storage per S15-F2). Reference grammar: Simpler / Phase Plant (labelled value-strip
under a hero waveform).
- [ ] **S-VIEW-3 — envelope overlay on the hero waveform (new visual).** Draw the amp envelope
(AHDSR for Gate, fade/%-length for Trigger) as a curve over the Sample waveform at accurate
wall-clock time. New pure `envelope_overlay` module (params + frame-length → polyline; unit-
tested); shell traces it in an accent hue. Informative + decorative — sliders stay the edit
surface (node-drag deferred, fork S-VIEW-F2).
- [ ] **S-VIEW-4 — preview-trigger + velocity knob (new, Sample view).** A button firing the
sampler at the loaded capture's root note through the live voice engine (off the audio-thread
commit path — no MIDI controller needed) + an adjacent velocity knob. Preview velocity leans
**transient editor state** (fork S-VIEW-F1). RT discipline: no `process`-thread work.
- [ ] **S-VIEW-5 — Browse reduced to *choosing* (modal over Sample).** Keep search + bank tabs +
captures grid + scroll + selection; add **confirm/cancel** (double-click loads). **Remove** the
large waveform preview, Mono/Stereo toggle, root keyboard-strip (all moved to Sample), and the
loop-point labels + track-root message (cut). Render as a full-window modal overlay (fork
S-VIEW-F3).
- [ ] **S-VIEW-6 — key-tracking parameter (Zone + Sample, 0%200%, default 100%).** A per-
`PerformanceZone` scalar on keyboard pitch tracking around the root (100% = 12-tone-ET, 0% = no
tracking, 200% = double). Additive/version-bumped, defaults 100% (bit-identical). **Key-track
math lives in the pure sampler core** (unit-tested note/root/keyTrack → ratio), applied in both
Varispeed and Preserve repitch. Surfaces as a control on the Zone param panel + Sample control
strip.
- [ ] **S-VIEW-7 — real piano-key pattern on the keyboard strip (new visual).** Overlay the actual
alternating white/black (bright/dark per palette) key pattern over the pastel spectral fill so
the strip reads as a keyboard. Pure `keyboard_strip` gains a natural/accidental predicate (12-
tone, unit-tested); shell draws the two-tone overlay. Shared by the Zone strip + Sample root
affordance.
- [ ] **S-VIEW-8 — Zone view retained + wired.** Keep +Add Zone / Delete, the per-zone keyboard
strip (now with the piano pattern), the Low/High/Root numeric-entry legend, and the per-zone
param panel; add the key-tracking control. Nothing from today's Zones view dropped.
### Phase S editor prerequisites — routed to staff-engineer (bug + default; NOT design forks)
> Framed in CONTEXT.md §S-VIEW; both are functional prerequisites with the SDK swept and no
> platform ceiling — a diagnosis and a one-line default, not product decisions.
- [ ] **S-VIEW-BUG-1 — drop-to-FX diagnosis (staff-engineer).** Dropping a capture onto a track's
FX chain does not instantiate + init ReaSampler 9000 (drops as audio to arrange instead). The
S17 machinery is **SDK-correct and present** (`TrackFX_AddByName` + `vst_chunk` injection, all
verified); this is a **runtime bug**. Investigate in order: (1) FX-name match (`"VST3:" +
vstPluginName()` vs REAPER's scanned name); (2) gesture routing (`decideGesture` →
`InstrumentDrop` vs falling to `OsDrag` — the observed symptom); (3) `vst_chunk` write result +
blob-format match with `setState`. **Verify in DAW:** drop → a playing instance on the track,
one Ctrl-Z removes it.
- [ ] **S-VIEW-SIZE-1 — 1080p default window size (staff-engineer).** The editor opens too small
(`ViewRect(0,0,560,400)`). Mechanism **verified** (`getSize`/`setRect`/`checkSizeConstraint`/
`onSize`/`canResize` in `vendor/vst3sdk/public.sdk/source/common/pluginview.h`): set a larger
default `ViewRect` sized for the new three-band Sample face on 1080p (~840×560+ starting point,
tuned to the band heights) + a `checkSizeConstraint` minimum floor. **Verify in DAW:** opens at
the new default showing the full Sample face without scroll; cannot shrink below the floor.
### Phase S editor redesign — forks (Daniel's to call)
- **S-VIEW-F1 — preview velocity persistence:** transient editor state (**lean**) vs. a small
persisted convenience. Lean transient — no compat surface, no version bump.
- **S-VIEW-F2 — envelope-overlay interactivity:** read-only overlay + sliders edit (**lean**, ship
first) vs. draggable envelope nodes (deferred stretch).
- **S-VIEW-F3 — Browse modal presentation:** full-window overlay (**lean**) vs. a centered sheet
over a dimmed Sample. Presentation detail.
---
# Phase L — Look-and-feel (system-wide visual design language)
@@ -194,13 +286,17 @@ is **ReaSampler 9000**. Framing + propagation surfaces:
> directions: `docs/product/visual-design-language.md`. When a point lands, doc-keeper
> moves it to `COMPLETED.md`.
>
> **L1 (shared LICE drawing kit — the foundation), L2 (dock-panel layout redesign), L4
> (dock-panel button layout enhancement), L5 (dock-panel button refinements), L6 (toolbar
> polish), and L7 (capture ordering, card metadata, and selection styling) have landed** —
> **L1 (shared LICE drawing kit — the foundation), L2 (dock-panel layout redesign), L3
> (VST editor + embed-strip restyle), L4 (dock-panel button layout enhancement), L5
> (dock-panel button refinements), L6 (toolbar polish), and L7 (capture ordering, card
> metadata, and selection styling) have all landed** —
> `theme`/palette module, `component_geometry` geometry/hit-test helpers, `draw_kit` shell, GDI
> `DrawText` retirement in `bank_panel` (L1); `action_bar` pure task-grouped layout module, full
> M11-aware button inventory placed by task cluster, `bank_panel` redesigned through the L1 kit
> (L2); three-zone layout (top capture/placement/maintenance toolbar, bottom Design-View toolbar,
> (L2); VST editor (`reasampler_editor.cpp`) + embed strip (`reasampler_embed.cpp`) restyled
> through the L1 kit — REAPER-grey neutrals + three pastel accents, pastel spectral keyboard
> strip + zone bars, hover/pressed/drag states, local `kCol*` forest-green palette retired (L3);
> three-zone layout (top capture/placement/maintenance toolbar, bottom Design-View toolbar,
> footer toggle + Tail button + Prune), `footer_bar` pure module, `ActionCluster::Tagging`/
> `Switching` in `action_bar` (L4); top-bar overflow menu (`overflow_menu` pure module),
> custom LICE-kit hover-delay tooltips (`tooltip` pure module), opposite-mode Item/Track tag
@@ -209,7 +305,7 @@ is **ReaSampler 9000**. Framing + propagation surfaces:
> Cancel RT moved to overflow, top-bar cluster order tidied (L6); per-bank `SlotMap`
> (id→slot) in `bank_book`, sparse-grid rendering, `card_drag` + `card_meta` pure modules,
> `captureTimeSigNum`/`captureTimeSigDenom` on `Sample`, tertiary-border selection (L7). See
> `COMPLETED.md`. **L3 remains** — L3 gated on Phase S landing on dev.
> `COMPLETED.md`. **Phase L is complete.**
>
> **This section is self-contained for a team without Phase S context.** Where a point
> touches a Phase S surface (the VST editor, the embed strip, the keyboard strip), the
@@ -245,84 +341,16 @@ is **ReaSampler 9000**. Framing + propagation surfaces:
> out the full button inventory (including M11's action-button additions) intuitively,
> uncluttered, and useful — then applies the kit. Sequenced after M11 merges.
## L3 — VST editor + embed-strip restyle (GATED on Phase S landing on dev)
**Goal:** Bring the ReaSampler 9000 VST editor (`IPlugView` LICE surface) and the S6 embed
strip up to the settled-and-revised **B + three-accent pastel** look via the L1 kit: kit
cached-font text (§3.1 — the kit's current face, no font change), kit component draws, the
Neon-Console palette with **REAPER-grey neutrals** (`#2b2b2b`/`#333333`/`#3a3a3a`) and the
**three pastel accents** (primary lime / secondary teal / tertiary purple), the **pastel
spectral keyboard strip** as the signature surface (DS-2
revised), and hover/pressed/drag states throughout. CONTEXT.md §Phase L (VST restyle + the
Phase S coordination contract). Product framing: `docs/product/visual-design-language.md` §5.3.
**Verify (in DAW):** the VST editor + embed strip render in the settled B + three-accent
pastel language through the L1 kit — kit AA cached-font text, gradient/rounded kit
components, the pastel spectral keyboard strip, working hover/pressed/drag; the VST3 class
UID is unchanged (a visual refresh is not a compat event).
**Depends on:** L1 (the kit) **AND — GATE — Phase S landing on dev.** The VST editor, embed
strip, and keyboard strip live in Phase S, which is **not on dev yet** (it exists on the
phase-s worktree). **L3 cannot be built on dev until Phase S's drawing shells
(`src/vst/reasampler_editor.cpp` + `reasampler_embed.cpp`) merge to dev. The Phase L team
must NOT chase these files on dev — they are not there.** Until then L3 is a planned,
blocked point; L1 and L2 are the live Phase L work.
> **Coordination contract with Phase S (load-bearing).** Phase S's editor/embed UX
> (S10S13, S15S18) build their interaction UX with the **current** drawing and **adopt the
> L1 kit when it is available — they are NOT gated on Phase L.** Whichever lands first (the
> L1 kit or the Phase S UX), the kit is the **one source of drawing**: if the Phase S
> surfaces reach dev before L1, they draw in the current language and L3 restyles them; if
> L1 lands first, they are born in the kit. **Reality check (product-designer, 2026-07-27,
> read-only against the phase-s worktree): the Phase S surfaces did NOT adopt the kit —
> `reasampler_editor.cpp` and `reasampler_embed.cpp` still draw flat `LICE_FillRect` blocks
> + raw GDI `DrawTextA`, off a local pre-L1 forest-green palette (`kColBackground` etc.),
> not `theme`/`draw_kit`.** So L3 lands as a **full restyle**, not a born-in-kit no-op — all
> three plan points below are live and confirmed still-needed. Either way there is one kit
> and one look, and L3 is the point that completes the VST/embed adoption and applies the
> settled B+spectral treatment.
>
> **Landed-scope note (product-designer, 2026-07-27).** All Phase S drawing is centralized
> in **exactly two shells**`reasampler_editor.cpp` (the `IPlugView` editor) and
> `reasampler_embed.cpp` (the S6 embed strip). Every other `src/vst/` UI file
> (`editor_geometry`, `keyboard_strip`, `waveform_view`, `capture_browser`, `param_slider`,
> `browser_scroll`, `embed_strip`) is a **pure geometry/hit-test module — zero LICE, zero
> draw** (the house discipline held on the branch). So L3 touches two draw shells, but their
> rendered component set is broader than the original plan named: the editor draws a
> **capture-first browser** (search + tabs + thumbnails, S10), **channel toggles** (S7),
> **waveform + start/loop markers** (S11), the **keyboard strip + zone bars** (S10/S10-Z),
> and **ADSR / pitch sliders + mode toggles** (S12/S15/S16). Every one of these is on the
> restyle surface — enumerated in the points below.
- [ ] Route the VST editor's + embed strip's text through the kit's cached-font `text()`
(the kit's current face — §3.1, no font change); retire their raw GDI `DrawTextA` path
(present in both shells today). (Gated — Phase S surfaces must be on dev.)
- [ ] Retire the shells' **local pre-L1 palette** — the `kColBackground`/`kColCardBg`/
`kColThumb`/… forest-green-on-charcoal constants block in `reasampler_editor.cpp` (and the
mirrored constants in `reasampler_embed.cpp`) — and draw every surface through the L1
`theme` roles instead. **This is the coordination-contract reconcile: today there are two
palettes (the landed `theme` kit + this local constants block); L3 collapses them to the
one kit.** (Gated.)
- [ ] Restyle the editor + embed components through the kit (browser search/tabs/thumbnails,
channel toggles, ADSR + pitch sliders, mode toggles, zone bars, list rows, waveform,
segmented controls) in the B (Neon Console) palette — **REAPER-grey neutrals** (`bg/base`
`#2b2b2b` / `bg/panel` `#333333` / `bg/cell` `#3a3a3a`) with the **three pastel accents**
(primary lime = live layer; secondary teal / tertiary purple = categorical); add
hover/pressed/drag states. (Gated.)
- [ ] Apply **Direction C's pastel spectral treatment to the keyboard strip + zone bars**
(hue-mapped zones as a pastel sweep anchored on the three accents, active zone lifting to
`accent/primary` + a static glow — never a pulse, per the speed constraint) as the
signature surface; also carry the waveform + loop/start markers onto the kit's
`drawWaveform` + `warn`/accent marker roles; VST3 class UID unchanged. (Gated.)
## Phase L — sequencing
```
L1 (shared kit) ──► L2 (dock-panel layout redesign) [LANDED]
├─────────► L3 (VST editor + embed-strip restyle; GATED on Phase S landing on dev)
├─────────► L3 (VST editor + embed-strip restyle) [LANDED]
└─────────► L4 (dock-panel button layout enhancement) [LANDED]
└────► L5 (dock-panel button refinements; ungated, after L4) [LANDED]
└────► L6 (toolbar polish; ungated, after L5) [LANDED]
└────► L7 (capture ordering + card metadata + selection styling; ungated, after L6) [LANDED]
```
L1, L2, L4, L5, L6, and L7 have all landed. L3 waits for Phase S to reach dev. Phase S
feature work runs in parallel, ungated by Phase L.
L1, L2, L3, L4, L5, L6, and L7 have all landed. **Phase L is complete.**
## Phase L — must-verify-before-build
- **LICE design-kit surfaces (L1)**`LICE_GradRect`, `LICE_RoundRect`, AA
@@ -352,23 +380,6 @@ feature work runs in parallel, ungated by Phase L.
cursors chosen for drop-result cues (Reorder→IDC_SIZEALL, Move→IDC_HAND, Copy→IDC_UPARROW,
Replace→IDC_SIZEWE); existing pool-privilege guard reused as-is for Alt-replace. Gap navigation
in the grid = skip gaps (arrow keys skip empty slots). See `COMPLETED.md` §L7.
- **L3 restyle surface (L3)** — **PRE-CONFIRMED read-only against the phase-s worktree
(product-designer, 2026-07-27); re-confirm against dev once Phase S merges.** (1) Both draw
shells (`reasampler_editor.cpp`, `reasampler_embed.cpp`) still draw flat — `LICE_FillRect` +
raw GDI `DrawTextA`, no `theme`/`draw_kit`/`LICE_CachedFont` include — so all three L3 points
are live (no born-in-kit no-op). (2) There is a **second palette** to reconcile: a local
`kCol*` forest-green-on-charcoal constants block in the editor shell, divergent from the
landed `theme` roles. (3) Draw is centralized in those two shells only; every other
`src/vst/` UI file is pure geometry (kit adoption is a shell-side change, no geometry
rework). (4) The phase-s branch carries an **older L1 kit** (its `action_bar` predates dev's
L4L7 `theme` palette revisions) — after Phase S merges, L3 draws against dev's current
`theme`/`draw_kit`, so re-confirm the palette roles L3 consumes are dev's, not the branch's
stale copy. **The single open question is now RESOLVED (Daniel, 2026-07-27):** the S18 beta
channel title band gets **no distinct visual accent** — L3 restyles the title band in the
standard B pastel palette, and the beta-vs-stable distinction stays **purely textual** (the
channel-derived plugin name, as today), no channel-specific accent color. With that closed,
the settled DS-1/2/3 spec forces the rest.
---
# Phase Q — Quality (structural reorganization; zero-runtime-cost)
@@ -388,29 +399,26 @@ feature work runs in parallel, ungated by Phase L.
>
> **THE GATE (load-bearing — state first; reconciled to reality 2026-07-27).** Phase Q is
> **gated on the tree being otherwise quiescent.** Daniel's plain readiness target: **"when
> Phase S and L3 are finished."** As of 2026-07-27 the outstanding work is precisely: **(1)
> Phase S** merged to dev (the large second-artifact branch, currently on the phase-s worktree —
> the dominant gate item); **(2) Phase L L3** merged to dev (the VST restyle, itself gated on
> Phase S landing on dev). **L1/L2/L4/L5/L6/L7 have already landed** (see `COMPLETED.md`) — the
> earlier "L2 + L3" wording was stale and is corrected here to **L3 only**. **D2** is functionally
> complete (D2-W1..W3-B landed; the lone open item, a per-track lane-split panel indicator, is
> *explicitly deferred*, not a blocking residual). **M9** (slots) is *explicitly deferred*
> (Daniel, 2026-07-26), not scheduled work. D2 and M9 are named in the gate only so that
> *reactivating* either re-arms the quiescence condition; neither blocks the gate today. *Why the
> gate:* Phase Q touches **nearly every file in `src/`** (relocate into subdirectories,
> re-namespace every header, split the four largest TUs, plus the §2b renames). Every large
> in-flight branch (Phase S on its worktree, and L3 once it lands) is diffed against the *current
> flat layout*; landing a rename-and-relocate-everything reorg mid-flight forces every open branch
> through the worst conflict class (every hunk moved, every qualified reference changed) — a
> combinatorial re-resolution, not a linear one. Phase Q is *last* precisely because it reshapes
> the ground every other pillar stands on. Landing it early taxes every subsequent phase; landing
> it last taxes nothing. **Do not begin any Q point until the gate is satisfied.**
> Phase S and L3 are finished."** **As of 2026-07-27 both gate conditions are satisfied:**
> Phase S merged to dev (2026-07-27); Phase L L3 (the VST restyle) merged to dev
> (2026-07-27, commit `c53683e`) — **Phase L is complete** (L1/L2/L3/L4/L5/L6/L7 all
> landed, see `COMPLETED.md`). **D2** is functionally complete (D2-W1..W3-B landed; the
> lone open item, a per-track lane-split panel indicator, is *explicitly deferred*, not a
> blocking residual). **M9** (slots) is **abandoned** (Daniel, 2026-07-27) — will not be
> built. D2 is named in the gate only so that *reactivating* its deferred panel indicator
> re-arms the quiescence condition; neither D2 nor M9 blocks the gate today. *Why the gate:* Phase Q
> touches **nearly every file in `src/`** (relocate into subdirectories, re-namespace every
> header, split the four largest TUs, plus the §2b renames). Every large in-flight branch
> (Phase S on its worktree, and L3 once it lands) is diffed against the *current flat
> layout*; landing a rename-and-relocate-everything reorg mid-flight forces every open
> branch through the worst conflict class (every hunk moved, every qualified reference
> changed) — a combinatorial re-resolution, not a linear one. Phase Q is *last* precisely
> because it reshapes the ground every other pillar stands on. Landing it early taxes every
> subsequent phase; landing it last taxes nothing. **The gate is now satisfied — Phase Q
> may begin.**
>
> **M9 disposition (Daniel-decision note).** M9 is recorded "deferred indefinitely," which is not
> the same as "abandoned." Immaterial to the gate (both clear it). It matters only if M9 is ever
> reactivated: *before* Phase Q it lands cheaply on the flat layout; *after*, it is authored
> against the reorganized tree. Surfaced, not silently resolved — no action unless Daniel schedules
> M9. (Full note: `docs/product/code-organization.md` §4.)
> **M9 disposition — resolved (Daniel, 2026-07-27): abandoned.** M9 is out; it will not be
> reactivated. The gate remains satisfied; no re-arm condition applies.
>
> **Settled (Q-1, this-doc):** the phase is **`Q` (Quality)**; point-id family `Q1..Qn`, wave
> prefixes `Q-W1..Q-W6`. **Recommended, Daniel's to call (Q-2..Q-9, see
@@ -612,8 +620,8 @@ droppable point if the phase needs narrowing (Q-6).
## Phase Q — sequencing
```
GATE: Phase S + Phase L L3 merged to dev (D2 complete, M9 deferred) — tree quiescent
("when Phase S and L3 are finished" — L1/L2/L4L7 already landed)
GATE: Phase S + Phase L L3 merged to dev (D2 complete, M9 abandoned) — tree quiescent
("when Phase S and L3 are finished" — L1/L2/L3/L4L7 all landed — GATE SATISFIED)
Q-W1 (safe opener: core/json extract + directory/namespace layout on clean modules)
@@ -646,5 +654,4 @@ is independently landable and CTest-green.
or VST3 class UID is renamed (naming audit §2b.5). A rename that would alter a shipped contract
literal is out of scope, full stop.
- **The GATE** — do not begin any Q point until **Phase S + Phase L L3** are merged to dev and the
tree is quiescent (D2 complete, M9 deferred; a reactivation of either re-arms the gate).
Re-confirm quiescence against dev before W1.
tree is quiescent (D2 complete, M9 abandoned). Re-confirm quiescence against dev before W1.
+113
View File
@@ -965,6 +965,119 @@ adds only the identity fork on top.
---
### Addendum — three-view editor model + drop-to-FX + default window size (Daniel, 2026-07-27, r9)
**Framing.** Daniel opened a fresh design pass on the ReaSampler 9000 **editor** after living
with the S10S18 UX in the DAW. The instrument *works* and the capture browser is genuinely
good — but the two-view editor (today's "Browser" + "Zones" toggle) misallocates its space,
the default window is undersized for a 1080p world, and the drop-a-capture-onto-FX gesture is
broken in practice. The directive: **make the one job — pick a capture, tune it, play it —
fast, easy, and fun. Style is a critical ingredient. No spreadsheet aesthetics.** These are the
`r9` calls. Authoritative spec: **CONTEXT.md §Phase S — editor view-model redesign (S-VIEW)**;
build roadmap: **PLAN.md §Phase S — editor view-model redesign**.
**The reference devices (the north star for control density).** Daniel named Ableton **Simpler**
and a Kilohearts/Phase-Plant **sampler group** as the composition targets. Both share one
grammar, and it is the grammar the redesign adopts:
- **A large waveform up top is the hero**, with markers and the envelope drawn *over it at
accurate time* — not a separate abstract envelope box. Simpler shades the sustain-loop region
teal directly on the waveform; Phase Plant traces the amp envelope as a bright curve across the
sample. The waveform is where the eye lives.
- **A thin, dense value-strip of labelled controls** sits under the waveform (Simpler's
Gain/Start/Loop/Length/Fade ribbon; Phase Plant's LEVEL/SEMI/HARMONIC/SHIFT/PHASE row) — bold
ALL-CAPS micro-labels over a value, compact, scannable in one pass.
- **Root note is fenced off as its own affordance** (Phase Plant boxes "Root C 4 +00" and an
"Offset" knob to the right; Simpler puts Key% and Transp in the pitch cluster). Root is not
buried in a list of sliders — it is a first-class, always-visible control.
- **Modular horizontal bands.** Each concern (sample/pitch, then envelope) is its own band with
its own drawn visual. This is how both devices avoid the spreadsheet look: every band leads
with a *picture* (waveform, filter curve, envelope shape), and the numbers hang off it.
**The three calls (r9).**
1. **Three views, not two — and they are NOT a flat toggle.** Today "Browser" and "Zone" sit as
two equal segments. That framing is wrong: it implies the browser and the zone editor are
peers, when in truth **the loaded sample is the home** and browsing/zoning are things you
*reach for*. So: **Sample is the home view** (the default face — the hero waveform, the
envelope overlay, all the per-sample controls). **Browse** is a **modal page layered over
Sample** — a full-window overlay you summon to change the loaded capture (select + confirm),
the way a file-open sheet layers over a document. **Zone** is a **dedicated editing surface**
opened by its own button when you want to map across the keyboard. This is a
*document-with-modal-picker* model (Simpler's own shape: the device face is the sample, and
loading a new one is a distinct act), not a three-way radio. *Why the reframe matters:* it
makes "I just want to play this capture" the zero-click default, and "I want a different one"
a single deliberate gesture, instead of making the user re-choose their whole stance every
time. See CONTEXT.md §S-VIEW for the precise navigation model.
2. **The Sample view earns the hero treatment; Browse gets ruthlessly cut.** Browse today
carries a waveform preview, root-note piano-roll, loop-point labels, a track-root message, and
a Mono/Stereo toggle — all of which are *tuning* concerns, not *choosing* concerns. Browse's
only job is **pick a capture**; the grid thumbnails already show the waveform, so every one of
those tuning affordances moves to Sample (where the hero waveform lives) and Browse shrinks to
filters + grid + select/confirm. Conversely, Sample **gains a new envelope overlay** drawn on
the hero waveform at accurate time (the Simpler/Phase-Plant move), a **preview-trigger button +
velocity knob** (audition the sample at its root without a MIDI controller — the single most
"fun/fast" affordance the current editor lacks), and inherits the Mono/Stereo toggle and every
"Modes-and-down" control that lives on Zone today.
3. **Two engineering prerequisites, framed but routed to implementation.** The **drop-to-FX bug**
(dropping a capture onto a track's FX chain does not instantiate + init ReaSampler 9000) and
the **undersized default window** are not design decisions — they are a bug and a one-line
default. Both are framed in CONTEXT.md §S-VIEW with the SDK reality swept (drop-to-FX: the S17
machinery is SDK-correct, so this is a *diagnosis* task, not a redesign; window size: the
`getSize`/`checkSizeConstraint` mechanism is verified), and both are flagged for
staff-engineer, not for a product fork.
**New parameters this introduces (both instrument performance state, D-B — never bank facts):**
- **Key-tracking (Zone view).** A 0%200% scalar on how sample pitch tracks the keyboard around
the root note. **100% (default) = standard 12-tone equal-temperament tracking** (a semitone up
the keyboard = a semitone of repitch — exactly today's behavior). 0% = the sample plays at root
pitch on every key (a fixed, un-tracked one-shot — the classic drum-machine "no tracking"
mode). 200% = double-rate tracking (an octave of keyboard spans two octaves of pitch — an
intentional special effect). This is a *per-zone performance choice*, additive on
`PerformanceZone`, defaulting to 100% so existing playback is bit-identical. It is the sampler
idiom every serious sampler has (RS5K's "Pitch for start/end note", Kontakt's "Tracking",
Simpler's "Key %" — visible in the reference screenshot at "Key 100%"). *Why 200% and not
higher:* the useful musical range is 0200%; past 200% it is noise, and a bounded slider reads
cleaner than an open one.
- **Preview velocity (Sample view).** The velocity level the preview-trigger button fires at,
set by an adjacent knob. Not persisted as a musical parameter of the sample — it is a
*utility* setting for the audition button (like a metronome level), so it can live as
transient editor state or a small persisted convenience; the spec leans transient (see
§S-VIEW). This is what makes the preview button *fun*: tap it hard or soft without reaching for
a controller.
**Two visual components the redesign commits to:**
- **The envelope overlay** on the Sample view's hero waveform: the amp envelope (AHDSR for Gate,
the fade/%-length shape for Trigger) drawn as a curve *at accurate wall-clock time* across the
waveform, the way Simpler draws its amp envelope over the sample and Phase Plant traces it in
bright blue. This is the single biggest "reads like a real sampler, not a spreadsheet" move —
the envelope stops being four abstract sliders and becomes a *shape you see over the sound*.
The sliders stay (precise entry), but the picture leads.
- **The real piano-keyboard pattern** on the Zone view's keyboard strip. Today's spectral strip
is pretty but does not *read as a keyboard* — Daniel's note. The redesign keeps the pastel
spectral hue but overlays the **actual alternating white/black key pattern** (bright/dark per
the palette) so the strip is instantly identifiable as a keyboard. It need not be *shaped* like
a keyboard (no protruding black keys) — just carry the pattern as an overlay, so a glance reads
"this is C, this is F#" without counting.
**What does NOT change (guardrails restated).** The instrument stays a **read-only bank
consumer** — every new control (key-tracking, preview velocity, the loop/start markers Sample
inherits) is *performance map*, never written to `Sample` or the bank (D-B). LICE/SWELL drawing
only, all layout/hit-test in pure geometry modules (D-A). RT discipline untouched — the
preview-trigger fires a note through the same voice engine off the audio-thread commit path, no
new audio-thread work. The VST3 class UID is unchanged — a view reorganization is not a compat
event. And this is an *editor* redesign: the S3 voice engine, the keymap resolution, the
component-state format (extended additively for key-tracking) are the same load-bearing core.
**Where the spec lives:** CONTEXT.md §Phase S — editor view-model redesign (S-VIEW); PLAN.md
§Phase S — editor view-model redesign. This Addendum is the *why*; those are the *what/how*.
---
## Where this landed
With D1/D5/D6 locked and **D-A..D-D all settled (2026-07-26)**, the instrument is scoped