// reaper_bridge.cpp — see reaper_bridge.h. The DAW-facing edge; keep it thin. #include "reaper_bridge.h" #include #include "bridge_marshal.h" // The VST3 base types must be included before REAPER's VST3 interface header, which // uses FUnknown / CStringA / uint32 / DECLARE_CLASS_IID / PLUGIN_API from // pluginterfaces/base — all in namespace Steinberg. #include "pluginterfaces/base/funknown.h" #include "pluginterfaces/base/ftypes.h" // REAPER's VST3-side bridge interface (vendored). IReaperHostApplication is what REAPER // passes (as an IHostApplication) to IComponent::initialize; it exposes getReaperApi // (resolve-by-name) and getReaperParent (host context). The header uses UNQUALIFIED // Steinberg types (FUnknown, CStringA, uint32, FUID, DECLARE_CLASS_IID, PLUGIN_API), so // it must be pulled into the Steinberg namespace — the same way REAPER's own VST3 // examples include it. namespace Steinberg { #include "reaper_vst3_interfaces.h" } // namespace Steinberg // DECLARE_CLASS_IID in the REAPER header only DECLARES IReaperHostApplication::iid; some // TU must DEFINE it. We do it here — this is the only place that queries for the // interface (FUnknownPtr uses the iid), so the definition lives with its sole use. DEF_CLASS_IID(Steinberg::IReaperHostApplication) // The "reasampler" ext-state namespace + bank key. Kept in sync with persist.h by // value (the extension writes them); we only READ here, so we duplicate the two string // constants rather than pull the whole REAPER-facing persist.h into the VST artifact. // If persist.h's kProjExtNamespace / kProjExtBanksKey ever change, these must follow — // they are the shared wire contract between the extension (writer) and instrument // (reader). VERIFY against persist.h. namespace { constexpr const char* kReasamplerNamespace = "reasampler"; } namespace reasampler::vst { bool ReaperBridge::connect(Steinberg::FUnknown* context) { getProjExtState_ = nullptr; enumProjExtState_ = nullptr; hostApp_ = nullptr; if (!context) return false; // Query the host context for REAPER's bridge interface. In a non-REAPER host this // query fails and we stay unconnected — the instrument still loads. Steinberg::FUnknownPtr reaper(context); if (!reaper) return false; hostApp_ = reaper.get(); // Resolve the ext-state functions by name. getReaperApi returns the same function // pointers the extension resolves via rec->GetFunc; a null return means the symbol // is unavailable (very old REAPER) — degrade gracefully. getProjExtState_ = reinterpret_cast( reaper->getReaperApi("GetProjExtState")); enumProjExtState_ = reinterpret_cast( reaper->getReaperApi("EnumProjExtState")); return getProjExtState_ != nullptr; } std::optional ReaperBridge::readReasamplerExtState(const std::string& key) { if (!getProjExtState_ || !hostApp_) return std::nullopt; // Fetch the host project (getReaperParent(3) — project). Reads that live "reasampler" // ext-state against the ACTIVE project the instrument was instantiated in, so it // follows project switches for free (D6). auto* reaper = static_cast(hostApp_); void* proj = reaper->getReaperParent(3); // A null project is legitimate (e.g. instantiated before a project context exists); // REAPER treats null as the current project for these calls, so we pass it through // rather than bailing — but if the read yields nothing the caller sees nullopt. // GetProjExtState writes into a caller buffer; size it generously for a JSON blob // and let the pure decoder interpret the result. The buffer is NUL-terminated by // REAPER on success. std::vector buf(64 * 1024, '\0'); const int rv = getProjExtState_(proj, kReasamplerNamespace, key.c_str(), buf.data(), static_cast(buf.size())); // Marshal the raw result through the pure decoder (handles the absent-key case). return decodeGetProjExtState(rv, std::string(buf.data())); } } // namespace reasampler::vst