Minor issue with AudioProcessorGraph when fast turning a plugin on and off. (Found by pluginval)

Hi All,

This is my first post here and I hope it is descriptive and my English is good enough. I am very sorry if not.

Short description

I have a plugin which is using AudioProcessorGraph. I tried to test it with pluginval, but unfortunately, tests failed. My understanding of call trace in a log is that there can be a problem in AudioProcessorGraph. So I have checked if I can reproduce this issue with the plugin from Tutorial: Cascading plug-in effects and this plugin also is failing in pluginval.

What I found?

Logs from pluginval (ex. 1.txt (3.4 KB)) do not say much, so I attached Visual Studio to pluginval process and run pluginval with “validate in process” option selected.

Exception thrown: read access violation.
**this** was 0xFFFFFFFFFFFFFFFF. occurred

in HeapBlock:

inline operator ElementType*() const noexcept { return data; }
  • and sometimes an assert:
jassert (isPositiveAndBelow (channel, numChannels));

is failing in AudioBuffer#clear. (I did not save call stack and now I cannot repeat this one).

Exception thrown: read access violation.
**this** was 0xFFFFFFFFFFFFFFAF.

in AudioProcessorGraph::Node::prepare.

All call stacks have in common:

AudioProcessorGraph::buildRenderingSequence()
AudioProcessorGraph::handleAsyncUpdate()

And because failing test AudioProcessingTest (BasicTests.cpp in pluginval) is looping with:

instance.releaseResources();
instance.prepareToPlay (sr, bs);

it looks for me that the async update is trying to build the sequence after releaseResources, or even after deleting object of AudioProcessorGraph.

Checked with JUCE versions:

  • 5.4.3 release
  • dev (8a66f1f)
    and only with Windows 8.1.

Scenarios

  1. Build a plugin from tutorial “Tutorial: Cascading plug-in effects” and test it in pluginval.
  2. I was also able to reproduce such a problem in Ableton Live 9 (VST2) turning on and off (call_stack_from_ableton9.txt (2.0 KB)). I did not check with other DAWs.

Question

It looks like some minor issue :yum:, but maybe somebody knows if a plugin can be somehow secured?
In case of the plugin which I am tasting now, I will change AudioProcessorGraph to ProcessorChain, because I do not need changing processors and connections in runtime.
But still AudioProcessorGraph is a very interesting and helpful class, so it would be great to know how to deal with this issue.

Best regards,
Mateusz

HI Mateusz,
I too am having difficulties with the AudioProcessorGraph.
each of my nodes has a custom editor and my graph is really quite complex, unfortunately processorChain will probably not do the trick for me.
The problem I am experiencing is that everytime the audio system changes the graph gets torn down and re-instantiated.
I would prefer if the audioGraph had a weak reference or a shared pointer to the processor so that when tearing down the graph the entire application does not need to be re-started. deleting all of the nodes in the graph oprhans the onscreen editors…
I wonder, is there a JUCE best practice for avoiding this issue?

|K<

Hi @w3rkhof,

Thank you for your replay. :slight_smile:

What do you mean by “audio system changes”? Changing a driver or buffer size, so releaseResources and prepareToPlay are called?

I think AudioProcessorValueTreeState could help here. At least I am using it and in the prepareToPlay method of my main processor I am calling a function which is initializing all processors from actual controls.

void DelaysAudioProcessor::initFromControlls()
{
    auto wetGain = dynamic_cast<GainAudioProcessor*> (wetGainNode->getProcessor());
    wetGain->setGainLinear(wetControll->get());
    
    auto dryGain = dynamic_cast<GainAudioProcessor*> (dryGainNode->getProcessor());
    dryGain->setGainLinear(dryControll->get());
...
}

By a control I mean:

private:
    AudioProcessorValueTreeState state;

    AudioParameterFloat* wetControll;
    AudioParameterFloat* dryControll;

which are initialized in main processor constructor:

    wetControll = dynamic_cast<AudioParameterFloat*> (state.getParameter("wet"));
    wetControll->addListener(this);

    dryControll = dynamic_cast<AudioParameterFloat*> (state.getParameter("dry"));
    dryControll->addListener(this);

And this works with my simple “tape” delay plugin. It could be a little bit harder to implement in a “Reactor”-like app, but AudioPluginHost form JUCE/extras could be an example of how to handle this, IMHO.

If I misunderstood you, I am sorry. Do not hesitate to correct me.
And please keep in mind that I am a noob, so I can be wrong. :stuck_out_tongue:

P.S.
Own editor for each node sounds cool. :slight_smile:
I think modular synths are now very popular.

This sounds similar to problems I’m having with the graph, would be great to get these things sorted, a sync callbacks effecting the real time processing is dangerous. :frowning: