How to catch parameter change while in "Controls View" instead that in "Cocoa View"?


#1

I got my stuff working under all the aspects. Only i was searching for a better way to update parameters: 

When I change parameters from Editor (aka: from Editor View) in my DAW all works well and parameters get notified, automation works too. When I make parameter changes from Control View (aka raw DAW editor window, not Cocoa based one) i cannot realise a way to "catch" when a parameter is moved. Due the fact timerCallback() is called *only* when Editor View has re-switched on (in Ableton Live also) i am not able to get notified when the user moves a control in the Control View. The solution was to set my Processing based upon the parameters i added with addParameter(), due the fact they are synced in realtime, but no clue to catch if only a certain parameter is changed (like I can do when user changes a control in Control View, aka Cocoa View). In Example, when a user changes the "attack" parameter, having its value ready for the processing code isn't enough: the envelope detector needs to be updated with the new attack value, too. 

Further, I implemented a mapParameters() method that maps any parameter value (0 - 1) to my DSP's needs. I must recall this method at every renderBlock() call by now, should be safer to have an equivalent sliderValueChanged() method that would work also when user is in Control View and modifies values and calls custom code based on a specific control, when its value is changed. 

Am I missing something? 

Thank you


#2

Hi Mike,

Your AudioProcessorParameter class will always get the setValue callback if the DAW's generic editor (not your editor) or automation changes any of your parameters (the callback is here in the demo code). You can do anything you want in that callback. Also you do not need to use a single class for all your parameters (the demo uses FloatParameter for all its parameters). You can create several classes for different types of parameters if this makes things easier for you.

Let me know if you need more help!

Fabian


#3

Hi Fabian, 

thanks for the quik reply, I didn't think about setValue() function. 
However, an useful thing (in my scenario) would be: "When the value is set, do something in the Audio Processor". 

Might I solve this by adding a pointer to my processor in my WhateverParameter class ?

Also, this way in setValue() I shoud make a comparation in order to find *what* parameter has changed, i.e.:

​​

void setValue (float newValue) override

    {
        value = newValue;
        if ("threshold" == name) proc->doSomething();
        else if ("ratio" == name) proc->doSomeOtherThing();
        // etc. 
}

 

this would be not so functionally, there is a better way?

 

Thank you very much....


#4

Depending on how advanced your C++ abilities are (and if you have a c++11 compatible compiler) I would suggest maybe looking at a lambda expression (or some other function object/function pointer). You could pass in your lambda or function object/callable type into the constructor of your custom AudioProcessorParameter class and have the lambda/function object capture a reference to the AudioProcessor using its this-> pointer for example.

This is the approach I normally use. Then you can just call the lambda/function object that you pass into your parameter class within the set value method. 

I'm currently working but if you want to PM me or something I could send over some example code later on. 

Maybe have a littler google session and look up C++ lambdas and callback functions. 

 

Josh 

 


#5

Hey, thank you Josh!
I am currently using a pointer to AudioProcessor* my FloatParameter constructor. 

During the initialisation i cast it to MyPluginAudioProcessor* and put it in MyPluginAudioProcessor* private var (proc).

When i need to update something, from setValue() (in example) i use proc->doSomething(). 

In my Audio Processor, i pass "this" as last parameter when adding parameters. 

This actually does work, but lambda functions method would be (effectively) more *agnostic*, right?

 

Thank you


#6

Hi Mike, 

 

Yes the lambda encapsulates things nicely and done correcly can hide away access to the processor pointer (which could be benificial). 

 

But also it could get rid of the need for long if statements / switch-case checks in regards to which function to call when the parameter value is changed. 

 

So rather than calling: 

if (parameterName == "threshold")

{

    doSomething ();

else if (parameterName=="gain")

{

    doSomethingElse();

}

 

You could instantiate a new object of your custom parameter class in your AudioProcessor constructor. So, if you have a member variable in your AudioProcessor class called gainParam you could say something like the following in its constructor: 

MyAudioProcessor::MyAudioProcessor()

{

    auto myLambda = [this] (float gainValue) {this->setProcessorGainValue(gainValue);}

    addParameter (gainParam = new MyParameterClass (myLambda));

}

 

Where your custom AudioProcessorParameter class take in a std::function object as a constructor param.

Your customer parameter class could then have a member variable of type std::function called setValueCallback for example which gets assigned to myLambda passed in by the constructor of your parameter class. 

Then in your AudioProcessorParameter class you do the following:

 

MyParameterClass::setValue (float newValue)

{

   //Set the MyParamterClass value member and pass this into your lambda/function object as a parameter. 

    value = newValue;

    setValueCallback(value);

}

 

 

Hopefully that makes sense. Apologies if theres any syntax / code errors in that example. Just typed them up quickly on the fly as an example but hopefully the concept is obviousl.

Have a little read up on lambda expressions and how to capture variables by value or references within a lambda if any of that seems odd. (hint: the AudioProcessor pointer in the lambda should be safe as it shouldn't become a dangling pointer as a result of the AudioProcessor being destroyed before the parameter object etc.) 

 

Josh 


#7

Bare in mind you need to make the lambda's interface consitent. 

 

So for instance it should probably always be a void returning function that takes in a single float parameter. You still need to specify std::function<void(float)> for example as the member variable type inside your parameter class. 

 

//Decleration

Private:

    std::function<void(float)> setValueCallback;

//then later in consturctor

setValueCallback = myLambda;

 

See this link: 

http://en.cppreference.com/w/cpp/utility/functional/function


#8

Well, much appreciated! 
Very cleaner approach, without any doubt. 

I'll migrate my code to this, thanks again!

 


#9

It works like a charm.

 

Thank you very much, Sir!

 


#10

Glad to help Mike, 

 

I'll be posting up an example plugin in the next few weeks with a filter display component for response curves along with a custom svg slider implementation and the parameter handling concept I exaplained earlier. Hopefully it will help a few people starting out to get to grips with some slightly more advanced plugin concepts (things I struggled with initially). 

 

Cheers

 

Josh 


#11

Perfect, thanks a lot. 
In the future it will be my pleasure, too, to be useful and share my enhancements (I hope to do some) to the framework with the community.

Thanks again!


Beginners question - use of AudioFloatParameter
#12

Hi Mike, Hi Josh,
I am currently working on my Masters Thesis using JUCE. First time I get involved with the new ParameterClasses und I am using the “ParameterSlider” in the current JUCE audio plugin demo.

Josh, you mentioned to post a demo of the parameter handling concept you described. Any chance you have that online somewhere for me to learn from it?

Thanks,
Hendrik


#13

Found it on your GIT (google us my friend).
It helps a lot!! Thanks