From d272aab0df876277d89930ae20b742207147953d Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Mon, 27 Jul 2026 23:58:05 -0400 Subject: [PATCH] docs: record FB1 r11 Sample-view recompose + master gain (v8); move S-VIEW-11/12 to COMPLETED; correct stale r11 zero-state-change line --- CLAUDE.md | 7 ++++-- COMPLETED.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ CONTEXT.md | 9 ++++--- PLAN.md | 36 +++++++--------------------- 4 files changed, 86 insertions(+), 32 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9cdb6ce..4cfa156 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Repo identity and current state -**ReaSampler** is a per-project audio sample-bank capture tool that builds two artifacts: the REAPER extension (`reaper_reasampler`) and **ReaSampler 9000**, a Windows-only VST3 sampler instrument (`reasampler_9000.vst3`, `src/vst/`, second CMake target `reasampler_vst`, gated on the vendored `vendor/vst3sdk` submodule slice). The pure-testable-core / REAPER-facing-shell discipline is preserved throughout. CONTEXT.md is the authoritative spec — settled decisions, invariants, guardrails, and not-yet-built specs; it is large, so locate the relevant phase section by grepping its headings and read only that section with an offset rather than reading it whole. Build detail for landed phases lives in CONTEXT-ARCHIVE.md. Every REAPER API name cited there is correct-by-intent; verify argument order, types, and flag values against `vendor/reaper-sdk/sdk/reaper_plugin_functions.h` before use. A post-S-VIEW DAW-fix pass has landed (all 52 suite tests green): envelope nodes fully editable in both modes (every Gate stage A/H/D/S/R + Trigger zero-fade-out node, param-domain schematic scaling, 8 px min node separation, all nodes clamped in-canvas); gap-free per-column waveform render (`columnMinMax` homed in `peaks`, `waveformColumnCount` in `component_geometry`, shared via `drawWaveform`); `param_slider` `Knob` primitive (7→5 o'clock arc, needle, vertical-drag); zone-bleed fix 3a (`reconcileSingleCaptureZones` in `sample_map`). The voice-system redesign is also landed: `sampler_core` gains user-parameterized voice count (1–32, default 16), `VoiceMode` Poly/Mono (last-note held-note stack, `MonoTrigger` retrigger/legato toggle), an isolated `PreviewCard` (dedicated preview voice outside the MIDI pool — never steals from/into it; unity-Preserve zero-latency bypass scoped to it), and two-tier panic (CC 123 = release, CC 120 = immediate hard-stop incl. Trigger one-shots); processor sums the preview card alongside the engine + drain, `retireIdleDrain()` retires fully-idle drain snapshots, and voice-param edits rebuild from the already-decoded PCM (no bank re-read/WAV re-decode) via the drain-slot swap; `ComponentState` envelope bumped v6→v7 (voiceCount/voiceMode/monoTrigger bytes; pre-v7 blobs lift to 16/Poly/Retrigger). +**ReaSampler** is a per-project audio sample-bank capture tool that builds two artifacts: the REAPER extension (`reaper_reasampler`) and **ReaSampler 9000**, a Windows-only VST3 sampler instrument (`reasampler_9000.vst3`, `src/vst/`, second CMake target `reasampler_vst`, gated on the vendored `vendor/vst3sdk` submodule slice). The pure-testable-core / REAPER-facing-shell discipline is preserved throughout. CONTEXT.md is the authoritative spec — settled decisions, invariants, guardrails, and not-yet-built specs; it is large, so locate the relevant phase section by grepping its headings and read only that section with an offset rather than reading it whole. Build detail for landed phases lives in CONTEXT-ARCHIVE.md. Every REAPER API name cited there is correct-by-intent; verify argument order, types, and flag values against `vendor/reaper-sdk/sdk/reaper_plugin_functions.h` before use. A post-S-VIEW DAW-fix pass has landed (all 52 suite tests green): envelope nodes fully editable in both modes (every Gate stage A/H/D/S/R + Trigger zero-fade-out node, param-domain schematic scaling, 8 px min node separation, all nodes clamped in-canvas); gap-free per-column waveform render (`columnMinMax` homed in `peaks`, `waveformColumnCount` in `component_geometry`, shared via `drawWaveform`); `param_slider` `Knob` primitive (7→5 o'clock arc, needle, vertical-drag); zone-bleed fix 3a (`reconcileSingleCaptureZones` in `sample_map`). The voice-system redesign is also landed: `sampler_core` gains user-parameterized voice count (1–32, default 16), `VoiceMode` Poly/Mono (last-note held-note stack, `MonoTrigger` retrigger/legato toggle), an isolated `PreviewCard` (dedicated preview voice outside the MIDI pool — never steals from/into it; unity-Preserve zero-latency bypass scoped to it), and two-tier panic (CC 123 = release, CC 120 = immediate hard-stop incl. Trigger one-shots); processor sums the preview card alongside the engine + drain, `retireIdleDrain()` retires fully-idle drain snapshots, and voice-param edits rebuild from the already-decoded PCM (no bank re-read/WAV re-decode) via the drain-slot swap; `ComponentState` envelope bumped v6→v7 (voiceCount/voiceMode/monoTrigger bytes; pre-v7 blobs lift to 16/Poly/Retrigger). **FB1 Sample-view recomposition (r11) has also landed** (suite 55/55 green): all linear sliders replaced by radial **knobs** in a fenced **knob deck** (groups: AMP ENVELOPE / PITCH / PITCH ENV / VOICE / MASTER); mode toggles are compact in the caption row, not full-width; the **hero waveform runs full-width** (elastic band, 840×620 default preserved); the inline velocity-curve box is replaced by a **28×28 curve preview button → centered popup** with right-click node delete; voice-band controls (count / Poly-Mono / Retrig-Legato) are placed in the VOICE deck group; a **post-mixer per-sample-ramped master gain** (−∞…+24 dB, no zipper) is placed in the MASTER deck group, persisted as `masterGainLinear` — `ComponentState` envelope bumped v7→v8 (pre-v8 blobs lift to unity gain). Three new pure `src/vst/` modules landed: `knob_deck` (group-box + caption-row + knob-cell geometry, deterministic wrap, hit-test), `curve_popup` (sheet/close/box geometry + outside-sheet dismissal test), `master_gain` (dB↔linear taper math, −∞…+24 dB). ## One-time submodule setup @@ -113,10 +113,13 @@ There is no hot-reload. Copy the built binary into REAPER's `UserPlugins/` folde - `trigger_seam` — pure Trigger frames↔fraction converter: owns the shared formula for converting between engine source-frame fade counts and the overlay's fractional representation, threading `startFrame` correctly through pack and unpack directions. - `velocity_curve` — pure velocity→amp transfer curve: `VelocityCurve` evaluated by a Fritsch–Carlson monotone cubic Hermite spline (no overshoot outside [0,1]). `eval(velocity)` called once per note-on. `flat()` default (y=1, every velocity→unity) replaces the prior fixed `velocity/127` path — a deliberate non-back-compat behavior change (Daniel-approved). - `embed_strip` — compact single-row control layout for embed mode in the track FX chain. +- `knob_deck` — pure knob-deck layout + hit-test (FB1): group-box / caption-row / compact-toggle / knob-cell geometry, deterministic whole-group wrap, `DeckLayout` / `DeckHit`. Mirror of `action_bar`/`param_slider`; no LICE or REAPER types. +- `curve_popup` — pure curve-popup geometry + dismissal test (FB1): centered sheet over the Sample face — width/height clamps, title row, Close button rect, curve-box rect, outside-sheet dismissal test. Mirror of `overflow_menu`; no LICE or REAPER types. +- `master_gain` — pure dB↔linear taper math (FB1): normalized [0,1] ↔ dB ↔ linear for the post-mixer master gain control (−∞…+24 dB, norm 0 = true silence, unity ≈ 0.714). Shared by the editor knob and the processor multiply so the needle, persisted value, and audio multiply cannot drift. **VST3 instrument (`src/vst/`) — shells:** - `reaper_bridge` — READ-ONLY bank consumer: receives bank snapshots from the extension and exposes them as a read-only view. **Never writes to the extension's bank** — this is a load-bearing invariant; no mutation path exists in this module. -- `reasampler_processor` — VST3 `SingleComponentEffect` shell: declares event-input bus + stereo output, marshals MIDI note-on/off into the VoiceEngine, renders audio; owns off-audio-thread `reloadFromBank` + atomic pointer swap so `process()` does no allocation, no file I/O, no bridge calls. Sums the `PreviewCard` alongside the engine + drain in `process()`; `retireIdleDrain()` retires fully-idle drain snapshots on the UI-timer cadence. Voice-param edits (`setVoiceCount`/`setVoiceMode`/`setMonoTrigger`) rebuild the engine from the already-decoded keymap via the drain-slot swap — no bank re-read, no WAV re-decode, no audible cut to ringing tails. +- `reasampler_processor` — VST3 `SingleComponentEffect` shell: declares event-input bus + stereo output, marshals MIDI note-on/off into the VoiceEngine, renders audio; owns off-audio-thread `reloadFromBank` + atomic pointer swap so `process()` does no allocation, no file I/O, no bridge calls. Sums the `PreviewCard` alongside the engine + drain in `process()`; `retireIdleDrain()` retires fully-idle drain snapshots on the UI-timer cadence. Voice-param edits (`setVoiceCount`/`setVoiceMode`/`setMonoTrigger`) rebuild the engine from the already-decoded keymap via the drain-slot swap — no bank re-read, no WAV re-decode, no audible cut to ringing tails. **FB1:** applies the post-mixer `masterGainLinear` (from `ComponentState` v8) as a per-sample ramp over the summed output — no zipper noise. - `reasampler_editor` — VST3 `IPlugView` LICE editor shell: hosts a LICE-drawn child window; default face is the capture browser, then single-capture setup, with opt-in zones panel. Drop-onto-editor ingest is NOT shipped (deferred). - `reasampler_embed` — implements `IReaperUIEmbedInterface` so the instrument draws inline in the TCP/MCP without a plugin-owned HWND; delegates layout/hit-test to `embed_strip`. - `vst_entry` — VST3 entry point: `GetPluginFactory` export, class registration, channel-forked class UIDs. diff --git a/COMPLETED.md b/COMPLETED.md index 6f876af..b04595f 100644 --- a/COMPLETED.md +++ b/COMPLETED.md @@ -2672,3 +2672,69 @@ stale full-range zone shadowing so the engine plays the zone the editor draws. C voice-param rebuild via drain-slot swap (no disk I/O). - [x] `ComponentState` envelope v6→v7; `voiceCount`/`voiceMode`/`monoTrigger` on `ComponentState`; back-compat lift in `deserializeComponentState`. + +--- + +## FB1 — Sample-view recomposition + master gain (r11; 2026-07-27) + +> **Merged to dev 2026-07-27. Integrated suite 55/55 green.** S-VIEW-11 + S-VIEW-12 from the +> Wave B plan. No corresponding PLAN.md points remain for these two; S-VIEW-13 (Zone-panel +> parity) stays open in PLAN.md. + +**Goal:** Recompose the S-VIEW Sample face after Daniel's post-landing DAW pass — radial knobs +in fenced groups, compact mode toggles, full-width elastic hero, curve preview button → popup. +Also adds a post-mixer master gain control (MASTER group, −∞…+24 dB) as a new persisted field. +**Verify:** Integrated suite 55/55 green. Sample face renders knob deck with correct group +geometry; hero runs full-width (elastic, 840×620 preserved); curve popup opens/dismisses; right- +click on a node in the popup deletes it (endpoint-guarded); master gain knob adjusts output level +and persists across project save/reopen; pre-v8 blobs lift to unity gain. + +- [x] **`knob_deck` pure module** (`src/vst/knob_deck.{h,cpp}`): group-box / caption-row / + compact-toggle / knob-cell geometry; deterministic whole-group wrap (a group that doesn't fit + the remaining row width starts a new deck row); `DeckLayout` / `DeckHit` structs; blank + reserved cells (id −1 for AMP ENVELOPE's 5-cell stability). Mirror of `action_bar`/ + `param_slider`; no LICE or REAPER types. New CTest target `knob_deck_tests`. +- [x] **`curve_popup` pure module** (`src/vst/curve_popup.{h,cpp}`): centered sheet geometry — + width `clamp(60% window, 360..520)`, height `clamp(55% window, 260..380)`; title row + 18×18 + Close button rect; curve-box rect (border rect; shell applies `curveBoxFromRect` inset); + outside-sheet dismissal test. Mirror of `overflow_menu`; no LICE or REAPER types. New CTest + target `curve_popup_tests`. +- [x] **`master_gain` pure module** (`src/vst/master_gain.{h,cpp}`): dB↔linear taper math for + the −∞…+24 dB master gain knob — `masterGainLinearFromNorm`, `masterGainNormFromLinear`, + `masterGainDbFromNorm`, `masterGainNormFromDb`, `formatMasterGainLabel`; norm 0 = true silence + (exact 0.0); unity ≈ 0.714 normalized. Shared by the editor knob and the processor multiply + so needle, persisted value, and audio multiply cannot drift. No LICE or REAPER types. New + CTest target `master_gain_tests`. +- [x] **Knob deck shell** (`reasampler_editor.cpp`): Sample control strip rebuilt as five fenced + groups — **AMP ENVELOPE** (Gate|Trigger caption toggle; 5-cell width reserved; Gate: Attack · + Hold · Decay · Sustain · Release; Trigger: Fade In · Length % · Fade Out), **PITCH** + (Varisp|Preserve caption toggle; Key Track knob), **PITCH ENV** (Off|On caption toggle; + P.Attack · P.Decay · P.Depth; Disabled-not-hidden when Off), **VOICE** (Voices knob + + Poly|Mono caption toggle + Retrig|Legato row toggle), **MASTER** (Gain knob). Slider rows + + full-width toggles retired from the Sample face. Hero band recomputed as elastic (absorbs + window height minus fixed bands; floor 150px); deck band bottom-anchored at fixed height via + `deckHeight()`. +- [x] **Full-width hero** (S-VIEW-12): velocity-curve carve-out removed from hero rect; hero + now runs edge-to-edge between the kPad margins. S-VIEW markers (start/loop) + envelope overlay + unchanged. +- [x] **Curve preview button + popup** (S-VIEW-12): 28×28 miniature curve preview button placed + immediately right of the Vel knob in the cluster band; left-click opens the `curve_popup`- + geometry centered sheet; **right-click on a node in the popup deletes it** (endpoint-guarded, + commits via the same path as Alt-click — `deletePoint` guard makes endpoint right-clicks a safe + no-op). Dismiss: Close ×, outside-click (no drag in flight), Esc. All landed curve-editor + interactions preserved (drag, click-add, Alt-click delete, drag-off delete). +- [x] **Post-mixer master gain** (new): `masterGainLinear` field added to `ComponentState`; + `kComponentStateVersion` bumped **v7→v8**; pre-v8 blobs lift to `masterGainLinear = 1.0` + (unity). Processor applies the gain as a per-sample ramp over the summed output — no zipper + noise. `master_gain` pure module is the shared math seam between the editor knob and the + processor multiply. + +**Notes/decisions:** +- **Persistence is NOT zero-change.** The CONTEXT.md r11 spec described r11 as a pure view + recomposition; master gain added `ComponentState` v8. CONTEXT.md §S-VIEW r11 intro updated to + record this correctly (the "zero component-state change, no new persisted fields" clause + replaced with a factual note on the v7→v8 bump and the master gain field). +- **R11-F1 (hero height) settled at build:** elastic hero, 840×620 default preserved — the + hero grows with the window, floor 150px. No window-size change required. +- **R11-F2 (Zone-panel parity) deferred to FB2:** S-VIEW-13 remains open in PLAN.md. +- New CTest targets: `knob_deck_tests`, `curve_popup_tests`, `master_gain_tests`. diff --git a/CONTEXT.md b/CONTEXT.md index e2667d1..a611549 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -1681,9 +1681,12 @@ S-VIEW-2 Sample face + S-VIEW-3 envelope overlay. > small radial knob** grouped into a fenced **knob deck**, the two **mode toggles shrink** from > full-width control rows to compact group-header segments, and the **inline velocity-curve box > is replaced by a miniature curve preview button** that summons a **full-size popup editor** — -> freeing the **hero waveform to run full width**. Engine, params, storage, and persistence are -> all **unchanged** — this is a pure view recomposition of existing controls (zero component-state -> change, no new persisted fields, VST3 class UID unchanged). The **knob primitive** itself +> freeing the **hero waveform to run full width**. The view recomposition is otherwise **parameter- +> preserving** — existing params, their persisted seams, and the VST3 class UID are all unchanged. +> **Exception (landed in FB1):** r11 added a post-mixer **master gain** control (MASTER group, +> −∞…+24 dB dB-taper, per-sample ramped in the processor) backed by a new `masterGainLinear` +> field in `ComponentState`, bumping the **envelope version v7→v8**; pre-v8 blobs lift to unity +> gain. The **knob primitive** itself > (minimal arc ~6→4 o'clock, needle indicator, vertical drag) is a **separate in-flight track**; > this section specs the layout that consumes it. Directives (Daniel, 2026-07-27): radial knobs > replacing all sliders, grouped "in a reasonable way" with **the envelope controls grouped diff --git a/PLAN.md b/PLAN.md index b28d112..4860d2a 100644 --- a/PLAN.md +++ b/PLAN.md @@ -143,40 +143,22 @@ piano-key pattern work; drop-a-capture-onto-FX instantiates a playing instance. > knobs** grouped into a fenced knob deck (envelope controls grouped as a unit); the **mode > toggles compact**, not full-width; the **inline velocity-curve box → a miniature curve > preview button + full-size popup editor** (right-click deletes a node in the popup); the -> **hero waveform full-width**. A pure view recomposition — zero engine/state/persistence -> change, VST3 class UID unchanged. Authoritative layout spec: **CONTEXT.md §S-VIEW → "The +> **hero waveform full-width**. Authoritative layout spec: **CONTEXT.md §S-VIEW → "The > Sample-face recomposition (r11)"** (band order, group taxonomy, cell metrics, popup > geometry, inventory contract — nothing silently dropped). Product framing: -> `docs/product/midi-playback.md` §Addendum r11. **GATED on the knob-primitive track** -> (separate, in flight) — S-VIEW-11/12 consume the primitive, they do not build it. -> All drawing through the L1 kit; all layout/hit-test in pure geometry modules. +> `docs/product/midi-playback.md` §Addendum r11. All drawing through the L1 kit; all +> layout/hit-test in pure geometry modules. +> +> **S-VIEW-11 and S-VIEW-12 have landed (FB1, merged 2026-07-27, suite 55/55 green)** — +> knob deck + master gain + curve popup + full-width hero all on dev; see `COMPLETED.md`. +> **S-VIEW-13 (Zone-panel parity) is NOT done** — in-flight FB2 track; remains open below. -- [ ] **S-VIEW-11 — the knob deck.** Pure **`knob_deck`** (group boxes + caption rows + - compact-toggle rects + knob-cell rects + deterministic group wrap + hit-test; mirror of - `action_bar`/`param_slider`; unit-tested) + shell: the Sample control strip becomes three - fenced groups — **AMP ENVELOPE** (Gate|Trigger caption toggle; Attack·Hold·Decay·Sustain· - Release or Fade In·Length %·Fade Out knobs, 5-cell width reserved so mode flips never - reflow), **PITCH** (Varisp|Preserve caption toggle; Key Track knob), **PITCH ENV** (Off|On - caption toggle; P.Attack·P.Decay·P.Depth knobs, Disabled-not-hidden when Off). Slider rows - + full-width toggles retire from the Sample face; the hero becomes the elastic band - (bottom-anchored fixed deck). Preview-velocity slider becomes a cluster knob cell (same - persisted seam). -- [ ] **S-VIEW-12 — curve popup + full-width hero.** Pure **`curve_popup`** (sheet/close/box - geometry + outside-sheet dismissal test; mirror of `overflow_menu`; unit-tested) + shell: - the inline velocity-curve box leaves the hero band (**hero runs full width**, S11 markers + - envelope overlay unchanged); a **28×28 miniature curve preview button** lands immediately - right of the preview-velocity knob (live miniature trace; Active while its popup is open); - click opens the **centered popup** hosting the full-size curve editor — all landed - interactions preserved (drag, click-add, Alt-click delete, drag-off delete) **plus NEW - right-click node delete** (endpoint-guarded, commits like Alt-click). Dismiss: Close ×, - outside-click (no drag in flight), Esc. - [ ] **S-VIEW-13 — Zone-panel parity (gated on fork R11-F2).** The Zone param panel adopts the same knob deck + curve-preview-button/popup; retires `param_slider`'s slider rows outright (one control grammar on both surfaces of the one storage site). -**Open forks (Daniel's to call before Wave B builds)** — recorded in the CONTEXT.md §S-VIEW -fork ledger: **R11-F1** hero height vs. default window (lean: elastic hero, keep 840×620); -**R11-F2** Zone-panel parity (lean: yes — one grammar). +**Open forks** — **R11-F1** (hero height vs. default window) settled at FB1 build: elastic +hero, 840×620 default kept. **R11-F2** (Zone-panel parity) is the gate for S-VIEW-13. ### Phase S editor redesign — forks (Daniel's to call) *(r9/r10 forks all settled: S-VIEW-F1 and S-VIEW-F2 SETTLED 2026-07-27 — folded into S-VIEW-4 /