Getting Started With AudioProcessorGraph

I’ve been playing around with the AudioProcessorGraph for a few days now, trying to understand how it all works, but to no avail. I’ve copied the ‘Sine Synthesis’ tutorial and moved this code into a class which derives from AudioProcessor.

I create the input and output nodes using this code:

void AudioEngine::initialiseGraph()
{
    audioGraph.clear();

    auto* output = new AudioProcessorGraph::AudioGraphIOProcessor
                   (AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode);
    auto* input = new AudioProcessorGraph::AudioGraphIOProcessor
                   (AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode);

    audioGraph.addNode (input, 1);
    audioGraph.addNode (output, 2);
}

Next I call the initialiseGraph function above and create a node for my sineSynth class using this function:

void AudioEngine::addNode (juce::AudioProcessor *processorNode, uint32 nodeId)
{
    audioGraph.addNode (processorModule, nodeId);
}

SineSynthesiser* sineSynth;
sineSynth = new SineSynthesiser();
audioEngine.addModule (sineSynth, 3);

However, the code below to check if this would be a valid connection returns false.

auto output = audioEngine.getNode(2)->nodeId;
auto synth = audioEngine.getNode(3)->nodeId;
audioEngine.getAudioGraph().canConnect (synthNode, 0, outputNode, 0)`

The synth node appears to be created as the number of nodes is correct so I can’t understand why this isn’t a valid connection. Do I need to cast my derived class back to a base class to use this method? If I do that, how can I have access to the extra functions needed in my derived to control specific parts of the DSP code?

Any help would be greatly appreciated as the processorGraph class would expand my audio coding capabilities greatly as it’s such a powerful class!

Thanks
Andrew

Bump?
I’d really love to get started with this class!

Maybe your AudioProcessor subclass doesn’t correctly report its I/O counts or something? Can you show the code for that?

Which part of the code would I find that in? I’ve quite a few classes which I’ve overridden (below) but yet to implement as I was trying to get the graph processing working first

const String SineSynthesiser::getName() const    { return };
double SineSynthesiser::getTailLengthSeconds() const    { return };
bool SineSynthesiser::acceptsMidi() const    { return false };
bool SineSynthesiser::producesMidi() const    { return false };
AudioProcessorEditor* SineSynthesiser::createEditor()    { return };
bool SineSynthesiser::hasEditor() const
{
    return false;
}
int SineSynthesiser::getNumPrograms()    { return };
int SineSynthesiser::getCurrentProgram()    { return };
void SineSynthesiser::setCurrentProgram (int index)    { return };
const String SineSynthesiser::getProgramName (int index)    { return };
void SineSynthesiser::changeProgramName (int index, const String& newName)    { return };
void SineSynthesiser::getStateInformation (juce::MemoryBlock& destData)    { return };
void SineSynthesiser::setStateInformation (const void* data, int sizeInBytes)    { return };

I’m still learning how to use all the AudioProcessing classes well, there is a whole heap of them and I don’t find it very clear how to link them all well

Do you do any AudioProcessor::setPlayConfigDetails calls in the code? If I recall right, that was important in getting the AudioProcessorGraph to work. (Sorry I am a bit vague, it’s been a long time since I’ve worked with bare AudioProcessors and the AudioProcessorGraph.)

1 Like

In the prepareToPlay, I have:

void SineSynthesiser::prepareToPlay (double sampleRate, int samplesPerBlockExpected)
{
    currentSampleRate = sampleRate;
    updateAngleDelta();
    setPlayConfigDetails (getTotalNumInputChannels(), 
                          getTotalNumOutputChannels(),
                          sampleRate, 
                          samplesPerBlockExpected);
}

In my code, I do the setPlayConfigDetails calls in the constructor. I wonder what do you get back from your getTotalNumOutputChannels if you call it from prepareToPlay…?

getTotalNumInputChannels()/ getTotalNumOutputChannels() both return 0 in the prepareToPlay, so that’s definitely part of the problem! How can I call setPlayConfigDetails in the constructor if I don’t know what the sample-rate or block size are going to be?

You could do the setPlayConfigDetails calls outside of the class, for example in the code where you set up your AudioProcessorGraph. You might also get away by providing a bogus samplerate and buffer size for the call in your SineSynthesiser’s constructor in order to initially just set up the IO counts. (Might be a good idea to use nominally proper values like 44100 for the samplerate and 1024 for the buffer size though.) The samplerate and maximum expected buffer size that are coming via prepareToPlay should be the ones you will actually use anyway.

1 Like

Calling it from outside works, thanks @Xenakios! Any chance of getting some basic example code other than the plugin host for future users @jules? The temporary sample rate number seems a bit of an unnecessary step, but at least it’s working! Will setPlayConfigDetails need to be called again after any of the values are changed? What if the graph needs to be updated after the number of inputs changes?

Those “what if the inputs change” questions are very good questions indeed, to which I do not know the answer.

However, I do at least have a minimal AudioProcessorGraph sample:

This is the JUCE “Processing Audio Input” tutorial, modified to use an AudioProcessorGraph and an AudioProcessorPlayer, rather than the default processBlock method in MainContentComponent. The initialization order of things is indeed a bit tricky and not well documented. It works for me on Windows using a Scarlett FocusRite 2i2 USB audio interface and the “Windows Audio” (not yet exclusive) device.

(However, it doesn’t shut down properly; any tips on that would be nice :slight_smile: )

Hopefully this will be useful for posterity.

Hi everyone,

I followed the AndroidProcessorGraph tutorial and it worked. Now, I want to add sliders or toggles to modify the value of gain, for instance. I am using an AudioProcessorEditor instead of GenericEditor.

Any idea or suggestion to add parameters in each node of the graph to be handled in the main editor?

Cheers

In response to Paul’s question above, this is something I’m still trying to figure out, to no avail whatsoever.

This might be a hint. It’s from @daniel:

I haven’t achieved the desired goal, yet.