Hi all,
I’ve been working on developing a basic VST3 host and succeeded in implementing both audio and GUI functionality. It’s working correctly with several plugins, but unfortunately with many others (such as Oril River and most of the iZotope plugins) IAudioProcessor::setProcessing
returns false after setting everything up. When this happens, the plugin simply does no processing and I hear silence. I’ll post the code below in case it helps anyone, I did my best to remove some of the unnecessary parts and focus on the VST-specific sections but it’s still kind of lengthy (if this isn’t the appropriate place for code snippets then moderators feel free to edit the post and remove or relocate it)…
VST3::Hosting::Module::Ptr _module = nullptr;
Steinberg::IPtr<Steinberg::Vst::PlugProvider> _plugProvider = nullptr;
Steinberg::OPtr<Steinberg::Vst::IComponent> _component = nullptr;
Steinberg::OPtr<Steinberg::Vst::IAudioProcessor> _processor = nullptr;
Steinberg::OPtr<Steinberg::Vst::IEditController> _controller = nullptr;
Steinberg::IPtr<Steinberg::IPlugView> _view = nullptr;
Steinberg::Vst::AudioBusBuffers _input, _output;
Steinberg::Vst::ProcessData _processData;
bool _processingIsSet = false;
float **inBufs = nullptr, **outBufs = nullptr;
namespace Steinberg {
Steinberg::FUnknown *gStandardPluginContext = nullptr;
}
static int audioCallback(const void *input, void *voidOutput, unsigned long numFrames, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *voidUserData)
{
auto output = static_cast<float *>(voidOutput);
//
// Clear output to zero
// Read audio file data into inBufs[0] and inBufs[1]
//
if (!_processingIsSet) {
if (_processor->setProcessing(true) != Steinberg::kResultOk) {
std::cerr << "Failed to set VST processing to true" << std::endl;
return false;
}
_processingIsSet = true;
}
_input.silenceFlags = 0;
_input.channelBuffers32 = inBufs;
_output.silenceFlags = 0;
_output.channelBuffers32 = outBufs;
if (_processor->process(_processData) == Steinberg::kResultOk) {
for (ma_uint64 i = 0; i < numFrames; ++i) {
output[i * 2 + 0] = outBufs[0][i];
output[i * 2 + 1] = outBufs[1][i];
}
} else {
std::cerr << "VST processing failed" << std::endl;
}
return 0;
}
// Usage: app [plugin filename].vst3 [audio file name].(wav|flac|mp3)
int main(int argc, char *argv[])
{
const int BLOCK_SIZE = 4096;
const int SAMPLE_RATE = 44100;
if (!Steinberg::gStandardPluginContext) {
Steinberg::gStandardPluginContext = new Steinberg::Vst::HostApplication();
Steinberg::Vst::PluginContextFactory::instance().setPluginContext(Steinberg::gStandardPluginContext);
}
std::string err;
_module = VST3::Hosting::Module::create(argv[1], err);
if (!_module) {
_logError(err);
return -1;
}
auto &factory = _module->getFactory();
for (auto &classInfo : factory.classInfos()) {
if (classInfo.category() == kVstAudioEffectClass) {
_plugProvider = Steinberg::owned(NEW Steinberg::Vst::PlugProvider(factory, classInfo, true));
break;
}
}
if (!_plugProvider) {
_logError("No audio module class found");
return -1;
}
_component = _plugProvider->getComponent();
if (!_component) {
_logError("No component found");
return -1;
}
if (_component->queryInterface(Steinberg::Vst::IAudioProcessor::iid, reinterpret_cast<void **>(&_processor)) != Steinberg::kResultOk || !_processor) {
_logError("Component does not implement IAudioProcessor");
return -1;
}
Steinberg::Vst::SpeakerArrangement stereoArrangement = Steinberg::Vst::SpeakerArr::kStereo;
if (_processor->setBusArrangements(&stereoArrangement, 1, &stereoArrangement, 1) != Steinberg::kResultOk) {
_logError("Failed to set bus arrangements");
return -1;
}
Steinberg::Vst::ProcessSetup setup = {};
setup.processMode = Steinberg::Vst::kRealtime;
setup.symbolicSampleSize = Steinberg::Vst::kSample32;
setup.maxSamplesPerBlock = BLOCK_SIZE;
setup.sampleRate = SAMPLE_RATE;
if (_processor->setupProcessing(setup) != Steinberg::kResultOk) {
_logError("Failed to set up processing");
return -1;
}
if (_component->setActive(true) != Steinberg::kResultOk) {
_logError("Failed to set component active");
return -1;
}
_input.numChannels = 2;
_output.numChannels = 2;
_processData.processMode = Steinberg::Vst::ProcessModes::kRealtime;
_processData.symbolicSampleSize = Steinberg::Vst::kSample32;
_processData.numSamples = BLOCK_SIZE;
_processData.numInputs = 1;
_processData.numOutputs = 1;
_processData.inputs = &_input;
_processData.outputs = &_output;
_controller = _plugProvider->getController();
if (!_controller) {
_logError("No edit controller found");
return -1;
}
_view = Steinberg::owned(_controller->createView(Steinberg::Vst::ViewType::kEditor));
if (!_view) {
_logError("EditController does not provide its own view");
return -1;
}
Steinberg::ViewRect viewRect = {};
if (_view->getSize(&viewRect) != Steinberg::kResultOk) {
_logError("Failed to get editor view size");
return -1;
}
if (_view->isPlatformTypeSupported(Steinberg::kPlatformTypeHWND) != Steinberg::kResultTrue) {
_logError("Editor view does not support HWND");
return -1;
}
//
// Create a window using SDL
// wmInfo.info.win.window stores the HWND of the window
//
if (_view->attached(wmInfo.info.win.window, Steinberg::kPlatformTypeHWND) != Steinberg::kResultOk) {
_logError("Failed to attach editor view to HWND");
return -1;
}
inBufs = new float *[2];
inBufs[0] = new float[BLOCK_SIZE];
inBufs[1] = new float[BLOCK_SIZE];
outBufs = new float *[2];
outBufs[0] = new float[BLOCK_SIZE];
outBufs[1] = new float[BLOCK_SIZE];
//
// Start PortAudio stream
//
//
// GUI Main Loop
//
//
// Destroy everything
//
return 0;
}