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



我想在我的Delphi程序中使用VST插件,它作为VST主机。我试过玩具熊的例子,用过delphiasiovst的东西,其中一些甚至有效,但是……我不知道如何发送MIDI消息的插件(我知道,大多数插件不会处理MIDI,但我有一个例子插件)。

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

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

阿诺德


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

我确实插入了TvstEvent记录并初始化了它,我插入了middata和addmiddata过程以及一个计时器来提供测试数据并执行插件的ProcessEvents例程。ProcessEvents获得正确的测试数据,但没有听到任何声音。当我把它直接发送到midi输出端口时,我听到了一些声音。

在下面的代码中processevents应该是足够的,额外的代码是一个测试是否正确发送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 //

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

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

有midi事件的使用。基本上

  1. 你有一个TVstEvents变量(我们说mymidevents: TVstEvents)。
  2. 在整个运行时为这个变量分配内存(例如在应用的构造函数中)
  3. 当你在MIDI回调中有一个事件时,你把它复制到TVstEvents堆栈中。
  4. 在TVstHost中调用process之前,先调用MyVstHost。procesevents (@MyMidiEvents)。

这是在示例(minihost core)中如何完成的,对于前面的每个步骤:

1/at第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。processsevents被称为

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; 

如果你不能分析使用的方法,这将对你有很大的帮助。

如果您正在托管vst2。你可以使用AudioEffectX.ProcessEvents()向插件发送MIDI事件。

来自VST文档。

  • 事件总是与当前音频块相关。

  • 对于每个进程周期,processEvents()在processreplacement()调用之前被调用一次(如果有新的事件可用)

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

如果你想换一种编程语言,你可以试试VST。.NET允许你用c#和VB.NET编写插件和主机。

希望能有所帮助。

最新更新