Merge dev into phase-b-multibank (m11 console cleanup + Phase S/V docs) before dev promotion

# Conflicts:
#	src/actions.cpp
#	src/main.cpp
This commit is contained in:
2026-07-26 15:41:25 -04:00
7 changed files with 1418 additions and 191 deletions
+16 -33
View File
@@ -545,7 +545,6 @@ void doBankCreate() {
return;
}
persistBankOp("ReaSampler: create bank");
ShowConsoleMsg(("ReaSampler: created bank \"" + name + "\".\n").c_str());
}
// Rename a bank: prompt for which bank (by current display name) and the new name.
@@ -571,8 +570,6 @@ void doBankRename() {
return;
}
persistBankOp("ReaSampler: rename bank");
ShowConsoleMsg(("ReaSampler: renamed \"" + which + "\" -> \"" + newName + "\".\n")
.c_str());
}
// Delete a named bank. Bindable safe-form of the confirm-on-non-empty guardrail:
@@ -615,7 +612,6 @@ void doBankDelete() {
return;
}
persistBankOp("ReaSampler: delete bank");
ShowConsoleMsg(("ReaSampler: deleted bank \"" + which + "\".\n").c_str());
}
// Evacuate a named bank: move every member back to the pool (index-only, collapse by
@@ -637,7 +633,6 @@ void doBankEvacuate() {
return;
}
persistBankOp("ReaSampler: evacuate bank");
ShowConsoleMsg(("ReaSampler: evacuated \"" + which + "\" to the pool.\n").c_str());
}
// Cycle the active bank forward in ordinal order (pool -> named -> ... -> pool),
@@ -652,10 +647,6 @@ void doBankActivateNext() {
if (target.empty()) return; // degenerate (no banks) — cannot happen (pool seeded)
if (!g_session->book().setActiveBank(target)) return;
persistBankOp("ReaSampler: activate bank");
const Bank* b = g_session->book().bank(target);
ShowConsoleMsg(("ReaSampler: active bank -> \"" +
(b ? b->displayName : target) + "\".\n")
.c_str());
}
// Activate the pool directly (the common "back to the default target" jump). Bindable
@@ -663,7 +654,6 @@ void doBankActivateNext() {
void doBankActivatePool() {
if (!g_session->book().setActiveBank(kPoolBankId)) return;
persistBankOp("ReaSampler: activate bank");
ShowConsoleMsg("ReaSampler: active bank -> \"Pool\".\n");
}
// Move or copy the panel's selected samples into a named destination bank (prompted
@@ -698,7 +688,10 @@ void doBankTransferSelected(bool copy) {
return;
}
int ok = 0, collapsed = 0, absent = 0;
// Tally per-sample transfer outcomes so the no-op guardrail below can decide whether
// the index actually mutated (R-B). The console summary m11 stripped is gone; the
// counts remain because the verb-aware undo guardrail is driven by them.
int ok = 0, collapsed = 0;
for (const std::string& sampleId : selected) {
const TransferResult r =
copy ? g_session->book().copySample(sampleId, srcId, destId)
@@ -707,8 +700,9 @@ void doBankTransferSelected(bool copy) {
case TransferResult::Moved:
case TransferResult::Copied: ++ok; break;
case TransferResult::Collapsed: ++collapsed; break;
case TransferResult::RejectedSampleAbsent: ++absent; break;
// Unknown-bank / same-bank are pre-checked above; treat defensively as no-ops.
// RejectedSampleAbsent and unknown-bank / same-bank (pre-checked above) are
// no-ops for the guardrail; nothing mutated for those ids.
case TransferResult::RejectedSampleAbsent:
case TransferResult::RejectedUnknownBank:
case TransferResult::RejectedSameBank: break;
}
@@ -726,12 +720,6 @@ void doBankTransferSelected(bool copy) {
std::string("ReaSampler: ") + verb + " sample(s)";
persistBankOp(label.c_str());
}
std::string log = std::string("ReaSampler: ") + verb + " -> \"" + destName +
"\": " + std::to_string(ok) + " " + verb + "d";
if (collapsed) log += ", " + std::to_string(collapsed) + " collapsed on hash";
if (absent) log += ", " + std::to_string(absent) + " no longer present";
log += ".\n";
ShowConsoleMsg(log.c_str());
}
// Remove the panel's selected samples from the SOURCE bank (the focused region's
@@ -788,25 +776,20 @@ void doBankRemoveSelected() {
// Perform the removes (this-bank scope). Pass ids by value — no BankIndex& is cached
// across the loop's mutations. Count real drops so the no-op guardrail can skip the
// undo point when nothing was removed (every id was already absent).
int removed = 0, absent = 0;
// undo point when nothing was removed (every id was already absent). The per-outcome
// console summary was dropped (m11 chatter policy); only the "did anything change?"
// signal the undo guardrail needs is retained.
int removed = 0;
for (const std::string& sampleId : selected) {
switch (book.removeSample(sampleId, srcId, RemoveScope::ThisBank)) {
case RemoveResult::Removed: ++removed; break;
case RemoveResult::RejectedSampleAbsent: ++absent; break;
// Unknown bank cannot occur — srcId was resolved to a live bank above.
case RemoveResult::RejectedUnknownBank: break;
}
if (book.removeSample(sampleId, srcId, RemoveScope::ThisBank) ==
RemoveResult::Removed)
++removed;
// RejectedSampleAbsent / RejectedUnknownBank are no-ops for the guardrail.
// (Unknown bank cannot occur — srcId was resolved to a live bank above.)
}
// No-op guardrail (R-B): open an undo point only if the index actually mutated.
if (removed > 0) persistBankOp("ReaSampler: remove sample(s)");
std::string log = "ReaSampler: removed " + std::to_string(removed) +
(removed == 1 ? " sample" : " samples");
if (absent) log += ", " + std::to_string(absent) + " no longer present";
log += ".\n";
ShowConsoleMsg(log.c_str());
}
} // namespace
-1
View File
@@ -52,7 +52,6 @@
#define REAPERAPI_WANT_Main_OnCommand
#define REAPERAPI_WANT_Main_SaveProject
#define REAPERAPI_WANT_Master_GetTempo
#define REAPERAPI_WANT_ShowConsoleMsg
#include "reaper_plugin_functions.h"
namespace reasampler {
+7 -37
View File
@@ -135,7 +135,7 @@ static ReaProject* g_rtCaptureProject = nullptr;
// Commit a finished realtime capture (a Done tick/abort with an Ok result): add the
// Sample to the ACTIVE bank (g_session.bank() resolves to book.activeIndex() — B2),
// persist + MarkProjectDirty, log. Shared by the tick-completion path and the abort
// persist + MarkProjectDirty. Shared by the tick-completion path and the abort
// paths. On a non-Ok result, logs the failure only.
static void CommitRealtimeResult(const reasampler::CaptureResult& res)
{
@@ -144,20 +144,13 @@ static void CommitRealtimeResult(const reasampler::CaptureResult& res)
ShowConsoleMsg(("ReaSampler realtime capture failed: " + res.message + "\n").c_str());
return;
}
reasampler::AddResult added = g_session.bank().add(res.sample);
g_session.bank().add(res.sample);
// B-cap: record the file the capture created in the owned-file manifest, at the same
// point the Sample is added and before the same persist. Recorded regardless of the
// index AddResult — even a hash-collapse still WROTE a file the tool owns, and the
// manifest dedups a repeat path itself (Phase R prune reconciles manifest vs index).
g_session.owned().add(res.sample.relativePath);
g_session.saveToActiveProject(); // persist book + manifest + MarkProjectDirty (travels with .rpp)
std::string log = "ReaSampler: " + res.message + "\n";
log += " bank size now " + std::to_string(g_session.bank().size()) +
(added == reasampler::AddResult::Added ? " (added)\n"
: added == reasampler::AddResult::Collapsed ? " (collapsed on hash)\n"
: " (rejected)\n");
ShowConsoleMsg(log.c_str());
}
// Advance any in-flight realtime capture one tick. Cheap when none is running (a
@@ -619,7 +612,7 @@ static void RunCapture(const reasampler::CaptureActionDef& def)
}
// Add to the ACTIVE bank: g_session.bank() resolves to book.activeIndex() (B2).
reasampler::AddResult added = g_session.bank().add(res.sample);
g_session.bank().add(res.sample);
// B-cap: record the created file in the owned-file manifest, at the same point the
// Sample is added and before the same persist. Recorded regardless of the index
// AddResult — even a hash-collapse still WROTE a file the tool owns, and the manifest
@@ -630,13 +623,6 @@ static void RunCapture(const reasampler::CaptureActionDef& def)
// travels with the .rpp. saveToActiveProject also clears the retired legacy key and
// calls MarkProjectDirty. Non-destructive: writes only our own ext-state keys.
g_session.saveToActiveProject();
std::string log = "ReaSampler: " + res.message + "\n";
log += " bank size now " + std::to_string(g_session.bank().size()) +
(added == reasampler::AddResult::Added ? " (added)\n"
: added == reasampler::AddResult::Collapsed ? " (collapsed on hash)\n"
: " (rejected)\n");
ShowConsoleMsg(log.c_str());
}
// STARTS the REALTIME track capture and returns immediately — the record runs across
@@ -708,15 +694,6 @@ static void RunCaptureRealtimeTrack()
// completion across ticks (UI stays responsive).
g_rtCaptureProject = EnumProjects(-1, nullptr, 0);
g_rtCapture = std::move(st);
// With a tail mode the recorded window runs PAST the range end (Auto: +8 s then
// decay-trim; Manual: +the set length), so the completion note names the window,
// not just the range end.
const char* doneWhen =
(tail.mode == reasampler::TailMode::None)
? "the bank updates when it reaches the range end."
: "the bank updates after the extra tail window (past the range end).";
ShowConsoleMsg((std::string("ReaSampler: realtime capture started — recording in "
"the background; ") + doneWhen + "\n").c_str());
}
// Cancels the in-flight realtime capture on demand (bindable action). Force-terminates
@@ -758,27 +735,22 @@ static void RunInsertSelected(bool conform)
reasampler::InsertResult res = reasampler::runInsert(&g_session, req);
std::string msg;
switch (res.status)
{
case reasampler::InsertStatus::Ok:
msg = "ReaSampler: inserted onto " + std::to_string(res.inserted) +
(res.inserted == 1 ? " track" : " tracks") +
(conform ? " (conformed to tempo)" : " (native length)") + "\n";
break;
break; // success — no console chatter
case reasampler::InsertStatus::NoSelection:
// "select a track first" is printed by runInsert when no track is
// selected; this branch covers the no-panel-selection case.
msg = "ReaSampler insert: nothing selected in the bank panel.\n";
ShowConsoleMsg("ReaSampler insert: nothing selected in the bank panel.\n");
break;
case reasampler::InsertStatus::NoProject:
msg = "ReaSampler insert: no saved project, so the bank has no location.\n";
ShowConsoleMsg("ReaSampler insert: no saved project, so the bank has no location.\n");
break;
case reasampler::InsertStatus::NothingResolved:
msg = "ReaSampler insert: selected sample(s) could not be resolved to a file.\n";
ShowConsoleMsg("ReaSampler insert: selected sample(s) could not be resolved to a file.\n");
break;
}
ShowConsoleMsg(msg.c_str());
}
// REAPER calls this for EVERY action fired anywhere; claim only our own id,
@@ -1024,7 +996,5 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
// requests a deferred reload that the next timer tick drains (see the hook comment).
rec->Register("projectconfig", (void*)&g_projectConfig);
ShowConsoleMsg("ReaSampler loaded.\n");
return 1; // success — REAPER keeps us loaded
}