Strip FX writes from the Design View mode switch; split view.cpp into park/lanes/reconcile/tracks and add the record-scoped undo repair pass
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
// See view_park.h. Compiled into the reaper_reasampler module; includes
|
||||
// reaper_plugin_functions.h without REAPERAPI_IMPLEMENT (main.cpp owns that).
|
||||
|
||||
#include "shell/view/view_park.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define REAPERAPI_MINIMAL
|
||||
#define REAPERAPI_WANT_GetMediaTrackInfo_Value
|
||||
#define REAPERAPI_WANT_SetMediaTrackInfo_Value
|
||||
#define REAPERAPI_WANT_TrackFX_GetCount
|
||||
#define REAPERAPI_WANT_TrackFX_GetFXGUID
|
||||
#define REAPERAPI_WANT_TrackFX_GetOffline
|
||||
#define REAPERAPI_WANT_guidToString
|
||||
#include "reaper_plugin_functions.h"
|
||||
|
||||
namespace reasampler {
|
||||
|
||||
namespace {
|
||||
|
||||
// The FX's own durable identity, braced exactly like the track GUID keys. Empty
|
||||
// when REAPER reports none — an FX we cannot name is one we cannot restore, and
|
||||
// fx_offline treats it that way rather than guessing at its slot. Lifetime is
|
||||
// settled: the string copy is taken immediately and the GUID* is never held past
|
||||
// this call (reaper_plugin_functions.h:7348 documents no null contract for
|
||||
// TrackFX_GetFXGUID; treating null as "no identity" is the safe read).
|
||||
//
|
||||
// [verify — DAW] STABILITY across a chain mutation is not settled the same way:
|
||||
// confirm the GUID for one FX instance survives a native drag-reorder, an SWS move
|
||||
// (SNM_MoveOrRemoveTrackFX — SWS issue #802 reports the FXID lines do not follow
|
||||
// the plugin after that call, i.e. wrong-plugin restores or mass drops through
|
||||
// fx_offline on that path specifically), a save/reload round trip, and two live
|
||||
// instances of one plugin type staying distinguishable. See src/shell/view/CLAUDE.md's
|
||||
// Gotchas for the SWS-path risk this leaves open.
|
||||
std::string fxGuidString(MediaTrack* tr, int fx) {
|
||||
GUID* g = TrackFX_GetFXGUID(tr, fx);
|
||||
if (!g) return {};
|
||||
char buf[64] = {0}; // guidToString needs a >=64-char destination (SDK contract)
|
||||
guidToString(g, buf);
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
// Captures prior driven-flag state, and the chain's per-FX offline state, before
|
||||
// parking. Never reads B_MUTE/I_SOLO; ints preserve whatever REAPER reported
|
||||
// (TrackSnapshot's defensive contract). The plain 0..TrackFX_GetCount-1 enumeration
|
||||
// — never the 0x1000000/0x2000000 input-FX or container forms — is the same one the
|
||||
// reclaimer resolves against. `fxKeying` stays Identity: a live read always knows
|
||||
// the chain.
|
||||
TrackSnapshot snapshotTrack(MediaTrack* tr) {
|
||||
TrackSnapshot snap;
|
||||
auto read = [tr](Flag f) {
|
||||
return static_cast<int>(GetMediaTrackInfo_Value(tr, trackFlagParm(f)));
|
||||
};
|
||||
snap.showInTcp = read(Flag::ShowInTcp);
|
||||
snap.showInMixer = read(Flag::ShowInMixer);
|
||||
snap.mainSend = read(Flag::MainSend);
|
||||
snap.fxEnable = read(Flag::FxEnable);
|
||||
|
||||
const int fxCount = TrackFX_GetCount(tr);
|
||||
snap.fxOffline.reserve(static_cast<std::size_t>(fxCount));
|
||||
for (int fx = 0; fx < fxCount; ++fx)
|
||||
snap.fxOffline.push_back(
|
||||
FxOfflineState{fxGuidString(tr, fx), TrackFX_GetOffline(tr, fx) ? 1 : 0});
|
||||
return snap;
|
||||
}
|
||||
|
||||
bool applyFlags(MediaTrack* tr, const std::vector<TrackFlagOp>& flags) {
|
||||
bool wrote = false;
|
||||
for (const TrackFlagOp& op : flags)
|
||||
if (writeIfChanged(tr, trackFlagParm(op.flag), static_cast<double>(op.value)))
|
||||
wrote = true;
|
||||
return wrote;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool writeIfChanged(MediaTrack* tr, const char* parm, double value) {
|
||||
if (GetMediaTrackInfo_Value(tr, parm) == value) return false;
|
||||
SetMediaTrackInfo_Value(tr, parm, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parkTrack(ViewModeModel& model, const std::string& guid, MediaTrack* tr,
|
||||
const std::vector<TrackFlagOp>& flags) {
|
||||
bool wrote = false;
|
||||
|
||||
if (!model.holdsSnapshotFlags(guid)) {
|
||||
TrackSnapshot snap = snapshotTrack(tr);
|
||||
// A restore hands the flags back immediately but leaves the chain offline
|
||||
// until the reclaimer gets to it, so a re-park lands here with the FX half
|
||||
// still held over a still-parked chain. Keep it: the live read above would
|
||||
// record the park's own offline state as the user's, permanently.
|
||||
const TrackSnapshot* prior = model.snapshot(guid);
|
||||
if (prior && prior->fxHeld) {
|
||||
snap.fxOffline = prior->fxOffline;
|
||||
snap.fxKeying = prior->fxKeying;
|
||||
}
|
||||
model.storeSnapshot(guid, snap);
|
||||
wrote = true;
|
||||
}
|
||||
|
||||
if (applyFlags(tr, flags)) wrote = true;
|
||||
return wrote;
|
||||
}
|
||||
|
||||
bool restoreTrack(ViewModeModel& model, const std::string& guid, MediaTrack* tr,
|
||||
const std::vector<TrackFlagOp>& flags) {
|
||||
applyFlags(tr, flags);
|
||||
model.releaseSnapshotFlags(guid);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace reasampler
|
||||
Reference in New Issue
Block a user