Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top