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.
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.
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?)
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.
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.
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.
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.
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.
start the background thread in the MidiOutput you’re using (and stop it when done), e.g., myDeviceManager->getDefaultMidiOutput()->startBackgroundThread()
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?