AudioProcessorPararmeter: How to call code on value change?


#1

Sorry if this is a newbie question...

While I have many parameters that are simple floats or ints, I have several where some code needs to be called when the value changes...typically something that changes a mode inside my synth. I looked at the delay parameter in the demo plugin, but that's just a simple value change.

How is anyone doing somethig like this?

Thanks.

 


#2

I'm not sure if I really understand what you mean, but I'm guessing that you could call whatever code you need in the AudioProcessorParameter::setValue() member function. If I understand your question, then that seems like it would really be the only way, but idk. 


#3

I'm probably missing something, but here's an example.

I have an audio plugin that simulates an electric piano. My PluginProcessor is the parent of an Editor and the EP simulator. Inside my EP sim I have a phase shifter and a flanger.

The PluginProcessor has an AudioProcessorParameterFloat called volume that is controlled by a ParameterSlider or similar, so that when I move the volume slider in the editor, it changes the parameter value for the volume inside the EP sim. That makes sense to me.

But when I have a parameterInt for choosing between the phaser or the flanger, when that value changes, I need to run a function inside the EP that makes some changes. So changing the int value of isn't enough. When the user changes the switch from phaser to flanger, some code needs to change a bunch of things.

See what I mean? 

Maybe I'm missing something terribly obvious.

 

 

 


#4

Maybe I'm missing something terribly obvious.

I don't think there is a terribly obvious solution.

Have you seen this thread?:
http://www.juce.com/forum/topic/whats-best-practice-gui-change-notification
It has been written before the AudioProcessorParameter got created.

You could create a derivative of AudioProcessorParameter with a private Value member to store the parameter. and a public getValueObject(), such that your editor and EP simulator can be listeners to it and get informed as soon as the Value gets changed.
BUT Value isn't thread safe and the AudioProcessorParameter can be called from within the audio thread (in some hosts). So this should not be done. That said, I gave it a try and it worked beautifully in a lot of different hosts without any issue.

Well... I did not have the guts to use this solution in my commercial plugin. There I use e.g. AudioProcessorParameterFloat (like the one in the Juce plugin demo). And an additional separate Value object for each parameter. My AudioProcessor is also a timer and in its timerCallback it compares the value of each AudioProcessorParameterFloat to the value of the corresponding Value object. If they differ, the parameter must have been changed and the Value object gets updated.
Make your EP simulator as well as your editor listen to those Value objects.


#5

Hey pizzafilms, 

 

Check out the below thread. I stuck in some simple example code for using a lambda/callback function with the AudioProcessorParameter by creating your own derived class which takes in a function object in its constructor and then gets called automatically whenever the parameter changes. 

You might need to have a quick read up on lambda functions if you don't understand anything in the example.  

 

http://www.juce.com/forum/topic/parameters-are-driving-me-crazy

 

I'm working on a little filter gui demonstration for people to use that shows this and I'll pop up soon hopefully (it was ready to go up a while ago but I'm at the JUCE summit this week and thought given that JUCE 4 has just shot out I probably better re-factor/change the code a little to be JUCE 4 Savvy) I will get it up asap. 

 

Josh 


#6

Wow, passing a lambda is such a nice way of doing this. Before, I would have to pass a reference to an object, then call a member function on that reference in setValue() (such as Filter::setFreq() or something). This would force me to create new class for each parameter. Passing a lambda gets rid of that problem completely. I could just capture that object in the lambda (by reference) and do whatever I need with it there. Thanks for posting this man.  I missed that other thread. I'll have to look at how that's implemented because I'm really curious. 

To the OP: I say do it this way if you can. It seems like the most elegant solution. 


#7

Yeah its pretty tidy, 

The niceness I guess is in capturing the AudioProcessor's this pointer, there's no lifetime issues to worry about if im not mistaken as the processor has ownership of the parameters and you can automatically call functions on any of your processor's members (dsp units etc.) Means you can just create one derived AudioParameter class of your own with a bit of thought (good old std::function) rather than multiple parameter classes. 

Someone with more CPP experience may spot a danger I've missed but I'm fairly confident its a solid approach. Suspect their is a nicer way to wrap this up rather than declaring each of your callbacks/lamdas in your processor constructor, I'll have a think. 

 

P.S I will get that filter GUI up for you asap. I'm at the summit as we speak so just hanging on to see if any JUCE 4 best practice topics are brought up before I ost for others, hopefuly then the code won't be too awful for others to use. 

 

Josh


#8

...what has become of the AudioProcessorListener class http://www.juce.com/doc/classAudioProcessorListener ?

I just tried it with an AudioParameterFloat, but it seems not to work any more:

MyAudioProcessor::MyAudioProcessor()
{
    addParameter(new AudioParameterFloat("Tilt", "Tilt", -180.0, 180.0, 0));
}

AudioProcessorEditor* MyAudioProcessor::createEditor()
{
    MyProcessorEditor* e = new MyAudioProcessorEditor (*this);
    addListener (e);
    return e;
}

//==============================================================================

class MyAudioProcessorEditor  : public AudioProcessorEditor, public AudioProcessorListener, public AsyncUpdater
{
public:
    // [...]

    void audioProcessorParameterChanged (AudioProcessor *processor, int parameterIndex, float newValue) override
    {
        mySlider->setValue (newValue); // setValue defaults to async notification. 
        triggerUpdate();               // trigger asynchronous repaint
    }
    void audioProcessorChanged (AudioProcessor *processor) override {}
    
    void handleAsyncUpdate () override
    {
        repaint();
    }

    // [...]

};

but the audioProcessorParameterChanged is never called...
 

 


#9

The class works also with the AudioProcessorParamer classes. It was not called when I changed the value from the host. But when I call setValueNotifyingHost the AudioProcessorListener::audioProcessorParameterChanged is called, so you can override this to react to value changes.


#10

hi @JUCEMARLER did you ever create that filter gui ? is it on github ?

i think it could be helpful for a sine generator project where im having trouble … with changing values …

best
b.


#11

Hey bud,

Yeah loosley termed. It’s by no means finished, I keep intending too but have had various other projects get in the way, maybe it’s time to finally finish it off as people keep asking about it lately.

Anyway there is an example of a display which is calculated using the transfer function of the filter and redraws whenever the cutoff slider is moved.

Just uses a Virtual Analogue one pole for the moment with highpass and lowpass but I promise I will finish it asap to include other shapes and be a little more tidy!

I will send you a PM with a link to the current unfinished version.

Edit: In fact I may well have a semi free day in the office today (perks of working in a family owned tech company).
Maybe today is going to be Filter GUI day.

Josh