Hey @gavinburke
It can get pretty involved, and it’s probably too complicated to go into all details. Some more stuff I remember:
It’s reliable to detect setStateInformation. I am doing some preprocessing on the loaded tree before attaching it to my “live data model” tree in this case. In one of the products I developed, that entails going through up to 50 upgrade migrations first, and then doing some modifications to not include certain things I may not want to load, while forcing some others. It depends on the concrete application.
I vaguely remember that my data model uses different strategies to work on the tree, so you may be right that I make a difference between redirected (which is the assignment to a tree) vs. attached. I may have used the assignment for one thing and reattachment for the other, but without diving into the code I can’t remember exactly right now - but something like that was involved. I am pretty certain that for the case of setStateInformation, it comes down to a liveTree = importedTree, and the rest is taken care of by all the listeners. An assignment is causing a redirect, doing deep copies is a different challenge.
There are indeed a lot of those, and it can easily get into 1000s of properties changing. If you compare this to the amount of stuff I’m doing per sample, or to what many games need to update per frame, this is not causing any noticable lag or stutter. I’ll try to describe a bit more what’s going on.
I’m only using the value trees to hold data for the ui/message thread side of my plugins, because they provide easy serialization support and you can attach listeners to everything. I mirror this data completely for consumption by my audio processing in simpler c++ data structures, with strong control over what is updated exactly when, and different policies to govern the process. For example, some data is streamed to the audio engine (like turning a filter knob), while changes on other data may require a full rebuild of the processing chain (like changing global oversampling or introducing a new oscillator in a modular synth). So, over the years I’ve built a data model on top of value trees that ensures strong type safety, comes with it’s own listeners and data types (user definable curves come to mind), and many other things that go beyond value trees. There are factories for various components, some stuff uses binary data and the value tree just holds a bit of base64.
If you plan to grow and extend a product over a longer time, I can recommend spending the extra effort to build a reusable framework of these things (and test the foundation thoroughly).
About your second point:
My audio processing doesn’t rely on the ValueTree at all. It retains its state entirely if I swap out the tree or sub-trees, and I have some additional threads at work with various responsibilities of processing the data before handing it over to the audio processor in lock-free swaps. Resource loading (samples) and compiling processing graphs are offloaded, preprocessing convolution data, and everything that needs to allocate something. The system retains whatever it can, and it goes through various levels of protocoling and analyzing to only change what needs to be changed. There is also a garbage collection system that makes sure data is deallocated on the correct thread.
To come full circle: almost all of that is indeed triggered by value tree listeners somewhere in the data model. This works so well that my undo/redo system is based on saving/reloading entire value trees, in a brute force memento pattern approach (it’s just a few kbs of data, so why not…).
It can be a little tedious to get the ValueTree-reactive part robust, but once it’s there, defining data model schemas and getting both UI and behavior for it automagically can become both easy and reliable. I definitely became a fan of the ValueTree data structure for reactive programming in JUCE.
I wouldn’t overdo it for small/simple plugins that just have a handful of parameters though, while at a certain complexity and size, there may be other approaches to store data that work better.
Hm, I got lost here a little, so sorry for the wall of text, but maybe it gives you some ideas tl;dr: valuetrees are great and fast enough for most things, but you need to constrain them a little, and they sure can be confusing.