// instrument_drop_win — the REAPER shell for S17 drop-and-load. See instrument_drop_win.h. // // Compiled into the reaper_reasampler MODULE. Includes reaper_plugin_functions.h WITHOUT // REAPERAPI_IMPLEMENT (main.cpp owns the pointers; here they are extern via the WANT list). #include "instrument_drop_win.h" #include #include "app_version.h" // vstPluginName() — the CHANNEL-correct FX name (stable/beta pairing) #include "instrument_drop.h" // infoNamesFxHotspot — the PURE, unit-tested hotspot classifier #include "reaper_plugin.h" #define REAPERAPI_MINIMAL #define REAPERAPI_WANT_GetThingFromPoint #define REAPERAPI_WANT_TrackFX_AddByName #define REAPERAPI_WANT_TrackFX_Delete #define REAPERAPI_WANT_TrackFX_SetNamedConfigParm #define REAPERAPI_WANT_Undo_BeginBlock2 #define REAPERAPI_WANT_Undo_EndBlock2 #include "reaper_plugin_functions.h" namespace reasampler { FxDropTarget resolveFxDropTarget(int screenX, int screenY) { FxDropTarget out; char info[256] = {0}; // GetThingFromPoint returns the track under the point (may be null for a non-track thing) // and fills `info` with what was hit. A non-empty info OR a non-null track means the point // is over REAPER's own UI; a null track with an empty info means the pointer has left // REAPER entirely (over another app / the desktop) — the OsDrag boundary. MediaTrack* track = GetThingFromPoint(screenX, screenY, info, sizeof(info)); out.track = track; out.overReaperUi = (track != nullptr) || (info[0] != '\0'); // S-VIEW-BUG-1: the hotspot is either the FX chain/floating window ("fx_*") OR the track/ // mixer panel that hosts the FX button ("tcp*"/"mcp*"). The pure classifier owns the rule. out.overFxHotspot = (track != nullptr) && infoNamesFxHotspot(info); return out; } bool loadInstrumentOntoTrack(MediaTrack* track, const std::string& chunkBase64) { if (!track || chunkBase64.empty()) return false; // The CHANNEL-correct FX name: "VST3:ReaSampler 9000" on stable, "VST3:ReaSampler 9000 // beta" on beta. Sourcing it from app_version::vstPluginName() (the same accessor the VST // factory display name derives from) keeps the pairing invariant intact — a beta extension // drops the beta VST, a stable extension the stable VST — with no literal to drift. const std::string fxName = "VST3:" + vstPluginName(); // Negative `instantiate` => always create a NEW instance (verified in the header). recFX // = false: a normal track FX chain instance, not a record/monitoring FX. const int fxIndex = TrackFX_AddByName(track, fxName.c_str(), /*recFX=*/false, /*instantiate=*/-1); if (fxIndex < 0) return false; // Inject the instrument's OWN component-state blob (the dragged capture pre-selected) // via the documented vst_chunk write-parm. The blob was built by the shared writer // (instrument_drop::buildInstrumentDropChunk -> sample_map::serializeComponentState), // so these bytes are exactly what ReaSampler 9000's setState accepts. const bool ok = TrackFX_SetNamedConfigParm(track, fxIndex, "vst_chunk", chunkBase64.c_str()); if (!ok) { // All-or-nothing: if the chunk write fails, remove the empty FX instance we just // added so the track is left exactly as it was. TrackFX_Delete signature (verified // in reaper_plugin_functions.h:7236): bool TrackFX_Delete(MediaTrack*, int fx). TrackFX_Delete(track, fxIndex); } return ok; } bool performInstrumentDrop(MediaTrack* track, const std::string& chunkBase64) { if (!track || chunkBase64.empty()) return false; // One undo point for the whole gesture (mirrors the bank-verb undo discipline). Both the // FX add and the state write are REAPER-undoable, so Ctrl-Z removes the instance cleanly. Undo_BeginBlock2(nullptr); const bool ok = loadInstrumentOntoTrack(track, chunkBase64); // The undo label reflects the placement-of-the-player framing (not a capture, not an insert). Undo_EndBlock2(nullptr, "ReaSampler: drop capture onto FX chain", -1); return ok; } } // namespace reasampler