132 lines
7.1 KiB
C++
132 lines
7.1 KiB
C++
|
|
// 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
|