feat(S9/S8): bank-generation change-detection + instrument-side assignment reader

Extension stamps a monotonic bank_generation counter, bumped at content
mutations; the VST3 instrument polls it off-thread on an editor timer and
consumes assignment requests, refreshing playback hands-free.
This commit is contained in:
2026-07-26 23:46:06 -04:00
parent 725f3e7d3c
commit 66d47fb410
21 changed files with 838 additions and 36 deletions
+21
View File
@@ -96,6 +96,7 @@
#include "app_version.h"
#include "capture_paths.h"
#include "prune_reconcile.h"
#include "vst/bank_sync.h" // parseBankGeneration / formatBankGeneration (SHARED with the instrument reader)
#define REAPERAPI_MINIMAL
#define REAPERAPI_WANT_EnumProjects
@@ -252,6 +253,16 @@ bool ReaSamplerSession::saveToActiveProject() {
SetProjExtState(static_cast<ReaProject*>(proj), projExtNamespace(),
kProjExtVersionKey, stampVersion().c_str());
// S9: stamp the current bank-generation counter under its own wire-shared key, on the SAME
// seam so the counter and MarkProjectDirty stay paired. The value is whatever
// bumpBankGeneration() advanced it to since the last save (0 if never bumped / pre-S9), so
// every content mutation's own save carries the fresh generation the instrument reads. The
// format is the SHARED pure encoder (vst::formatBankGeneration) so writer and reader agree
// byte-for-byte — a decimal integer. Additive: does not disturb the blobs above.
SetProjExtState(static_cast<ReaProject*>(proj), projExtNamespace(),
kProjExtBankGenKey,
vst::formatBankGeneration(bankGeneration_).c_str());
MarkProjectDirty(static_cast<ReaProject*>(proj));
return true;
}
@@ -580,6 +591,16 @@ void ReaSamplerSession::loadFromProject(void* proj, const std::string& projectDi
kProjExtVersionKey)
: std::string{});
// S9: recover the bank-generation counter on EVERY load path (peer-symmetry with
// writingVersion_/tail_/view_ above), so it continues monotonic from the stored value
// rather than resetting to 0 on reopen — a next bump then reads > the stored value. A
// project switch reads THAT project's counter, not the previous one's; an absent/malformed
// stamp (pre-S9 or corrupt) parses to 0 via the SHARED decoder. proj == nullptr -> 0.
bankGeneration_ = vst::parseBankGeneration(
proj ? getProjExtStateString(static_cast<ReaProject*>(proj), projExtNamespace(),
kProjExtBankGenKey)
: std::string{});
if (!proj) {
book_ = BankBook{};
return;