c++audiopluginssignal-processingvst

How to Instantiate a Vst3 plugin in code? For a vst3 host app


I am trying to create a Vst3 plugin from a simple host app.

Here I have a simple code just to create an instance of a Vst3 plugin from a *.vst3 file.

    auto proc = (GetFactoryProc)GetFunction(hmodule, "GetPluginFactory");
    Steinberg::IPluginFactory* rawFactory = proc();

    // Get factory info.
    Steinberg::PFactoryInfo factoryInfo;
    rawFactory->getFactoryInfo(&factoryInfo);

    // Get classes.
    for (size_t i = 0; i < rawFactory->countClasses(); i++)
    {
        Steinberg::PClassInfo info;
        rawFactory->getClassInfo(i, &info);

        // ------------------------------------
        // ----------HOW TO USE THIS IDs-------
        // ------------------------------------
        Steinberg::FIDString cid = info.cid; // Is this correct?
        Steinberg::FIDString iid = Steinberg::Vst::IComponent::iid; // I dont know what I am doing...

        // ------------------------------------
        // HOW TO USE THE createInstance FUNCTION?
        // ------------------------------------
        void* instance(nullptr);
        Steinberg::tresult result = rawFactory->createInstance(cid, iid, &instance);
    }

The questions is: What are this ids for? I can guess that cid stands for class-id. But what is the iid for and how can I get it to create an instance of a plugin class?

Every iid I take from any classes, IPluginFactory, IComponent and so on, I get unresolved external symbol.

The createInstance function return Steinberg::kNoInterface by the way so no classes is found when I try to insert an empty iid.

Anyone who know anything about Vst3 from Steinberg? Any code example or documentation how to use the Vst3 for plugin hosting?

Thanks // Alex.


Solution

  • About module initialization.

    A *.vst3 module may require additional initialization.

    If a module exports some predefined functions, you should call it before getting the IPluginFactory.

    The exported function names are "InitDll" and "ExitDll" for Windows platform.

        // after the module is loaded.
        auto initDll = (bool(*)())GetFunction(hmodule, "InitDll");
        if(initDll) { initDll(); }
    
        auto proc = (GetFactoryProc)GetFunction(hmodule, "GetPluginFactory");
        Steinberg::IPluginFactory* rawFactory = proc();
    
        // before the module is unloaded.
        auto exitDll = (bool(*)())GetFunction(hmodule, "ExitDll");
        if(exitDll) { exitDll(); }
    

    You can also use VST3::Hosting::Module class defined in public.sdk/source/vst/hosting/module.h for this purpose.

    About IDs.

    The CID is the class-id (a.k.a. component-id) which is used for identifying the actual plugin component class in a vst3 module file.

    A *.vst3 module file can contain multiple plugins however a host application can't identify a plugin by its actual C++ class name (because the host never knows it). That's why the VST3 SDK provides the way to identify a actual plugin component class with CID.

    The IID is the interface-id which is used for specifying a interface class. In plugin loading context, IID represents which type of Interface class you want to get the created plugin as, normally it will be Vst::IComponent.

    VST3 SDK is based on VST Module Architecture (VST-MA) which is very much like Component Object Model (COM) of Microsoft. Learning COM will help you understand VST-MA.

    Additionally, each plugins in a *.vst3 module file normally consist of two components: The Processor component and The EditController component.

    Basic Conception A VST 3 audio effect or instrument basically consists of two parts: a processing part and an edit controller part. The corresponding interfaces are:

    Processor : Steinberg::Vst::IAudioProcessor + Steinberg::Vst::IComponent Controller : Steinberg::Vst::IEditController The design of VST 3 suggests a complete separation of processor and edit controller by implementing two components. Splitting up an effect into these two parts requires some extra efforts for an implementation of course. But this separation enables the host to run each component in a different context. It can even run them on different computers. Another benefit is that parameter changes can be separated when it comes to automation. While for processing these changes need to be transmitted in a sample accurate way, the GUI part can be updated with a much lower frequency and it can be shifted by the amount that results from any delay compensation or other processing offset.

    A Plug-in that supports this separation has to set the Steinberg::Vst::kDistributable flag in the class info of the processor component (Steinberg::PClassInfo2::classFlags). Of course not every Plug-in can support this, for example if it depends deeply on resources that can not be easily moved to another computer. So when this flag is not set, the host must not try to separate the components in any way. Although it is not recommended, it is possible to implement both, the processing part and the controller part in one component class. The host tries to query the Steinberg::Vst::IEditController interface after creating an Steinberg::Vst::IAudioProcessor and on success uses it as controller.

    -- VST3 API Documentation (VST_SDK 3.6.13)

    A plugins consists of two components, so you will call createInstance() twice. This is the step to load a plugin from a *.vst3 module file:

    1. Create the Processor component of the plugin from the module file, as Vst::IComponent class.
    2. Initialize the Processor component.
    3. Get the CID of the EditController component corresponding to the Processor component.
    4. Create the EditController component from the module file with the CID.
    5. Initialize the EditController component too.
    6. Connect and setup them.
        // Get classes.
        for (size_t i = 0; i < rawFactory->countClasses(); i++)
        {
            Steinberg::PClassInfo info;
            rawFactory->getClassInfo(i, &info);
    
            // info.category will be kVstAudioEffectClass for Processor component.
            // skip this component if not.
            if(info.category != kVstAudioEffectClass) {
                continue;
            }
    
            Vst::IComponent *comp(nullptr);
            Steinberg::tresult result
                = rawFactory->createInstance(info.cid, // tell factory which plugin to be created.
                                             Vst::IComponent::iid, // tell factory which type of interface you want.
                                             (void **)&comp // get the pointer to `comp`, and pass it as (void **)
                                             );
            if(result != kResultTrue) {
                // TODO: error handling
                return;
            }
    
            // now `comp` shall be valid pointer of Vst::IComponent.
    
            // initialize comp
            comp->setIoMode(Vst::IoModes::kAdvanced);
    
            // you should define host context object before and pass it here as `FUnknown *`.
            // the host context object is the class which normally derives Vst::IHostApplication,
            // Vst::IComponentHandler, Vst::IPluginInterfaceSupport, etc.
            comp->initialize(host_context);
    
            TUID edit_cid;
            comp->getControllerClassId(edit_cid);
            // (in detail, IEditController interface may be obtained from IComponent directly if the plugin
            //  derives SingleComponentEffect.
            //  For such plugins, do not use this method and obtain IEditController with `comp->queryInstance()`
            // )
    
            Vst::IEditController *edit(nullptr);        
            result = rawFactory->createInstance(edit_cid,
                                                Vst::IEditController::iid,
                                                (void **)&edit);
            if(result != kResultTrue) {
                // TODO: error handling
                return;
            }
    
            // initialize the EditController component too.
            edit->initialize(host_context);
    
            //...
    
            // now the two components are created.
            // connect and setup them.
    
            // use the plugin.
    
            // ...
    
            // don't forget destruction after using it.
            edit->terminate();
            comp->terminate();
    
            edit->release();
            comp->release();
        }
    

    FYI, I develop an open-source VST3 Host Application called Terra.

    https://github.com/hotwatermorning/Terra

    It is still alpha version now. But it may be helpful for you.

    Thank you.