AudioProcessorValueTreeState Undo behaviour - Juce demo plugin example

Hi all

I have posted briefly about this before, and there seem to be a few threads discussing some of the issues surrounding this, but they seem to be side-discussions. I apologise if this is regarded as a duplication.

I am having issues with implementing undo and redo behaviour in a plugin using the AudioProcessorValueTreeState class. To illustrate this I have modified the Juce demo plugin using AudioProcessorValueTreeState, and tried to implement undo and redo functionality as I have in my own plugin. The modified demo can be found here:

https://github.com/gordcragg/JUCE_Plugin_Demo-AudioProcessorValueTreeState

The demo now includes an undo and redo button which are enabled if canUndo() and canRedo() respectively return true. When a slider drag ends, a timer is started and in its callback beginNewTransaction() is called in order to separate transactions.

The issues I am having are as follows:

  1. After an instance of the plugin is loaded in the DAW, canUndo() returns true even though no parameters have been altered by the user. This means the undo button is enabled, but clicking it results in no change (other than the undo button being subsequently disabled).

  2. If the user performs one parameter change and attempts to undo it, the action is successfully undone, but it is not possible to redo this action. canRedo() returns true momentarily and the redo button is enabled, but after a fraction of time canRedo() returns false and the redo button is disabled. Only multiple (> 1) valid undo() calls result in redo functionality working as expected.

Is this the expected behaviour, or have I implemented something incorrectly? Or is it a bug in Juce (very unlikely)? If anyone has the chance to run the code to assist in figuring it out, or just glance at it to see if I have done something obviously incorrect, I will appreciate it greatly.

Sorry for answering so late. The canUndo becomes true because the DAW will often try to load a saved-state of a plug-in. Have you tried calling clearUndoHistory after loading the state in setStateInformation?

Hi Fabian, thank you for the response.

Unfortunately that doesn’t appear to work. Both of the issues I listed above remain, even when I call clearUndoHistory at the the end of setStateInformation. Assuming it did work wouldn’t this clear the undo history when a preset is loaded? I would accept this as a compromise but it certainly isn’t ideal.

Any other ideas or suggestions? Has anyone been able to get undo and redo behaviour working successfully with AudioProcessorValueTreeState that can point out if I have done something incorrect in my example? Everything seems to work perfectly after two or more actions have been completed i.e slider values changed two or more times.

No I can confirm that it doesn’t seem to be working correctly. The problem is that every parameter change will issue multiple valuetree changes and each of them will be a single undo step. We need to re-work some of this code to issue transactions instead. Sorry, we will need to add this to our backlog… :frowning:

Ok, no problem. Thank you Fabian!

Hi @fabian ,
sorry if I step in , but I experience similar problems using AudioProcessorValueTreeState together with UndoManager, and I would like to understand if there is an available “exit strategy” for a plugin that (a) uses AudioProcessorValueTreeState extensively, and (b) needs an undo mechanism that works.

My initial thought would be to use an “external” (i.e. not passed to the AudioProcessorValueTreeState) Undo Manager, and leverage the SliderDragEnded callback in the editor and the ParameterChanged callback in the processor to basically replicate what the UndoManager should be doing in the first place within the AudioProcessorValueTreeState.
This seems very a painful way to proceed :frowning: as well as as something that will become useless as soon as You finalise the UndoManager behaviour within the AudioProcessorValueTreeState

Anybody has other ideas?
Thanks a lot,
Michelangelo

NOTE: regarding Your comment about setStateInformation being responsible for the issue.
The behaviour mentioned by @gordcragg is the same even when using the juce plugin demo above in “AUV3 standalone” mode - I tried debugging this scenario and setStateInformation does not seem to be called at all.
So where could this issue come from?
At the state of understanding I am at, I suspect there is an “external entity” that pushes actions in the undo manager even if those actions are not actual value changes.
The only promising “smoking gun” in this scenario seems to me the timer callback of AudioProcessorValueTreeState (in particular the p->copyValueToValueTree(); call), but I still haven’t been able to study that in depth.