Multithreaded audio processor graph


A multithreaded audio processor graph : do we have a chance to see that one day ? :)



I have used this:

And modified it a bit to support midi and my specific needs.

There are other post on the forum on the same topic


Is it just a matter of replacing Juce's AudioProcessorGraph by your version ? In that case I'd be interested ...



The link is not to my code, but yes, it can replace the standard graph 1-1


Can you share what you did to support MIDI?

Perhaps we can combine this with Graeme’s changes which are working great (see thread:




well my modified version of the audioprocessorgraph was a proof-of-concept, and i'm happy to see that someone did a full working version of it. Would happy to see the code ;)

I'm came to the conclusion the current preordering algorithm makes no sense in a multithread context ( i wrote something as explanation  here , which does not mean that any pre-sorting is needless or can be used to optimize the algorithm  )



I´ll share my version when I get my head above water here.

As I recall the midi part was pretty easy to do.

I also modded it a bit so that it does processes nodes that are not patched to output. (For example a node that only has an input)


Great job here guys ! :D Can't wait to see the code !! I hope Jules will consider integrating it to Juce !


​Hi chkn,

​i read your code and found it really smart, for 2 reasons :

1. it manages obviously the multithreading, as the title said, (and that is nice !)

and 2. the repetitive way the graph is traversed with your waiting flags system enables (with a little work) to update connections or nodes without recalculating the all graph with buildRenderingSequence(), simply locally, parents to children, without even stoping threads or locking anything... which is a more dynamic AudioProcessorGraph than the orginal... !

​i'll have to work a little bit on it but at the moment it looks to work well, and the midi is a simple corollary of the audio buffer, it works the same...

Well thanks! i'll share this​ soon.


Interesting stuff. I see the use of ScopedTryLock within AudioProcessorGraphMultiThreaded::Node::process. Isn't using mutexes in a real time thread a no-no?



An approach that avoids the locks might be:

  • the audio callback thread iterates over the nodes looking for ones ready for processing (using numberOfProcessedInputs)
  • when it finds one it schedules it on a worker thread via that thread's lock-free queue

If you can find a lock-free wait-free queue that supports multiple consumers (I don't know of one) you could use a single FIFO, which would be better.

I suppose the disadvantage of this is that the worker threads would spin (busy-wait). I don't know of a way around that in the lock-free world.




>>I suppose the disadvantage of this is that the worker threads would spin (busy-wait).

Yes, thats the point, and if all cpu-kernels would spin (i think with some kind of compare-set-bit operation) it would be unbelievable inefficient.

And modern CPUs may throtteling the clock for energy/temperature then.

If somebody implements something cool i would be happy, but at some point there needs to be some-kind of synchronization.


But from a practical point of view: All my attempts to measure problems through performance intrusion failed, why? Modern CPUs are just to fast.

Also it seems the  thread- scedulers of modern OSs are highly optimized. 

The real bottleneck were always, ram-read/write-access (or ram allocation, especially on windows)




EDIT: I removed the code because I must admit that it was not fully operational at this point... (And nobody gave any advice about it!) For the Midi management in Chkn's code: 1. add the midiBuffer member in the Node class

ScopedPointer<MidiBuffer> midiBuffer;

2. Init. in the Node::prepare method:

midiBuffer=new MidiBuffer();

3. add this in the Node::process method (before "processor->processBlock(*buffer, *midiBuffer);")

    bool unwritten=true;
    int o =AudioProcessorGraphMultiThreaded::midiChannelIndex;
    for (int i=0; iinputConnection->destChannelIndex==o)
            if (ri->inputConnection->sourceChannelIndex==o)
                if (unwritten)
                } else
            } else
                DBG("Impossible Connection: Source Channel "+String(ri->inputConnection->sourceChannelIndex)+" is higher than available channels "+String(buffer->getNumChannels()));

    if (unwritten)


More about…Thread Locking