【问题标题】:Delphi: How to send MIDI to a hosted VST plugin?Delphi:如何将 MIDI 发送到托管的 VST 插件?
【发布时间】:2011-11-19 09:02:08
【问题描述】:

我想在我的 Delphi 程序中使用 VST 插件,它充当 VST 主机。我已经尝试了 tobybear 示例,使用了 delphiasiovst stuf,其中一些甚至可以工作,但是......我不知道如何将 MIDI 消息发送到插件(我知道大多数插件不会处理 MIDI,但我有一个示例插件)。

更具体地说:我希望当我发送 MIDI 消息时,我必须在 VST 插件中使用一种或其他方法,或者重新路由 MIDI 输出。我只是不知道怎么做。

谁能指出我如何做到这一点的文档或代码?提前致谢。

阿诺德


我使用两个测试插件:一个从 DelphiAsioVst 包和 PolyIblit 编译而来。两者都在 Finale 和 LMMS 中工作。加载到我的测试程序中都显示了他们的 VST 编辑器。

我确实插入了 TvstEvent 记录并对其进行了初始化,我插入了 MIDIData 和 AddMIDIData 过程以及一个计时器来提供测试数据并执行插件的 ProcessEvents 例程。 ProcessEvents 获得正确的测试数据,但听不到声音。当我将它直接发送到 midi 输出端口时,我会听到一些声音。

在下面的代码中 PropcessEvents 应该足够恕我直言,附加代码是测试 MIDI 信息是否正确发送。 VstHost [0] 是第一个插件,可以是 PolyIblit 也可以是 VSTPlugin,具体取决于测试。

procedure TMain_VST_Demo.TimerTimer (Sender: TObject);
var i: Int32;
begin
//   MIDIOutput.PutShort ($90, 60, 127);
   MIDIData (0, $90, 60, 127);

   if FMDataCnt > 0 then
   begin
      FMyEvents.numEvents := FMDataCnt;
      VSTHost[0].ProcessEvents(@FMyEvents);
//    if (FCurrentMIDIOut > 0) and MIMidiThru.Checked then
//     begin
      for i := 0 to FMDataCnt - 1 do
      MIDIOutput.PutShort (PVstMidiEvent (FMyEvents.events[i])^.midiData[0],
                           PVstMidiEvent (FMyEvents.events[i])^.midiData[1],
                           PVstMidiEvent (FMyEvents.events[i])^.midiData[2]);
//       FMidiOutput.Send(//FCurrentMIDIOut - 1,
//                   PVstMidiEvent(FMyEvents.events[i])^.midiData[0],
//                   PVstMidiEvent(FMyEvents.events[i])^.midiData[1],
//                   PVstMidiEvent(FMyEvents.events[i])^.midiData[2]);
//     end;
     FMDataCnt := 0;
   end;
end; // TimerTimer //

所以我没有得到插件中的事件。知道我做错了什么吗?

【问题讨论】:

    标签: delphi midi host vst


    【解决方案1】:

    您应该真正查看 minihost 核心示例(Delphi ASIO 项目,v1.4)。

    有一个MIDI事件的使用。基本上

    1. 您有一个 TVstEvents 变量(比如说 MyMidiEvents: TvstEvents)。
    2. 在整个运行时中,您为此变量分配内存(例如在应用程序构造函数中)
    3. 当您的 MIDI 回调中有一个事件时,您将其复制到 TVstEvents 堆栈中。
    4. 在 TVstHost 中调用进程之前,调用 MyVstHost.ProcessEvents(@MyMidiEvents)。

    这就是示例中的完成方式(微型主机核心),对于前面的每个步骤:

    1/ 在第 215 行,声明

    FMyEvents: TVstEvents;
    

    2/ 在第 376 行,分配:

    for i := 0 to 2047 do
    begin
     GetMem(FMyEvents.Events[i], SizeOf(TVSTMidiEvent));
     FillChar(FMyEvents.Events[i]^, SizeOf(TVSTMidiEvent), 0);
     with PVstMidiEvent(FMyEvents.Events[i])^ do
      begin
       EventType := etMidi;
       ByteSize := 24;
      end;
    end;
    

    3/ 在第 986 行,然后在第 1782 行,从回调中复制了 midi 事件:

    回调

    procedure TFmMiniHost.MidiData(const aDeviceIndex: Integer; const aStatus, aData1, aData2: Byte);
    begin
     if aStatus = $FE then exit; // ignore active sensing
     if (not Player.CbOnlyChannel1.Checked) or ((aStatus and $0F) = 0) then
      begin
       if (aStatus and $F0) = $90
        then NoteOn(aStatus, aData1, aData2) //ok
        else
       if (aStatus and $F0) = $80
        then NoteOff(aStatus, aData1)
        else AddMidiData(aStatus, aData1, aData2);
      end;
    end;
    

    活动副本

    procedure TFmMiniHost.AddMIDIData(d1, d2, d3: byte; pos: Integer = 0);
    begin
     FDataSection.Acquire; 
     try
      if FMDataCnt > 2046 
       then exit;                 
    
      inc(FMDataCnt);
      with PVstMidiEvent(FMyEvents.events[FMDataCnt - 1])^ do
       begin
        EventType := etMidi;
        deltaFrames := pos;
        midiData[0] := d1;
        midiData[1] := d2;
        midiData[2] := d3;
       end;
     finally 
      FDataSection.Release;
     end; 
    end;
    

    4/ 在第 2322 行,在 TAsioHost.Bufferswitch 中,调用了 TVstHost.ProcessEvents

    FDataSection.Acquire;
    try
      if FMDataCnt > 0 then
       begin
        FMyEvents.numEvents := FMDataCnt;
    
        VSTHost[0].ProcessEvents(FMyEvents);
    
        if (FCurrentMIDIOut > 0) and MIMidiThru.Checked then
         begin
          for i := 0 to FMDataCnt - 1 do
           FMidiOutput.Send(FCurrentMIDIOut - 1,
                       PVstMidiEvent(FMyEvents.events[i])^.midiData[0],
                       PVstMidiEvent(FMyEvents.events[i])^.midiData[1],
                       PVstMidiEvent(FMyEvents.events[i])^.midiData[2]);
         end;
         FMDataCnt := 0;
       end;
     finally  
      FDataSection.Release;
     end; 
    

    如果您无法分析所使用的方法,这应该会对您有很大帮助。

    【讨论】:

    • 非常感谢您的解释。我几乎没有研究过迷你主机,特别是因为它不能很好地与 XE 一起编译(我认为是 unicode 字符串问题)。我会试试这个,然后再找你。并感谢代码示例!
    • 你的答案是正确的。我花了一些时间才意识到我的 VST 概念可能是错误的。我使用的插件(polyiblit)接受一个 MIDI 事件并输出一个波形。我的程序不处理波浪,所以没有声音。对于 MIDI,我或多或少地按照您建议的方式工作。谢谢。感谢您将 KVR 网站指向我,我会尝试的。
    • 如果你没问题,你可以检查答案是否正确;)
    • 我很抱歉从未检查过这个答案。我现在已经更正了,但已经很晚了。
    【解决方案2】:

    如果您托管 VST 2.x 插件,您可以使用 AudioEffectX.ProcessEvents() 将 MIDI 事件发送到插件。

    来自 VST 文档。

    • 事件始终与当前音频块相关。

    • 对于每个流程周期,processEvents() 在 processReplacing() 调用之前调用一次(如果有新事件可用)。

    我不知道任何代码示例。 DelphiAsioVST 中可能有一些东西。

    【讨论】:

    • 谢谢,我会调查的。
    【解决方案3】:

    如果您希望更改编程语言,可以尝试VST.NET,它允许您使用 C# 和 VB.NET 编写插件和主机。

    希望对你有帮助。

    【讨论】:

      猜你喜欢
      • 2011-10-16
      • 2012-11-12
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      • 2011-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多