From 13b23801a085f83ed4a59bcab0651fb6af3a0def Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Mon, 27 Jul 2026 12:46:43 -0400 Subject: [PATCH] =?UTF-8?q?docs(phase-s):=20spec=20three-view=20editor=20r?= =?UTF-8?q?edesign=20(S-VIEW)=20=E2=80=94=20Sample/Browse/Zone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONTEXT.md | 317 +++++++++++++++++++++++++++++++--- PLAN.md | 247 +++++++++++++------------- docs/product/midi-playback.md | 113 ++++++++++++ 3 files changed, 530 insertions(+), 147 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 73c8960..76f5c6e 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -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 0–200% 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", )`; 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 S10–S13 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 S10–S13 UX), the kit is the **one source -of drawing**: if S10–S13 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 S10–S13 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. diff --git a/PLAN.md b/PLAN.md index 52c2629..a67b461 100644 --- a/PLAN.md +++ b/PLAN.md @@ -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 -> (S10–S13, S15–S18) 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 - L4–L7 `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/L4–L7 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/L4–L7 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. diff --git a/docs/product/midi-playback.md b/docs/product/midi-playback.md index ddc1ddc..f9942be 100644 --- a/docs/product/midi-playback.md +++ b/docs/product/midi-playback.md @@ -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 S10–S18 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 0–200%; 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