What's the standard ValueTree interaction with controls and set calls?

I’m using a ValueTree to store a bunch of parameters for some gui interaction, which are then sent through to the DSP via a lock free buffering system, and I’m interested to hear what the standard way to use a ValueTree for this.

Let’s say we have a PluginEditor with a single ValueTree tree; with one Identifier id_sliderval; and double value for the slider value for that property, and we have a Slider that is somewhere in a SubPanel that the PluginEditor doesn’t have to know about directly.

Both the SubPanel that holds the slider, and the PluginEditor that holds the SubPanel would want to add themselves as a Listener to the ValueTree, so the PluginEditor listener can push values to the DSP, and the SubPanel can update the position of the slider when the valueTreePropertyChanged call is made (eg when undo / redo changes the property and the slider needs to reflect what is there):

void SubPanel::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged, const Identifier& property)
{
    if (treeWhosePropertyHasChanged == tree && property == id_slider)
    {
        slider.setValue(double(tree.getProperty(id_sliderval)), NotificationType::dontSendNotification);
    }
}

Now when the sub panel receives a sliderValueChanged message from the slider it will naturally want to call:

void SubPanel::sliderValueChanged(Slider* s)
{
    if (s == &slider)
    {
        tree.setProperty(id_sliderval, slider.getValue(), &undomanager);
    }
}

and this will correctly call down to the PluginEditor, but it will also call back to the SubPanel valueTreePropertyChanged, which is not ideal, especially when the situation is a little more complicated with multiple trees all interacting, possibly multiplying the number of updates going round.

Is there an easy way to re-structure things so this sort of thing doesn’t happen without having to resort to hacks like temporarily adding / removing listeners. Is it best just to let the loop happen once, with the terminating condition of no notification being sent on the second call to slider.setValue?

I figured this sort of thing should be standard practice by now, so to save time on my part I thought I’d ask you guys what it is :slight_smile:

One thing that comes to me mind, valueTree-Listeners work synchronously.
So you could set a flag (or using some RAII-mechanism) while using setProperty, and check that in the listener class.

A few thoughts about this, @ andy-cytomic:

  1. I don’t know if you were just presenting a simplified version, but if there’s only a single property in your ValueTree, then instead of managing a whole tree you could instead use a Value object, which has a Listener as well.

  2. I’m not clear the advantage in having the SubPanel be a Listener to the ValueTree just so it can forward values to the Slider. Why not have the Slider itself be the ValueTree Listener?

  3. Regarding your concern about listener notifications looping, isn’t that what calling Slider::setValue with NotificationType::dontSendNotification (as you have done in your code examples) is supposed to prevent?

As you suggested I was leaning towards just setting a flag in my class that doesn’t want to receive the callback on itself, but I was hoping there was an easy way to prevent the callback itself, not just receive the callback and ignore it based on a flag.

Thanks for the input much appreciated :slight_smile:

Yes, this is a basic example to keep things very clear.

I like having the SubPanel handle all management of values being set / and received so the it’s more symmetric logic, the SubPanel is a listener to the Slider and also sets the Sliders position.

The end example I want to handle is when a someone clicks on the Randomisation button on my plugin, and all values in a tree get changed. If a single values changes this triggers heavyweight update of a background thread job being started to pre-calculate large numbers of parameters. Obviously I don’t want this to happen hundreds of times, once once all the values have been updated. I was removing the listener from the value tree, but I think it’s better to set a flag and let the calls happen, and just deal with the extra overhead of the callback that will be ignored.

In your case I wouldn’t make the side panel a valuetree listener. I would connect the slider directly to the value.

Slider.getValueObject().referTo(valuetree.getPropertyAsValue(name, nullptr);

That’s magic. Your slider will move when the value is changed. And the value will be changed when you move the slider.

Ah, I think I see. What if you add a debouncing stage, between the ValueTree listener and the DSP calculations? Something based on a Timer that only triggers once there have been no changes for N milliseconds. I can post some code tomorrow if that would help.

Anything based on timers is bound to break as I can’t guarantee how long the bulk set will take. I would really love if when making a shallow copy of a ValueTree that the listeners would remain unique for that shallow copy, that would solve my problem perfectly. As it stands a shallow copy still registers itself as a ValueTree with listeners to the underlying SharedObject, when in turn calls back to all listeners of the underlying ValueTree, and so there is no point doing this.

So the situation is basically this: I need to differentiate between a single value being set on the ValueTree, which then triggers an update to calculate a set of coefficients to be sent to the dsp, vs multiple values being set, which should only trigger a single update to calculate the set of coefficients (which are passed via a lock free double buffer to the dsp from a background thread - several pre-computed tables are required here so this is a heavyweight operation).

In terms of the randomise button, the simplest situation to describe is either the user edits a text box with a value, and so triggers the set of coefficients calculation, or the user clicks a randomise button, which changes all values in the value tree and triggers the set of coefficients calculation once all values are set.

When using PropertyPanel with a bunch of TextPropertyComponent’s based on a ValueTree there is no way to know from where the valueTreePropertyChanged is called from, the randomise button, or the user editing a value. The least bad solution I could come up with is to have a duplicate ValueTree, and use that with the TextPropertyComponets. When a change comes in from the original ValueTree, I set a flag saying “updating_duplicate_tree”, then setProperty on the duplicate tree, which, when I get the callback I check the flag and know it’s not coming from a user edit. In this way I can tell when the user edits a value, vs when I’m doing a bulk update via the randomisation button, and so only trigger a single update to the dsp coefficients. This is still a bit clunky all just to work out where the update came from, but it works. I would much prefer if there was a mechanism where shallow copies of ValueTrees could have unique sets of listeners, but still update the same underlying values.

I got lost there. I mean, each copy does have its own listeners, but they all listen to the same thing. How would a change happen for some and not for others if it’s the same object?

Probably the Juce way would be an ignoreCallbacks flag, but it seems too intrusive in this case -wouldn’t you have to reference the flag everywhere? For a similar situation with attachments, I clear them before and recreate them after, so I’d probably go with a detach/attach pair to remove/readd the listeners.

All I’m trying to achieve is to have some additional data associated with each setProperty call, either who called the setProperty, or some flag associated with that particular setProperty, then I can change what I do inside the listener callback based on this extra information.

I think just setting a flag is enough, since otherwise I’ll be called add / remove listener possibly hundreds of times per update. Below is the circuit schematic editor code for the valueTreePropertyChanged callback, which is listening to both the main filter.fullrndvalues as well as its own duplicate copy of this ValueTree called fullrndvalues_edit:

void CySchematicEditor::valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property)
{
    PluginFilter& filter = editor->filter;
    if (treeWhosePropertyHasChanged == filter.fullrndvalues)
    {
        const String& key = property.toString ();
        const String& val = tree.getProperty(property).toString();
        auto found = component_map.find(key);
        if (found != component_map.end())
        {
            CySchematicComponent* comp = found->second;
            comp->value->setText (val.upToFirstOccurrenceOf(",", false, false));
            comp->repaint ();
        }
        if (!updating_tree)
        {
            updating_tree = true;
            // this will call back to the listner case below
            fullrndvalues_edit.setProperty(property, val, nullptr);
            updating_tree = false;
        }
    }
    else if (treeWhosePropertyHasChanged == fullrndvalues_edit && !updating_tree)
    {
        filter.undomanager.beginNewTransaction();
        const String& val = fullrndvalues_edit.getProperty(property);
        filter.fullvalues.setProperty(property, val, &filter.undomanager);
        filter.rndvalues.setProperty(property, "0", &filter.undomanager);
        // this will call back to the listener case above
        filter.fullrndvalues.setProperty(property, filter.computeStereoScaling(property), nullptr);
        filter.UpdateDeviceValues(false); // heavy weight background thread update
    }
}

If anyone knows a way to get this sort of result, ie filtering based on who called setProperty or some flag passed in to setProperty, then I’m all ears, otherwise I’ll just leave this ugly ValueTree duplication and flag setting in place since at least it works.

I understand the reluctance to use timers, it can feel hacky. But with the timer as a debouncer, you don’t have to know “how long the bulk set will take” as a whole, you just have to put a reasonable value that’s longer than it takes to change a single property.

In pseudocode,

class Editor : private Timer
{
	void valueTreePropertyChanged
	{
		...
		if (treeWhosePropertyHasChanged == filter.fullrndvalues_edit)
		{
			...
			filter.fullrndvalues.setProperty(property, filter.computeStereoScaling(property), nullptr);

            // if the timer is already started, startTimer resets it
			startTimer (20);  // ms
		}
	}

	void timerCallback()
	{
		filter.UpdateDeviceValues();
		stopTimer();
	}
}

This solves the situation you describe here (well, it doesn’t differentiate between the two, it just insulates your heavyweight operation from being called too often):

And, if you should in the future allow users to edit values with a Slider instead of just a text box, debouncing a Slider’s stream of values would be essential.

Thanks for your continued help, much appreciated :slight_smile: In the case of a slider I definitely agree that a timer is required, and this is exactly what I’m already doing to limit the number of updates / undo states when moving the slider. I currently have two sliders, one for the “mono” variation depth, and one for the “stereo” variation depth, and these scale the set of random numbers generated when the Randomise / Mutate buttons are pressed.