How I can change continuous AudioParameterFloat parameter to discrete ( 4 steps)

Hi,
I have created a bunch of parameters in the constructor of the audioplugin by using createAndAddParameter function of AudioProcessorValueTreeState. Some them are continuous some of the are discrete. But I want to change some parameters behavior depending on others parameters value. For example, the well know LFO rates which is continuous when a togglebutton is selected free type, or discrete (1/1, 1/2, 3/4, 1/4 1/8 etc…) when togglebutton is selected to “sync to DAW.”

How I can change a parameter from continuous interval to discrete interval steepest (or vice versa) during runtime.

Thanks in advance for your helps
tugrul

What I understand from the topic Is it possible to change a parameter's name? it is impossible to change any defined parameter’s range or interval. Instead it seems a good solution to create another parameter and make it visible alternately by the other .

1 Like

That’s what we do. We have two knobs in the same location, each tied to a different parameter, and one is continuous and one is discrete, and we simply hide/show enable/disable according to whether we’re synching or not.

1 Like

Thanks to wkundrus, widdershins and tom for their hard work on dynamic parameter changes.
Changing a name to an empty string “”, will hide it from most DAWs. Below is what I do for dynamic parameters. I am not sure how you can do a std::swap on parameters. This appears another option, but someone else, like tom, might want to chime in on that.

Please excuse my lack of experience in coding below.
in AudioProcessorParameterWithID.h change const String name to String name.
In your processor.

  			//variables
 			std::function<String(float value, int maximumStringLength)> stringFromValueLambda
             =  [this] (float value, int maximumStringLength)
             {
                 return String(value,2);
             };
             std::function<float(const String& text)> valueFromStringLambda
             =  [this] (const String& text)
             {
                 return text.getFloatValue();
             };
 
 			//my constructor
             auto tempParameter
             =  std::make_unique<AudioParameterFloat>
             (
              “old name”
              , ""
              , NormalisableRange<float> (0.0f, 1.0f, 1.0f)
              , 0.0f
              , ""
              , AudioProcessorParameter::genericParameter
              , [this] (float value, int maximumStringLength)
              {
                 return stringFromValueLambda (value, maximumStringLength);
             }
              , [this] (const String& text)
              {
                 return valueFromStringLambda (text);
             }
              );
            parameters.createAndAddParameter( std::move (tempParameter) );


 
 		//dynamic changes-not in processblock
        for ( auto* param:getParameters() )
                 {
                     if ( auto* paramWithID =  dynamic_cast<AudioProcessorParameterWithID*> (param) )
                     {
                         paramWithID -> name =  "new name";
                     }
                     if ( auto* audioParameterFloat  =  dynamic_cast<AudioParameterFloat*> (param) )
                     {
                         audioParameterFloat -> range =  NormalisableRange<float> (0.0f, 1.0f, 0.001);
                     }
                     stringFromValueLambda =  [this] (float value, int maximumStringLength)
                     {
                         return String(value, 2);
                     };
                     valueFromStringLambda =  [this] (const String& text)
                     {
                         return text.getFloatValue();
                     };
                 }
         refreshParameterList();//TODO: don’t think this is needed??
         updateHostDisplay();

Cheers

1 Like

You’re going to run into problems attempting to do this. It’s likely the host will crash.

Even if you changed the parameter type at compile time you can still hit serious issues. In a handful of hosts the automation for discrete parameters is stored in a different format than that for continuous parameters. You cannot switch between the representations and attempting to interpret one format as the other will produce nonsense.

1 Like

Thanks Tom,
These are just hacks for testing in limited capacity whilst I wait for some form of dynamic parameters. There was little enthusiasm for dynamic parameters in feature requests. It appears like some pretty big companies, who are using Juce, are changing parameter names on the fly. They may have painted themselves into a corner and not be ready to change to the new Juce system yet.
Looking forward to dynamic parameters with that Soul plugin at some point. In the meantime I am stuck between a rock and a hard place, and wanting to experiment, is this how to add parameters to groups outside of the constructor, but in the constructor method? I am sure someone can suggest better, but…

  auto newGroup =  std::make_unique<AudioProcessorParameterGroup> ( "group", "group", " - " );
  auto newParameter
  =  std::make_unique<AudioParameterFloat>
  (
   "param"
   , "param"
   , NormalisableRange<float> ( 0.0f, 1.0f, 1.0f)
   , 0.0f
   , ""
   , AudioProcessorParameter::genericParameter
   , [this](float value, int maximumStringLength)
   {
    return someValue;
  }
   , [this]( const String& text)
   {
    return someString;
  }
   );
  newGroup -> addChild ( std::move (newParameter) );
  for ( const auto param : newGroup -> getParameters (true) )
  {
    if ( const auto rangedParam = dynamic_cast<RangedAudioParameter*> (param) )
    {
      audioProcessorValueTreeState.addParameterAdapter (*rangedParam);//mark this private function as public:\nfunction\n private:
    }
  }
  audioProcessorValueTreeState.processor.addParameterGroup ( move (newGroup) );
  parameters.state    =  ValueTree (JucePlugin_Name);
  refreshParameterList();
  updateHostDisplay();

Thank you for your time.