feat(vst): stand up VST3 instrument spike (S1) — skeleton, IPlugView↔LICE editor, REAPER bridge read

Vendor Steinberg VST3 SDK (v3.7.9_build_61); add reasampler_vst.vst3 as a second, additive build artifact with pure editor-geometry + bridge-marshal helpers under CTest.
This commit is contained in:
2026-07-26 15:29:54 -04:00
parent 5595ba42f9
commit 3c2a7c45b2
17 changed files with 1256 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
// reasampler_processor.cpp — see reasampler_processor.h.
#include "reasampler_processor.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/vstspeaker.h"
#include "reasampler_editor.h"
using namespace Steinberg;
using namespace Steinberg::Vst;
namespace reasampler::vst {
FUnknown* ReaSamplerProcessor::createInstance(void* /*context*/) {
// The host owns the returned reference. Cast up to the combined interface the SDK
// exposes (IAudioProcessor) so the FUnknown refcount is correctly rooted.
return static_cast<IAudioProcessor*>(new ReaSamplerProcessor());
}
tresult PLUGIN_API ReaSamplerProcessor::initialize(FUnknown* context) {
tresult result = SingleComponentEffect::initialize(context);
if (result != kResultOk) return result;
// Connect the REAPER bridge. Non-fatal if it fails (non-REAPER host): the
// instrument still loads, the editor just shows "no bridge".
bridge_.connect(context);
// Instrument bus topology: one event input (MIDI in, 16 channels), one stereo audio
// output, no audio input. This is the standard VSTi arrangement.
addEventInput(STR16("MIDI In"), 16);
addAudioOutput(STR16("Stereo Out"), SpeakerArr::kStereo);
return kResultOk;
}
tresult PLUGIN_API ReaSamplerProcessor::terminate() {
return SingleComponentEffect::terminate();
}
tresult PLUGIN_API ReaSamplerProcessor::setActive(TBool /*state*/) {
// Nothing to allocate/free in the silent skeleton; S4 will size voice buffers here
// against the setupProcessing block size.
return kResultOk;
}
tresult PLUGIN_API ReaSamplerProcessor::setupProcessing(ProcessSetup& setup) {
return SingleComponentEffect::setupProcessing(setup);
}
tresult PLUGIN_API ReaSamplerProcessor::process(ProcessData& data) {
// Silent skeleton: emit silence on the output bus so the instrument runs cleanly in
// REAPER's render/record path without a null buffer. S4 marshals MIDI->core->audio.
if (data.numOutputs > 0 && data.outputs && data.numSamples > 0) {
AudioBusBuffers& out = data.outputs[0];
for (int32 ch = 0; ch < out.numChannels; ++ch) {
if (data.symbolicSampleSize == kSample32) {
if (float* buf = out.channelBuffers32[ch]) {
for (int32 i = 0; i < data.numSamples; ++i) buf[i] = 0.f;
}
} else if (data.symbolicSampleSize == kSample64) {
if (double* buf = out.channelBuffers64[ch]) {
for (int32 i = 0; i < data.numSamples; ++i) buf[i] = 0.0;
}
}
}
// Flag output silence so the host can optimize (nothing plays yet).
out.silenceFlags = (out.numChannels >= 64)
? ~0ULL
: ((1ULL << out.numChannels) - 1);
}
return kResultOk;
}
IPlugView* PLUGIN_API ReaSamplerProcessor::createView(FIDString name) {
if (name && FIDStringsEqual(name, ViewType::kEditor)) {
return new ReaSamplerEditor(&bridge_);
}
return nullptr;
}
} // namespace reasampler::vst