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

发布于 2024-12-05 15:01:03 字数 1686 浏览 3 评论 0原文

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

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

任何人都可以向我指出如何执行此操作的文档或代码吗?提前致谢。

Arnold


我使用两个测试插件:一个是从 DelphiAsioVst 包编译的,另一个是 PolyIblit。两者都适用于 Finale 和 LMMS。加载到我的测试程序中都显示它们的 VST 编辑器。

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

在下面的代码中,ProprocessEvents 应该足够了,恕我直言,附加代码是测试 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 //

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

I want to use VST plugins in my Delphi program which acts as a VST host. I have tried the tobybear examples, used the delphiasiovst stuf, got some of it even working, but... I don't know how to send MIDI messages to the plugin (I am aware that most plugins will not handle MIDI, but I have an example plugin that does).

To be more specific: I expect that when I send a MIDI message, I have to either use one or other method in the VST plugin or reroute the MIDI output. I just don't know how.

Can anyone point me to documentation or code on how to do this? Thanks in advance.

Arnold


I use two test plugins: the one compiled from the DelphiAsioVst package and PolyIblit. Both work in Finale and LMMS. Loaded into my test program both show their VST editor.

I did insert the TvstEvent record and initialized it, I inserted the MIDIData and the AddMIDIData procedures and a timer to provide test data and to execute the ProcessEvents routine of the plugin. ProcessEvents gets the correct test data, but no sound is heard. I hear something when I send it directly to the midi output port.

In the code below the PropcessEvents should be sufficient imho, the additional code is a test whether the MIDI information is correctly sent. VstHost [0] is the first plugin, being either the PolyIblit or the VSTPlugin, depending on the test.

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 //

So I don't get the events in the plugin. Any idea what do I wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

笑看君怀她人 2024-12-12 15:01:03

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

有一个 midi 事件的用途。基本上

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

这就是示例(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 行,从回调:

回调

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; 

如果您不这样做,这应该对您有很大帮助能够分析所使用的方法。

You should really look at the minihost core example (Delphi ASIO project, v1.4).

There is a use of midi events. Basically

  1. you have a TVstEvents variable ( let's say MyMidiEvents: TvstEvents).
  2. for the whole runtime you allocate the memory for this variable ( in the app constructor for exmaple)
  3. When you have an event in your MIDI callback, you copy it on the TVstEvents stack.
  4. Before calling process in the TVstHost, you call MyVstHost.ProcessEvents( @MyMidiEvents ).

this is how it's done in the example (minihost core), for each previously steps:

1/ at line 215, declaration

FMyEvents: TVstEvents;

2/ at line 376, allocation:

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/ at line 986 then at line 1782, the midi event is copied from the callback:

the callback

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;

event copy

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/ at line 2322, in TAsioHost.Bufferswitch, the TVstHost.ProcessEvents is called

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; 

this should help you a lot if you were not able to analyse the method used.

永言不败 2024-12-12 15:01:03

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

来自 VST 文档。

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

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

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

If you are hosting VST 2.x plugins, you can send MIDI events to the plugin using AudioEffectX.ProcessEvents().

From the VST docs.

  • Events are always related to the current audio block.

  • For each process cycle, processEvents() is called once before a processReplacing() call (if new events are available).

I don't know of any code examples. There might be something in DelphiAsioVST.

Bonjour°[大白 2024-12-12 15:01:03

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

希望有帮助。

If you're up for a change of programming language you could try VST.NET that allows you to write plugins and hosts in C# and VB.NET.

Hope it helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文