VST3 (midi) and JUCE

After a week of compiling rewriting and cursing I came to the conclusion that VST3 (midi side) doesn’t like JUCE at all. eg try midi.clear() which should clear the input midibuffer doesn’t scratch VST3 - it just outputs the input . Try the JUCE Arpeggiator example … VST3 is completely muted… vst2.4 and AU work though

follow up - seems to eliviate problem a bit
1 tick : isSynth
2 make audio outputs or just : AudioProcessor () in constructor
still doesn’t clear input buffer

I’m also having midi problems with VST3 -> MPE VST3 issue

follow up 2nd
after testing a dozen midi processors I give up . VST3 definitely does not obey
midi.swap or midi.clear(). It just mirrors the input in any case .

I am seeing the same thing in the JUCE Plug-in Host. Thanks for clearing this up for me.

So does this mean that there is no way to stop an incoming note_on from being output?

Hi there,
I am having an issue with VST3 also.
I can’t seem to build VST2 plugins anymore there are build errors in xcode.
I battled with a plugin all day trying to troubleshoot.
After reading this thread I tried it as an .au and it works fine.
It seems like the midi juce classes aren’t doing what they should in VST3
I am just using basic stuff like

MidiBuffer processedMidi;
for(MidiBuffer::Iterator i (midiMessages); i.getNextEvent(m, time);)


I noticed that recently (03.12.2018), Steinberg updated their VST3 SDK to 3.6.12, introducing some changes that should make MIDI plugins easier to implement.

Interfaces Changes:

  • New Steinberg::Vst::LegacyMIDICCOutEvent: allowing a Plug-in to generate MIDI CC as output event.
  • New Steinberg::Vst::IMidiLearn (Plug-in): allow a Plug-in to get the currently live playing MIDI CC to implement custom MIDI learning.

Did JUCE integrate these new features already or is planning to? Anybody knows how far are we from being able to release VST3 MIDI plugins with the JUCE platform?

I’m not in a hurry, I have a license to release VST2 plugins, but I dread the moment where users will start asking for a VST3 version of my plugins.

1 Like

Are there any VST3 MIDI plugin available and how do they work in DAW’s?
Is it possible to add MIDI insert plugins into a slot before synth plugins like in Logic with AU MIDI FX plugins? Or is track to track routing required like in Ableton Live?

  • Jussi

@ohthepain I think I was having this problem and found a solution ~ see

Hey there, have you ever heard of Juce implementing Vst3 CC Output?

Nope. I’m still using VST2 and nobody is asking for VST3. Fingers crossed.

I was looking into this recently. The fact is, I was able to modify the JUCE AudioPluginHost to send CC Output from VST3’s, using the kLegacyMIDICCOutEvent. The problem is, it seems, is this: the modifications do not have anything to do with your plugin itself, but only the host. So fixing this in JUCE’s host does nothing to fix your VST3 plugin’s ability to send CCs in Reaper, Digital Performer, or any other host. It seems nobody supports this.

Here’s the completely unfinished hack (two modified functions), but it did work as a test. Note that I only implemented it for actual real CCs, but they have a way of using this for pitch bend, aftertouch, maybe a few other things, with Controller Numbers > 127.


    static void toMidiBuffer (MidiBuffer& result, Steinberg::Vst::IEventList& eventList)
        const int32 numEvents = eventList.getEventCount();

        for (Steinberg::int32 i = 0; i < numEvents; ++i)
            Steinberg::Vst::Event e;

            if (eventList.getEvent (i, e) == Steinberg::kResultOk)
                switch (e.type)
                    case Steinberg::Vst::Event::kNoteOnEvent:
                        result.addEvent (MidiMessage::noteOn (createSafeChannel (e.noteOn.channel),
                                                              createSafeNote (e.noteOn.pitch),
                                                              (Steinberg::uint8) denormaliseToMidiValue (e.noteOn.velocity)),

                    case Steinberg::Vst::Event::kNoteOffEvent:
                        result.addEvent (MidiMessage::noteOff (createSafeChannel (e.noteOff.channel),
                                                               createSafeNote (e.noteOff.pitch),
                                                               (Steinberg::uint8) denormaliseToMidiValue (e.noteOff.velocity)),

                    case Steinberg::Vst::Event::kPolyPressureEvent:
                        result.addEvent (MidiMessage::channelPressureChange (createSafeChannel (e.polyPressure.channel),
                                                                             denormaliseToMidiValue (e.polyPressure.pressure)),

                    case Steinberg::Vst::Event::kDataEvent:
                        result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, (int) e.data.size),
                    // srk - unfinished hack to allow VST3s to send Midi Controllers
                    // fixes it in AudioPluginHost, but has no effect in other DAWs
                    case Steinberg::Vst::Event::kLegacyMIDICCOutEvent:
                        result.addEvent (MidiMessage::controllerEvent (e.midiCCOut.channel, e.midiCCOut.controlNumber, e.midiCCOut.value),
    static void toEventList (Steinberg::Vst::IEventList& result, MidiBuffer& midiBuffer,
                             Steinberg::Vst::IParameterChanges* parameterChanges = nullptr,
                             Steinberg::Vst::IMidiMapping* midiMapping = nullptr)
        MidiBuffer::Iterator iterator (midiBuffer);
        const uint8* midiEventData = nullptr;
        int midiEventSize = 0;
        int midiEventPosition = 0;

        enum { maxNumEvents = 2048 }; // Steinberg's Host Checker states that no more than 2048 events are allowed at once
        int numEvents = 0;

        while (iterator.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
            if (++numEvents > maxNumEvents)

            MidiMessage msg (midiEventData, midiEventSize);

            if (midiMapping != nullptr && parameterChanges != nullptr)
                Vst3MidiControlEvent controlEvent;

                if (toVst3ControlEvent (msg, controlEvent))
                    Steinberg::Vst::ParamID controlParamID;

                    if (midiMapping->getMidiControllerAssignment (0, createSafeChannel (msg.getChannel()),
                                                                  controlParamID) == Steinberg::kResultOk)
                        Steinberg::int32 ignore;

                        if (auto* queue = parameterChanges->addParameterData (controlParamID, ignore))
                            queue->addPoint (midiEventPosition, controlEvent.paramValue, ignore);


            Steinberg::Vst::Event e = { 0 };

            if (msg.isNoteOn())
                e.type              = Steinberg::Vst::Event::kNoteOnEvent;
                e.noteOn.channel    = createSafeChannel (msg.getChannel());
                e.noteOn.pitch      = createSafeNote (msg.getNoteNumber());
                e.noteOn.velocity   = normaliseMidiValue (msg.getVelocity());
                e.noteOn.length     = 0;
                e.noteOn.tuning     = 0.0f;
                e.noteOn.noteId     = -1;
            else if (msg.isNoteOff())
                e.type              = Steinberg::Vst::Event::kNoteOffEvent;
                e.noteOff.channel   = createSafeChannel (msg.getChannel());
                e.noteOff.pitch     = createSafeNote (msg.getNoteNumber());
                e.noteOff.velocity  = normaliseMidiValue (msg.getVelocity());
                e.noteOff.tuning    = 0.0f;
                e.noteOff.noteId    = -1;
            else if (msg.isSysEx())
                e.type          = Steinberg::Vst::Event::kDataEvent;
                e.data.bytes    = midiEventData + 1;
                e.data.size     = (uint32) msg.getSysExDataSize();
                e.data.type     = Steinberg::Vst::DataEvent::kMidiSysEx;
            else if (msg.isChannelPressure())
                e.type                   = Steinberg::Vst::Event::kPolyPressureEvent;
                e.polyPressure.channel   = createSafeChannel (msg.getChannel());
                e.polyPressure.pitch     = createSafeNote (msg.getNoteNumber());
                e.polyPressure.pressure  = normaliseMidiValue (msg.getChannelPressureValue());
            // srk - unfinished hack to allow VST3s to send Midi Controllers
            // fixes it in AudioPluginHost, but has no effect in other DAWs
            else if (msg.isController())
                e.type                   = Steinberg::Vst::Event::kLegacyMIDICCOutEvent;
                e.midiCCOut.channel   = (Steinberg::int8) msg.getChannel();
                e.midiCCOut.controlNumber = (Steinberg::uint8) msg.getControllerNumber();
                e.midiCCOut.value     = (Steinberg::int8) msg.getControllerValue();
                e.midiCCOut.value2    = 0;
            } */

            e.busIndex = 0;
            e.sampleOffset = midiEventPosition;

            result.addEvent (e);

Thanks for your replies and this fix @stephenk,

I found a forum entry from Aug 27, 2019 (https://sdk.steinberg.net/viewtopic.php?f=4&t=694) that reads: “since VST SDK Version 3.6.12 (2018/12/03), the plugin have the possibility to generate MIDI CC as output event. This is supported by Cubase/Nuendo DAW”.

Have you tried your fix in Cubase or Nuendo?

I don’t have either of those to try; but if you do, you can easily test it. Just put in those two sections and uncomment them out.

The thing is: even if Cubase supports it; many do not…

Neither do I have cubase or nuendo unfortunately, that’s why I asked :wink: thanks again, seems like we will have to wait for DAWs implementing this

As this post is almost a year old, could anyone (ideally someone from JUCE’s team) tell us if there is any plan to solve MIDI usage with VST3 in JUCE ?

Looking around the Internet MIDI and VST3 seem to be a vast problem, not at all JUCE specific, but it’s a real pain, meaning going back to VST2 format (and losing the ease of use of JUCE’s Plugin Host during dev), but also losing a lot of time trying to understand the problem before reading posts as this.

With a simple test, I discovered that Process Block seems not even to be running if there is nothing else than MIDI handling (even a simple text message handling will not do, to the opposite of VST2).

Thank you by advance for your answers!

There is not much JUCE can do about poor midi support in VST3. Steinberg screwed that up with VST3. They tried to patch it up a bit a few years ago with that legacyOut feature-add, but as noted, not all hosts are aware of that or supporting that. There are other problems with VST3 midi also that I won’t get into now. Suffice it to say that Steinberg does not believe that VST plugins should be used for midi plugins. They don’t even feel VST2 should have been used for that and they say publicly that people who make VST2 midi plugins are simply exploiting the fact that it can be done but in their view its not what VST is intended for and make no promises about it. Their view is that VST is an audio plugin standard, not midi.

The only real support for MIDI in VST3, according to their philosophy, is for accepting midi data for instrument plugins. But midi out, they see not point, pur4pose or reason to even talk about it. they don’t view that as being “proper” use of VST. I’m not making this up, that is their official viewpoint.

In VST2, many people have gotten away with making midi plugins because of the way the API works, you get the midi buffer to play with inside your plugin. IN VST3, they have reduced that midi buffer to only notes. So you can play with notes-only in a VST3 plugin, but when you start talking about all other midi event types, such as CC, PitchBend, etc… those are not in the midi buffer at all…and Steinberg does not feel it should be. They feel that all of those things should be converted by the host into “parameters” and that the plugin can access those parameters, in a similar way as it would access automation parameters; and then use that information however you want in your plugin to effect the audio, but outputting any of that midi is not the intention for VST plugins according to Steinberg and they refuse to support it.

They came out with the legacyCCOut thing relatively recently as a way to get CC messages out because people were screaming about it and somehow someone must have gotten past their defiant attitude enough to convince them to support it, so that added the ability for a plugin to output CC, but the host has to call that special callback function in order to do it. if that host doesn’t do it, then you can’t get CC’s out of a VST3 plugin.

So bottom line, if you have a license for VST2, I would not bother messing around with VST3 midi. Its way too broken and Steinberg doesn’t wish to fix it, as I said, they don’t view midi processing as the purpose of VST.

If you don’t have a VST2 license, well then officially, you can’t really make a VST midi plugin that works reliably today. Simple as that. Sorry to bear the bad news, but that is all how it is today in 2020.

1 Like

All that being said, I do wish JUCE would at least support multiple midi input ports for receiving midi to more than 16 midi channels, as is possible with VST3. Then it would be possible to create a multi-port VST3 instrument. VST3 is definitely capable of that. JUCE does not support that though.

But any kind of midi thruput in VST3…forget it. Not with JUCE and not directly with VST3 sdk either.

1 Like

This is simply not true . Here is a list of MIDI processing VST3 plugins I own and they work. Without glitches.

Riffer Generator
Orb Composer
Midi Madnes

I*d say it is rather JUCE that does not care - for their own Arp Example does not work on VST3

Everything i said is absolutely true!

If you limit your midi processing to note events only then it should be fine in vst3, which still supports that even though steinberg doesn’t really officially support it