I’m trying to develop an understanding of how I might use an AudioProcessorGraph to route custom modules. For proof of concept, I have a SimpleOsc class that wraps a juce::dsp::oscillator and inherits from AudioProcessor.
When I use SimpleOsc->processBlock in MainComponent::getNextAudioBlock directly, the audio is a pure sine as expected, so I’m fairly certain my issue is not with SimpleOsc. However, when I try to construct an AudioProcessorGraph with a SimpleOsc connected to an AudioGraphIOProcessor::audioOutputNode, the resulting waveform is disturbed at each block boundary. In the screenshot provided, you can see the amplitude and phase of the waveform are disrupted every 480 samples - the block size. This repeats uniformly every 5 blocks.
I’m guessing my issue is a result of missing some element of initializing the APG, or improper initialization order. I’m sure the solution is simple, but I’ve been stumped by this for quite some time now. Let me know if more info or sections of my code is needed.
void MainComponent::prepareToPlay (int samplesPerBlockExpected, double sampleRate)
{
// ProcessSpec for SimpleOsc internal initialization
juce::dsp::ProcessSpec spec;
spec.sampleRate = sampleRate;
spec.maximumBlockSize = samplesPerBlockExpected;
spec.numChannels = 2;
// Initialize AudioProcessorGraph
audioGraph->setPlayConfigDetails(0, 2, sampleRate, samplesPerBlockExpected);
audioGraph->prepareToPlay(sampleRate, samplesPerBlockExpected);
// Create, add, and initialize an audioOutputNode
auto ioProcessorOut = std::make_unique<juce::AudioProcessorGraph::AudioGraphIOProcessor>
(juce::AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode);
auto outNode = audioGraph->addNode(std::move(ioProcessorOut));
outNode->getProcessor()->setPlayConfigDetails(0, 2, sampleRate, samplesPerBlockExpected);
// Initialize and add the SimpleOsc
osc = std::make_unique<SimpleOsc>(spec);
osc->setPlayConfigDetails(0, 2, sampleRate, samplesPerBlockExpected);
oscNode = audioGraph->addNode(std::move(osc));
// Connect the SimpleOsc node to the audioOutputNode
for (int channel = 0; channel < 2; ++channel)
{
audioGraph->addConnection({ {oscNode->nodeID, channel}, {outNode->nodeID, channel} });
}
}
void MainComponent::getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill)
{
juce::MidiBuffer midi;
audioGraph->processBlock(*bufferToFill.buffer, midi);
}