Getting MidiOutput from AudioProcessorGraph


#1

There doesn’t appear to be any way to get midi output from AudioProcessorGraph. The processBlock method for an AudioGraphIOProcessor with an IODeviceType of midiOutputNode copies outbound MidiMessages to a private member, currentMidiOutputBuffer, in AudioProcessorGraph. However, neither AudioProcessorGraph nor AudioGraphIOProcessor provides a way to access this buffer.

To get around this, I added a public method to AudioProcessorGraph to access this buffer:

I then output the midi in the processBlock of a class derived from
AudioProcessorPlayer:

if(getMidiOut()) { AudioProcessorGraph* graph = dynamic_cast<AudioProcessorGraph*>(this->getCurrentProcessor()); if(graph) { double time = Time::getMillisecondCounterHiRes() /*+ numSamples * secondsPerSample */; getMidiOut()->sendBlockOfMessages(graph->getMidiOutputBuffer(), time, samplesPerSecond); } }
Is there is a better way to do this, perhaps without modifying any of the supplied Juce classes? There seems to be a glaring hole in implementation AudioProcessorGraph (and, incidently, in the plug-host which lacks a midi-out internal plug-in, though it has the other three: audioIn, audioOut, and midiIn). However, I couldn’t turn up any complaints about it in a search of the forum which leads me to believe I may be missing the whole concept of how to do midi out when using the AudioProcessor classes.

Also note the commented out code in calculating the second argument to sendBlockOfMessages. It doesn’t seem to make much difference if I leave it commented out or not. Any insights into what the value to this argument should be would be greatly appreciated.


#2

I don’t think I ever quite finished the midi-out stuff for graphs, but the plan was that it’s not something you’d try to add to the AudioProcessorGraph class - it’s the role of the thing that’s using the graph to collect its output. So in the AudioProcessorPlayer, the code that actually calls processBlock for the whole graph would end up with the output midi buffer, and would send it to the output.


#3

But you’d still need to get midi out of the graph. What if you had an AudioProcessor inside the graph that generated midi events?


#4

Well then you’d connect that node to a midi output AudioGraphIOProcessor, and that’d make sure the midi came out of the graph’s output… (is that what you meant?)


#5

Yes, that’s exactly what I mean. However the midi output AudioGraphIOProcessor’s processBlock method queues midi coming into that node to its containing graph’s currentMidiOutputBuffer member which is private. I just added a public method to AudioProcessorGraph to access currentMidiOutputBuffer.


#6

Right, but that’s the wrong way of doing it. What’s supposed to happen is that the graph’s processBlock method should emit that buffer, and the caller should use it for whatever purpose. Allowing access to the graph’s internal buffer isn’t a safe thing to do.


#7

Do you mean something like the following?

void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    //...
    // copy audio to the output channels
    for (i = 0; i < buffer.getNumChannels(); ++i)
        buffer.copyFrom (i, 0, currentAudioOutputBuffer, i, 0, numSamples);

    // copy midi to outgoing MidiBuffer
    midiMessages.addEvents(currentMidiOutputBuffer, 0,buffer.getNumSamples(), 0);
}

That makes more sense than my original scheme. I left the last argument to addEvents 0 making it the caller’s responsibility to timestamp the MidiMessages.


#8

Yes, that’s the sort of thing I meant.


#9

To make this work you still have to output midi from whatever’s playing the graph. In the audio plugin host, that’s an instance of AudioProcessorPlayer.

The quick fix would be to modify AudioProcessorPlayer to get the MidiOutput instance from the AudioDeviceManager and output the contents of midiMessages to it after calling the processBlock method of the AudioProcessor attached to the player.

I also have a solution that avoids modifying AudioProcessor but it’s more work: Implement an AudioProcessor that wraps the one passed into AudioProcessorPlayer. All of the calls from AudioProcessorPlayer are routed unchanged to the wrapped AudioProcessor except for processBlock. The input to processBlock is first passed to the wrapped AudioProcessor’s processBlock and then midiMessages is copied to a MidiOut.

The first solution is cleaner and, I believe more elegant, as the current AudioProcessor can stream both midi and audio input, but only audio output. This solution restores I/O symmetry. The downside is it violates the integrity of the library (yes I realize that I’ve already violated it by modifying AudioProcessorGraph as shown above).

The second solution respects library integrity but it is redundant. I’m leaning towards the first solution but I’m also hoping someone will have a third solution that is both elegant and preserves Juce’s integrity.


#10

My plan was to just add midi out to the audioprocessorplayer like you suggest - I just never got around to it. If you get something working, I’d be happy to clean it up and add it to the build, it’s probably not much work at all to add.


#11

Guys, just checking out if this has been fixed, as I’m using the graph too now, and I need to get Midi-Out working. :frowning:

Wk


#12

Actually, I updated the Juce code with

And its working. :mrgreen:

I just need to remember to add this every time I download a new Juce version… unless you plan on adding it officially?

Wk


#13

Is it really as simple as that!?


#14

No clue, but its working. :wink:

Wk


#15

Ok… I think it should at least clear the buffer first, though:

midiMessages.clear(); midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0);


#16

Also you need to the midi to the output device:

  1. start the background thread in the MidiOutput you’re using (and stop it when done), e.g., myDeviceManager->getDefaultMidiOutput()->startBackgroundThread()
  2. Send the midi to the output:

#17

This is very helpful, thank you. I got the output module to show up in the UI, and put addEvent() in proccessBlock(), but there’s still no MIDI output. Do I need to put MidiOutput::run() in as well, or does addEvent() take care of that?

And from the docs I would guess sendBlockOfMessages(…) is indeed required. Do I really need to make a new class for it, and where exactly do I put it?


#18

I did some more checking, and I’m not getting any MIDI input either, even after the tweak from here:

http://www.rawmaterialsoftware.com/viewtopic.php?t=&f=&p=17111

As other people got it working, perhaps the 1.5 distribution is just missing a block of code for external MIDI?


#19

Is this really solved?

It seems to me you still have to resort to the first solution and add something like

getMidiOut()->sendBlockOfMessages(graph->getMidiOutputBuffer(), time, samplesPerSecond);

to AudioProcessorPlayer.audioDeviceIOCallback() in order to extract any midi from the AudioProcessorGraph.

If anyone have managed to squeeze out any midi from the AudioProcessorGraph without this, please step forward…:slight_smile:


#20

Write an AudioProcessor to handle this, and put an instance of it in the graph with the final MIDI result connected to it?