mtytel / helm

Helm - a free polyphonic synth with lots of modulation
http://tytel.org/helm
GNU General Public License v3.0
2.36k stars 202 forks source link

Access violation when running helm from a minimal VST3 host #236

Closed LaylBongers closed 5 years ago

LaylBongers commented 5 years ago
Helm 0.9.0

I'm writing a minimal host for VST3 plugins. All initialization is done following the VST3 host workflow: https://steinbergmedia.github.io/vst3_doc/vstinterfaces/workflow.html

While various other VST3s work with this minimal host (LABS for example works), Helm crashes with an access violation. It is not unique in this, PLAY crashes as well. It crashes specifically on the following line:

vst_plugin->processor->setProcessing(true)

As far as I know I've followed the bare basics of what VST3 requires me to provide to the plugin, is there anything I'm missing, or does helm require more than just this?

#include <stdio.h>
#include <stdexcept>
#include <memory>

#include <vst/hosting/module.h>
#include <vst/hosting/plugprovider.h>
#include <vst/hosting/hostclasses.h>
#include <pluginterfaces/base/funknown.h>
#include <pluginterfaces/vst/ivstcomponent.h>
#include <pluginterfaces/vst/ivstaudioprocessor.h>

extern "C" {

typedef struct RdPlugin RdPlugin;

typedef struct {
    RdPlugin* (*create)(double_t sample_rate);
    void (*destroy)(RdPlugin *plugin);
    void (*process)(RdPlugin *plugin, float_t **inputs, float_t **outputs);
} RdPluginInterface;

}

namespace Steinberg {
    Steinberg::FUnknown *gStandardPluginContext = nullptr;
}

struct VstPlugin {
    Steinberg::IPtr<Steinberg::Vst::IComponent> component;
    Steinberg::IPtr<Steinberg::Vst::IAudioProcessor> processor;
    bool processing_is_set = false;
};

RdPlugin* create(double_t sample_rate) {
    // TODO: This currently has various memory leaks in error conditions

    Steinberg::gStandardPluginContext = new Steinberg::Vst::HostApplication();

    auto plugin = new VstPlugin();

    std::string err;

    auto module = VST3::Hosting::Module::create("C:\\Program Files\\Common Files\\VST3\\Helm\\helm64.vst3", err);

    if (!module) {
        printf("Failed to load VST module: %s\n", err.c_str());
        return nullptr;
    }

    printf("Loaded VST module\n");

    auto factory = module->getFactory();

    for (auto &class_info : factory.classInfos())
    {
        if (class_info.category() == kVstAudioEffectClass)
        {
            printf("Found processor: %s\n", class_info.name().c_str());

            plugin->component = factory.createInstance<Steinberg::Vst::IComponent>(class_info.ID());

            break;
        }
    }

    if (!plugin->component) {
        printf("Couldn't initialize component\n");
        return nullptr;
    }

    printf("Created component\n");

    plugin->component->initialize(Steinberg::gStandardPluginContext);

    printf("Initialized component\n");

    Steinberg::Vst::IAudioProcessor *processor_ptr = nullptr;
    if (plugin->component->queryInterface(Steinberg::Vst::IAudioProcessor::iid, (void **)&processor_ptr) != Steinberg::kResultOk
        || !processor_ptr) {
        printf("Component does not implement IAudioProcessor interface\n");
        return nullptr;
    }
    plugin->processor = Steinberg::shared(processor_ptr);

    printf("Retrieved IAudioProcessor\n");

    Steinberg::Vst::ProcessSetup setup; // { kRealtime, kSample32, blockSize, sampleRate };
    setup.processMode = Steinberg::Vst::kRealtime;
    setup.symbolicSampleSize = Steinberg::Vst::kSample32;
    setup.maxSamplesPerBlock = 512;
    setup.sampleRate = sample_rate;

    if (plugin->processor->setupProcessing(setup) != Steinberg::kResultOk) {
        printf("Failed to setup processing");
        return nullptr;
    }

    if (plugin->component->setActive(true) != Steinberg::kResultOk) {
        printf("Failed to set component active");
        return nullptr;
    }

    plugin->processing_is_set = false;

    return (RdPlugin*)plugin;
}

void destroy(RdPlugin *plugin) {
    VstPlugin *vst_plugin = (VstPlugin*)plugin;

    printf("Cleaning up\n");

    delete plugin;
    delete Steinberg::gStandardPluginContext;
}

void process(RdPlugin *plugin, float_t **inputs, float_t **outputs) {
    VstPlugin *vst_plugin = (VstPlugin*)plugin;

    printf("1\n");

    if (!vst_plugin->processing_is_set) {
        printf("Setting processing to true\n");

        if (vst_plugin->processor->setProcessing(true) != Steinberg::kResultOk) {
            printf("Failed to set processing to true\n");
            return;
        }

        vst_plugin->processing_is_set = true;
    }

    printf("2\n");

    Steinberg::Vst::ProcessData data;

    data.processMode = Steinberg::Vst::ProcessModes::kRealtime;
    data.symbolicSampleSize = Steinberg::Vst::kSample32;

    data.numSamples = 512;
    data.numInputs = 1;
    data.numOutputs = 1;

    printf("3\n");

    Steinberg::Vst::AudioBusBuffers input;

    input.numChannels = 2;
    input.silenceFlags = 0;
    input.channelBuffers32 = (Steinberg::Vst::Sample32 **)inputs;

    data.inputs = &input;

    Steinberg::Vst::AudioBusBuffers output;

    output.numChannels = 2;
    output.silenceFlags = 0;
    output.channelBuffers32 = (Steinberg::Vst::Sample32 **)outputs;

    data.outputs = &output;

    printf("4\n");

    vst_plugin->processor->process(data);

    printf("5\n");
}

#define DLL_EXPORT __declspec(dllexport)

extern "C" {

DLL_EXPORT bool rd_plugin_get_interface(RdPluginInterface *interface) {
    interface->create = create;
    interface->destroy = destroy;
    interface->process = process;

    return true;
}

}
LaylBongers commented 5 years ago

I've found the error, module went out of scope and cleaned up stuff that should have stayed alive.