S17 drop-and-load onto track FX button; S13 editor drop-accept (relay degraded)
S17: drag_out InstrumentDrop gesture + instrument_drop blob reusing the instrument's own serializer; bank_panel FX hover-track + add-VST/vst_chunk inject. S13 relay deferred (read-only bridge) — editor shows drop affordance.
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
// 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<std::uint8_t> 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, mono, generation 0) — the same
|
||||
// ComponentState a browser click would produce. 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));
|
||||
}
|
||||
|
||||
std::string encodeBase64(const std::vector<std::uint8_t>& 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<std::uint32_t>(bytes[i]) << 16) |
|
||||
(static_cast<std::uint32_t>(bytes[i + 1]) << 8) |
|
||||
static_cast<std::uint32_t>(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<std::uint32_t>(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<std::uint32_t>(bytes[i]) << 16) |
|
||||
(static_cast<std::uint32_t>(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<std::uint8_t> decodeBase64(const std::string& b64) {
|
||||
std::vector<std::uint8_t> 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<unsigned char>(c0));
|
||||
const int v1 = b64Value(static_cast<unsigned char>(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<std::uint32_t>(v0) << 18) |
|
||||
(static_cast<std::uint32_t>(v1) << 12);
|
||||
out.push_back(static_cast<std::uint8_t>((triple >> 16) & 0xFF));
|
||||
if (!pad2) {
|
||||
const int v2 = b64Value(static_cast<unsigned char>(c2));
|
||||
if (v2 < 0) return {};
|
||||
triple |= static_cast<std::uint32_t>(v2) << 6;
|
||||
out.push_back(static_cast<std::uint8_t>((triple >> 8) & 0xFF));
|
||||
if (!pad3) {
|
||||
const int v3 = b64Value(static_cast<unsigned char>(c3));
|
||||
if (v3 < 0) return {};
|
||||
triple |= static_cast<std::uint32_t>(v3);
|
||||
out.push_back(static_cast<std::uint8_t>(triple & 0xFF));
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace reasampler
|
||||
Reference in New Issue
Block a user