From 1d2da9d4568ceafff0c0d0c2029edf71fdb69553 Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Tue, 28 Jul 2026 09:16:13 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20GA2=20fix=20pass=20=E2=80=94=20Preserve?= =?UTF-8?q?=20ring-prime=20(gap-free=20frame-0=20onset,=20no=2025ms=20late?= =?UTF-8?q?ncy)=20+=20bounded-blend=20takeover=20declick=20(supersedes=20s?= =?UTF-8?q?tale=20(1-amp)=20gate)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 4 ++-- COMPLETED.md | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 70c15bf..3aed7b2 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). **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). **FB2 Zone-panel parity (r11, 2026-07-28) has also landed** (suite 55/55 green): the Zone param panel now uses the same knob deck + curve-preview-button/popup grammar as the Sample face — one control grammar across both surfaces of the one per-zone storage site; Zone-authoring affordances (+Add Zone / Delete, the piano-key strip, Low/High/Root legend) are preserved; VOICE and MASTER groups remain Sample-only (per-instance). `param_slider`'s linear slider rows are retired on the Zone panel (the FA4 `Knob` primitive is now the only live consumer of that half of `param_slider`). **This completes the r11 editor recomposition (Wave B / Phase S editor redesign).** A **GA post-launch DAW-fix pass** has also landed (suite 55/55 green): `pitch_shift` rewritten from dual-tap OLA (anti-phase cancellation → spectral garbage on repitched notes) to **correlation-aligned SOLA splices** with a ratio-scaled raised-cosine fade (clean pitch shift past +24 st); `Voice::start` gained a `takeoverDeclick` gate (applied on the envelope complement) for mono retrig/fallback + poly at-cap steal; the output bus is now **permanently stereo** (`ChannelMode` is decode-only; the dynamic mono↔stereo bus renegotiation is deleted) with channel mode **auto-defaulting from the loaded capture** via new `ComponentState` **v9** (`channelModeExplicit` flag) + a pure `channelModeFor` helper; `SetCapture` moved to drag-arm in `bank_panel` so a first straight-out drag arms correctly; and the Design/Arrange mode-toggle action now calls `bankPanelInvalidate()` so the panel footer reflects the new mode without requiring a button click. +**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). **FB2 Zone-panel parity (r11, 2026-07-28) has also landed** (suite 55/55 green): the Zone param panel now uses the same knob deck + curve-preview-button/popup grammar as the Sample face — one control grammar across both surfaces of the one per-zone storage site; Zone-authoring affordances (+Add Zone / Delete, the piano-key strip, Low/High/Root legend) are preserved; VOICE and MASTER groups remain Sample-only (per-instance). `param_slider`'s linear slider rows are retired on the Zone panel (the FA4 `Knob` primitive is now the only live consumer of that half of `param_slider`). **This completes the r11 editor recomposition (Wave B / Phase S editor redesign).** A **GA post-launch DAW-fix pass** has also landed (suite 55/55 green): `pitch_shift` rewritten from dual-tap OLA (anti-phase cancellation → spectral garbage on repitched notes) to **correlation-aligned SOLA splices** with a ratio-scaled raised-cosine fade (clean pitch shift past +24 st); `Voice::start` applies a **bounded blend** (`out*(1-w) + ref*w`, w decaying from 1.0) at takeover boundaries — mono retrig/fallback, poly at-cap steal, and preview re-trigger — superseding the earlier `(1-amp)` envelope-complement gate that zeroed the compensation on Trigger/zero-attack restarts; the output bus is now **permanently stereo** (`ChannelMode` is decode-only; the dynamic mono↔stereo bus renegotiation is deleted) with channel mode **auto-defaulting from the loaded capture** via new `ComponentState` **v9** (`channelModeExplicit` flag) + a pure `channelModeFor` helper; `SetCapture` moved to drag-arm in `bank_panel` so a first straight-out drag arms correctly; and the Design/Arrange mode-toggle action now calls `bankPanelInvalidate()` so the panel footer reflects the new mode without requiring a button click. ## One-time submodule setup @@ -100,7 +100,7 @@ There is no hot-reload. Copy the built binary into REAPER's `UserPlugins/` folde **VST3 instrument (`src/vst/`) — pure core:** - `sampler_core` — polyphonic voice engine with bounded stealing, user-parameterized voice count (1–32, default 16), `VoiceMode` Poly/Mono (last-note held-note stack, `MonoTrigger` Retrigger/Legato toggle), isolated `PreviewCard` (dedicated preview voice outside the MIDI pool — never steals from/into it; unity-Preserve zero-latency bypass scoped to it), two-tier panic (CC 123 = all-notes-off release, CC 120 = immediate hard-stop including Trigger one-shots); per-zone `ZonePlayParams` (Gate/Trigger, AHDSR, pitch engine Varispeed/Preserve, AD pitch mod envelope), repitch/interpolation with loop-point-aware sustain. - `sample_map` — zone payload: zones keyed by note range. **Wall-clock times stored as rate-free SECONDS, resolved against the live project rate — NO hardcoded sample rates in `src/`** (Daniel's standing ruling, load-bearing). JSON round-trip. -- `pitch_shift` — hand-rolled **correlation-aligned SOLA** (splice-overlap-add) pitch shifter for the Preserve playback mode: one active read tap chases the write head at the shift ratio; each splice jump is refined by a cross-correlation search so the new read point is waveform-aligned, then old and new taps are crossfaded (raised-cosine, amplitude-complementary). Replaces the prior dual-tap OLA whose fixed half-window tap offset caused anti-phase cancellation on many source frequencies. No third-party dependencies; RT-discipline: no allocation in `process()`. +- `pitch_shift` — hand-rolled **correlation-aligned SOLA** (splice-overlap-add) pitch shifter for the Preserve playback mode: one active read tap chases the write head at the shift ratio; each splice jump is refined by a cross-correlation search so the new read point is waveform-aligned, then old and new taps are crossfaded (raised-cosine, amplitude-complementary). Replaces the prior dual-tap OLA whose fixed half-window tap offset caused anti-phase cancellation on many source frequencies. **GA2:** ring buffer **primed with the actual upcoming source** at note-on (was zero-filled) → gap-free frame-0 onset, ~25 ms Preserve onset latency eliminated (Preserve now speaks on frame 0, matching Varispeed), and real-content-bounded tail (last-window tail-truncation gone). No third-party dependencies; RT-discipline: no allocation in `process()`. - `bank_sync` — generation change-detection + assignment-request consume: owns the yes/no decision logic so the rules are provable without a host. The processor shell owns cadence and side effects. - `bridge_marshal` — pure marshalling helper for the REAPER VST-host bridge read: interprets the `GetProjExtState` int return against its filled buffer. - `editor_geometry` — VST3 editor layout: defines the shared `Rect` type + `contains()` hit-test; provides `EditorLayout` and `layoutEditor(w,h)`. diff --git a/COMPLETED.md b/COMPLETED.md index 7ecedcc..c04159a 100644 --- a/COMPLETED.md +++ b/COMPLETED.md @@ -2843,3 +2843,28 @@ keybinding). `.vstpreset` write + `TrackFX_SetPreset` call replaces `TrackFX_SetNamedConfigParm` "vst_chunk". - [x] `bank_panel`: `SetCapture` moved to drag-arm branch; `bankPanelInvalidate()` called from `actions.cpp` after mode-toggle actions. + +--- + +## GA2 fix pass (2026-07-28) + +> **Merged to dev 2026-07-28. Integrated suite 55/55 green.** Two fixes found during +> GA DAW testing — not tracked as PLAN points. PLAN.md has no corresponding entries to move. + +**Preserve pitch engine — ring prime:** `pitch_shift` now **primes its ring buffer with the +actual upcoming source** at note-on (was zero-filled). Result: gap-free frame-0 onset (the +~25 ms Preserve onset latency is gone — Preserve now speaks on frame 0, matching Varispeed), +clean repitch across the full C1–C8 range, and a real-content-bounded tail (the last-window +tail-truncation that previously clipped the decay is gone). + +**Takeover declick rev-2/3 — bounded blend:** The declick mechanism is now a **bounded blend** +(`out*(1-w) + ref*w`, w decaying from 1.0 over the blend window), applied at every takeover +boundary: mono retrig/fallback, poly at-cap steal, and **preview re-trigger**. This supersedes +the earlier `(1-amp)` envelope-complement gate that zeroed the compensation on Trigger and +zero-attack restarts, leaving a click Daniel still heard in those cases. The preview card is +now covered by the same mechanism. + +- [x] `pitch_shift`: ring buffer primed with actual upcoming source at note-on; `pitch_shift_tests` + updated. +- [x] `sampler_core` `Voice::start`: declick changed from `(1-amp)` gate to bounded blend + (`out*(1-w) + ref*w`, w 1.0→0); covers mono retrig + poly at-cap steal + preview re-trigger.