Parameter using multi segment envelope

The parameters are dynamic I would guess but how to add to value tree state just make a fixed number 64 parameters for instance?

How are multi segment envelopes lfos usually saved ?

If you don’t need every single point to be automatable/modulatable you could just store each point in an array and save and load that along with the state of your parameters. that enables you to use a dynamic amount of points and/or other envelope properties like curvature between points

to deal with multiple parameters i create one parameter per index using the same id for all, but adding the index to the end of the string. so i perform any calls involving the parameters in a similar way to single parameters, but always getting the id with a getID function that concatenates the index.

  juce::String getID(juce::String strID, int index) {
      return juce::String(strID) + juce::String(index)
  }

Hi Mrugalla how would you access the arrays on the gui thread if i have declared everything as juce::Array x;
juce::Array y;
juce::Array curve;

looks like we have to always use attachments from what i am reading but i dont plan for these to be automatable

My value tree looks like this

<PARAM id="MasterGain" value="1.490116119384766e-6"/>
  <MSEG>
    <MSEG1X>
      <PARAM id="MSEG1X0" value="0.0"/>
      <PARAM id="MSEG1X1" value="0.0"/>
    </MSEG1X>
    <MSEG1Y>
      <PARAM id="MSEG1Y0" value="1.0"/>
      <PARAM id="MSEG1Y1" value="1.0"/>
    </MSEG1Y>
    <MSEG1V>
      <PARAM id="MSEG1V0" value="0.5"/>
    </MSEG1V>
  </MSEG>

I think I should add non automatable dynamic params like this

mMod1X.push_back(new juce::AudioParameterFloat(getID("MSEG1X", 0), getID("MSEG1X", 0), 0, 1, 0));
mMod1Y.push_back(new juce::AudioParameterFloat(getID("MSEG1Y", 0), getID("MSEG1Y", 0), 0, 1, 1));
mMod1V.push_back(new juce::AudioParameterFloat(getID("MSEG1V", 0), getID("MSEG1V", 0), 0, 1, 0.5));
mMod1X.push_back(new juce::AudioParameterFloat(getID("MSEG1X", 1), getID("MSEG1X", 1), 0, 1, 0));
mMod1Y.push_back(new juce::AudioParameterFloat(getID("MSEG1Y", 1), getID("MSEG1Y", 1), 0, 1, 1));
  juce::String getID(juce::String strID, int index) {
      return juce::String(strID) + juce::String(index)
  }

thank you marcusonic for this

I always do it like this: the processor owns the arrays with the normalized values of the points and the editor has the same array but for the scaled values, with regard for width and height. a timer callback checks if the processor has changed. you can use an atomic bool for that. if the processor has changed the gui needs to update its array and repaint. mouse events can then be implemented to change the values on the processor side and to switch the atomic bool back

Why do you use timerCallback instead of value listener or something like that ? Is it one in the same to just set a timer

it shouldn’t make a big difference indeed. maybe it’s even doing the same thing under the hood. i just like to see what mechanism exactly is going on. and i know timerCallback is always on the message thread so it can not process at the same time as the mouse event methods or repaint, making it easy to reason about when there are bugs