S6: embedded TCP/MCP UI via IReaperUIEmbedInterface

Inline keymap/level strip drawn into REAPER's embed bitmap, reusing the LICE
idiom. Pure embed_strip layout/hit-test (tested); processor exposes the embed
interface off queryInterface and publishes an RT-safe block peak for the level.
This commit is contained in:
2026-07-26 18:12:27 -04:00
parent b653a4f6a7
commit 03e760631c
8 changed files with 706 additions and 1 deletions
+28
View File
@@ -17,6 +17,7 @@
#include "capture_paths.h" // resolveBankFile (shared M4 path resolution)
#include "ext_keys.h" // kProjExtBanksKey (shared wire contract)
#include "reasampler_editor.h"
#include "reasampler_embed.h" // S6 embed shell + IReaperUIEmbedInterface (its iid DEF'd there)
#include "sample_map.h" // selectSample, resolvePerformance, buildZonedKeymap, state (de)ser
#include "wav_trim.h" // parseWavLayout, extractFloatFrames (shared WAV parse)
@@ -91,6 +92,23 @@ FUnknown* ReaSamplerProcessor::createInstance(void* /*context*/) {
return static_cast<IAudioProcessor*>(new ReaSamplerProcessor());
}
// Out-of-line so unique_ptr<ReaSamplerEmbed> sees the complete type here.
ReaSamplerProcessor::~ReaSamplerProcessor() = default;
tresult PLUGIN_API ReaSamplerProcessor::queryInterface(const TUID iid, void** obj) {
// S6: expose REAPER's inline-embed interface. REAPER queries the IEditController for
// IReaperUIEmbedInterface (reaper_vst3_interfaces.h); hand it our lazily-created embed
// shell. We own the shell (unique_ptr); the borrowed reference is valid because the
// processor outlives it. All other iids fall through to the SDK's queryInterface.
if (FUnknownPrivate::iidEqual(iid, IReaperUIEmbedInterface::iid)) {
if (!embed_) embed_ = std::make_unique<ReaSamplerEmbed>(this);
embed_->addRef();
*obj = static_cast<IReaperUIEmbedInterface*>(embed_.get());
return kResultOk;
}
return SingleComponentEffect::queryInterface(iid, obj);
}
tresult PLUGIN_API ReaSamplerProcessor::initialize(FUnknown* context) {
tresult result = SingleComponentEffect::initialize(context);
if (result != kResultOk) return result;
@@ -357,6 +375,16 @@ tresult PLUGIN_API ReaSamplerProcessor::process(ProcessData& data) {
if (inst) {
inst->engine.render(ch0, static_cast<std::size_t>(frames));
}
// Block peak (mono, pre-replicate) for the embedded strip's level indicator. A
// single scan of ch0 + one relaxed atomic store — RT-safe (no alloc/IO/lock). The
// UI thread reads it via embedActivityLevel(); a plain store is sufficient because
// the readout is advisory (no ordering dependency on other state).
float peak = 0.f;
for (int32 i = 0; i < frames; ++i) {
const float a = ch0[i] < 0.f ? -ch0[i] : ch0[i];
if (a > peak) peak = a;
}
embedPeak_.store(peak, std::memory_order_relaxed);
// Duplicate the mono render across the remaining output channels.
for (int32 ch = 1; ch < out.numChannels; ++ch) {
if (float* buf = out.channelBuffers32[ch]) {