// instrument_drop — pure implementation. See instrument_drop.h. // NO REAPER / SWELL / VST3 SDK / vendor. Reuses sample_map's ComponentState serializer. #include "instrument_drop.h" #include "vst/sample_map.h" // ComponentState + serializeComponentState (the SHARED writer) namespace reasampler { namespace { constexpr char kB64Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // -1 = not a base64 char; index by unsigned byte. Built once. int b64Value(unsigned char c) { if (c >= 'A' && c <= 'Z') return c - 'A'; if (c >= 'a' && c <= 'z') return c - 'a' + 26; if (c >= '0' && c <= '9') return c - '0' + 52; if (c == '+') return 62; if (c == '/') return 63; return -1; } } // namespace std::vector instrumentDropStateBytes(const std::string& sampleId) { // The ONE fact the drop carries: this capture is the instance's selection. Everything // else stays at the fresh-instance defaults (no zones, implicit channel mode, generation // 0) — the same ComponentState a browser click would produce. The implicit mode means // the GA auto-default will follow the loaded capture's channel count on first reload. // serializeComponentState is the instrument's own writer (the single source of truth for // the byte layout), so this is NOT a parallel encoder — it IS the instrument's encoder. ComponentState cs; cs.selectionId = sampleId; return serializeComponentState(cs); } std::string buildInstrumentDropChunk(const std::string& sampleId) { return encodeBase64(instrumentDropStateBytes(sampleId)); } bool infoNamesFxHotspot(const std::string& info) { // See the header contract. Two documented FX-bearing surfaces (SDK §GetThingFromPoint): // "fx_chain" / "fx_N" — the FX-chain and floating-FX windows. // "tcp.fx" / "mcp.fx" — the TCP / MCP FX button specifically. // Bare "tcp" / "mcp" and any other "tcp.*" / "mcp.*" sub-element (e.g. "tcp.mute", // "tcp.vol") are track-panel hits that are NOT on the FX button — those must not trigger // an instrument drop. Exact-string match for the two .fx tokens; prefix-match for fx_. if (info == "tcp.fx" || info == "mcp.fx") return true; auto startsWith = [&info](const char* p) { return info.rfind(p, 0) == 0; }; return startsWith("fx_"); } std::string encodeBase64(const std::vector& bytes) { std::string out; out.reserve(((bytes.size() + 2) / 3) * 4); std::size_t i = 0; const std::size_t n = bytes.size(); while (i + 3 <= n) { const std::uint32_t triple = (static_cast(bytes[i]) << 16) | (static_cast(bytes[i + 1]) << 8) | static_cast(bytes[i + 2]); out.push_back(kB64Alphabet[(triple >> 18) & 0x3F]); out.push_back(kB64Alphabet[(triple >> 12) & 0x3F]); out.push_back(kB64Alphabet[(triple >> 6) & 0x3F]); out.push_back(kB64Alphabet[triple & 0x3F]); i += 3; } const std::size_t rem = n - i; if (rem == 1) { const std::uint32_t triple = static_cast(bytes[i]) << 16; out.push_back(kB64Alphabet[(triple >> 18) & 0x3F]); out.push_back(kB64Alphabet[(triple >> 12) & 0x3F]); out.push_back('='); out.push_back('='); } else if (rem == 2) { const std::uint32_t triple = (static_cast(bytes[i]) << 16) | (static_cast(bytes[i + 1]) << 8); out.push_back(kB64Alphabet[(triple >> 18) & 0x3F]); out.push_back(kB64Alphabet[(triple >> 12) & 0x3F]); out.push_back(kB64Alphabet[(triple >> 6) & 0x3F]); out.push_back('='); } return out; } std::vector decodeBase64(const std::string& b64) { std::vector out; if (b64.size() % 4 != 0) return out; // malformed length -> empty (never throws) out.reserve((b64.size() / 4) * 3); for (std::size_t i = 0; i < b64.size(); i += 4) { const char c0 = b64[i], c1 = b64[i + 1], c2 = b64[i + 2], c3 = b64[i + 3]; const int v0 = b64Value(static_cast(c0)); const int v1 = b64Value(static_cast(c1)); if (v0 < 0 || v1 < 0) return {}; // illegal char in a non-pad position -> empty // Padding is only legal in the last two positions of the last quad. const bool pad2 = (c2 == '='); const bool pad3 = (c3 == '='); if ((pad2 || pad3) && i + 4 != b64.size()) return {}; // pad before the final quad if (pad2 && !pad3) return {}; // "=X" is malformed std::uint32_t triple = (static_cast(v0) << 18) | (static_cast(v1) << 12); out.push_back(static_cast((triple >> 16) & 0xFF)); if (!pad2) { const int v2 = b64Value(static_cast(c2)); if (v2 < 0) return {}; triple |= static_cast(v2) << 6; out.push_back(static_cast((triple >> 8) & 0xFF)); if (!pad3) { const int v3 = b64Value(static_cast(c3)); if (v3 < 0) return {}; triple |= static_cast(v3); out.push_back(static_cast(triple & 0xFF)); } } } return out; } } // namespace reasampler