Is there any way to stop 'parameterChanged' from happening during processBlock?

Other than locking of course. :slight_smile:
I thought the whole idea of using listeners was to do exactly that - to run through the parameter changes then ‘processBlock’ to be called ALL on the audio thread?
I guess not…

The documentation doesn’t mention threads at all. If it is AudioProcessorValueTreeState::Listener::parameterChanged you are writing about. The docs vaguely imply something asynchronous may be going on.

Yes, it appears I’m getting a parameterChanged callback during a processBlock on VST3, Windows, FL Studio - it’s all I’ve tried so far. This is during a moused slider movement. I didn’t see any mention of the callback being asynchronous, sorry.

Well, like I said the mention in the docs is vague, it doesn’t explicitly mention threads. Which I take to mean “can be called from any thread”.

In practice what I guess happens is that parameter updates happening because of the plugin’s GUI will cause the method to be called on the GUI thread and automation envelopes in the host cause the method to be called from an audio thread.

Thank-you for your replies, Xenakios. I thought the whole reason for the listener management stuff was to synchronise the updates with the processing - all on the audio thread? I guess not… :frowning:

1 Like

Well, some of the Juce broadcaster and listener classes do have guarantees about in which threads the callbacks will happen, but it’s usually made clear in the documentation what is going to be the case. If it isn’t explicitly mentioned, it probably means there are no guarantees.

It would have been nice to know ALL changes are ‘squirted’ before processing is called though. :slight_smile:
I don’t understand why they can’t be.
So I have to pile up the changes internally in my own FIFO buffer, then and go through any changes internally on the processBlock, just like I used to before, outside of Juce-land. I thought that work had now been done for me, but it seems not.

Hohum, “such is life without a wife and the man next door has two!” :smile:

Why not just use the latest values of the parameters you have available in processBlock? Juce doesn’t support stuff like sample accurate automation anyway, so there isn’t really any point to go through all the parameter values that might have been set since the last call to processBlock…(Unless you are doing some special purpose plugin that requires catching all the GUI component moves or something…)

2 Likes

Yes, that works, thanks, I’ve used that before, and it always seems a little expensive to me, when I could set up anything internal in the parameter change function. Never mind, I’m sure I’ll cope. :slight_smile: It was just a disappointing surprise that’s all.
Thanks again,
Dave H

Sure, it can get expensive if you recalculate some internal states each time processBlock is called and there are no checks if the parameter value is still the same.

Anyway, I agree this stuff should be way easier with Juce… :face_with_raised_eyebrow:

2 Likes

Just found this thread after coming to similar conclusions to you @DaveH and @xenakios, and was feeling like I was hitting a dead end or missing something. So thanks for the sanity check.

Without knowing that parameterChanged will be called in a thread-safe way, I don’t see that I would ever use it within a plug-in’s Processor (where the use would be to update DSP objects with new parameter values).

Right, I was wondering about doing those checks, and looking for a way to still make use of parameterChanged. For example, you could have the Processor hold a set of atomic bools, each named for an “internal state” (e.g. a DSP object like a filter) that might need updating. And then in parameterChanged, set those bools when the appropriate parameters get changed. And finally, in processBlock, check those bools and only recalculate the internal states if the flag is set. Like this:

// in Processor
AudioProcessorValueTreeState apvts;
std::atomic<bool> updateFilter;
std::atomic<float>* rawCutoff;
std::atomic<float>* rawResonance;

void Processor()
{
    updateFilter = true;

    rawCutoff = apvts.getRawParameterValue ("cutoff");
    rawResonance = apvts.getRawParameterValue ("resonance");

    apvts.addParameterListener ("cutoff", this);
    apvts.addParameterListener ("resonance", this);
}

void parameterChanged (const String &parameterID, float newValue)
{
    if (parameterID == "cutoff" || parameterID == "resonance")
        updateFilter = true;
}

void processBlock...
{...
    if (updateFilter)
    {
        filterObject.setParams (*rawCutoff, *rawResonance);
        updateFilter = false;
    }
}

With the overhead of having an AudioProcessorValueTreeState::Listener, setting the bools, and checking the bools, I’m not sure if that’s more or less efficient than just having the Processor hold a set of floats to compare old values with new ones in processBlock:

void processBlock...
{...
    if (oldCutoff != *rawCutoff || oldResonance != *rawResonance)
    {
        filterObject.setParams (*rawCutoff, *rawResonance);
        oldCutoff = *rawCutoff;
        oldResonance = *rawResonance;
    }
}

Do you see a strong advantage in one of these approaches over the other?

For two parameters, I’d go with the second approach. For many parameters with many derived internals, you’d make a lot of checks for nothing most of the time, so it’s better to use a message queue -a multiple producer, single consumer fifo. What I have is a type

struct Message
{
    enum class Type { /* ... */ };
    Type type; float value;
}

Then I hold a

std::map<juce::String, Message::Type> msgTypes;

to map parameter names to my message types, and a

SomeFifoType<Message> msgQueue;

to hold the messages. In parameterChanged, I just

msgQueue.push ({ msgTypes.at (parameterID), newValue });

Then I have a

void checkMessages()
{
    Message msg;
    while (msgQueue.pop (msg))
        switch (msg.type) { /* update your internals */ }
}

You could spare the map and hold a juce::String in your message, then replace the switch with

if      (msg.type == "this") { /* ... */ }
else if (msg.type == "that") { /* ... */ }
// etc.

Then you can check your messages whenever you want -reasonably once per block.

4 Likes

For VST3 and AAX, at least, I believe all parameter changes come down from the host during the process callback, and the JUCE wrappers parse those changes and sends them through parameterChanged() before calling processBlock(). So any tricks you do to try to isolate parameterChanged() from the processBlock() call will not really accomplish anything. If you have to do a lot of work for a parameter change, especially if it will take some time, then you can handle those changes in a separate thread (esp. if re-allocations are needed), set a flag that the changes are complete and the new data is ready, and let processBlock() check that flag upon entry every time, to see if new data is available. We do that with some array-type data, and keep two arrays. One for the thread to update and one that is “live” (used by processBlock()). Then we keep an array of 2 pointers to that data, so that all we have to do is swap the index (“liveIndex = 1-liveIndex;”) to change to the newly available data.

Well, if there are attached controls parameterChanged() could be called from the message thread, in which case there’s an actual problem.

I always wondered why there’s no infrastructure in Juce to deal with this, but it seems we all do different things and there’s not much of a common ground beyond APVTS. In my case, I don’t have anything that deserves a separate thread, but I have more than 150 parameters, most of which are used in derived forms. Even if each derivation is simple, it doesn’t seem reasonable to check for 150 changes, or make all 150 derivations every time. So the queue approach seems fit. For just a few parameters, I think checking all of them every block would be alright.

1 Like

Thanks so much for sharing your approach. I’ve heard people mention using a FIFO to store parameter changes, and it’s helpful to see a working implementation.

What kind of naming do you wind up using for the “message types” enum? Since you’re associating them with a single value, I would guess they are named along the lines of the parameters themselves.

Right, or I wonder about using a namespaced set of Identifiers for the Message::Type. Faster to compare an Identifier than a String, and compiler would show errors for typos in the type names.

I suppose the FIFO could keep a memory location and what to store there, as the FIFO will only have one purpose - to update variables. And in the beginning of process you run through them, splatting all the changes in place, for want of a better phrase! But I suppose that’s not very C++ correct. :wink:

Yep, they’re basically the same names. I use them just to avoid a large if-elseif chain on strings and switch on ints instead.

That could work too, as you can compare Identifiers with StringRefs. In terms of code though, it would be just as verbose, and the switch would still probably be faster. I use Identifiers when dealing with ValueTrees for pure UI, message thread stuff. This is audio thread stuff, so I prefer an enum and a switch.

In my case that wouldn’t quite work, as each parameter may update many variables, with calculations and logic involved. For example, a frequency parameter may update the coefficients of various filters.

At least in VST3, parameter changes may originate from the UI thread. These are useful diagrams for understand threading in VST3

Yeah, I looked back through my code, and it’s my old (non-JUCE) AAX version that processes all “incoming” parameter changes at the start of the algorithm callback, not the VST3 code. But the JUCE AAX wrapper doesn’t seem to do that. I don’t know how that works in JUCE, I guess. Sorry for the noise.

You could update all the variables you want and just shove them through the FIFO, It would automatically update all the locations you specify, there will be no variable names, no strings, and no enum references, no ifs , no case statements and you could even order them in a correct way - it’s just a list of memory locations and floats. It’s not even on a thread.
Yeah, it’s simple, but I’m trying to simplify as much as possible as my latest project is getting BIIG - I’m considering trying this soon. But perhaps I’ll sleep on it…