// 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 #include 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(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(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, 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(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(key)-69.0)/12.0) / processSetup.sampleRate; v.phase = std::fmod(v.phase, 6.283185307179586); } data.outputs[0].channelBuffers32[0][i] = static_cast(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