Generating MIDI messages within a VST plugin and passing them to the host

Thanks for taking the time to respond tomto66, I think I have it straight in my head now.

Let me try to put this concept into words for the benefit of others who are new to VST programming and may have the same confusion: What was tripping me up here was thinking in terms of sending a midi message to an output device, as you would in a stand-alone application. A VST does not communicate with external devices, that is the domain of the host application. The host has a buffer where it stores incoming midi messages. That buffer is passed by reference (as “MidiBuffer& midiMessages”) to the VST’s processBlock method, which has the opportunity to modify the messages in the buffer or add new messages to it. The modified buffer is then passed on by the host to whatever is next in line (another plugin, an output device, etc.).

Let’s say you have a MidiBuffer object in your processor called “myBuffer.”
New midi messages generated by the VST should be added to myBuffer. In processBlock, iterate through the events stored in myBuffer and add them to midiMessages to pass them to the host.

I hope that makes sense! :grinning:

2 Likes

That is all correct, however it’s also totally possible for an audio plugin to send messages directly to a MIDI device, bypassing the host.

Well, yes, it is possible to send directly to MIDI devices, but you are not sure when your plugin code that generates the MIDI message will run. This means that your MIDI messages will suffer from timing jitter (unless you do some very fancy scheduling using timestamps from a reference clock etc…) That jitter will depend on the audio buffer size, and if it’s 32 samples or so at say 44100 Hz, that might not be detectable.

If you use the MIDIBuffer you get from the host, then you can add your MIDI events with a specific sampleNumber, so that they will be synchronized to the audio buffer of that same process call, which means that the message should play exactly at the moment the audio sample you specified is sounding out of your speakers (assuming the host does a good job of compensating for IO delays of the sound card I suppose). At least, there shouldn’t be any jitter related to the moment your plugin happens to get processed.

Just thought I’d add that for good measure :wink:

1 Like

Yes, all true and well explained. Our use case was for communicating with a MIDI device (to switch LED’s on and off) so sample accuracy was not important.

@KoenT, @adamski Thank you, that is all great information! Another consideration about generating MIDI within VSTs that I have run into is that Steinberg originally designed VST3 without support for MIDI controller messages. They back-pedaled a bit on this last year, and now plugins built with Juce will receive incoming controller messages in the processBlock. However, VST3 still will not send controller messages out to the host. This is frustrating for me because I am trying to create a plugin that generates NRPN messages for editing a hardware synthesizer. Apparently the latest VST build (3.6.12) now includes a LegacyMIDICCOutEvent which sounds like it would help me, but I don’t know how long it is going to be before Juce incorporates it. I will probably have to go the route of directly addressing an external device.

My 2 cents: If the messages are not timing-sensitive, why not go directly to the MIDI device rather than via the host? Unless you are allowing users to automate parameters in the host which then get sent out as NRPN messages…

Yes, this “LegacyMIDICCOutEvent” needs to be added, so that VST3 midi plugins can generate CCs! It’s been apparently a year and a half since Steinberg added these changes to VST3. Jules, any reason why this isn’t in there, or am I missing something? Is there some #define that needs to be made to enable this functionality?

Everything I’m coning up with when searching for info about this is that “it has been possible to generate CCs in a VST3 plugin for awhile, but JUCE does not incorporate those changes.”

5 Likes

Bumping this just to check if anything has been done to incorporate LegacyMIDICCOutEvent into Juce, or if anyone has found a workaround for passing CC messages from a VST3 to its host. I’ve noticed that pretty much every VST3 that I have from other developers (Arturia, Eventide, U-he, Audio Thing, etc.) allows incoming CC messages to pass through and on to the next plugin or the host (Reaper in my case), unlike the VST3s I create with Juce, which block those messages. Is there any word on this @jules or @t0m ?

3 Likes

Here’s something weird that I stumbled upon which might be of interest: in processBlock(), if I add a noteOn or noteOff message to the MidiBuffer when an incoming CC message is received, the CC message also gets passed through to the host (I’m using Reaper). Here’s my code:

void CcpassThruTestAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
    buffer.clear();

    MidiMessage msg;
    int time;
    MidiBuffer myBuffer;
    for (MidiBuffer::Iterator i(midiMessages); i.getNextEvent(msg, time);)
    {
        if (msg.isController())
        {
            MidiMessage myNoteOff(MidiMessage::noteOff(16, 127));
            myBuffer.addEvent(myNoteOff, time);
        }
    }

    midiMessages.swapWith(myBuffer);
}
1 Like

Testing my VST3 plugin in the AudioPluginHost, it would appear that JUCE VST3 plugins output pitch bend and CC messages just fine. But many hosts don’t forward them - at least not Ableton Live and Studio One.

But perhaps Reaper does?

[Edit] the VST3s I create all block CC messages, including pitch bend, when run in Reaper, which is why it’s so weird that adding in a note on/off lets the CCs pass through.[/Edit]

Reaper claimed that their latest version supports LegacyMidiCCOutEvent, but I tried out Steinberg’s testing plugin and it failed to output anything. I was in contact with one of Reaper’s developers a couple of days ago and he claims to have fixed the issue, so hopefully it will be working in their next update.

Does that happen in other hosts?

I don’t have any other hosts to confirm with :frowning_face:

What happens with the JUCE AudioPluginHost if your plugin generates CC’s, but no notes? Do they get out?

I tried a version which adds a controller message to the buffer rather than a note off. In Reaper, neither the generated CC nor the CC from the device are passed through. I also tried a version that adds both a note off and a CC to the buffer. In Reaper, the CCs from the device would once again pass through but the generated CC would not.

In AudioPluginHost, unlike Reaper, all my plugins pass along incoming CC messages. However, controller messages generated within a plugin are still not passed along.

Really? That’s odd… does your plugin receive the CC messages from the AudioPluginHost or do they just magically appear at the output?

Rather confusing isn’t it!

Hard to diagnose when daws are all handling it differently, some of them using hacks to play well with vst2 instead of moving to full and complete vst3 compliance.

How Studio One handles CC ...

I noticed with studio one, for example, that if I set up a chain of midi plugins in studio one that generate midi cc’s with vst2 and feed that into a vst3 plugin, the vst3 doesn’t see the generated cc’s. On the other hand if I add midi cc events to a controller lane in studio one, then those do show up as parameters to the vst3 plugin.

My feeling is that studio one is actually being very true to the vst3 approach. It’s keeping the cc controller lane data seperate from midi notes in a separate queue and then when calling back to vst3, it already has the cc’s ready to go as parameters for the vst3 plugin. But since it handles it that way it does not bother to look at the normal midi stream going through earlier vst2 plugins to see if there are cc’s there too. So the vst3 never sees them.

In truth they are handling it the way steinberg wants it to be handled, but that also means vst2 generated cc’s can’t be passed along the midi signal chain in studio one.

How Cubase handles it ...

Ironically, cubase does grab cc’s out of the midi stream before calling back into vst3’s, so at least for now they support that but according to many comments I have seen from a Arne, I suspect they might remove that at any time because it’s not officially supported by the api.

So anyway reaper could be doing who knows what to distinguish between cc’s recorded in the controller lane (or coming from your midi controller), versus midi cc generated via vst2 midi plugin (which is not a supported feature by steinberg even in vst2 by the way). In the past we got away with it because vst2 had all midi in one queue but since people are now trying to be more compliant with vst3, there is no core single midi queue anymore. So it makes sense that generated cc’s might not get through with totally undependable behavior per host and they happen to be rolling out their vst3 support.

On top of that you have juce which is trying to convert back and forth between the vst2 concept we see inside juce plugins as a single midi buffer with all midi, and the vst3 concept where there is no midi stream inside the plugin.

Juce SHOULD be converting cc Based parameters into cc events in the processblock midi buffer. And it SHOULD be doing something after the callback so that IF the host is using the LegacyCC the cc’s in the buffer will be handed to the host that way. But that assumes juce is doing its job, it assumes the host is looking for the LegacyCC conversion and it assumes there is nothing in the host or juce that would prevent that from happening in the case when there are also not at least some notes generated and added to the buffer by the plugin.

Someone needs to diagram all this out so we can all get in the same page about what is happening where.

Audiopluginhost is something by juce and we can debug the source so we should start there to see what is going whether there can be solved through improvements to juce or if simpley audiopluginhost needs to be updated, as do some of the daws

1 Like

Does it have something to do with the macro JUCE_VST3_EMULATE_MIDI_CC_WITH_PARAMETERS:

This is because of the lack of support for LegacyMIDICCOutEvent, as I mentioned before:

In the cases above, I was only adding a Juce controller message to the buffer, not a LegacyMIDICCOutEvent. I’m not going to experiment with that until Reaper releases their update and I can confirm that Steinberg’s testing VST3 works within it.

When you add a CC to the buffer inside VST3, it doesn’t make it out to the host, EXCEPT if and only if JUCE is copying it from there to the LegacyCC out feature. VST3 does not add CC’s to that buffer, nor look for them after callback. That is the VST2 way.

Juce is attempting to give you the same JUCE api of seeing both midi and cc in that midi buffer, but unless JUCE moves those to the VST3 way of getting them, then NO host will get them.

So fundamentally JUCE has to use legacy feature to get those CC events out…and not all DAW’s will support that legacyCC feature.