"audio plugin host" MIDI Output

Hey all,

I like to use the "audio plugin host" under the "examples" folder (recently moved from "extras") to debug my VST's. It's lightweight, I can configure I/O how I want, doesn't crash as violently as some hosts, etc. My newest VST produces MIDI output that I would like to send to an external MIDI device, but the "audio plugin host" has no filter for MIDI Output despite being configurable from its AudioDeviceSelectorComponent.

I noticed that the AudioProcessorGraph::AudioGraphIOProcessor namespace already has a midiOutputNode class, so I made a simple modification to the "audio plugin host" to add the functionality... but it doesn't work! No crashes or anything, I'm just not getting MIDI output on the channel I have slected.

Any one have any clue why? Or any clue why this obvious functionality is not included in the "audio plugin host" to begin with? I'm using a workaround for now so no rush :D

Code comparison: https://github.com/chrisdonahue/JUCE/compare/julianstorer:master...audio_plugin_host_midi_out

Screenshot1: 

Screenshot2: 

For clarity MIDI Input DOES work :)

I'm also using the Juce plugin host for testing and could do with a MIDI Output. Is this easily fixed?

Well easily is relative I guess, but it's quite doable. Have a look at my post 

http://www.juce.com/forum/topic/processblock-and-midioutput?page=1#comment-302200

Hi oxxyyd,

I’m trying to do exactly the same thing as the original poster. Thanks for the long description in the other thread. It gave me some insights but it didn’t work, as JUCE has evolved quite a bit since 2015…

So I’d like to ask: what’s the best way in 2019 / JUCE 5.4.4 to get MIDI output in the plugin host example?

BTW, I’m a bit worried about the dynamic_cast<AudioProcessorGraph*> in your solution. I suppose there may be situations where you’ll get a null pointer back and which may cause problems.

Best regards
Fritz

Hi MNSP,

I haven’t updated my Juce Code since last summer. Are you saying there’s still no Juce tool to test pure midi plugins yet? I’ve seen lots of plugin tutorials popping up over the last years, so I would have thought matters had changed…

About the dynamic_cast, I don’t think there’s any problem utilizing a cast per se, either you have a pointer and then it’s always AudioProcessorGraph * or else it’s a null pointer which, hopefully :), I check for or assume it can’t be for reasons I don’t remember by now. However IIRC, if you removed the midi source (or was it the midi destination?) in the AudioDeviceManger, while playing, you wound up with a null pointer in the processBlock. I didn’t follow up on that though, while it was only test tool…

Hi

Thanks for your reply. Actually what I tried (and finally succeeded) to do is to extend code based on the Audio Plugin Host to have a MIDI output (I’m working on a live coding tool that should work with plugins and MIDI devices -> Sneak peek).

It seems that some of the modifications are not necessary anymore, but still, MIDI output does not seem to work without changing some of the JUCE library’s source. :frowning:

I’ll update your instructions for JUCE 5.4.4 one of these days.

Best regards,
Fritz

1 Like

As promised, here are the instructions originally posted by oxxyyd on how to modify the plugin host to handle midi out, updated to JUCE version 5.4.4:

1 First you need to add some code to actually create the midi out connection so to say
In InternalPlugins.h you change

PluginDescription audioInDesc, audioOutDesc, midiInDesc;

to

PluginDescription audioInDesc, audioOutDesc, midiInDesc, midiOutDesc;

in InternalPlugins.cpp you add in the relevant places:

{
    AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::midiOutputNode);
    p.fillInPluginDescription (midiOutDesc);
}

and

if (name == midiOutDesc.name)  return std::make_unique<AudioProcessorGraph::AudioGraphIOProcessor> (AudioProcessorGraph::AudioGraphIOProcessor::midiOutputNode);

and

midiOutDesc,

to the line that says

results.add (audioInDesc, audioOutDesc, midiInDesc,

(hint: search for midiInDesc and add the corresponding code for midiOutDesc).

In PluginGraph.cpp you add

addPlugin (internalFormat.midiOutDesc,  { 0.25, 0.9 });

NOTE: at this point your code should be in a state that will compile and the MIDI Output node should appear in the plugin graph (but it doesn’t work yet).

2 Some more updates
Add to the collection of privates of GraphDocumentComponent in GraphEditorPanel.h

MidiOutput *midiOut;

Make GraphDocumentComponent a ChangeListener i.e in GraphEditorPanel.h it should look like

class GraphDocumentComponent  : public Component,
                                public DragAndDropTarget,
                                public DragAndDropContainer,
                                public ChangeListener

and add the following line to the public part of the class declaration:

void changeListenerCallback(ChangeBroadcaster* source) override;

3 Add to GraphEditorPanel.cpp

void GraphDocumentComponent::changeListenerCallback(ChangeBroadcaster* source) {
  if (source == &deviceManager) {
    midiOut = deviceManager.getDefaultMidiOutput();
    
    if (midiOut) {
        midiOut->startBackgroundThread();
    }
    
    graphPlayer.setMidiOutput(midiOut);
  }
}

4 Now add the line

changeListenerCallback(&deviceManager);

somewhere in the constructor of GraphDocumentComponent (GraphEditorPanel.cpp).

and put following at the to beginning of ~GraphDocumentComponent

if (midiOut)
    midiOut->stopBackgroundThread();

and add to the method init(), after the line that begins with “keyState.addListener”, the line

deviceManager.addChangeListener(this);

5 Now we’ve come to the part where midi output is actually taken from the AudioProcessorGraph.
Modify the code below by adding the 7 lines after

// modification for MIDI output:

In juce_AudioProcessorPlayer.cpp:

        if (! processor->isSuspended())
        {
            if (processor->isUsingDoublePrecision())
            {
                conversionBuffer.makeCopyOf (buffer, true);
                processor->processBlock (conversionBuffer, incomingMidi);
                buffer.makeCopyOf (conversionBuffer, true);
            }
            else
            {
                processor->processBlock (buffer, incomingMidi);
            }

            // modification for MIDI output:
            AudioProcessorGraph* processorgraph=dynamic_cast<AudioProcessorGraph*>(processor);
            if (processorgraph) {
                MidiBuffer* midiBuffer (processorgraph->getCurrentMidiOutputBuffer());
                if (midiBuffer && !midiBuffer->isEmpty() && midiOutput) {
                    midiOutput->sendBlockOfMessages(*midiBuffer, Time::getMillisecondCounter(), getCurrentProcessor()->getSampleRate());
                }
            }

            return;
        }

6 And last add following method declaration to the publics of juce_AudioProcessorGraph.h

// modification for MIDI output:
MidiBuffer* getCurrentMidiOutputBuffer();

and the corresponding implementation in juce_AudioProcessorGraph.cpp:

// modification for MIDI output:
MidiBuffer* AudioProcessorGraph::getCurrentMidiOutputBuffer() {
  if (isUsingDoublePrecision()) {
    if (renderSequenceDouble != nullptr) {
        return &renderSequenceDouble->currentMidiOutputBuffer;
    } else {
        return nullptr;
    }
} else {
    if (renderSequenceFloat != nullptr) {
        return &renderSequenceFloat->currentMidiOutputBuffer;
    } else {
        return nullptr;
    }
  }
}

After building and starting the plugin host you should now have a MIDI output. Draw a connection between midi input and midi output. If you have selected a midiout device in the audiosettings (e.g Microsft GS Wavetable Synth in windows) you should now here a piano when playing on the midikeyboard (or see the MIDI Out LED blink if you selected a physical MIDI interface).

Note from original instructions (which I would not necessarily recommend, especially if it involves copying and pasting a lot of JUCE library code, which then has to be maintained to keep track with the changes that are being made to JUCE), added here for the sake of completeness:

If you’re more into copy & paste programming you could instead of modifying the two [now three] juce files create a “midiout processor” by subclassing juce_AudioProcessor. But I leave that as an excersise for the reader…

2 Likes

Thank you for this! I will be needing this very shortly…

Hi Stephen

You’re welcome. I’d like to add though that this solution supports only one MIDI output at a time, which is ok for testing plugins I guess, but not for making music when you have many physical synths you want to connect.

For example I have an 8x8 MIDI interface and currently I can only use one of the eight output channels at a time. Ideally I’d like to have in my plugin graph a block with eight inputs representing the eight outputs of the interface.

If anyone here has an idea how this can be achieved, I’d be happy to hear about it.

Fritz

There seems to be a slight discrepancy between the code you have quoted in juce_AudioProcessorPlayer.cpp and what I have in my 5.4.4 version:

In the AudioBuffer() method, you show this:

    if (! processor->isSuspended())
    {
        if (processor->isUsingDoublePrecision())
        {
            conversionBuffer.makeCopyOf (buffer, true);
            processor->processBlock (conversionBuffer, incomingMidi);
            buffer.makeCopyOf (conversionBuffer, true);
        }
        else
        {
            processor->processBlock (buffer, incomingMidi);
        }

        // modification for MIDI output:
        AudioProcessorGraph* processorgraph=dynamic_cast<AudioProcessorGraph*>(processor);
        if (processorgraph) {
            MidiBuffer* midiBuffer (processorgraph->getCurrentMidiOutputBuffer());
            if (midiBuffer && !midiBuffer->isEmpty() && midiOutput) {
                midiOutput->sendBlockOfMessages(*midiBuffer, Time::getMillisecondCounter(), getCurrentProcessor()->getSampleRate());
            }
        }

        return;
    }

However, in the 5.4.4 code I have, there are two additional lines at the bottom already which would make it look like this:

        if (! processor->isSuspended())
        {
            if (processor->isUsingDoublePrecision())
            {
                conversionBuffer.makeCopyOf (buffer, true);
                processor->processBlock (conversionBuffer, incomingMidi);
                buffer.makeCopyOf (conversionBuffer, true);
            }
            else
            {
                processor->processBlock (buffer, incomingMidi);
            }

            
            // modification for MIDI output:
            AudioProcessorGraph* processorgraph=dynamic_cast<AudioProcessorGraph*>(processor);
            if (processorgraph) {
                MidiBuffer* midiBuffer (processorgraph->getCurrentMidiOutputBuffer());
                if (midiBuffer && !midiBuffer->isEmpty() && midiOutput) {
                    midiOutput->sendBlockOfMessages(*midiBuffer, Time::getMillisecondCounter(), getCurrentProcessor()->getSampleRate());
                }
            }
            
            if (midiOutput != nullptr)
                midiOutput->sendBlockOfMessagesNow (incomingMidi);

            return;
        }

Since this has something to do with a midiOutput, I wonder how this figures in to your modifications?

Secondly, in your instructions, part 6, your line:

and the corresponding implementation in juce_AudioProcessorGraph.h:

Should be, I believe:

and the corresponding implementation in juce_AudioProcessorGraph.cpp

So, I have made all the changes and compiled this, and indeed I now have a MidiOut in the AudioPlugInHost. However, and this may be a different issue, when I compile and add the ArpeggiatorPlugin Demo and connect it, it does seem to pass MIDI input notes to the selected MIDI Output, but no arpeggiation takes place…

This code block seems totally unnecessary in juce_AudioProcessorGraph.cpp:

            // this block seems totally unnecessary - I removed it
            /*AudioProcessorGraph* processorgraph=dynamic_cast<AudioProcessorGraph*>(processor);
            if (processorgraph) {
                MidiBuffer* midiBuffer (processorgraph->getCurrentMidiOutputBuffer());
                if (midiBuffer && !midiBuffer->isEmpty() && midiOutput) {
                    midiOutput->sendBlockOfMessages(*midiBuffer, Time::getMillisecondCounter(), getCurrentProcessor()->getSampleRate());
                }
            }// to here
            */

As long as you have the following two lines, which were not present in your example and must have been added to JUCE recently:

            if (midiOutput != nullptr)
                midiOutput->sendBlockOfMessagesNow (incomingMidi);

In fact, having the top block of code in there causes notes to be sent twice when the plugin is compiled as a standalone.

Hi Stephen,
I just tried out just leaving the two lines you pointed out and effectively it seems to work. However, quickly going through the code in juce_audioProcessorPlayer.cpp, it is not clear to me where the “incomingMidi” buffer actually gets filled and if it always is current output buffer of the processorgraph. Unless you’re sure it’s totally redundant I’d propose something like this:

            // modification for MIDI output:
            AudioProcessorGraph* processorgraph=dynamic_cast<AudioProcessorGraph*>(processor);
            if (processorgraph) {
                MidiBuffer* midiBuffer (processorgraph->getCurrentMidiOutputBuffer());
                if (midiBuffer && !midiBuffer->isEmpty() && midiOutput) {
                    midiOutput->sendBlockOfMessages(*midiBuffer, Time::getMillisecondCounter(), getCurrentProcessor()->getSampleRate());
                }
            } else {
                if (midiOutput != nullptr)
                    midiOutput->sendBlockOfMessagesNow (incomingMidi);
            }

That sounds like a good approach. I have no real idea how this is working; just that I managed to get double notes with both of them in there at one point. So one or the other seems like a good bet. I’ll put it in there like that and see what happens. :slight_smile:

@mnsp
Hi, Here’s a JUCE newbie question regarding this topic:
My plugin generates MIDI events (something like audio-to-midi). I couldn’t see from the MidiOutput class nor from the above thread how my plugin process should to add events to currentMidiOutputBuffer. processBlock doesn’t pass the out buffer for me to fill.
As far as I can see, whenever the midi input buffer is passed for incoming midi, so should the midi output buffer for generated midi, e.g. in all processBlock calls… am I wrong?

Another puzzling observation, in AudioProcessorGraph, the call:
midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0);
is using the number of audio samples to add MIDI events? (buffer is an audio buffer…)

There are many posts on this here. Basically, you cannot send just MIDI unless you have an audio input. ProcessBlock() will not be called unless you do. At that point, the numberOfSamples will be correct and you can use processBlock to fill the midiBuffer. Check out the Arpeggiator Tutorial, but remember that it is wrong and to make it work you have to add an audio input.

Here’s a few recent posts I can think of:

Thanks @stephenk. I had a look in the links you posted here, but they didn’t help me here.

Well I do have audio input: in short, my plugin receives audio in (mic) & midi in (say from a keyboard), and outputs audio out & midi out (voice to midi).
And of course, I’ve added the changes mentioned in MNSP’s cookbook above.

(I’m using JUCE 4.2, so code might differ from the latest):
In AudioProcessorGraph::processAudio, currentMidiOutputBuffer is cleared, the process is called, and then currentMidiOutputBuffer is expected to contain the midi events processed earlier in the plugin.
However I can’t see where and how currentMidiOutputBuffer can be filled if it is not passed to the plugin process.

I used some brute force as a proof of concept, and passed a reference to currentMidiOutputBuffer as a new argument to the relevant processBlock / perform calls.
I’ve done this all the way down to my plugin process code, where I could now add the generated midi events to this buffer (as well as processing the audio).

Doing this, I could finally get the desired midi out events! However this makes a total mess of the SDK and I’m looking for a ‘by the book’ solution…

To sum up, my questions are:

  1. How is the midi out buffer supposed to be filled if not passed down to the plugin process? Shouldn’t the out buffer be passed wherever the midi in buffer is passed?
  2. What does the number of audio samples have to do with the midi buffer (in midiMessages.addEvents)?

Why are you using JUCE 4.2?

It wasn’t up to me, but from what I understand - since Apple deprecated QuickTime and moved from QTKit to AVKit, JUCE didn’t catch up with every aspect of the SDK. Again, take this with a grain of salt but that’s how it is.
However in respect to midi out I don’t think this changed much.

I would say that almost everybody here uses 5.4.x or close to it. Knowing that you’re on 4.2 doesn’t inspire me to search for answers to something that may be different now… I never used anything that old…

The midi output buffer is indeed passed down to your plugin’s processBlock() function. I am working on a plugin/plugin host that only processes MIDI. That’s where I access incoming MIDI in the buffer, and fill up the midiBuffer on the way out with generated MIDI events, and it works.

EDIT (more) : Why wouldn’t the Arpeggiator Tutorial help you? It shows a plugin that receives MIDI into its processBlock() function, then passes new MIDI into the MIDI Buffer which makes its way out to the DAW. Isn’t that precisely what you want to do? (I’m not understanding how you made your plugin so that it doesn’t get MIDI events in the midi buffer…)

Ok, so that’s what I’ve been missing all along. I wasn’t aware that the midi buffer was reused for writing output events. Thanks @stephenk for pointing that out, I’ll try it.