docs: record FB1 r11 Sample-view recompose + master gain (v8); move S-VIEW-11/12 to COMPLETED; correct stale r11 zero-state-change line
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
+6
-3
@@ -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
|
||||
|
||||
@@ -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 /
|
||||
|
||||
Reference in New Issue
Block a user