Files
reasampler/PLAN.md
T
daniel d9081090fd docs(multi-bank): frame Phase B — pool + named banks spec
Add the multi-bank pillar as Phase B (lettered, parallel to M-line and
Phase D): CONTEXT.md §Multi-bank spec, PLAN.md Phase B block (B1 bank_book
pure / B2 persist / B3 actions / B4 panel split), and docs/product/multi-bank.md
framing. Additive only — BankIndex, the M0–M11 roadmap, and Phase D untouched.
2026-07-23 12:52:06 -04:00

185 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PLAN.md — ReaSampler milestone roadmap
Living milestone roadmap for ReaSampler. Derived from CONTEXT.md's 11-step build
order; CONTEXT.md remains the authoritative spec — this file is the tickable
checklist, not a re-statement of the spec. When a point lands, doc-keeper removes
it here and appends it to `COMPLETED.md`.
**Conventions**
- One checkbox `- [ ]` = one discrete, independently-landable point.
- Each milestone opens with a **Goal** (one line) and a **Verify** criterion
(the acceptance gate; precision invariants pulled in where one applies).
- Verify-in-DAW points require a manual REAPER run; pure points are gated by CTest.
- "See CONTEXT.md §…" points at the authoritative detail — do not duplicate it here.
---
## Milestone 8 — RealtimeRecordBackend
**Goal:** Realtime record behind the same `ICaptureBackend`, producing identical
bank entries. CONTEXT.md §capture (realtime), §Precision invariants.
**Verify (in DAW):** Hidden temp track resamples wet output; recorded file moves
into the bank; **non-destructive** — temp track removed cleanly, source routing
and user monitoring restored unchanged.
**Note (from M3):** The realtime backend captures during playback and does NOT invoke the offline-render path, so it is inherently dialog-free (no render-progress window) — a secondary benefit beyond hardware/performed-FX capture.
- [ ] Hidden-track resample recipe (`I_RECMODE`/`I_RECINPUT`/`I_RECARM`,
`CSurf_OnRecord`/`CSurf_OnStop`); verify record-mode values against SDK.
- [ ] Resolve wet-master routing that does not alter user monitoring (open
question).
- [ ] Move recorded source into bank; populate identical `Sample`; clean teardown.
## Milestone 9 — slots (MPC-style)
**Goal:** "Capture to slot N" / "insert slot N", MIDI-bindable. CONTEXT.md
Build order 9.
**Verify (in DAW):** Slot capture and slot insert fire from MIDI bindings; slot
state persists via the index.
- [ ] Slot model + slot↔sample assignment.
- [ ] "Capture to slot N" / "insert slot N" actions, MIDI-bindable.
## Milestone 10 — provenance + null-test verify action
**Goal:** Provenance (parent sample id + FX-chain snapshot) and "re-capture from
source"; ship the null-test verification action. CONTEXT.md §Precision invariants,
Build order 10.
**Verify (in DAW):** **Null test** — a dry offline capture of a range, re-inserted
at its source position, nulls to silence against the source. This action is the
tool's trust anchor and must pass.
- [ ] Provenance fields populated on resample-from-sample (parent id + FX-chain
snapshot string).
- [ ] "Re-capture from source" action.
- [ ] Null-test verification action (capture → re-insert at source pos → assert
silence sum).
**Note (from M7):** The null test requires a TRUE pre-FX dry capture, which REAPER offline render cannot produce via RENDER_SETTINGS (there is no pre-FX bit). True dry must be obtained by bypassing the source FX around an offline render (snapshot→bypass→render→restore) OR via the M8 realtime pre-FX path — so the dry-capture mechanism should be designed as part of the M10 null-test work.
## Milestone 11 — polish
**Goal:** Batch capture (per selected item / per razor area),
resample-and-mute-source, conform-on-insert, native OS drag-out. CONTEXT.md
Build order 11, §Non-goals (drag-out deferred to last).
**Verify (in DAW):** Each polish action works without regressing the precision
invariants; drag-out places a valid file in the OS target.
- [ ] Batch capture: per selected item / per razor area.
- [ ] Resample-and-mute-source.
- [ ] Conform-on-insert (explicit).
- [ ] Native OS drag-out (deferred final; `InsertMedia` path must already work).
---
## Open questions to resolve during build
Carried from CONTEXT.md §Open questions — keep visible until each is closed by a
landed milestone.
- **Realtime wet-master routing** that captures master output without altering the
user's monitoring. (blocks M8)
- **`parseInt` narrowing hardening:** `src/bank_model.cpp` `parseInt` casts
`int64_t → int` via `static_cast` without a range check; integers that fit
in int64 but exceed `INT_MAX` are implementation-defined. Hardening candidate
— add bounds check before the cast when integer-field validation is in scope.
---
# Phase B — Multi-bank (parallel to the M0M11 capture roadmap and Phase D)
> **Separate phase namespace.** The M-numbers belong to the capture pillar
> (M0M11); the D-letters belong to Design View. Multi-bank is a third orthogonal
> pillar — generalizing the single bank into a pool + named banks — so it takes its
> own **lettered** namespace (B1, B2, …). "B" reads for **Banks** and, like Phase D,
> keeps the roadmaps from colliding on numbering: Phase B is not "the twelfth
> capture step," it is a different pillar. Authoritative spec: **CONTEXT.md
> §Multi-bank**. Product framing: `docs/product/multi-bank.md`. When a point lands,
> doc-keeper moves it to `COMPLETED.md`.
## B1 — bank_book (pure)
**Goal:** REAPER-free bank registry wrapping N `BankIndex` instances: pool seeded +
privileged, create/rename/reorder/delete named banks, active-bank id, move/copy a
sample between banks, JSON round-trip + legacy-migration. The heart of the phase;
mirror of `bank_model` / `view_mode_model`; **`BankIndex` untouched (additive)**.
CONTEXT.md §Multi-bank (Module architecture — pure).
**Verify:** CTest green. Pool always present, un-deletable, un-renamable (rules
rejected in-model). Active-bank defaults to pool. Move is index-only (source loses
entry, destination gains it) and observes destination collapse-by-hash; copy leaves
source intact. JSON round-trip lossless across banks + per-bank indices + ordinals +
active id. Legacy `bank_index` JSON parses into `{ pool }` with zero named banks.
- [ ] Bank registry: ordered `{ bank id, display name, ordinal, BankIndex }`; pool
seeded with fixed id + fixed name; create / rename / reorder / delete named banks.
- [ ] Pool-privilege rules enforced in-model: reject delete-pool, reject
rename-pool, never allow zero banks.
- [ ] Active-bank id (get/set; defaults to pool); resolve active bank's `BankIndex`.
- [ ] Move sample between banks (index-only; destination collapse-by-hash observed;
source entry removed).
- [ ] Copy sample between banks (index-only; source entry retained; destination
collapse-by-hash observed).
- [ ] JSON round-trip: banks + per-bank indices + ordinals + active id.
- [ ] Legacy migration: a bare `bank_index` JSON promotes to the pool's index with
zero named banks (one-way, lossless).
- [ ] Tests: pool privileges (delete/rename rejected); move source-loses/dest-gains;
copy source-retained; cross-bank same-hash coexistence; dest collapse on move into
a bank already holding the hash; JSON lossless; legacy migration.
## B2 — persist slice (banks ↔ project ext state)
**Goal:** Serialize the book under the `banks` key in `"reasampler"` alongside the
existing sections; migrate a legacy `bank_index` key into the pool on first load;
reload-on-open and Save-As survival via the existing M4 machinery. CONTEXT.md
§Multi-bank (persist). **Verify (in DAW):** Banks + named banks + active bank + all
per-bank samples survive Save / Save As / close+reopen; **relative paths only**;
bank travels with the `.rpp`; a project saved before this phase (legacy `bank_index`
only) loads as pool + zero named banks with no sample loss.
**Depends on:** B1. Resolve the `bank_index` retirement-vs-retention fork first
(see Phase B open questions).
- [ ] Serialize/deserialize the book under the `banks` key (shared blob, distinct
section from `bank_index` / `view_state`).
- [ ] Legacy-migration path on load: absent `banks` + present `bank_index` → pool.
- [ ] Session exposes the book; the active bank's `BankIndex` is the capture add
target (route the M7 capture family through it — additive to M7, no M7 rewrite).
- [ ] Confirm survival across Save / Save As; confirm legacy-project load path.
## B3 — actions
**Goal:** Bindable action set for the multi-bank workflow. CONTEXT.md §Multi-bank
(actions). **Verify (in DAW):** Each action registered (bindable in Actions list);
bank-activate + move/copy MIDI-bindable; create/rename/delete drive the B1 model via
the B2-persisted session.
**Depends on:** B1, B2.
- [ ] Create bank / rename bank / delete bank (delete honors the resolved
member-disposition rule — see open questions).
- [ ] Activate bank (direct-by-id + cycle).
- [ ] Move selected samples → bank / copy selected samples → bank.
- [ ] Pool full-height / banks full-height toggles.
- [ ] Register each (`command_id`/`gaccel`/`hookcommand`); bank-activate + move/copy
MIDI-bindable.
## B4 — bank_panel vertical split (UI)
**Goal:** The vertical-split bank window — pool on top, named-banks tab-page region
below, full-height toggles — extending the M5 docked grid. CONTEXT.md §Multi-bank
(bank_panel). **Verify (in DAW):** Pool grid renders on top; named-banks tab strip
below (empty when no named banks, one tab per named bank); active-bank indicated;
both full-height toggles collapse the split correctly; sample move/copy affordance
works (drag and/or menu); the Design View mode switch in the header is unaffected.
**Depends on:** B1, B2, B3.
- [ ] Vertical split: pool grid region (top) + named-banks tab-page region (bottom).
- [ ] Named-banks tab strip: one tab per named bank; empty state when none.
- [ ] Pool full-height / banks full-height toggle affordances wired to B3.
- [ ] Active-bank indicator.
- [ ] Create / rename / delete / activate affordances driving B3 actions.
- [ ] Sample move/copy affordance (drag between regions and/or "send to bank" menu).
## Phase B open questions
- **`bank_index` key retirement vs. retention** — (a) fold the pool into the `banks`
blob and retire the legacy key, vs (b) keep `bank_index` as the pool's canonical
slot and store only named banks under `banks`. Leaning (b) for additive-minimalism
against in-flight M7/M8 persist work. Resolve before B2. (blocks B2)
- **Named-bank delete → member disposition** — (i) reabsorb into pool, (ii)
orphan-check by file reference, (iii) forbid non-empty delete. Leaning (i).
(touches B1/B3)
- **Move affordance + tab rendering** — drag vs. menu as primary; SWELL-native vs.
LICE-drawn tabs. Verify SWELL tab-control availability against the M5 reference.
(touches B4)
- **Active-bank indicator placement** — per-region headers vs. single header
readout. (touches B4)