Clear the park snapshot where the restore is planned, not where it drains; make the drain re-entrant and reload-aware
This commit is contained in:
@@ -9,7 +9,6 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "core/view/view_mode_model.h"
|
||||
#include "shell/capture/track_guid.h"
|
||||
|
||||
#define REAPERAPI_MINIMAL
|
||||
@@ -40,9 +39,11 @@ void adoptOwner(ReaProject* proj) {
|
||||
g_owner = p;
|
||||
}
|
||||
|
||||
// TrackFX_SetOffline unloads and re-instantiates the plugin, so writing a state
|
||||
// that already holds is not a no-op — it is the expensive half of a mode switch
|
||||
// spent on nothing.
|
||||
// [verify — DAW] TrackFX_SetOffline unloads and re-instantiates the plugin, so
|
||||
// writing a state that already holds is assumed to cost the same unload/reload
|
||||
// as a real change — the header documents no internal short-circuit either way.
|
||||
// The compare is cheap and correct regardless; only the size of what it saves is
|
||||
// unconfirmed.
|
||||
void setOfflineIfChanged(MediaTrack* tr, int fx, bool offline) {
|
||||
if (TrackFX_GetOffline(tr, fx) == offline) return;
|
||||
TrackFX_SetOffline(tr, fx, offline);
|
||||
@@ -94,9 +95,24 @@ std::vector<std::string> liveFxGuids(MediaTrack* tr) {
|
||||
return guids;
|
||||
}
|
||||
|
||||
void deferFxPark(ReaProject* proj, const std::string& guid) {
|
||||
PreParkFx snapshotFxOffline(MediaTrack* tr, const std::vector<FxOfflineOp>& cancelled) {
|
||||
PreParkFx carried = preParkFxFromCancelledRestore(cancelled);
|
||||
if (!carried.states.empty()) return carried;
|
||||
|
||||
// Nothing was cancelled, so the chain reads true. fxKeying stays Identity —
|
||||
// a live capture always knows the chain.
|
||||
PreParkFx live;
|
||||
const std::vector<std::string> guids = liveFxGuids(tr);
|
||||
live.states.reserve(guids.size());
|
||||
for (std::size_t fx = 0; fx < guids.size(); ++fx)
|
||||
live.states.push_back(
|
||||
FxOfflineState{guids[fx], TrackFX_GetOffline(tr, static_cast<int>(fx)) ? 1 : 0});
|
||||
return live;
|
||||
}
|
||||
|
||||
std::vector<FxOfflineOp> deferFxPark(ReaProject* proj, const std::string& guid) {
|
||||
adoptOwner(proj);
|
||||
g_queue.park(guid);
|
||||
return g_queue.park(guid);
|
||||
}
|
||||
|
||||
void deferFxRestore(ReaProject* proj, const std::string& guid, std::vector<FxOfflineOp> ops) {
|
||||
@@ -104,23 +120,35 @@ void deferFxRestore(ReaProject* proj, const std::string& guid, std::vector<FxOff
|
||||
g_queue.restore(guid, std::move(ops));
|
||||
}
|
||||
|
||||
void drainDeferredFxParks(ViewModeModel& model) {
|
||||
void discardDeferredFxParks() {
|
||||
g_queue.clear();
|
||||
g_owner = nullptr;
|
||||
}
|
||||
|
||||
void drainDeferredFxParks() {
|
||||
if (g_queue.empty()) return;
|
||||
|
||||
if (g_owner != currentProject()) {
|
||||
// The project the intents were planned against was closed or switched
|
||||
// away from: its tracks are not ours to write and its handles may be
|
||||
// gone. Discard rather than apply.
|
||||
g_queue.clear();
|
||||
g_owner = nullptr;
|
||||
// gone. Discard rather than apply. This catches only a DIFFERENT live
|
||||
// pointer; a REAPER-recycled address is caught upstream, by the caller
|
||||
// discarding on the session's own load transition.
|
||||
discardDeferredFxParks();
|
||||
return;
|
||||
}
|
||||
|
||||
ReaProject* const proj = g_owner;
|
||||
|
||||
// Detached before the first write: applying pumps the message loop (plugins
|
||||
// load and unload), so a re-entrant switch can enqueue while this runs.
|
||||
const std::vector<FxParkIntent> draining = g_queue.take();
|
||||
|
||||
std::unordered_map<std::string, MediaTrack*> byGuid;
|
||||
const int count = CountTracks(g_owner);
|
||||
const int count = CountTracks(proj);
|
||||
byGuid.reserve(static_cast<std::size_t>(count));
|
||||
for (int i = 0; i < count; ++i) {
|
||||
MediaTrack* tr = GetTrack(g_owner, i);
|
||||
MediaTrack* tr = GetTrack(proj, i);
|
||||
if (!tr) continue;
|
||||
std::string guid = guidString(tr);
|
||||
if (!guid.empty()) byGuid.emplace(std::move(guid), tr);
|
||||
@@ -128,7 +156,7 @@ void drainDeferredFxParks(ViewModeModel& model) {
|
||||
|
||||
FxRestoreDrops drops;
|
||||
int dropTracks = 0;
|
||||
for (const FxParkIntent& intent : g_queue.pending()) {
|
||||
for (const FxParkIntent& intent : draining) {
|
||||
auto it = byGuid.find(intent.guid);
|
||||
if (it == byGuid.end()) continue; // track deleted since the switch — prune
|
||||
if (intent.park) {
|
||||
@@ -140,11 +168,8 @@ void drainDeferredFxParks(ViewModeModel& model) {
|
||||
drops.add(d);
|
||||
++dropTracks;
|
||||
}
|
||||
model.clearSnapshot(intent.guid);
|
||||
}
|
||||
|
||||
g_queue.clear();
|
||||
|
||||
// Captured FX state that could not be applied is REPORTED — silence would read
|
||||
// to the user as "restore worked" while an FX sat at whatever state the park
|
||||
// left it in. The "!SHOW:" prefix (reaper_plugin_functions.h:6536) keeps it from
|
||||
|
||||
Reference in New Issue
Block a user