VST3 (midi) and JUCE

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.

4 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

Thnx I’ll try that

<< 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.>>
Well, you code ? No, easier : You compile ? Fire up compiler, compile and try .
What are you talking about ? Yes . It is a shame for amateurs - but I listed plugins of pros - and these work . I just wonder IF they used JUCE …

As Stephen pointed out a solution for you to try I guess the discussion is over. The juce team ought to fix the arp example to avoid confusion.

As I said, steinberg views vst as an audio sdk and whatever it is able to do with midi is only considered as being related to feeding software instrument plugins with data to produce audio. All midi-only plugins are happy accidents that steinberg does not endorse nor support.

In this case you have to make sure the vst3 plugin thinks it is processing audio in order for the callback to happen properly. Then you can sneak in there and manipulate the midi buffer as you wish to do, but ONLY note events will be there.

Juce is generally handling vst3 midi correctly per the vst3 specification. The vst3 spec is the limiting factor. Except juce is not supporting multiple midi ports which I wish it would

Makes sense …

can you make some midi plugin tutorials? would be cool to see some different applications of midi in vst3 :slight_smile:

FWIW: just did a quick test with the example plugin ArpeggiatorPluginDemo.h and this is my conclusion (JUCE 5.4.5):

  1. The line stephenk mentioned above is necessary.

  2. For the Plugin characteristics flags I had only “Plugin MIDI Input”, “Plugin MIDI Output” and “MIDI Effect Plugin” selected, the rest not.

  3. I tested in Cubase 10.5 by inserting the plugin on an audio track, and selecting the plugin as MIDI input for an instrument track (and then making sure either “Record enable” or “Monitor” is enabled for the instrument track).

  4. When I used the code that is present in the example’s processBlock, it did NOT work. So I replaced it with some very crude and primitive test code that just sends a note on and note off and that did work:

    void processBlock(AudioBuffer<float>& buffer, MidiBuffer& midi) override
    {
     // the audio buffer in a midi effect will have zero channels!
     jassert (buffer.getNumChannels() == 0);
    
     // however we use the buffer to get timing information
     auto numSamples = buffer.getNumSamples();
    
     // get note duration
     auto noteDuration = static_cast<int> (std::ceil(rate * 0.25f * (0.1f + (1.0f - (*speed)))));
    
     if (0) // set to 1 to use JUCE's code
     {
         MidiMessage msg;
         int ignore;
    
         for (MidiBuffer::Iterator it(midi); it.getNextEvent(msg, ignore);)
         {
             if (msg.isNoteOn())  notes.add(msg.getNoteNumber());
             else if (msg.isNoteOff()) notes.removeValue(msg.getNoteNumber());
         }
    
         midi.clear();
    
         if ((time + numSamples) >= noteDuration)
         {
             auto offset = jmax(0, jmin((int)(noteDuration - time), numSamples - 1));
    
             if (lastNoteValue > 0)
             {
                 midi.addEvent(MidiMessage::noteOff(1, lastNoteValue), offset);
                 lastNoteValue = -1;
             }
    
             if (notes.size() > 0)
             {
                 currentNote = (currentNote + 1) % notes.size();
                 lastNoteValue = notes[currentNote];
                 midi.addEvent(MidiMessage::noteOn(1, lastNoteValue, (uint8)127), offset);
             }
         }
     }
     else // some horribly jittery primitive test code
     {
         midi.clear();
    
         if ((time + numSamples) >= noteDuration)
         {
             midi.addEvent(MidiMessage::noteOn(1, 65, (uint8)127), 0);
             midi.addEvent(MidiMessage::noteOff(1, 65), numSamples / 2);
         }
     }
    
     time = (time + numSamples) % noteDuration;
    }
    

I was in a hurry, so I didn’t try to understand / check the example code, but at least that new test code above showed that VST3 can send out note on/off messages, even though the example code doesn’t seem to work. I didn’t test any other events than note on/off, given that there are known issues with that apparently.

1 Like

I made a tutorial with TheAudioProgrammer on how to create a midi vst3 plugin (that only processes notes though) right here:

Code can be found here:

P.S. some of the plugins mentioned in this thread, like Xfer Records Cthulhu, are actually VST2. Not sure about the rest.

2 Likes

Great . So problem solved … now i fired up JUCE Arp Example did this swapWith and … does not do … so I githubbed and found this :

But this guy uses the exact same code ( besides some minor changes )
he got rid of auto noteDuration = static_cast (std::ceil(rate * 0.25f * 0.1f));
and devised a slightly different version
and as an VST3 i t w o r k s … now … back to the top … which was the Q: why does code work for AU and VST2 but NOT for VST3

1 Like

Thank you all for your answers.

My project is mainly a MIDI translator from CC coming from a physical controller to multiple logical occurrences, depending on the user interaction.

I went back to VST2.

I am new to Juce and am in the process of developing a MIDI plugin. The base works as long as the host is playing, great so far. But I have seen plugins (e.g. Cthulu) that also output MIDI (notes) when the host is stopped. Since my DAW (Ableton Live) no longer calls processBlock in this case, how can I continue to output MIDI notes in VST3, e.g. using a HighResolutionTimer?

@bobo Ableton Live (like most other hosts except for Logic) doesn’t support the concept of MIDI FX.

Instead, define your app as a synth with MIDI Out, and your process block will get called.

For Logic to see it as a MIDI Effect, add kAudioUnitType_MIDIProcessor as your AU plugin type.

Hello Eyal,
I followed your tutorial and now I also get the answer from you in no time, thank you very much!
By the way, I also listened to your music, I really love it! Don’t forget to keep making music with all your programming :wink:

2 Likes

Thank you!
And so glad to see you liked my music. :slight_smile:

That tutorial is actually slightly out of date due to updates in JUCE 6. We need to update it… Let’s do it this or next week @theaudioprogrammer?

Eyal

So you can make a MIDI processing VST3 plugin using JUCE if you configure it as having one MIDI input, one MIDI output, plus one audio output onto which you put nothing but zeroes, but which forces processBlock() to run. If you configure it with more than one MIDI input or output, JUCE provides no way to distinguish them, so structure your code to use channels instead.

That’s what I’ve found so far, but I have a question before I invest more time in a technology that might be just plain insufficient for my use…

As of JUCE7 and the latest underlying VST3 API in autumn 2022, can a VST3 plugin built with JUCE emit CC messages? Other messages like aftertouch and program change? If it’s not available now, is it blocked on the JUCE team or the VST folks at Steinberg?

I don’t think either the plugin APIs or hosts have the concept of multiple MIDI inputs into a plugin, so channels is the only way that I know of to do that.

Yes, all of them work but some hosts (Ableton Live IIRC) will filter them going out of the plugin.

I’m pretty sure Cantabile, my live performance host of choice, supports plugins with multiple MIDI inputs and outputs. It’s only natural for any host organized around a flexible flow graph rather than a fixed track structure.

But thank you, it sounds like it’s worth further testing to see if all the outputs make it through. I was getting discouraged when reading this thread and wondered if I should go back to writing standalone MIDI processing apps as I have for many years.

Many hosts allow you to route multiple physical inputs into the same plugin, for example if you want all you keyboard controllers to trigger one instrument.

But, the plugin isn’t aware of that, these are routes done by the host that the plugin sees as a single MIDI input/output, possibly split to different channels.