Noob questions regarding refresh rate, JUCE threads etc. in audio plugins

Hi,

I recently started using JUCE. I have a solid signal processing background, also in programming (C), maybe not so much in C++ (but it’s ok) and went through some Juce tutorials.

Whan remains so far unclear to me is the internal handling of the paint process and audio processing, and how they are interleaved and when repainting is triggered and how often. Too often might impact CPU.

For some specific application I defined sliders to change audio parameters.

So the questions are:

  1. Can the editor thread interrupt the audio thread during processing of the current buffer or can we assume that parameters which are changed based on sliders are fixed during audio buffer processing?

  2. When is the painting called? What defines the refresh rate of painting?

  3. I want to include metering. Is refresh immediately called when changing lets say the height of a rectangle, or does it rather happen from time to time (like fixed rate at 60 Hz).

Answers are very appreciated.
Thanks

  1. the message thread (GUI) can send a value change message for any audio processor parameter. use ParameterAttachment for that! there are also version of this specifically made for buttons and sliders.

  2. the DAW continuously asks all the plugins if they need to be repainted. your repaint calls are in a queue of things to do on the message thread and when it’s their turn they wait for the DAW to let them repaint. that’s why there can be frame drops, even if you use HighPrecisionTimer instead of Timer for animation.

1 Like

Hi and thanks for your replies.
So from my understanding if there are some collective GUI changes to do, they have to wait in a queue until it happens. Frame drops are probably not so much of an issue (if GUI updates are ment), but audio frames are.

I know how to define listeners from the tutorials, so I guess it is one variant of parameter attachment.

But also the question was like this, let me reformulate it:

when the ProcessBlock routine is started to process an audio buffer, can it be interrupted by the editor callbacks or not. If yes, I would have to make a freeze copy of all relevant parameters as a first step inside the ProcessBlock routine, since they could be changed anytime leading to inconsistent processing.

For example: you have a biquad depending on a cutoff frequency. Now you calculate 4 out of 5 coefficients. Suddenly the cutoff changes, and the fifth coefficient sees a different value, and the filter is spoiled. You first would have to copy the cutoff frequency to another variable to void any transitional changes via an interfering callback routine that reacts to the cutoff slider, UNLESS any callback is prohibited up to the end of the ProcessBuffer routine.

the tutorials are misleading unfortunately. listeners make it unnecesarily complicated to handle gui representations of parameters. attachments are much better. they automatically syncronize a lot of slider/button values with the actual parameter, like their NormalisableRanges, default values and the lambdas that define how values are converted to strings and the other way around.

also no matter if listener or attachment: just update parameter values from the message thread whenever you want. for each processBlock call you only get one parameter value per parameter anyway

1 Like

Ok, thanks, I will look into attachments deeper …

I’m not sure this is 100% true, but please correct if my understanding is awry.

I was under the impression that getting a value from an attachmed parameter is using std::atomic values under the hood so that you are guaranteed to not have any “tearing” of values:

eg.

read begins
write begins
read ends
write ends

if the value was not atomic there could be situations arising where you end up reading a mangled value.

So in terms of processBlock I don’t think there is any reason why the following could not happen:

loop starts
read value
loop continues
another thread writes value
read value <-- not the same value as you had in previous loop iterations

thus my understanding is that if you want to guarantee you only have one value per processBlock you should read them to a local variable at the top of the processBlock and only use that “cached” value in the loop.

I got a bit curious about this and wrote some test code. To my surprise, the parameter value indeed can change while processBlock executes and the parameter value is manipulated from the GUI thread. I had assumed for years the parameter updates are synchronized so that they only happen before processBlock is executed, but that does not seem to be the case. So, the proper pattern is to read the parameter value once into a local variable in processBlock and use the local copy. (Which is how I’ve written my stuff anyway.)

4 Likes

surprising indeed. maybe i misunderstood this when people taught me about it and they were just talking about updates that come from automation.

that is likely still the best way to handle this. most parameters have to be smoothed to sound good at modulation, so it makes no sense trying to make a difference between automation and gui-induced value changes anyway. the procedure has to be tweaked to work for the worst case, which is that it does take an entire block to update

I also found this here:

https://docs.juce.com/master/classAudioProcessorParameter_1_1Listener.html#details

IMPORTANT NOTE: This will be called synchronously when a parameter changes, and many audio processors will change their parameter during their audio callback. This means that not only has your handler code got to be completely thread-safe, but it’s also got to be VERY fast, and avoid blocking. If you need to handle this event on your message thread, use this callback to trigger an AsyncUpdater or ChangeBroadcaster which you can respond to on the message thread.