AudioProcessorGraph PDC and changing latency

I have an AudioProcessorGraph which includes some 3rd party plug-ins… when I add the connections to the plug-in and the graph is rendered it calculates the PDC okay… the problem is that some plug-ins will change their latency when you change a preset (iZotope Ozone for example) – and the PDC isn’t recalculated for the AudioProcessorGraph. Is there any way to have the graph re-calcuculate it’s delay (DelayChannelOp) when the latency of one of the plug-ins in the graph changes?

Thanks,

Rail

I think I may have a work-around…

I derived my PluginWindow class from AudioProcessorListener and added it as a Listener to the 3rd party plug-in’s AudioProcessor. When PluginWindow receives audioProcessorChanged() it triggers an AsyncUpdate to the AudioProcessorGraph which causes it to buildRenderingSequence(). It appears to be working okay.

I added a public method to AudioProcessorGraph:

void refreshGraph() { triggerAsyncUpdate(); }

I would prefer that I only could get a notification when the AudioProcessor latency changes though.

Rail

BTW this doesn’t set the total latency correctly for reporting to the Host (which will be my next task)… It does however correctly delay all the processors in the AudioProcessorGraph.

Rail

Okay, to update the host’s latency…

in AudioProcessorGraph::createRenderingOpsForNode() I had to change

if (numOuts == 0)
            totalLatency = maxLatency;

to:

if (maxLatency > totalLatency)
            totalLatency = maxLatency;

derive AudioProcessorGraph from ChangeBroadcaster and in AudioProcessorGraph::buildRenderingSequence() call

sendChangeMessage();

at the end of the method. In my main processor class handle the ChangeCallback and update the latency.

Rail

1 Like

I came over from my own thread on the same subject… It would be very nice if that refreshGraph() call could be added to the Juce repository. Alternatively if things (the AsyncUpdater base class) in AudioProcessorGraph would be protected instead of private we could just subclass the class which would be much cleaner.

Getting a notification for graph rebuilds would of course also be very nice.

I was hoping this thread would inspire one of the JUCE folks to add some of this into AudioProcessorGraph… which also needs a little love to improve it’s speed and make it thread safe.

Cheers,

Rail

As this PDC is completely new territory for me from the implementation perspective, and the question I asked in the old thread still endures. So: if I’m using AudioProcessorGraph and have several plugins that I know cause delays inside the graph, it seems that the compensation for the whole graph does not get applied properly, at least not for my own custom plugins (see below)

My original question was: should the graph nowadays calculate and adjust itself automatically to the delays the plugins in the graph cause, or do I have to somehow make use of the mechanisms more manually for things to work properly?

I could not figure out how should I make use of those mechanisms mentioned in the the old thread - either that, or they should already be doing their PDC calculation and adjustment magic automatically in the background, right out of the box…?

It might just be that the problem actually resides in the custom plugins I’ve implemented, and the graph itself works just right with all the delay compensations in place. I haven’t done anything in my plugins to handle any delays or anything, and really, I wouldn’t even know where to begin with this one.

Are there any good references on this topic anywhere? If there is, I haven’t been able to find any… any help on this matter, or any reference to any article considering PDC + JUCE is very much appreciated!

You first need to check each of your plugins individually in a known host with ADC (like Pro Tools) and make sure that your plugin is reporting the correct amount of delay to the host.

Once you’ve confirmed all your plugins’ delay reporting is correct – then you can move onto checking the AudioProcessorGraph’s PDC handling.

Rail

So, you’re implying that the PDC handling in AudioProcessorGraph is not automatic? How do get an access to the mechanisms and make use of them, as they seem to just be more or less private structs defined in juce_AudioProcessorGraph.cpp (DelayChannelOp and RenderingOpSequenceCalculator) and not directly found anywhere from the AudioProcessorGraph-class itself?

How these mechanisms can actually be used?

I’m saying that if you look at the code you’ll see that when the graph is built and/or updated it calculates the PDC correctly… but that if you load a plugin preset which changes the delay amount for the plugin… that doesn’t trigger the graph to recalculate it’s latency (PDC)… which the changes I documented in this thread fixes.

So bottom line – the graph can calculate PDC correctly… but it sometimes needs to be updated if anything in the graph causes the latency to change – that isn’t currently automatic without my changes.

Also if you’re hosting plugins… then the changes I document allow you to let your main processor/app know that there’s been a change to the latency from the graph.

Rail

Aha, I think I finally got it. And it’s actually quite possible that it’s just that same update/refresh-problem I’m seeing also, as the graph gets built with default plugins first, and then I just add plugins to the graph. I should either instantiate the graph with the proper plugin configuration directly or add those hooks you mentioned, which makes the graph then update it’s PDC.

I’ll have a look tonight if I’ve understood this correctly and check out if I can get my graph fixed. Will come back tomorrow with either further related questions if I get stuck, or a confirmation that your fix worked for me also :slight_smile:

Ps. There is still a small possibility that I’ve just messed up something else though, but all things considered I’m quite certain that it’s indeed the PDC that gets messed up. It might be though that the problem is mainly in my own plugins and how they don’t take the delay currently into account, rather than the more delicate problem of the PDC not getting recalculated on demand.

To ensure you’re not chasing your tail… test your plugins in a known host first to ensure that they report the correct delay to the host.

See AudioProcessor::setLatencySamples()

Rail

My problem was the simpler one, which is that I just didn’t take into account any PDC compensation on my internal plugins. Now that it’s fixed, I noticed that there doesn’t seem to be any way around on implementing that change notification though, as I’ve this delicate situation where it’s possible that those latencies are not reported properly, if some of the user interface actions are triggered when the graph is still in a middle of it’s loading cycle.

Is there any way to get a notification from a vst/au -plugin about its real state, ie. “the plugin is now guaranteed to be truthfully initialized and ready”? I didn’t see any methods in the AudioProcessor-class that is used to wrap these plugins so that they can be run inside a JUCE based host. The problem is that even though the plugin has loaded and is responding to the calls made to it by the host, it’s still in a middle of it’s initialization routine somehow, and reports it’s latency as 0 samples, though it should be 10000 (using voxengos latency delay plugin for this dev, fyi) I’m hoping that I could prevent all user action before all the plugins are “truthfully initialized and ready”, the only other option would be to implement the fix you’ve given here and somehow revert/rewind/undo all the user action made before the plugins report their changed latencies.

You need to set your latency as soon as possible. If you have a known initial setting for your plugin… use that sample value.

I have just implemented a “Splash Screen” for my plugin to cover the GUI until it’s initialized… but it’s not a simple task… and you have to deal with the different hosts which initialize differently… and you can’t rely on there even being an Editor object.

Rail

Measuring / just knowing the plugin latency was something I thought about right away. It still feels a bit suboptimal, as one would assume that there has to be a state when any vst/au plugin should now it to be fully initialized and ready - but there seems to be no methods or callbacks, at least in JUCE, that could or should be called…

The case here is not my plugins, but any other plugins that can be dynamically loaded into the same graph. There isn’t much I can do there to change their behavior, just pondering what are my options on knowing for certain when all the plugins inside a graph are properly initialized and ready…

You can implement my notification system as I posted in this thread… so your host can get notified when an AudioProcessor has changed it’s latency – that’s as close as you can hope for. The JUCE API for setLatency even warns to set the value as soon as possible – you can only hope that all plugin vendors heed that advice.

Rail

Yeah, that unfortunately seems to be the case. Good to have a confirmation on that also so I don’t waste my time any further trying to find an non existent solution to an unsolvable problem :slight_smile: I’ll return to this thread when I’ve implemented the fixes you’ve given and seen how they perform.

Thanks for everything so far, this discussion has been very eye opening - especially for a guy like me with a background of not knowing even the slightest bit about PDC implementation!

Edit: just to be clear on the matter, your solution will work almost perfectly for me if I get it to work, and fix the actual underlying problem. It’s definately not a nice solution, but a real solution nonetheless!

Is there a reason this latency patch was never adopted into the APG?

1 Like

Hello @railjonrogut and thanks for this very useful fix!
Has the situation changed since 2016? Do we still have to change the juce source code to get dynamic latency compensation working?

They recently made some changes to the AudioProcessorGraph class including adding

AudioProcessorGraph ::rebuild() which is equivalent to my refreshGraph() method.

I haven’t had the time to check the rest of the graph code to see if they’ve got the latency right yet… but they definitely haven’t added a bypass delay to the AudioProcessor class… and the line is still

        if (numOuts == 0)
            totalLatency = maxLatency;

Rail