vst实例创建编辑器

Posted Luo大哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vst实例创建编辑器相关的知识,希望对你有一定的参考价值。

先上编辑器单元的代码:

unit editlink;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, VirtualTrees;
type
   tcomboeditlink=class(TInterfacedObject, IVTEditLink)
   private
    Fedit:TComboBox;
    itemstrs:TStringList;
    Fnode:PVirtualNode;
    Fcolumn:Integer;
    Ftree:TVirtualStringTree;
   public
    constructor create;
    constructor createof(strings:TStrings);
    function BeginEdit: Boolean; stdcall;                  // Called when editing actually starts.
    function CancelEdit: Boolean; stdcall;                 // Called when editing has been cancelled by the tree.
    function EndEdit: Boolean; stdcall;                    // Called when editing has been finished by the tree. Returns True if successful, False if edit mode is still active.
    function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;
                                                           // Called after creation to allow a setup.
    procedure ProcessMessage(var Message: TMessage); stdcall;
                                                           // Used to forward messages to the edit window(s)-
    procedure SetBounds(R: TRect); stdcall;
   end;
implementation
constructor tcomboeditlink.create;
begin
  inherited create;
  itemstrs:=TStringList.Create;
end;

constructor tcomboeditlink.createof(strings: TStrings);
begin
  create;
  itemstrs.Assign(strings);
end;

function tcomboeditlink.BeginEdit:Boolean;
begin
  Result:=True;
end;

function tcomboeditlink.CancelEdit:boolean;
begin
  Result:=True;
end;

function tcomboeditlink.EndEdit:boolean;
begin
  Fedit.Hide;
  Ftree.Text[Fnode,Fcolumn]:=Fedit.Text;
  Result:=True;
end;

function tcomboeditlink.PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean;
begin
  Ftree:=TVirtualStringTree(Tree);
  Fnode:=Node;
  Fcolumn:=Column;
  fedit:=TComboBox.Create(Ftree);
  Fedit.Hide;
  Fedit.Parent:=Ftree;
  Fedit.Items.Assign(itemstrs);
  Fedit.Text:=Ftree.Text[Fnode,Fcolumn];
  FreeAndNil(itemstrs);
  Fedit.Show;
  Result:=True;
end;

procedure tcomboeditlink.SetBounds(R: TRect);
begin
  Fedit.BoundsRect:=r;
  Fedit.Width:=80;
end;

procedure tcomboeditlink.ProcessMessage(var Message: TMessage);
begin
  Fedit.WindowProc(Message);
end;
end.

编辑器通常由两个部分组成:

TInterfacedObject:编辑器的源控件,可以是tedit,可以是tcombobox,还可以是其它任何可视控件。

IVTEditLink:编辑器接口。

根据编辑器的接口,至少应该为以下部分写代码:

function BeginEdit: Boolean; stdcall;

用于开始编辑,如果返回false,则不允许编辑。

function CancelEdit: Boolean; stdcall;

用于取消编辑。通常情况下,编辑是允许取消的,但你可以在特定条件下不允许取消。如果不允许取消,只需设置result:=false即可。

function EndEdit: Boolean; stdcall;

用于编辑结束,在此阶段你应该写上你对于编辑结果的处理。

function PrepareEdit(Tree: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex): Boolean; stdcall;

用于准备开始编辑时的处理,例如本程序在此阶段才开始对combobox的items赋值,对编辑器的text进行赋值等等。

procedure ProcessMessage(var Message: TMessage); stdcall;

对消息的处理。

procedure SetBounds(R: TRect); stdcall;

对编辑器的边界的处理。

以上部分是必须重写的部分,事实上,你还可以写上对可视控件的诸如“onchange”“onkeypressed”等事件的处理,或者创建代码的重写等等。

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

【中文标题】如何在代码中实例化 Vst3 插件?对于 vst3 主机应用程序【英文标题】:How to Instantiate a Vst3 plugin in code? For a vst3 host app 【发布时间】:2019-04-11 20:40:18 【问题描述】:

我正在尝试从一个简单的主机应用程序创建一个 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 等)中获取的每个 iid,都会得到无法解析的外部符号。

顺便说一句,createInstance 函数返回 Steinberg::kNoInterface,因此当我尝试插入空 iid 时找不到类。

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

谢谢 // 亚历克斯。

【问题讨论】:

看看我的 VSTx 库:turboirc.com/vstx 【参考方案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(); 

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

关于 ID。

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

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

IID 是用于指定接口类的接口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 模块文件加载插件的步骤:

    从模块文件创建插件的处理器组件,作为 Vst::IComponent 类。 初始化处理器组件。 获取Processor组件对应的EditController组件的CID。 从具有 CID 的模块文件创建 EditController 组件。 也初始化 EditController 组件。 连接并设置它们。
    // 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 版本。但它可能对你有帮助。

谢谢。

【讨论】:

Ahokey,很好的解释!我想我坚持最后一件事。当使用 Steinberg::Vst::IComponent:iid 作为 createInstance 中的参数时。然后我得到未解析的外部符号 "public: static class Steinberg::FUID const Steinberg::Vst::IComponent::iid" (?iid@IComponent@Vst@Steinberg@@2VFUID@3@B) 我已经包含了基础。 Vst3-SDK 的 vsxproj。为什么我会收到未解决的外部符号错误? ? 我明白了。 Vst::IComponent::iid 在public.sdk/source/vst/vstinitiids.cpp 中定义,并编译到 sdk.lib 而不是 base.lib。所以不仅要链接base.lib,还要链接sdk.lib。

以上是关于vst实例创建编辑器的主要内容,如果未能解决你的问题,请参考以下文章

vst实例 创建VST

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

如何使用 MFC 创建 VST 插件?

VST SDK & VST 模块 SDK

VST实例绘制VST

VST实例(10) hint(提示)