Close the FX reclaimer's undo span at the state-load hook, and expose the drain's direction
A Ctrl-Z mid-span used to close the pre-undo block over post-undo state; it now terminates at BeginLoadProjectState and stops the drain where it stands. fxReclaimActivity reports Online/Offline/Idle; fxReclaimPending is unchanged.
This commit is contained in:
@@ -60,6 +60,15 @@ void closeSpan() {
|
||||
if (ValidatePtr2(nullptr, proj, "ReaProject*")) Undo_EndBlock2(proj, label.c_str(), mask);
|
||||
}
|
||||
|
||||
// Monotonic and sub-millisecond: the drain's budget discriminates at 30ms, which
|
||||
// GetTickCount's ~16ms granularity cannot do. No wraparound to guard against, so
|
||||
// util::elapsedAtLeast (whose whole reason for existing is that wrap) does not apply.
|
||||
double monotonicMs() {
|
||||
return std::chrono::duration<double, std::milli>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
|
||||
// TrackFX_SetOffline is ASSUMED to pump the message loop, so a plugin load can re-enter
|
||||
// the timer and land back here mid-drain. Fail closed: a re-entered drain would derive a
|
||||
// second queue for the same project and race the outer one's writes track for track.
|
||||
@@ -122,21 +131,18 @@ struct ReclaimHost {
|
||||
return cachedTrack_;
|
||||
}
|
||||
|
||||
// Monotonic and sub-millisecond: the budget below discriminates at 30ms, which
|
||||
// GetTickCount's ~16ms granularity cannot do. No wraparound to guard against, so
|
||||
// util::elapsedAtLeast (whose whole reason for existing is that wrap) does not apply.
|
||||
double nowMs() const {
|
||||
return std::chrono::duration<double, std::milli>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
double nowMs() const { return monotonicMs(); }
|
||||
|
||||
// The SAME predicate applyMode refuses a real mode switch on, deliberately shared
|
||||
// rather than re-read here: "the transport is rolling" is one fact with one home
|
||||
// (view.h). drainTick narrows what it gates with this answer to offline work only.
|
||||
bool transportRolling() const { return transportBlocksModeSwitch(project); }
|
||||
|
||||
// Refusing here ends the drain's REMAINING tracks after an abandon: drainTick treats a
|
||||
// false as "this track is gone", so the rest of the queue costs one bool apiece and
|
||||
// reaches no REAPER call at all.
|
||||
bool liveChain(const std::string& guid, std::vector<std::string>& out) {
|
||||
if (g_queue.abandoned()) return false;
|
||||
MediaTrack* tr = resolveForStep(guid);
|
||||
if (!tr) return false;
|
||||
const int count = TrackFX_GetCount(tr);
|
||||
@@ -148,7 +154,11 @@ struct ReclaimHost {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ...and here for the track being written RIGHT NOW, whose own pump is how the state
|
||||
// load arrived. FIRST, before the openSpan below: a write past the terminator would
|
||||
// both land in the project the load is replacing and reopen the span it just closed.
|
||||
bool writeOffline(const std::string& guid, int fxIndex, bool offline) {
|
||||
if (g_queue.abandoned()) return false;
|
||||
MediaTrack* tr = resolveForStep(guid);
|
||||
if (!tr) return false;
|
||||
if (fxIndex < 0 || fxIndex >= TrackFX_GetCount(tr)) return false; // chain shrank
|
||||
@@ -199,6 +209,20 @@ void reportDrops(ReaProject* proj, const std::vector<TrackFxDrops>& drops) {
|
||||
|
||||
bool fxReclaimPending() { return g_queue.pending(); }
|
||||
|
||||
FxReclaimActivity fxReclaimActivity() {
|
||||
// The QUEUE's own pin, never the active tab: the direction is a fact about the project
|
||||
// the drain is converging, and reading another tab's transport into it would gate the
|
||||
// answer on a playhead that has nothing to do with this work. No pin means idle — and
|
||||
// costs the caller no REAPER call at all, which is what makes this safe to poll per
|
||||
// frame.
|
||||
ReaProject* const proj = g_queue.project();
|
||||
if (!proj) return FxReclaimActivity::Idle;
|
||||
// The pin is only re-tested at beginTick; a poll landing between two ticks can find the
|
||||
// project already gone, and GetPlayStateEx is about to be handed this pointer.
|
||||
if (!ValidatePtr2(nullptr, proj, "ReaProject*")) return FxReclaimActivity::Idle;
|
||||
return g_queue.activity(monotonicMs(), transportBlocksModeSwitch(proj));
|
||||
}
|
||||
|
||||
void invalidateFxReclaim() {
|
||||
// A generation bump and a flag, and deliberately nothing more. Discarding the queue
|
||||
// here is what let a re-entered apply free the element the outer drain was writing
|
||||
@@ -256,6 +280,14 @@ void fxReclaimLabelUndoSpan(const char* label, int mask) {
|
||||
|
||||
void fxReclaimEndUndoSpan() { closeSpan(); }
|
||||
|
||||
void fxReclaimAbandonForStateLoad() {
|
||||
// Close FIRST and unconditionally. The abandon below only stops future writes; the
|
||||
// block is what a Ctrl-Z would otherwise close over the wrong state, and it is open
|
||||
// whether or not a drain is on the stack at all.
|
||||
closeSpan();
|
||||
g_queue.abandon();
|
||||
}
|
||||
|
||||
void fxReclaimFlushRelease(ReaSamplerSession& session) {
|
||||
// GetCurrentProjectInLoadSave() names the project THIS hook is being asked to save
|
||||
// (reaper_plugin_functions.h:1582-1587) -- not merely the active tab, which a
|
||||
|
||||
Reference in New Issue
Block a user