Added VST SDK support.

This commit is contained in:
Johannes Findeisen 2026-11-06 18:13:49 +01:00
commit 8e09f58944
25 changed files with 850 additions and 23 deletions

View file

@ -0,0 +1,132 @@
// Original MIT-licensed xtc examples using an external official Steinberg SDK.
// C++ is confined to this optional SDK example, never linked into libxtc.
#include "public.sdk/source/vst/vstaudioeffect.h"
#include "public.sdk/source/vst/vsteditcontroller.h"
#include "public.sdk/source/main/pluginfactory.h"
#include "pluginterfaces/base/ibstream.h"
#include "pluginterfaces/vst/ivstevents.h"
#include <array>
#include <cmath>
namespace {
using namespace Steinberg;
using namespace Steinberg::Vst;
const FUID controllerID(0x58544343,0x4F4E5431,0x98561234,0xABCD0003);
class Controller final : public EditController {
public:
static FUnknown* create(void*) { return static_cast<IEditController*>(new Controller); }
tresult PLUGIN_API setComponentState(IBStream* stream) override { return stream ? kResultOk : kInvalidArgument; }
};
class Gain final : public AudioEffect {
public:
Gain() { setControllerClass(controllerID); }
static FUnknown* create(void*) { return static_cast<IAudioProcessor*>(new Gain); }
tresult PLUGIN_API initialize(FUnknown* host) override {
const auto result = AudioEffect::initialize(host);
if (result != kResultOk) return result;
addAudioInput(STR16("Input"), SpeakerArr::kMono);
addAudioOutput(STR16("Output"), SpeakerArr::kMono);
return kResultOk;
}
tresult PLUGIN_API setBusArrangements(SpeakerArrangement* in, int32 ni,
SpeakerArrangement* out, int32 no) override {
if (ni != 1 || no != 1 || !in || !out || in[0] != SpeakerArr::kMono || out[0] != SpeakerArr::kMono)
return kResultFalse;
return AudioEffect::setBusArrangements(in, ni, out, no);
}
tresult PLUGIN_API canProcessSampleSize(int32 size) override {
return size == kSample32 ? kResultOk : kResultFalse;
}
tresult PLUGIN_API setProcessing(TBool) override { return kResultOk; }
tresult PLUGIN_API process(ProcessData& data) override {
if (data.symbolicSampleSize != kSample32 || data.numSamples < 0) return kInvalidArgument;
if (!data.numSamples) return kResultOk;
if (data.numInputs != 1 || data.numOutputs != 1 || !data.inputs || !data.outputs ||
data.inputs[0].numChannels != 1 || data.outputs[0].numChannels != 1 ||
!data.inputs[0].channelBuffers32 || !data.outputs[0].channelBuffers32 ||
!data.inputs[0].channelBuffers32[0] || !data.outputs[0].channelBuffers32[0]) return kInvalidArgument;
for (int32 i = 0; i < data.numSamples; ++i)
data.outputs[0].channelBuffers32[0][i] = data.inputs[0].channelBuffers32[0][i] * .5f;
data.outputs[0].silenceFlags = data.inputs[0].silenceFlags;
return kResultOk;
}
// Stateless examples still implement the SDK's stream contract.
tresult PLUGIN_API setState(IBStream* stream) override { return stream ? kResultOk : kInvalidArgument; }
tresult PLUGIN_API getState(IBStream* stream) override { return stream ? kResultOk : kInvalidArgument; }
};
class Sine final : public AudioEffect {
struct Voice { double phase = 0; float velocity = 0; };
std::array<std::array<Voice, 128>, 16> voices{};
void event(const Event& e) {
if (e.type == Event::kNoteOnEvent && e.noteOn.channel >= 0 && e.noteOn.channel < 16 &&
e.noteOn.pitch >= 0 && e.noteOn.pitch < 128) {
auto& v = voices[e.noteOn.channel][e.noteOn.pitch];
v.phase = 0; v.velocity = e.noteOn.velocity;
} else if (e.type == Event::kNoteOffEvent && e.noteOff.channel >= 0 && e.noteOff.channel < 16 &&
e.noteOff.pitch >= 0 && e.noteOff.pitch < 128)
voices[e.noteOff.channel][e.noteOff.pitch].velocity = 0;
}
public:
Sine() { setControllerClass(controllerID); }
static FUnknown* create(void*) { return static_cast<IAudioProcessor*>(new Sine); }
tresult PLUGIN_API initialize(FUnknown* host) override {
const auto result = AudioEffect::initialize(host);
if (result != kResultOk) return result;
addEventInput(STR16("Notes"), 16);
addAudioOutput(STR16("Output"), SpeakerArr::kMono);
return kResultOk;
}
tresult PLUGIN_API setBusArrangements(SpeakerArrangement*, int32 ni,
SpeakerArrangement* out, int32 no) override {
if (ni != 0 || no != 1 || !out || out[0] != SpeakerArr::kMono) return kResultFalse;
return kResultOk;
}
tresult PLUGIN_API canProcessSampleSize(int32 size) override { return size == kSample32 ? kResultOk : kResultFalse; }
tresult PLUGIN_API setProcessing(TBool) override { voices = {}; return kResultOk; }
tresult PLUGIN_API process(ProcessData& data) override {
if (data.symbolicSampleSize != kSample32 || data.numSamples < 0) return kInvalidArgument;
if (!data.numSamples) return kResultOk;
if (data.numOutputs != 1 || !data.outputs || data.outputs[0].numChannels != 1 ||
!data.outputs[0].channelBuffers32 || !data.outputs[0].channelBuffers32[0]) return kInvalidArgument;
int32 index = 0;
const int32 count = data.inputEvents ? data.inputEvents->getEventCount() : 0;
Event next{};
bool pending = index < count && data.inputEvents->getEvent(index, next) == kResultOk;
bool silent = true;
for (int32 i = 0; i < data.numSamples; ++i) {
while (pending && next.sampleOffset <= i) {
event(next); ++index;
pending = index < count && data.inputEvents->getEvent(index, next) == kResultOk;
}
double value = 0;
for (auto& channel : voices) for (size_t key = 0; key < channel.size(); ++key) {
auto& v = channel[key];
if (v.velocity == 0) continue;
value += std::sin(v.phase) * v.velocity * .1;
v.phase += 6.283185307179586 * 440.0 * std::pow(2.0, (static_cast<double>(key)-69.0)/12.0) / processSetup.sampleRate;
v.phase = std::fmod(v.phase, 6.283185307179586);
}
data.outputs[0].channelBuffers32[0][i] = static_cast<float>(std::tanh(value));
if (value != 0) silent = false;
}
data.outputs[0].silenceFlags = silent ? 1 : 0;
return kResultOk;
}
tresult PLUGIN_API setState(IBStream* stream) override { return stream ? kResultOk : kInvalidArgument; }
tresult PLUGIN_API getState(IBStream* stream) override { return stream ? kResultOk : kInvalidArgument; }
};
}
BEGIN_FACTORY("xtc", "", "", 0)
DEF_CLASS2(INLINE_UID(0x58544347,0x41494E31,0x98561234,0xABCD0001),
PClassInfo::kManyInstances, kVstAudioEffectClass, "xtc SDK Half Gain", 0,
"Fx", "1.0.0", kVstVersionString, Gain::create)
DEF_CLASS2(INLINE_UID(0x58544353,0x494E4531,0x98561234,0xABCD0002),
PClassInfo::kManyInstances, kVstAudioEffectClass, "xtc SDK Sine", 0,
"Instrument|Synth", "1.0.0", kVstVersionString, Sine::create)
DEF_CLASS2(INLINE_UID(0x58544343,0x4F4E5431,0x98561234,0xABCD0003),
PClassInfo::kManyInstances, kVstComponentControllerClass, "xtc SDK Controller", 0,
"", "1.0.0", kVstVersionString, Controller::create)
END_FACTORY