ValueTree suitability

Hi everyone. I’ve been working on this drum machine for a while, which is now on the App Store for free ‎Syncopate on the App Store

It’s nowhere near the finished article but I decided to get it out there and then update as I add features. I’m actually really happy with the way it’s working, but have hit a hurdle with my next task.

I want to add save ability, as well as undo/redo and so my first thought was that the valuetree class would give me what I need. It probably will do but I’m at a loss as to how to go about it. Please bear in mind that I’ve been coding for less than a year and could have probably done things in a better way.

I have all my variables for the current (playing) state, and then another set of each of these variables for each of the 8 selectable sequences. When ‘saving’ to one of the sequences I basically copy the ‘current’ variables to it, and when I’m selecting another sequence (to play it) I copy the sequence variables into the current ones, and update all dials/pads/GUI components from there. Meaning I only have one set of components which are updated to whatever necessary set of values.

In a nutshell, all I actually need is to be able to save a large number of variables but with undo/redo functionality. Then if undo is pressed, I would just update gui with new (undone) set of values. Everything I’m reading about valuetree seems to require ranged parameters that are linked to components, which is not the case for me.

Thanks to anyone who can offer any advice, and again please excuse my convoluted methods (if they are) as it’s one hell of a steep learning curve!

Dan

The most important piece of advice when implementing Undo/Redo via ValueTree is, never do actions from the user directly in your classes. Instead add the user interaction into the ValueTree and let the ValueTree::Listener do the changes in your classes.
This way your code will function the same way regardless if the user set something or clicked redo.
You can best keep control by making your setter methods private:

// old:
// user sets position:
setPosition (event.x);

// instead
vt.setProperty ("position", event.x, undo);

void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged,
    const Identifier& property)
{
    if (treeWhosePropertyHasChanged == vt && property == "position")
        setPosition (treeWhosePropertyHasChanged.getProperty ("position"));
}

Something like that…

1 Like

Thanks. I’m not sure I fully understand so I’m just gonna check that why I’m trying to achieve is definitely possible before getting myself too involved.

Using a ValueTree am I able to have a series of integers/bools that can be saved to disk, and that can also make use of undo/redo? I’m talking about completely independently of anything that the user does and also independently of any of the gui components.

Thanks

Using ValueTree with the UndoManager means, you can restore the previous state of the ValueTree by calling Undo. But Often you mirror the values of ValueTree e.g. in an atomic so you can use i from the audio thread. Or you need to call something as a result of a position change, e.g. source->setNextReadPosition()…
To make that viable you listen to the ValueTree.

That is all independent of GUI, but of course user interactions, since that’s what the user usually wants to undo.

Good luck

This only applies if you use the AudioProcessorValueTreeState and the parameter attachment classes. The ValueTree itself has no concept of ranges(I sometimes wish it had…). It can be thought of like an xml or json file in memory and can of course also be serialized and deserialized to/from these formats. The listeners allow you to act on changes on any of the nodes and as the ValueTree only supports a small set of well defined change operations, the UndoManager functionality is already baked in.

So it’s the right tool for the job.

If you have any sliders or knobs you might also need to worry about undo transactions… that is which operations get grouped into one undo step. An easy solution found somewhere on the forum is to postpone completing a transaction as long as the mouse is pressed/touch is down.

Great, thank you both. I’m gonna try and get it working this week.

I’ve got the value tree working but its running really slow. As I only need it to store arrays of variables, I haven’t added any children to it, instead just setting up the tree as it’s done in the value tree demo.

Then vt.setProperty for the initialisation of arrays, as well as whenever the values need modifying (and vt.getProperty for the opposite). It’s slowed down every aspect of the app to an unacceptable level.

I know i’ll be doing something wrong. The vt now has around 28000 properties attached to it, am I iterating through the whole thing to find each property and that is what is taking the time? Should I use a number of different trees instead? Would I still be able to use an undo manager if I did this?

Thanks for any advice

28000 properties is bad. Accessing a property requires iteration through the list. You should make a hierarchical tree somehow. The undo manager will still work and then listeners can be attached to subtrees which makes everything much more efficient.

I thought as much. Can I ask how this efficiency is gained given that the getProperty function only takes a single identifier as argument? If I create a hierarchy as you say, am I then able to iterate and setProperty/getProperty from within the required child only?

Sorry for my newb questions but there isn’t much available online for Value Trees.

yes then you’d call getProperty on a child with far less properties - making it faster. And you’d also hook up listeners to the children making the listener logic much simpler.
I don’t know how your data is structured, but the child node could hopefully be accessed directly without searching.

Remember the ValueTree is a wrapper around a shared object. So each real object can hold it’s own ValueTree node so it is only aware of the subtree it is concerned with. And use a plain ValueTree, not a reference (&), because the ValueTree sharedObject is reference counted, so it is safe to hold it in the object.

Ok, so does that mean I can use my existing arrays and add them to the value tree nodes somehow? The reason I had so many properties is because I have a number of nested[8][8][32] arrays, and I’ve spent the morning doing setProperty on each of them. Do I need to do something similar but separated onto the individual nodes, or can I just add the originals, in their current int/double array format?