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

10 KiB
Raw Blame History

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)