bake: name the five ways the extension can fail to answer, and move the landing verdict into a pure, tab-provable classifier

A no-answer stays a failure; it now says whether the extension never ran the landing, answered a stale generation, spoke a wire this build cannot read, cleared the request, or refused it.
This commit is contained in:
2026-08-02 06:38:04 -04:00
parent 6e937b9c61
commit 962ab64ef0
8 changed files with 373 additions and 48 deletions
+38 -17
View File
@@ -187,24 +187,45 @@ BakeChainResult runBake(ReaSamplerProcessor& processor) {
if (!bridge.invokeExtensionAction(wire::bakeActionLookupName()))
return fail("the ReaSampler extension's bake action is not registered");
const std::optional<std::string> raw = bridge.readReasamplerExtState(key);
const std::optional<BakeOutcome> outcome =
raw ? wire::decodeBakeOutcome(*raw) : std::nullopt;
// No outcome at all means the action never reached our request — an older extension
// that registers the id but does not read this key, or an invocation REAPER deferred.
if (!outcome) return fail("the extension did not answer the bake request");
if (outcome->generation != request.generation)
return fail("the extension answered a different bake request");
if (outcome->status != BakeStatus::Ok)
return fail(outcome->message.empty() ? std::string("the bake was refused")
: outcome->message);
// What the key holds now is the only evidence this side gets, and each of the five
// non-answers is a different thing to go fix — collapsing them into one sentence is
// what made a stale install indistinguishable from a refusal.
const wire::BakeAnswer answer =
wire::classifyBakeAnswer(bridge.readReasamplerExtState(key), request);
switch (answer.kind) {
case wire::BakeAnswerKind::Answered:
break;
case wire::BakeAnswerKind::Unanswered:
return fail(
"the ReaSampler extension did not run the bake landing -- its action id "
"resolved but nothing read the request. Most likely the installed "
"extension is older than this plugin: reinstall it and restart REAPER");
case wire::BakeAnswerKind::Undecodable:
return fail(
"the extension answered in a format this plugin does not read -- the "
"extension and ReaSampler 9000 are from different builds");
case wire::BakeAnswerKind::Cleared:
return fail(
"the extension discarded the bake request as stale before answering it");
case wire::BakeAnswerKind::ForeignRequest:
return fail(
"another ReaSampler 9000 instance is baking under this instance's key -- "
"the two were copied from one another; reload this one and retry");
case wire::BakeAnswerKind::ForeignOutcome:
return fail("the extension answered a different bake request");
}
// Set by construction on Answered (bake_wire.h states the invariant at BakeAnswer).
const BakeOutcome& outcome = *answer.outcome;
if (outcome.status != BakeStatus::Ok)
return fail(outcome.message.empty() ? std::string("the bake was refused")
: outcome.message);
SampleRefEntry entry;
entry.sampleId = outcome->sampleId;
entry.displayName = outcome->displayName;
entry.ref.relativePath = outcome->relativePath;
entry.ref.rootNote = outcome->rootNote;
entry.ref.channelCount = outcome->channelCount;
entry.sampleId = outcome.sampleId;
entry.displayName = outcome.displayName;
entry.ref.relativePath = outcome.relativePath;
entry.ref.rootNote = outcome.rootNote;
entry.ref.channelCount = outcome.channelCount;
// No loop: the loop points shaped the render and are meaningless against the new file
// (bake_reset owns that rule for the parameter set; this is its bank-intrinsic peer).
@@ -213,7 +234,7 @@ BakeChainResult runBake(ReaSamplerProcessor& processor) {
// The extension's own wording, which distinguishes the three landings (replaced, added,
// and pointed at an identical existing entry) more precisely than this side can.
return BakeChainResult{true, "resampled -- " + outcome->message};
return BakeChainResult{true, "resampled -- " + outcome.message};
}
} // namespace reasampler::vst