Delphi:如何将MIDI发送到托管的VST插件?

时间:2011-09-17 17:42:07

标签: delphi midi host vst

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

更具体一点:我希望当我发送MIDI信息时,我必须在VST插件中使用一种或其他方法或重新路由MIDI输出。我只是不知道如何。

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

阿诺德


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

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

在下面的代码中,PropcessEvents应该足够imho,附加代码是测试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 //

所以我没有在插件中获取事件。不知道我错了什么?

3 个答案:

答案 0 :(得分:4)

你应该看看minihost核心示例(Delphi ASIO项目,v1.4)。

有midi事件的使用。基本上

  1. 你有一个TVstEvents变量(比方说MyMidiEvents:TvstEvents)。
  2. 对于整个运行时,您为此变量分配内存(在app构造函数中为exmaple)
  3. 当你的MIDI回调中有一个事件时,你将它复制到TVstEvents堆栈上。
  4. 在TVstHost中调用进程之前,请调用MyVstHost.ProcessEvents(@MyMidiEvents)。
  5. 这是在示例(minihost核心)中完成的,对于之前的每个步骤:

    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; 
    

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

答案 1 :(得分:2)

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

来自VST文档。

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

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

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

答案 2 :(得分:1)

如果您想要更改编程语言,可以试试VST.NET,它允许您在C#和VB.NET中编写插件和主机。

希望它有所帮助。