Q-W6: registration table (OCP) in main.cpp; bank verbs -> shell/bank_ops(Session&); persist.h + wav_trim + namespaces.h shims deleted; 61/61
capture.h realtime seam split to capture_realtime_shell.h; GetProjExtState grow-loop rehomed to core/wire/ext_state_read; stale persist.cpp/bank_panel.cpp comment refs fixed; CLAUDE.md persist/bank_book/actions bullets updated. Command-id suffixes, display phrases, and undo labels byte-identical.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
// The bridge shell (reaper_bridge.cpp) resolves REAPER API functions by name over the
|
||||
// host callback and invokes them; the one fiddly-and-easy-to-get-wrong part around
|
||||
// GetProjExtState — interpreting its int return against the buffer it filled — is pure
|
||||
// and unit-tested here. Mirror of capture_paths / wav_trim splitting the arithmetic out
|
||||
// and unit-tested here. Mirror of capture_paths / wav_codec splitting the arithmetic out
|
||||
// of a REAPER-facing shell.
|
||||
//
|
||||
// The S1 spike ALSO carried a string-scan JSON reader (extractJsonStringField) as a
|
||||
@@ -36,63 +36,8 @@ namespace reasampler::instrument::map {
|
||||
std::optional<std::string> decodeGetProjExtState(int apiReturn,
|
||||
const std::string& buffer);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The GetProjExtState GROW-LOOP retry policy (Q-W5 rider, T2-04).
|
||||
// ---------------------------------------------------------------------------
|
||||
// GetProjExtState writes into a caller-supplied buffer with no documented
|
||||
// query-the-size call, so a large value (bank blob, usage record) must be read by
|
||||
// growing a buffer until the value fits strictly inside it. Three shells carried
|
||||
// hand-rolled copies of that loop (persist's ext-state reads, usage_scan's
|
||||
// prune-safety-adjacent record read, reaper_bridge's VST-side bank read); the ONE
|
||||
// policy now lives here so the retry/termination rules cannot drift. The fiddly
|
||||
// part is the termination taxonomy, which each caller folds differently:
|
||||
//
|
||||
// * Absent — the API returned <= 0 on some attempt: the key holds no value.
|
||||
// (persist -> "" empty bank; usage_scan / bridge -> nullopt)
|
||||
// * Complete — the written C string fits STRICTLY inside the buffer (size+1 <
|
||||
// cap), so it cannot have been clipped: `value` is the whole value.
|
||||
// * Overflow — the value never fit under the 16 MB ceiling: it is unreadable
|
||||
// WHOLE, which is NOT the same as absent. (persist warns on the
|
||||
// console; usage_scan folds it to the prune fail-safe abort)
|
||||
//
|
||||
// `read` is one GetProjExtState-shaped attempt: int read(char* buf, int cap),
|
||||
// returning the API's int. A template, statically dispatched per call site — no
|
||||
// virtual calls, no std::function (the §3 performance guardrail); the caller binds
|
||||
// the project/namespace/key (or a resolved function pointer, VST side) in a lambda.
|
||||
struct GrowingExtStateRead {
|
||||
enum class Status { Absent, Complete, Overflow };
|
||||
Status status = Status::Absent;
|
||||
int apiReturn = 0; // the FINAL attempt's return (<= 0 iff Absent); feeds
|
||||
// decodeGetProjExtState on the bridge path unchanged
|
||||
std::string value; // the whole value; meaningful only when Complete
|
||||
};
|
||||
|
||||
template <class ReadFn>
|
||||
GrowingExtStateRead readProjExtStateGrowing(ReadFn&& read) {
|
||||
// Start generous; grow ×4 if REAPER reports the value may have been clipped
|
||||
// (the return is the value length; equal-to-capacity-minus-NUL is ambiguous,
|
||||
// so only a strict fit terminates). Ceiling 16 MB — give up rather than loop
|
||||
// forever on a pathological value.
|
||||
GrowingExtStateRead result;
|
||||
for (int cap = 1 << 16; cap <= (1 << 24); cap <<= 2) {
|
||||
std::vector<char> buf(static_cast<std::size_t>(cap), '\0');
|
||||
const int rv = read(buf.data(), cap);
|
||||
result.apiReturn = rv;
|
||||
if (rv <= 0) {
|
||||
result.status = GrowingExtStateRead::Status::Absent;
|
||||
return result;
|
||||
}
|
||||
buf[static_cast<std::size_t>(cap) - 1] = '\0'; // defensive: guard against a read() that fills the buffer without honoring NUL-termination within cap
|
||||
std::string s(buf.data());
|
||||
if (static_cast<int>(s.size()) + 1 < cap) {
|
||||
result.status = GrowingExtStateRead::Status::Complete;
|
||||
result.value = std::move(s);
|
||||
return result;
|
||||
}
|
||||
// else: possibly truncated -> grow and retry.
|
||||
}
|
||||
result.status = GrowingExtStateRead::Status::Overflow;
|
||||
return result;
|
||||
}
|
||||
// The GetProjExtState GROW-LOOP retry policy (T2-04) lived here through Q-W5; it
|
||||
// was rehomed to core/wire/ext_state_read.h in Q-W6 (its consumers are 2:1
|
||||
// extension-side, so it belongs on the neutral wire seam, not the instrument map).
|
||||
|
||||
} // namespace reasampler::instrument::map
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// sample_map — pure implementation (the RESOLUTION half; the ComponentState codec
|
||||
// lives in component_state_io.cpp since Q-W2v). See sample_map.h. NO VST3 / REAPER /
|
||||
// SWELL / vendor includes; standard library + the pure bank_book / wav_trim / sampler_core.
|
||||
// SWELL / vendor includes; standard library + the pure bank_book / wav_codec / sampler_core.
|
||||
|
||||
#include "core/instrument/map/sample_map.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// "reasampler" bank ext-state + a decoded WAV into the plain data the sampler core
|
||||
// plays, and (de)serialize the instance's selected-sample choice for VST3 component
|
||||
// state. NO VST3, NO REAPER, NO SWELL, NO vendor/ includes at the boundary — the
|
||||
// mirror of capture_paths / wav_trim / bridge_marshal splitting the fiddly, testable
|
||||
// mirror of capture_paths / wav_codec / bridge_marshal splitting the fiddly, testable
|
||||
// arithmetic out of a host-facing shell.
|
||||
//
|
||||
// WHY IT EXISTS (S4 seams). The instrument reads the bank over the live-state seam
|
||||
@@ -14,7 +14,7 @@
|
||||
// downmix its decoded PCM to the core's mono contract, and build the Tier-0 chromatic
|
||||
// Keymap — is pure and unit-tested here.
|
||||
//
|
||||
// It links bank_book (the shared BankBook::deserialize) and wav_trim (the shared
|
||||
// It links bank_book (the shared BankBook::deserialize) and wav_codec (the shared
|
||||
// 32-bit-float WAV parse — no third WAV reader) and sampler_core (the Keymap /
|
||||
// SampleData it produces). All three are pure; this stays pure.
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include "core/model/bank_book.h" // BankBook::deserialize (shared bank JSON parse)
|
||||
#include "core/instrument/engine/sampler_core.h" // Keymap, SampleData, SampleLoop
|
||||
#include "core/capture/wav_trim.h" // parseWavLayout, extractFloatFrames (shared WAV parse)
|
||||
#include "core/capture/wav_codec.h" // parseWavLayout, extractFloatFrames (shared WAV parse)
|
||||
|
||||
namespace reasampler::instrument::map {
|
||||
|
||||
@@ -167,7 +167,7 @@ struct BankChoice {
|
||||
};
|
||||
std::vector<BankChoice> listBanks(const std::string& banksJson);
|
||||
|
||||
// Downmix interleaved float frames (the shape wav_trim::extractFloatFrames yields:
|
||||
// Downmix interleaved float frames (the shape wav_codec's extractFloatFrames yields:
|
||||
// [f0c0,f0c1,...,f1c0,...]) to the core's MONO contract by AVERAGING channels per
|
||||
// frame. `channelCount` is the interleave stride (>= 1). CHANNEL POLICY (Tier 0,
|
||||
// documented + surfaced): the S3 core is mono-per-sample by design; bank WAVs preserve
|
||||
|
||||
Reference in New Issue
Block a user