DAW undo history bloat with my custom 2d slider gui component

Hi Jucers,

I have run into an edge case problem which may have been answered before but is hard to search for since it’s quite specific, so sorry if this has been answered before elsewhere.

I am making a plugin with a custom 2dSlider gui component for my plugin interface.

Basically the same one here

I am using the APVTS to handle automation of my other gui components including the position of my 2dSlider.

I’ve built the 2dSlider the recommended way by making 2 slider and 2 SliderAttachment s, one for each axis.

The problem I’m having is that when you move the slider position the undo history of Ableton becomes extremely bloated.
I wouldn’t post on here if it wasn’t really bad, as soon as you move the cursor around a little you have hundreds of undo events in Ableton and loose any chance of getting back to where you were previously.

My first hunch was that beginChangeGesture for the respective parameters was not being called since the sliders never get the mouseDown call in my component.
However I’ve just coded a whole 2dSlider class and 2dSliderAttachment class to do this safely and it doesn’t fix the problem.

What I believe is going on is that Ableton collects parameters changes into a new undo event from that parameter’s first setValueNotifyingHost function call.
And so, if you call setValueNotifyingHost for a different parameter is starts to create a new undo event for that parameter.

I’ve recreated this behavior by only moving the slider position along one axis which means it only changes one of the sliders values and it creates a one undo event for the whole gesture.

But as soon as you move it linearly along both axes, moving both sliders, your undo history represents hundreds of little changes for each parameter/slider movement on it’s own axis.

So my question is what would be the best way of still being able to automate my 2dSlider position in a DAW but not having it’s movement clog up the undo history?

I know I can still save it’s position in the APVTS externally for preset and such, but I’d quite like to automate it like any normal slider.

Thanks in advance!

1 Like

Hi @viadoe, have you or anyone figured out how to prevent a DAW from adding parameter changes to its undo list?

I don’t know about preventing the host from seeing parameter changes, but regarding the original post here, we issue a beginChangeGesture() for each of our X/Y parameters upon the mouseDown call, issue setValueNotifyingHost() for each change, then issue endChangeGesture() upon mouseUp. That makes sure that the whole set of changes is seen as one. I personally haven’t tested this in Ableton, but I don’t have problem reports about filled-up Undo stacks.

@HowardAntares thanks for your message!
@leleswam I never tried to make a work around for this and just sat on the behaviour but from what Howard has said it looks like I must be doing something wrong because I have implemented what he has written.

Well, I just tested in Live 11 Suite, and, like yours, I get a large number of items on their Undo stack, apparently, even though I don’t issue the endChangeGesture until I am in mouseUp. You might want to ask them about it. (This was with a VST3 version, by the way. Not sure if an AU version would behave the same or not.)

It seems that recently this issue has also occurred in other DAWs, like Reaper. So we have to find a solution to prevent the host from seeing parameter changes. Maybe someone on the JUCE team has a suggestion to what could be done?

Still no replies about this issue.
Problem for us is that we control parameters via MIDI, so there isn’t a way to call beginChangeGesture() / endChangeGesture().
If we call setValue(), instead of setValueNotifyingHost(), sliders and knobs on the GUI do not update according to the current MIDI value.
That’s a big problem for parameters continuously controlled via MIDI, like Expression or other modulations.
Any thought?

The correct way to automate is to call beginChangeGesture+setValueNotifyingHost+endChangeGesture also with MIDI input.

2 Likes

For mouse controlled changes, you can also call setValueNotifyingHost only on mouseUp. No need to notify the host during the many mouseDrag callbacks. As a user I expect that on undo my slider (or other control) jumps back to the last mouse down position.

@dimbouche I’m not sure about that: if you do that, the host will only record a single value at the end of your dragging action. So if you do a filter sweep with a cutoff frequency slider over say 5 seconds and you record that as parameter automation, you would only get the final value recorded, and not the sweeping action, right?

@JussiNeuralDSP how do I know that the user started or completed a given action, let’s say, on his Expression pedal? Or when blowing into a Breath Controller? On a GUI element is simple to get begin/end gestures, since it is part of the software, but how to do it on a physical device?

@KoenTanghe for sure this would not work when recording automations. But this would at least solve the issue when the transport is not rolling. Sorry I was not clear enough.

1 Like

@leleswam a trick would be to use a timer. If you don’t receive a change within, let’s say, 1 second, then you send an end gesture. As soon as you receive a new event, you send a begin gesture and begin a countdown (which gets reset on each new event).

I agree with @leleswam, this is not a suitable workaround.

Imagine what happens if the automation is set to latch: as soon as you send a endChangeGesture() the value would start moving to the old recorded value, and the player has no way to work against that than playing things they didn’t intend.

@leleswam Yes, with MIDI there is no way to detect reliably if the user has stopped manipulating a parameter, a big omission also in MIDI2.0. Like @dimbouche said you could use a timer to call the end gesture. A more experimental way would be not to send the end gesture immediately but only when the host playback/reccording has stopped or the playback position is changed (either manually or by host looping).

We have solved making a patch to the JUCE code.

We have removed:

if (processor != nullptr && parameterIndex >= 0)
{
// audioProcessorParameterChanged callbacks will shortly be deprecated and
// this code will be removed.
for (int i = processor->listeners.size(); --i >= 0;)
if (auto* l = processor->listeners[i])
l->audioProcessorParameterChanged (processor, getParameterIndex(), newValue);
}

from:

AudioProcessorParameter::sendValueChangedMessageToListeners

As you can see, there is already a comment that says:

audioProcessorParameterChanged callbacks will shortly be deprecated and this code will be removed.

the easiest solution imo would be to just ditch juce::Slider completely and just implement a custom component as xy-pad. this component would have the 2 parameterAttachments and rangedAudioParameters in it and would theirfore be in full control over when to send beginGesture, sendValueAsPartOfGesture and endGesture. no need to inform sliders that a value has changed with async messages or fancy attachments if there are no sliders. if you want their to be the illusion of a slider you can just draw them yourself to represent the current value, but without being actual sliders. Now the “but juce handles all the edge cases for you”-police will come and say i’m wrong. But I just continue to feel like when I am in control of all the mouse events myself I know what my application is doing and where to look if there’s a problem

Just check out @leleswam 's post:

1 Like

do I understand correctly, that I have to call sendValueChangedMessageToListeners instead of beginChangeGesture+setValueNotifyingHost+endChangeGesture ?