Ψ-W1-T2: disjoint per-mode solo surfaces and a playback-gated mode switch

Solo is cached, cleared and replayed per mode on a real switch only; the switch is refused visibly while the transport runs. The footer segment now routes through the activate actions, so a panel switch finally persists.
This commit is contained in:
2026-08-01 19:43:18 -04:00
parent 8bf6841f7b
commit 9c234c2e6b
23 changed files with 811 additions and 49 deletions
+47
View File
@@ -0,0 +1,47 @@
// See view_solo.h. Compiled into the reaper_reasampler module; includes
// reaper_plugin_functions.h without REAPERAPI_IMPLEMENT (main.cpp owns that).
#include "shell/view/view_solo.h"
#include <map>
#define REAPERAPI_MINIMAL
#define REAPERAPI_WANT_GetMediaTrackInfo_Value
#define REAPERAPI_WANT_SetMediaTrackInfo_Value
#include "reaper_plugin_functions.h"
namespace reasampler {
namespace {
// SDK: int, 0=not soloed, 1=soloed, 2=soloed in place, 5=safe soloed,
// 6=safe soloed in place. Documented under SetMediaTrackInfo_Value with no
// read-only marker (unlike B_RECMON_IN_EFFECT in the same list), so it is settable.
constexpr const char* kSoloParm = "I_SOLO";
} // namespace
std::vector<view::TrackSolo> readTrackSolos(const TrackHandles& handles) {
std::vector<view::TrackSolo> live;
live.reserve(handles.size());
for (const auto& [guid, tr] : handles) {
if (!tr) continue;
live.push_back(view::TrackSolo{
guid, static_cast<int>(GetMediaTrackInfo_Value(tr, kSoloParm))});
}
return live;
}
void applySoloOps(const TrackHandles& handles, const std::vector<view::SoloOp>& ops) {
if (ops.empty()) return; // the common case: nothing soloed, no project write
std::map<std::string, int> byGuid;
for (const view::SoloOp& op : ops) byGuid[op.guid] = op.value;
for (const auto& [guid, tr] : handles) {
if (!tr) continue;
auto it = byGuid.find(guid);
if (it == byGuid.end()) continue;
SetMediaTrackInfo_Value(tr, kSoloParm, static_cast<double>(it->second));
}
}
} // namespace reasampler