feat(persist): persist BankBook under banks key, retire legacy bank_index, route capture to active bank (B2)

This commit is contained in:
2026-07-23 20:26:15 -04:00
parent 5b19525ec0
commit 85993ebc3e
6 changed files with 218 additions and 55 deletions
+42 -15
View File
@@ -19,6 +19,7 @@
#include <string>
#include "bank_book.h"
#include "bank_model.h"
#include "view_mode_model.h"
@@ -28,10 +29,21 @@ namespace reasampler {
// shipped: changing it orphans every already-saved project's index.
inline constexpr const char* kProjExtNamespace = "reasampler";
// The ext-state key the index JSON is stored under (one key holds the whole
// serialized BankIndex). FOREVER-STABLE for the same reason.
// The RETIRED legacy ext-state key: pre-multi-bank projects stored the whole
// serialized BankIndex here (single bank). Phase B2 no longer WRITES it — on save
// the key is cleared (SetProjExtState with "" deletes it) and the book is written
// under kProjExtBanksKey instead. It is still READ once, on load of a legacy
// project, to migrate its single index into the pool (BankBook's parse-time
// promotion). FOREVER-STABLE as a read key for that migration path.
inline constexpr const char* kProjExtIndexKey = "bank_index";
// The multi-bank ext-state key (Phase B): one key holds the whole serialized
// BankBook — the pool folded in as bank-zero plus every named bank, each with its
// own BankIndex, ordinals, and the active-bank id. AUTHORITATIVE going forward;
// supersedes kProjExtIndexKey. FOREVER-STABLE once shipped: changing it orphans
// every already-saved project's banks.
inline constexpr const char* kProjExtBanksKey = "banks";
// The ext-state key the Design-View ViewModeModel JSON is stored under (one key
// holds the whole serialized model: modes + membership + show-both + snapshots +
// active mode). Distinct from kProjExtIndexKey — one namespace, two keys.
@@ -45,8 +57,9 @@ inline constexpr const char* kProjExtViewKey = "view_state";
// it strands the identity of every already-saved project. See persist.cpp.
inline constexpr const char* kProjExtGuidKey = "project_guid";
// Owns the session's BankIndex and drives persistence against the active REAPER
// project. One instance lives for the extension's lifetime (main.cpp). It tracks
// Owns the session's BankBook (Phase B: pool + named banks) and drives persistence
// against the active REAPER project. One instance lives for the extension's
// lifetime (main.cpp). It tracks
// the project identity it last saw so the timer tick can detect a project load
// (a different project became active) and a Save-As (SAME project, path changed):
//
@@ -62,16 +75,28 @@ inline constexpr const char* kProjExtGuidKey = "project_guid";
// W12 defect that stopped the bank reloading); the pointer catches forks (Save-As
// copies our GUID onto a distinct object — the W10 defect that clobbered a bank).
//
// The bank itself is exposed for the capture/action layer to mutate; persist
// The book itself is exposed for the capture/action layer to mutate; persist
// only reads it on save and replaces it on load.
class ReaSamplerSession {
public:
ReaSamplerSession() = default;
// The in-memory bank. The action/capture layer adds captures here; persist
// serializes it on save and replaces it on project load.
BankIndex& bank() { return bank_; }
const BankIndex& bank() const { return bank_; }
// The multi-bank book (Phase B): the pool + named banks, each wrapping a
// BankIndex, plus the active-bank id. The action layer (B3) creates / renames /
// reorders / deletes banks and moves samples here; the panel (B4) reads it;
// persist serializes it under the `banks` key on save and replaces it on load.
BankBook& book() { return book_; }
const BankBook& book() const { return book_; }
// The capture add-target: the ACTIVE bank's BankIndex (defaults to the pool).
// The capture path adds a captured Sample through this seam, so a capture lands
// in whichever bank is active — the single behavioural change B2 wires in over
// M7/M8 (the capture backends are untouched; only the target index moved). The
// panel/insert readers that displayed the single index continue to read it here
// unchanged; today it resolves to the pool (default active), matching prior
// single-bank behaviour, until B3/B4 let the user switch the active bank.
BankIndex& bank() { return book_.activeIndex(); }
const BankIndex& bank() const { return book_.activeIndex(); }
// The in-memory Design-View model. The view/action layer mutates it (tag,
// toggle, snapshot); persist serializes it on save and replaces it on project
@@ -80,8 +105,9 @@ public:
ViewModeModel& view() { return view_; }
const ViewModeModel& view() const { return view_; }
// Serialize the current bank to the active project's ext state (namespace
// "reasampler"). Non-destructive beyond writing our own ext-state key. Safe
// Serialize the current book (under the `banks` key) and view model to the active
// project's ext state (namespace "reasampler"), and clear the retired legacy
// `bank_index` key. Non-destructive beyond writing our own ext-state keys. Safe
// to call when there is no active/saved project (it no-ops).
void saveToActiveProject();
@@ -103,7 +129,7 @@ public:
bool consumeLoadSignal();
private:
BankIndex bank_;
BankBook book_;
// The Design-View model. Default-constructed = Arrange + Design seeded, active
// = Arrange; loadFromProject leaves this default when a project has no stored
@@ -124,9 +150,10 @@ private:
bool primed_ = false; // false until the first poll() observes state
bool loadPending_ = false; // raised by loadFromProject; drained by consumeLoadSignal
// Load the index from the given project's ext state and resolve bank paths
// against projectDir. Replaces the in-memory bank. projectDir empty -> clears
// the bank (unsaved project has no resolvable bank).
// Load the book from the given project's ext state (the `banks` key, else the
// legacy `bank_index` key migrated into the pool) and resolve bank paths against
// projectDir at read time. Replaces the in-memory book. projectDir empty -> the
// book is reset to empty (unsaved project has no resolvable banks).
void loadFromProject(void* proj, const std::string& projectDir);
};