feat(vst): stand up VST3 instrument spike (S1) — skeleton, IPlugView↔LICE editor, REAPER bridge read

Vendor Steinberg VST3 SDK (v3.7.9_build_61); add reasampler_vst.vst3 as a second, additive build artifact with pure editor-geometry + bridge-marshal helpers under CTest.
This commit is contained in:
2026-07-26 15:29:54 -04:00
parent 5595ba42f9
commit 3c2a7c45b2
17 changed files with 1256 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
// bridge_marshal.h — PURE marshalling helpers for the REAPER VST-host bridge read
// (Phase S1). NO VST3, NO REAPER types at the boundary.
//
// The bridge shell (reaper_bridge.cpp) resolves REAPER API functions by name over the
// host callback and invokes them; the fiddly-and-easy-to-get-wrong parts around those
// calls — interpreting GetProjExtState's int return, walking EnumProjExtState's
// index-until-false contract into a key set, and extracting a single value out of the
// "reasampler" bank JSON — are pure and unit-tested here. Mirror of capture_paths /
// wav_trim splitting the arithmetic out of a REAPER-facing shell.
//
// Verified against vendor/reaper-sdk/sdk/reaper_plugin_functions.h:
// int GetProjExtState (ReaProject*, extname, key, valOutNeedBig, valOutNeedBig_sz);
// -- returns the length written (0 when the key is absent).
// bool EnumProjExtState(ReaProject*, extname, idx, keyOut, keyOut_sz, valOut, valOut_sz);
// -- returns false when idx is past the last entry.
#pragma once
#include <optional>
#include <string>
namespace reasampler::vst {
// Interpret a GetProjExtState result: the int return value (bytes the API reports for
// the key) and the buffer it filled. Returns the value only when the API reported a
// non-empty result AND the buffer is non-empty — REAPER writes 0 and leaves the buffer
// untouched for an absent key, and we must not treat stale buffer contents as a hit.
//
// `apiReturn` is GetProjExtState's return; `buffer` is the NUL-terminated string it
// wrote (already truncated to the C string by the caller).
std::optional<std::string> decodeGetProjExtState(int apiReturn,
const std::string& buffer);
// Extract the string value for `key` out of a flat one-level JSON object — the shape
// persist.cpp writes under the "reasampler" ext-state (e.g. the bank blob's top-level
// fields). This is a deliberately small, dependency-free reader for the SPIKE's
// "read a known value" proof, NOT a general JSON parser: it finds "key" as an object
// member and returns its string value, handling the common escapes (\" \\ \n \t \/).
// Returns nullopt if the key is absent or its value is not a string.
//
// The real instrument (S4) will read the bank index through the shared bank_model JSON
// path, not this helper; this exists only to give the S1 bridge read a testable,
// REAPER-free decode step.
std::optional<std::string> extractJsonStringField(const std::string& json,
const std::string& key);
} // namespace reasampler::vst