// reaper_bridge.h — the REAPER VST-host bridge. Thin shell: resolves REAPER API functions // by name over the host context and reads the live "reasampler" project ext-state. The // fiddly decode lives in bridge_marshal (pure). // // Bridge mechanism: REAPER passes an IHostApplication as `context` to // IComponent::initialize; querying it for IReaperHostApplication yields getReaperApi // (resolve a REAPER API function pointer by name) and getReaperParent(3) (the host // ReaProject*; 1=track, 2=take, 3=project, 4=fxdsp, 5=trackchan) — not VST2 hostcb opcodes. #pragma once #include #include #include "pluginterfaces/base/funknown.h" namespace reasampler::vst { // Wraps the REAPER host bridge for a single plugin instance. Constructed cheaply; // connect() must be called with the initialize() context before any read. All reads // degrade to nullopt (never crash) when the host is not REAPER or a symbol is absent — // the instrument must load in non-REAPER hosts too, just without live state. class ReaperBridge { public: ReaperBridge() = default; // Binds to the host (`context` is the FUnknown* IComponent::initialize hands us). // Returns true when the host is REAPER and the ext-state API resolved; safe to call // with a null or non-REAPER context (returns false). bool connect(Steinberg::FUnknown* context); bool isConnected() const { return getProjExtState_ != nullptr; } // Reads a "reasampler" ext-state value by key from the host's active project. // Returns nullopt when unconnected, unresolvable, or the key is absent. // // NOT REAL-TIME SAFE (allocates + calls into REAPER): audio-thread callers MUST NOT // invoke this. The instrument reads on the main/UI thread and hands a snapshot to // the process path. std::optional readReasamplerExtState(const std::string& key); // The active project's directory (forward-slashed, no trailing slash) — the same // convention persist uses to place the bank alongside the .rpp. Empty for an unsaved // project or when unconnected. Not RT-safe. std::string activeProjectDir(); // Writes THIS INSTANCE's usage record: the ONE sanctioned instrument-side ext-state // write. `usageKey` MUST carry the "rsusage_" prefix (ext_keys.h's usageKeyFor); any // other key is refused, enforcing the read-only-bank invariant structurally (banks/ // view/tail/assign stay unwritable from the instrument). Returns true iff written // (the SetProjExtState return is checked). NOT RT-safe — publish sites are the // off-audio-thread reload path only. Deliberately does NOT mark the project dirty: a // usage change always rides a component-state change that already does. bool writeUsageExtState(const std::string& usageKey, const std::string& value); // The canonical GUID string of the track hosting this FX instance (same rendering as // the extension's track_guid::guidString, so usage records compare byte-equal // against its live-FX enumeration). Empty when unconnected or no track context (the // usage reader then falls back to any-instance liveness). Not RT-safe. std::string currentTrackGuid(); private: // Resolved REAPER API function pointers (by name via getReaperApi). Signatures // verified against reaper_plugin_functions.h. using GetProjExtStateFn = int (*)(void* proj, const char* extname, const char* key, char* valOutNeedBig, int valOutNeedBig_sz); using EnumProjExtStateFn = bool (*)(void* proj, const char* extname, int idx, char* keyOut, int keyOut_sz, char* valOut, int valOut_sz); // EnumProjects(-1, projfnOut, sz) -> active project + its .rpp path. idx=-1 (current // tab) follows the active project, same convention as the persist shell. using EnumProjectsFn = void* (*)(int idx, char* projfnOut, int projfnOut_sz); // Used ONLY by writeUsageExtState (prefix-guarded) — see the read-only-bank note there. using SetProjExtStateFn = int (*)(void* proj, const char* extname, const char* key, const char* value); // Opaque-pointer signatures so the header stays SDK-type-free; the GUID* is passed // straight through, never dereferenced here. using GetTrackGuidFn = void* (*)(void* tr); using GuidToStringFn = void (*)(const void* g, char* destNeed64); void* hostApp_ = nullptr; // IReaperHostApplication* (opaque here; used in .cpp) GetProjExtStateFn getProjExtState_ = nullptr; EnumProjExtStateFn enumProjExtState_ = nullptr; EnumProjectsFn enumProjects_ = nullptr; SetProjExtStateFn setProjExtState_ = nullptr; GetTrackGuidFn getTrackGuid_ = nullptr; GuidToStringFn guidToString_ = nullptr; }; } // namespace reasampler::vst