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

时间:2019-04-11 20:40:18

标签: c++ audio plugins signal-processing vst

我正在尝试通过一个简单的主机应用程序创建一个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的作用是什么?如何获得它来创建插件类的实例?

我从IPluginFactory,IComponent等任何类中获取的所有id,都得到了无法解析的外部符号。

createInstance函数通过以下方式返回 Steinberg :: kNoInterface ,因此当我尝试插入空的iid时找不到任何类。

有人对斯坦伯格(Steinberg)的Vst3有所了解吗? 任何代码示例或文档如何将Vst3用于插件托管?

谢谢// Alex。

1 个答案:

答案 0 :(得分:1)

关于模块初始化。

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

您也可以为此使用VST3::Hosting::Module中定义的public.sdk/source/vst/hosting/module.h类。

关于ID。

CID是用于标识vst3模块文件中实际插件组件类的类ID(也称为组件ID)。

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

IID是用于指定接口类的interface-id。 在插件加载上下文中,IID表示您要获取所创建插件的接口类的类型,通常为Vst :: IComponent。

VST3 SDK基于VST模块体系结构(VST-MA),与Microsoft的组件对象模型(COM)非常相似。 学习COM可以帮助您了解VST-MA。

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

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

基本概念   VST 3音频效果或乐器基本上由两部分组成:处理部分和编辑控制器部分。   相应的接口是:

     

处理器:Steinberg :: Vst :: IAudioProcessor + Steinberg :: Vst :: IComponent   控制器:Steinberg :: Vst :: IEditController   VST 3的设计建议通过实现两个组件,将处理器和编辑控制器完全分开。将效果分为这两个部分需要一定的额外精力来实现。   但是,这种分离使主机可以在不同的上下文中运行每个组件。它甚至可以在不同的计算机上运行它们。另一个好处是,在涉及自动化时,可以将参数更改分开。尽管为了处理这些更改而需要以样本精确的方式进行传输,但可以以更低的频率来更新GUI部件,并且可以将其偏移任意延迟补偿或其他处理偏移量所导致的数量。

     

支持这种分离的插件必须在处理器组件的类信息(Steinberg :: PClassInfo2 :: classFlags)中设置Steinberg :: Vst :: kDistributable标志。当然,并不是每个插件都可以支持此功能,例如,如果它严重依赖于无法轻易转移到另一台计算机上的资源。因此,如果未设置此标志,则主机不得尝试以任何方式分离组件。   尽管不建议这样做,但是可以在一个组件类中同时实现处理部分和控制器部分。主机在创建Steinberg :: Vst :: IAudioProcessor之后尝试查询Steinberg :: Vst :: IEditController接口,并成功使用它作为控制器。

     

-VST3 API文档(VST_SDK 3.6.13)

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

  1. 从模块文件中创建插件的Processor组件,作为Vst :: IComponent类。
  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版本。但这可能对您有帮助。

谢谢。