VST3 (midi) and JUCE

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);)

Sean

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.

juce_VST3Common.h

    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)),
                                         e.sampleOffset);
                        break;

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

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

                    case Steinberg::Vst::Event::kDataEvent:
                        result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, (int) e.data.size),
                                         e.sampleOffset);
                        break;
                        
                    // 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),
                                         e.sampleOffset);
                        break;
                    */
                    default:
                        break;
                }
            }
        }
    }
    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)
                break;

            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()),
                                                                  controlEvent.controllerNumber,
                                                                  controlParamID) == Steinberg::kResultOk)
                    {
                        Steinberg::int32 ignore;

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

                    continue;
                }
            }

            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;
            } */
            
            else
            {
                continue;
            }

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

            result.addEvent (e);
        }
    }
1 Like

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.

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.

reMidi
Playbeat
Riffer Generator
Thesys
Phrasebox
Orb Composer
Scaler
Instachord
Cthulhu
Midi Madnes
AutoTheory
LiquidNotes
MidiQ

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

This is also not true . Go To the top and see my initial post - and: JUCE Arp example is pretty simple and straightforward and MIDI only - but it does not work on VST3

I don’t know why they don’t fix that, but you just need to add an audio input and then it works:

    Arpeggiator()
    
    : AudioProcessor (BusesProperties().withInput("Input", AudioChannelSet::mono(), true)) // seem to need for sampleRate
    //    : AudioProcessor(BusesProperties()) // add no audio buses at all - Original, doesn't work
    
    {
        addParameter (speed = new AudioParameterFloat ("speed", "Arpeggiator Speed", 0.0, 1.0, 0.5));
    }

processBlock() will not be called with a valid number of samples unless there is an audio input.

2 Likes

I can’t think of a reason why the midi arp example should not work on vst3 if it’s only notes. Please provide more details about why and how it doesn’t work. I agree with you that juce should support as much of vst3 as possible, Notwithstanding the fact that vst3 cannot do everything with midi that vst2 can do. But still be forewarned, steinberg does not support or condone the use of either vst format for midi plugins! If juce arp is not working then get to the bottom of exactly why not. Most complaints about vst3 midi are related to non-note related problems.

Also does the arp example work when used inside the juce plugin host?

In my view steinberg does not condone or officially support vst midi plugin development. That means there is no Garauntee that all hosts will look for notes coming out of a vst3 plugin though that is likely to be ok if the midi buffer is passed by reference in and out of the plugin. Because it’s typically passed by reference then a plugin can manipulate the contents (notes only) and the changes will be seen by the host. But if the host copies the buffer there is no garauntee that the host will copy notes from output because steinberg doesn’t officially endorse or support that!!

Various hacks have been used by some hosts to handle cc and other types of events since vst3 abandoned them entirely. But not all hosts are doing that and it the same way so honestly I think if you need to process more then notes you should stick with vst2