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:
@@ -108,7 +108,7 @@ declared ahead of the instrument slots at that member in `reasampler_processor.h
|
||||
- `reasampler_editor` — VST3 `IPlugView` LICE editor shell: hosts a LICE-drawn child window; the Sample face is home and Browse is a modal picker over it. Split on the Sample face's BAND axis, mirroring the pure `sample_bands` allocator: `editor_session` (session/bridge state, caches, commit-and-reload), `editor_controls` (the ONE `faceLayout` band resolve every paint and hit-test path shares, the node-drag bounds, the value labels, and the per-instance controls the parameter set does not carry — the parameter-set binding itself is the pure `core/instrument/ui/deck_values` module this only adapts int ids onto), `editor_models` (the orthogonal half: which stored struct each transient editor selection names — the staged-envelope pack/unpack, the drawn contour, and the three velocity curves), then matching paint and input sets — `editor_paint`/`editor_input` (dispatch + drag router + hover dispatch), `_chrome`, `_waveform`, `_deck` — plus the two band-independent surfaces (`_browse` for the modal picker, `_curve` for the velocity-curve popup) and `editor_platform` (IPlugView/Win32 window plumbing). Shared internals in `editor_internal.h`, no TU of its own. Drop-onto-editor ingest is NOT shipped (deferred).
|
||||
- `reasampler_embed` — implements `IReaperUIEmbedInterface` so the instrument draws inline in the TCP/MCP without a plugin-owned HWND; delegates layout to `embed_strip`. A read-only readout: the loaded capture across the keyboard span with its root marked, plus the activity level. It takes no mouse input (there is nothing on the strip to select).
|
||||
- `editor_stroke` — the editor's LICE side of the analytic stroker: builds a coverage mask with the pure `core/ui/stroke_aa` and blends it into the bitmap ONCE, writing straight to the bitmap's bits (the arithmetic matches LICE's own mode-0 combine, so a stroke composites identically to every other kit draw). Every radial and spline stroke on the editor routes through `strokeArcAA` / `strokePolylineAA` / `strokeLineAA`. Holds the draw-thread-only scratch mask and arc point list — reuse, not a hidden dependency: threading a canvas through the eight paint sites would grow those signatures to carry an allocation detail. Deliberately does NOT touch `shell/panel/draw_kit`: the waveform stroke, the docked bank panel and the browse cards are out of this seam's blast radius.
|
||||
- `instrument_bake` — the instrument's half of the resample chain, on the UI thread: render the dialed sound through the pure `core/instrument/bake` modules at the instance's PERSISTED PREVIEW VELOCITY (the velocity the user has been auditioning at — three velocity curves are live, so it is a property of the sound and not a render detail), stage the WAV OUTSIDE the bank folder, publish one `rsbake_<guid>` request, invoke the extension's landing action SYNCHRONOUSLY, read the outcome back over the same key, then adopt + reset in one act. Two stack-RAII guards mirror `FxBypassGuard`'s discipline: the staged file and the request key are both cleared on every exit path, so a failed bake leaves no temp, no bank entry and no parameter reset. `bakeAvailable` is the affordance's paint gate.
|
||||
- `instrument_bake` — the instrument's half of the resample chain, on the UI thread: render the dialed sound through the pure `core/instrument/bake` modules at the instance's PERSISTED PREVIEW VELOCITY (the velocity the user has been auditioning at — three velocity curves are live, so it is a property of the sound and not a render detail), stage the WAV OUTSIDE the bank folder, publish one `rsbake_<guid>` request, invoke the extension's landing action SYNCHRONOUSLY, read the outcome back over the same key, then adopt + reset in one act. What that key holds afterwards is classified by `core/wire`'s pure `classifyBakeAnswer`, and each of its five non-answers gets its OWN sentence — a silent no-answer stays a failure, but the user is told whether the extension never ran the landing, answered a stale generation, answered in a wire this build cannot read, cleared the request, or refused it. Two stack-RAII guards mirror `FxBypassGuard`'s discipline: the staged file and the request key are both cleared on every exit path, so a failed bake leaves no temp, no bank entry and no parameter reset. `bakeAvailable` is the affordance's paint gate.
|
||||
- `vst_entry` — VST3 entry point: `GetPluginFactory` export, class registration, channel-forked class UIDs.
|
||||
- `editor_internal.h` — INTERNAL shared helpers for the `reasampler_editor` TU family, included only by the editor's own shell TUs (`editor_session` / `editor_controls` / `editor_paint_*` / `editor_input_*` / `editor_platform`), never a public seam: the `Rect`↔kit adapters, small draw primitives (knob face / title band), label helpers, and the velocity-curve box derivation — the helpers more than one band TU needs. The deck's control ids, group ids and group composition are the pure `deck_groups` module's, not this file's. The piano-strip and root-key draws live in `editor_paint_chrome`, their only consumer, not here.
|
||||
- `reasampler_vst.h` — shared identity constants for the ReaSampler VST3 instrument (Phase S): the plugin's class UID (the channel-selected `Steinberg::FUID`, built from the FOREVER-FROZEN macros in `core/wire/reasampler_uid.h`), vendor name/URL/email, so the processor, factory, and editor agree. A class UID is FOREVER-STABLE once shipped — minted once, never regenerated. *(Newly authored per this dispatch's brief — no existing root-CLAUDE.md bullet; verified by reading `src/shell/instrument/reasampler_vst.h` directly.)*
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user