Move completed M4 (persist) from PLAN.md to COMPLETED.md with a decisions note (GUID identity, Save-As copy, MarkProjectDirty, known limitation); update CLAUDE.md repo-identity to M0-M4 complete.
11 KiB
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 5 — bank_panel (docked grid)
Goal: Docked LICE-drawn grid: thumbnails (from peaks), audition,
multi-select, keyboard navigation. Reuses the docking setup from the retired
mpe_view.cpp. CONTEXT.md §bank_panel.
Verify (in DAW): Grid docks; thumbnails render from computed peaks; audition
plays selected sample; multi-select + keyboard nav work.
- Docked window + LICE grid render loop.
- Thumbnail draw from
peaksbins. - Audition (play selected sample) + stop.
- Multi-select + keyboard navigation.
Milestone 6 — insert (placement)
Goal: "Insert selected sample at edit cursor" via InsertMedia. CONTEXT.md
§insert, Build order 6.
Verify (in DAW): Selected sample inserts at the edit cursor wrapped in
Undo_BeginBlock2 / Undo_EndBlock2; conform-to-tempo is an explicit flag —
no silent time-stretch when off.
InsertMedia(path, mode)at edit cursor (verify mode bits against SDK).- Conform-to-project-tempo vs literal as an explicit flag (never silent).
- Undo-block wrapping.
Milestone 7 — capture action family
Goal: Bindable capture actions for master / selected tracks / selected items / razor area, each with wet-dry + tail options. CONTEXT.md §actions, Build order 7. Verify (in DAW): Each action registered (bindable in Actions list), routes to the offline backend, and honors wet/dry + tail. Load-bearing principle: none auto-inserts into the arrange.
- Source resolvers: master mix, selected tracks, selected items, razor area
(
GetSet_LoopTimeRange,P_RAZOREDITS,CountSelectedMediaItems, etc.). - Register each as a bindable action (
command_id/gaccel/hookcommand). - Wet-dry + tail options per action.
- Review gate: confirm no capture path touches the timeline.
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).
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;
InsertMediapath 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)
- Thumbnail cache: recompute peaks vs store peak bins alongside the index. (touches M2/M4/M5)
parseIntnarrowing hardening:src/bank_model.cppparseIntcastsint64_t → intviastatic_castwithout a range check; integers that fit in int64 but exceedINT_MAXare implementation-defined. Hardening candidate — add bounds check before the cast when integer-field validation is in scope.
Phase D — Design View (parallel to the M0–M11 capture roadmap)
Separate phase namespace. The M-numbers belong to the capture pillar (M0–M11, M4 in flight). Design View is an orthogonal feature on its own track, so it uses a lettered namespace (D1, D2, …) rather than extending the M-line. This keeps the two roadmaps from colliding on numbering and reads correctly: Phase D is not "the twelfth capture step," it's a different pillar. Authoritative spec: CONTEXT.md §Design View. Product framing:
docs/product/design-view.md. When a point lands, doc-keeper moves it toCOMPLETED.md.
D1 — view_model (pure)
Goal: REAPER-free mode registry + membership index + folder-tree-aware
visibility derivation + parking/restore planner + JSON round-trip. The heart of the
phase; mirror of bank_model. CONTEXT.md §Design View (Module architecture — pure).
Verify: CTest green. N-mode model (not a boolean); Arrange + Design seeded.
Restore-planner round-trip (snapshot → park → restore) returns every driven flag to
its captured value. Parent-derivation correct against a supplied folder tree.
JSON round-trip lossless across modes + membership + show-both + snapshots + active
mode.
- Mode registry: ordered (id, display name, ordinal); Arrange + Design seeded; add/query more modes (prove N-mode, not binary).
- Membership index:
GUID → { mode ids }+ per-track show-both flag; add / remove / retag / query; untagged = Arrange. - Folder-tree-aware visibility derivation: given a supplied parent↔child tree + active mode, compute the visible set (active leaves, derived-visible parents, show-both leaves, master always in).
- Parking/restore planner: emit exact (track, flag, value) op-lists for park and restore from active mode + snapshot record.
- JSON round-trip: modes + membership + show-both + snapshots + active mode.
- Tests: N-mode add/query; parent follows tagged leaf (multi-mode parent); restore-round-trip returns snapshot values (never hardcoded "on"); show-both leaf never parked; unknown/stale GUID tolerated; JSON lossless.
D2 — view shell (apply flags in the DAW)
Goal: Read the folder tree and drive REAPER flags per the planner.
CONTEXT.md §Design View (view shell, REAPER API surface).
Verify (in DAW): Toggling active mode hides + parks inactive leaves
(B_SHOWINTCP/B_SHOWINMIXER/B_MAINSEND/I_FXEN + per-FX offline) and restores
active ones from snapshot. Master untouched. B_MUTE/I_SOLO untouched.
Untagged tracks untouched. Parents follow their tagged descendants.
- Build parent↔child tree from
I_FOLDERDEPTH; feed toview_model. - Snapshot prior flag values (
GetMediaTrackInfo_Value) before parking. - Apply park/restore ops (
SetMediaTrackInfo_Valuefor the four flags;TrackFX_GetCount+ per-FXTrackFX_SetOffline). Verify flag names/signatures. - GUID resolution:
GetTrackGUID/guidToString/stringToGuid(never index). - Review gate: no path touches master visibility or
B_MUTE/I_SOLO, or any untagged track's owned flags.
D3 — persist slice (view state ↔ project ext state)
Goal: Serialize the view section into the "reasampler" namespace alongside the
bank; reapply the active mode on project open. CONTEXT.md §Design View (persist).
Verify (in DAW): Membership + active mode + snapshots survive Save / Save As /
close+reopen; on open, the active mode's visibility + processing is reapplied.
Saved-while-parked project restores parked tracks from persisted snapshots (not to a
guessed "on").
- Serialize/deserialize the view section under
"reasampler"(shared blob, distinct section from the bank index). - Reapply active mode on project open (rebuild tree, run the planner).
- Confirm survival across Save / Save As; snapshot durability across save-while-parked.
D4 — actions
Goal: Bindable action set for the mode workflow. CONTEXT.md §Design View (actions). Verify (in DAW): Each action registered (bindable in Actions list); toggle + mode-jumps MIDI-bindable; tag/untag acts on the current track selection.
- Toggle active mode (cycle; extensible to cycle-all for >2 modes).
- Activate mode: Arrange / Activate mode: Design (direct jumps).
- Tag selected tracks → Design / → Arrange; Untag selected (= → Arrange).
- Show-both for selected tracks (toggle).
- Register each (
command_id/gaccel/hookcommand); toggle + jumps MIDI-bindable.
D5 — in-window toggle affordance (UI)
Goal: The segmented mode switch in the ReaSampler / bank_panel window header. CONTEXT.md §Design View (UI). Verify (in DAW): Segmented control shows current mode (lit segment), one click flips modes via the D4 toggle action, per-mode membership count visible, offlined-FX caveat surfaced as a tooltip.
- Segmented mode switch
[ Arrange | Design ]in the window header; active lit. - Wire the switch to the toggle/activate actions from D4.
- Per-mode membership count display.
- Offlined-FX re-init caveat as a tooltip on the switch.
Phase D open questions
- Snapshot durability across save-while-parked — persisted here by decision; lean alternative is force-restore-to-Arrange on save (product notes item 4). (touches D3)
- Reconcile on delete/restructure — tagged leaf deleted or folder restructured while parked leaves a stale GUID; ignore-and-prune on next toggle/open. (touches D1/D2)
- Screenset coexistence — Design View drives the same flags a screenset recall would; confirm last-writer-wins is not surprising. (touches D2)