Automation of Slider

I am missing something basic here.

In my track footer, I have a volume slider for the track. I can control the volume, and record the automation. However, on playback of the automation, the slider does not follow the automation.

volumeSlider.setValue(te::volumeFaderPositionToGain(audioTrackPtr->getVolumePlugin()->getSliderPos()), dontSendNotification);
volumeSlider.onValueChange = [this, audioTrackPtr]
{
	audioTrackPtr->getVolumePlugin()->setSliderPos(te::gainToVolumeFaderPosition(float(volumeSlider.getValue())));
};

Please point me in the right direction for hooking this up correctly. I am sure it involves the plumbing associated with the automatable parameter, but I am not seeing the correct syntax.

Any help is most appreciated.

I’m not sure if this helps, but I connect my volume slider as follows:

m_volumeKnob.getValueObject().referTo(audioTrack->getVolumePlugin()->volume.getPropertyAsValue())

Excellent! Thank you!

But I think there is another piece. What are you doing in your .onClick() lambda?

you need to implement a so called juce::AudioProcessorValueTreeState. it handles all kinds of edge cases for your parameters. i’d like to show you example code but i’m not at my computer. you can probably find a lot of discussion about it and tutorials on youtube

I haven’t implemented a on click behavior, yet. What are you missing?

Thank you. I use AudipProcessorValueTreeState in my plugins. But, this is for the built-in track volume fader in tractionEngine, which already has CachedValue associated with it.

So, the line

volumeSlider.getValueObject().referTo(audioTrackPtr->getVolumePlugin()->volume.getPropertyAsValue());

should make the connection. But I am still not getting slider movement in the UI without the .onValueChanged() lambda;

volumeSlider.onValueChange = [this, audioTrackPtr]
{
	audioTrackPtr->getVolumePlugin()->setSliderPos(te::gainToVolumeFaderPosition(float(volumeSlider.getValue())));};

which seems like it should be unnecessary, so I feel I am still overlooking something.

Yes, this seems unnecessary to me, too. After the connection I had to set the slider to current volume (for some reason) and that’s it. When I add a modifier to this slider, I can see the movement.

You probably don’t want to use the getValueObject().referTo method here as it won’t work for parameters that aren’t backed by a CachedValue (basically any ExternalPlugin parameters).

A better approach is to use AutomatableParameter::setParameter and then update in AutomatableParameter::Listener::curveHasChanged/currentValueChanged callback.

Just be careful you don’t end up setting the parameter in a loop by setting the slider value then the parameter value in repeated callbacks. The best thing is probably to use the callbacks to async update the slider without sending a notification and using AutomatableParameter::getCurrentValue to determine the value the Slider should be at.

My use case is specifically for the volume built-in to the track, which does have a CachedValue. What do you recommend for the built-in track volume?

I am using;

volumeSlider.getValueObject().referTo(audioTrackPtr->getVolumePlugin()->volume.getPropertyAsValue());

which does not show movement of the fader when automation is played back. So, I had to add;

volumeSlider.onValueChange = [this, audioTrackPtr]
{
	audioTrackPtr->getVolumePlugin()->setSliderPos(te::gainToVolumeFaderPosition(float(volumeSlider.getValue())));
};

in order to see the movement. And, it just seems like the .onValueChanged should not be needed.

What do you recommend?

Are you sure it doesn’t update?

When your automation sets a new volume value, put a breakpoint in AutomatableParameter::setParameterValue and step through to the line if (isFollowingCurve), does the attachedValue->triggerAsyncUpdate() line get called?

If so, have a look at the AutomatableParameter::AttachedFloatValue class at the top of that file and see if the handleAsyncUpdate callback gets called. Does that value.setValue (parameter.currentValue, nullptr). If that gets called, step in to the CachedValue<Type>::setValue and see if the targetTree.setProperty line is called. If that is, presumably the internal Slider’s Value should update?


Basically I can’t think of a reason this shouldn’t work for the volume/pan plugin but it’s complicated so I might have overlooked something. Also, I’d still suggest doing what I showed above as it’s a more general case that will scale to other parameters.

Using the following line, for my track volume slider;

volumeSlider.getValueObject().referTo(audioTrackPtr->getVolumePlugin()->volume.getPropertyAsValue());

I checked if, attachedValue->triggerAsyncUpdate() in AutomatableParameter::setParameterValue is called. It is not!

So, the automation value is never recorded. That is why I am having to use the volumeSlider.onValueChange lambda to write the automation.

Once automation is written, it is then properly played back by the ``referTo``` connection.

Please advise?

And I am just realizing that the code you had me check is for the playback of automation. We need to check the recording side of things.

What do you suggest?

I think I may have unintentionally confused things.

To be clear, it is writing of automation that is not happening using the referTo on the CachedValue. Playback does work. And the line attachedValue->triggerAsyncUpdate() is triggered during playback.

So we need troubleshooting on the writing automation side of things with that referTo connection. It seems that the connection is one way only.

I wouldn’t use the CachedValue at all, just manually call AutomatableParameter::setParameterValue when your slider changes.

Honestly it’s the simplest way and will scale to 3rd party plugins.

The AttachedValue stuff is really to ensure the parameter gets flushed to the ValueTree state.

Thank you, Dave. I know how busy you are and I appreciate the help!

It will be no surprise to you that the AutomatableParameter::setParameter approach worked perfectly on the first try.

I do have a question concerning the AutomatableParameter::Listener::curveHasChanged callback. In my case, I am automating a simple volume slider. At the moment, my curveHasChanged callback is empty, and yet everything is working as expected.

Is there necessary functionality that will be lost if the callback remains empty?

So, what is the recommended usage scenario for the curveHasChanged callback?

Your slider will most likely be getting updated as the value in the state gets flushed via the AttachedValue?

As I said above, I’d probably remove this and only use the AutomatableParameter::setParameterValue to set the value and AutomatableParameter::Listener::curveHasChanged to update the slider.

Per your suggestion, I am already using AutomatableParameter::setParameter, etc. and it works great!

My question is specifically about the AutomatableParameter::Listener::curveHasChanged callback. Essentially, I am asking what functionality it provides, since I am getting automation recording and playback without using it. The callback is empty, yet everything works. So, what excatly will I be missing if I leave the callback blank?

How is your Slider updating when the volume parameter changes due to automation playback then?
Presumably you want it to move up/down as the volume automation is played back?

Yes, I get slider movement on playback of the automation. It works great!

I am simply using the other callback currentValueChanged with volumeSlider.setValue(). Everything works as expected.