如何在代码中实例化 Vst3 插件?对于 vst3 主机应用程序

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

我正在尝试从一个简单的主机应用程序创建一个 Vst3 插件。

这里我有一个简单的代码,用于从 *.vst3 文件创建 Vst3 插件的实例。

    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);
    }

问题是:这个 ID 有什么用?我猜 cid 代表 class-id。但是 iid 是做什么用的,我怎样才能得到它来创建插件实例 class?

我从任何 classes、IPluginFactory、IComponent 等获取的每个 iid,我都会得到无法解析的外部符号。

createInstance 函数 return Steinberg::kNoInterface 顺便说一下,所以当我尝试插入一个空的 iid 时没有找到 classes。

有人了解 Steinberg 的 Vst3 吗? 任何代码示例或文档如何使用 Vst3 进行插件托管?

谢谢 // Alex。

关于模块初始化。

*.vst3 模块可能需要额外的初始化。

如果一个模块导出了一些预定义的函数,你应该在获取IPluginFactory之前调用它。

对于 Windows 平台,导出的函数名称是 "InitDll" 和 "ExitDll"。

    // 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(); }

您也可以使用 public.sdk/source/vst/hosting/module.h 中定义的 VST3::Hosting::Module class 来达到此目的。

关于 ID。

CID 是 class-id (a.k.a.component-id),用于识别 vst3 模块文件中的实际插件组件 class。

一个 *.vst3 模块文件可以包含多个插件,但是主机应用程序无法通过其实际的 C++ class 名称来识别插件(因为主机永远不知道它)。 这就是为什么 VST3 SDK 提供了用 CID 识别实际插件组件的方法 class。

IID 是用于指定接口的接口 ID class。 在插件加载上下文中,IID 表示您希望将创建的插件作为哪种类型的接口 class,通常为 Vst::IComponent.

VST3 SDK基于VST Module Architecture (VST-MA),非常像Microsoft的Component Object Model (COM)。 学习COM,让你更懂VST-MA

此外,*.vst3 模块文件中的每个插件通常由两个组件组成:Processor 组件和 EditController 组件。

  • 处理器组件提供基本的插件 API 和 DSP API。
    • Processor 组件派生两个接口 classes:Vst::IComponent class 和 Vst::IAudioProcessor class.
  • EditController组件提供参数管理API和UI API。

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)

插件由两个组件组成,因此您将调用 createInstance() 两次。 这是从 *.vst3 模块文件加载插件的步骤:

  1. 从模块文件创建插件的处理器组件,如Vst::IComponent class。
  2. 初始化处理器组件。
  3. 获取Processor组件对应的EditController组件的CID
  4. 使用 CID 从模块文件创建 EditController 组件。
  5. 也初始化 EditController 组件。
  6. 连接并设置它们。
    // 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();
    }

仅供参考,我开发了一个名为 Terra 的开源 VST3 主机应用程序。

https://github.com/hotwatermorning/Terra

现在还是alpha版本。但它可能对你有帮助。

谢谢。