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:
@@ -122,6 +122,27 @@ public:
|
||||
// resolved selection id ("" if nothing was loaded) for the editor to reflect.
|
||||
std::string reloadFromBank();
|
||||
|
||||
// The result of a bank-sync poll (S9/S8): what pollBankSync did this tick, so the editor
|
||||
// can react (repaint / re-snapshot its own view) only when something actually changed.
|
||||
struct BankSyncResult {
|
||||
bool reloaded = false; // the bank generation changed -> reloadFromBank ran
|
||||
bool applied = false; // a new assignment request was applied -> selection changed
|
||||
};
|
||||
|
||||
// Poll the S9 bank-generation counter and the S8 assignment request over the bridge, OFF
|
||||
// THE AUDIO THREAD (the editor's UI timer drives this — NEVER process()). Semantics:
|
||||
// * S9: if the bank generation differs from what we last saw, call reloadFromBank() so a
|
||||
// recapture/ingest refreshes playback hands-free (atomic swap, glitch-free).
|
||||
// * S8: if a NEW (generation > last consumed) assignment request names a resolvable
|
||||
// sample AND this instance is the target (isFocusedTarget), apply it as the selection
|
||||
// and reload; an unresolvable request is DROPPED silently (marker advanced, no change);
|
||||
// a non-target instance neither applies nor advances its marker.
|
||||
// The consumed marker advances in component state (marked dirty via the host handler) so a
|
||||
// re-open does not re-apply. `isFocusedTarget` is the shell's thundering-herd policy input
|
||||
// (the editor passes true only for the instance whose editor is open — see the handoff).
|
||||
// Idempotent on an idle tick (generation unchanged + no new request -> no work).
|
||||
BankSyncResult pollBankSync(bool isFocusedTarget);
|
||||
|
||||
// The bridge, for the editor's live-state readout + sample list. Owned here; the
|
||||
// editor borrows it (outlives the editor).
|
||||
ReaperBridge& bridge() { return bridge_; }
|
||||
@@ -210,6 +231,22 @@ private:
|
||||
std::mutex channelModeMutex_;
|
||||
ChannelMode channelMode_ = ChannelMode::Mono;
|
||||
|
||||
// The last assignment-request generation this instance CONSUMED (S8 reader). Persisted in
|
||||
// component state (v5) so a re-open does not re-apply a request the user already got and
|
||||
// then changed away from. Written by pollBankSync (UI/timer thread) and getState; read by
|
||||
// pollBankSync + getState; seeded by setState. Guarded against a getState/poll race. NEVER
|
||||
// read on the audio thread. Default 0 -> a genuinely new first assign (gen >= 1) applies.
|
||||
std::mutex assignMarkerMutex_;
|
||||
std::int64_t lastConsumedAssignGeneration_ = 0;
|
||||
|
||||
// The bank generation this instance last SAW (S9 reader). UI/timer-thread only (pollBankSync
|
||||
// is the sole reader/writer) — no mutex needed, and it is NOT persisted. Initialized to a
|
||||
// -1 SENTINEL (no real generation can be negative — parseBankGeneration yields >= 0) so the
|
||||
// FIRST poll after an editor open BASELINES the seen value without a redundant reload (setState
|
||||
// already loaded the current bank); a subsequent generation CHANGE then drives the reload.
|
||||
// NOT read on the audio thread.
|
||||
std::int64_t lastSeenBankGeneration_ = -1;
|
||||
|
||||
// Latched from setupProcessing so setActive/reload can size against it. Read
|
||||
// off-thread only.
|
||||
double sampleRate_ = 44100.0;
|
||||
|
||||
Reference in New Issue
Block a user