I’m trying to figure out the best way to handle slider parameters in my plugin, using AudioProcessorValueTreeState. The SliderAttachment method given in the tutorials seems to work fine, expect when using a TwoValueSlider. One solution given in another thread is create a custom attachment, but this seems a little hacky and beyond my current understanding of JUCE, particular the use of the ControlBase. (see https://forum.juce.com/t/using-the-slider-attachment-class-with-2-value-sliders/18411)
So my question is: without necessarily using a custom attachment, what are some good ways synchronise processor parameters with the TwoValueSlider in the editor?
A good old fashioned Slider::Listener would be my recommendation.
void sliderValueChanged(Slider* slider)
{
if (slider == &twoValueSlider)
{
const auto min = twoValueSlider.getMinValue();
const auto max = twoValueSlider.getMaxValue();
minParam->setValueNotifyingHost(min);
maxParam->setValueNotifyingHost(max);
}
}
And then inherit from APVTS::Listener to get callbacks when the parameter is changed by the host:
void parameterChanged(const String& id, float newValue)
{
if (id == "min")
twoValueSlider.setMinValue(newValue);
else if (id == "max")
twoValueSlider.setMaxValue(newValue);
}
plus adding beginChangeGesture() and endGesture(),
plus atomic safety measure, because the notification from the parameter occurs most of the time from the audio thread, vs. the slider notification come from the message thread…
There is quite a bit of work involved to do it right.
The above solution will:
cock up when an automation is recorded, since it doesn’t know when you touch the slider
might go into a feedback spiral
has thread safety issues
I created a safe passage into the message thread with this helper class:
You can copy that class and create a ParameterAttachment. It is not specific to a TwoValueSlider, but you can use it like that (untested):
The ParameterAttachment class that @Daniel provided does not have setValueNotifyingHost(), so setNormalisedValue seemed like the closest option. However, I’m not necessarily passing in normalised values - could this be a problem?
There also appears to be an issue with the mouse tracking here; in certain positions, dragging the thumbs causes them to rapidly oscillate to and from their original position. Do I perhaps need to implement some more onMouse…() functions?
Thanks for your help here @Daniel, I really appreciate it.
Good spot, yes I didn’t test the code, so I made those mistakes you pointed out.
Using setNormalisedValue is indeed a problem, you are right, the class needs a setValue for unnormalised values. The logic follows the conversion in getNormalisedValue, just the other way round.
The oscillation effect you observed could be explained by that, since the value is handed to the host and is asynchronously returning, but wrongly not unnormalised. I hope it goes away once that is changed.
@Im_Jimmi sorry, I didn’t mean to sound that harsh, you gave a good starting point. It actually took me a year to realise, that my plugins had those problems with automation I were pointing out.
So I tried your implementation of setValue, and the same assertion was triggered for convertTo0to1() as for convertFrom0to1():
static ValueType clampTo0To1 (ValueType value)
{
auto clampedValue = jlimit (static_cast<ValueType> (0), static_cast<ValueType> (1), value);
// If you hit this assertion then either your normalisation function is not working
// correctly or your input is out of the expected bounds.
jassert (clampedValue == value);
return clampedValue;
}
Ok, that jassert makes sure, that your normalisation is legit.
The fact that it fails can have two reasons:
the normalisation function of your parameter delivers values outside 0 to 1
values outside from the legit unnormalised range are fed into the normalisation
e.g. the range is 0…10 and you feed 11, so the clampedValue will be different from the value coming in.
Go out the stack step by step and check the numbers to find out what happened.
If all else fails, please share the range, how you set up the parameter.
Oh, and since you are not using the regular SliderAttachment, you will have to set the slider range manually to be the same like your parameter’s one (and min and max parameters ranges need to be identical too).
Daniel, thank you so much for your help! Setting the range manually fixed the clamping issues and now the slider behaves perfectly. For any future visitors, here is a working TwoValueSliderAttachment class with the inclusion of a lambda that sets the text value in the “[min] - [max]” format: