This approach is not working however. When I load the state then the UI does not update and and I get no output when I press on my midi keyboard(its a synth btw) even if the state I saved did have output.
In order to connect my value tree to UI elements I have done this sort of thing:
Why not use APVTS? copyPropertiesAndChildrenFrom removes all children, breaking all connections to them. APVTS::replaceState uses operator=, which triggers valueTreeRedirected, which calls updateParameterConnectionsToChildTrees precisely to fix that. Are these properties that shouldn’t be visible from the host? Otherwise APVTS does a lot of work for you.
Yeah I’m using a raw value tree because it gives me a bit finer control and because I will have like 50~odd parameters by the end, which will be very cluttered if the host is able to see them all.
*sidenote: I tried myself to use operator= like this:
juce::ValueTree& newState = juce::ValueTree::fromXml(*xmlState);
state = newState;
Take a look at the implementations and see what they do. There’s no facility in ValueTree for setting the whole tree to the contents of another one without replacing the children themselves. To do that, you need to traverse the tree manually. replaceState lets operator= replace the nodes, but because APVTS is a ValueTree::Listener, it receives a valueTreeRedirected callback, which uses to recreate its connections to the ValueTree.
The main issue is that get/setStateInformation can be called from any thread, and ValueTree is not thread-safe by itself. If you’re using it both from the UI and processBlock, that’s not safe either. APVTS solves these problems for you (mostly). There are lots of plugins with more than a hundred parameters, I don’t think anyone would complain about it. It’s more or less manageable to have some UI parameters saved with state and not exposed, but once they’re needed by the audio process, you’d have to recreate a good part of APVTS to make it work.
Are all of your parameteres set to “isAutomatable”? Because I don’t want them to be visible to the host.
In ableton, my daw of choice, it is possible to view these parameters by pressing an arrow on the plugin:
if I have 50+ parameters then I don’t want to be able to see them all in this window.
If your parameters aren’t visible to the host, how did you make this change? I couldn’t a super easy way to do this.
All my parameters are automatable, and some hosts actually ignore the tag, but Live won’t show you all of them if there’s a lot. You can pick which parameters you want to have there.
ok thanks, I will try to implement an apvts into my synth. Secondly, in order to communicate from the ValueTreeState to the dsp, do you think its a good idea to use an AudioProcessorValueTreeState::Listener? Using getRawParameter() doesnt seem like a good idea to me as I have so many paramaters it would seem weird to update them every time a block is generated.
To me, at least, it makes more sense to implement the listener call back and then update member variables when a parameter is changed like so:
void AudioProcessorValueTreeState::Listener::parameterChanged(const String& parameterID, float newValue)
{
if(parameterID == "attack") //or something of that nature
osc[i].setAttack(newValue);
}
but I am open to ideas, I am fairly new to juce so I am still figureing out the best way to structure my project.
thanks
The intuition is right -it’s probably not a great idea to use raw parameter values directly if there’s a lot of them, especially if you have derived parameters. This is a good case for a mpsc fifo queue. You need a key-value type like
Message msg;
while (messages.pop (msg))
{
if (msg.id == param1id) // ...
else if (msg.id == param2id) // ...
else if (msg.id == param3id) // ...
}
and you assign the incoming values to wherever they need to live for processBlock. These locations don’t have to be atomic -the queue synchronizes the whole thing. To replace the if-else-if chain with a switch, I actually use enum ids and have a mapping from Strings to the enum. There are a number of fifo implementations out there, I use Fabian Renn’s farbot::fifo.
Thanks for your reply, thats something I’ve never thought of doing.
However one question immediately pops into my mind:
Why not implement the update directly into the parameterChanged() function.
i.e.
void APVTSListener::parameterChanged (const juce::String& id, float value)
{
if (msg.id == param1id) // ...
else if (msg.id == param2id) // ...
else if (msg.id == param3id) // ...
}
is there some reason why you need to go out of your way to create this data
Because parameterChanged can be called from any thread. Specifically, setStateInformation can, and also automation. And the UI would call it from the message thread. Take a look at thesevideos.
Ah i see thank you.
One last question (I have done some googling but can’t get it to work): I am unable to get my project to recognise farbot i.e. I can’t say #include "fifo.hpp"
I have never used libraries/external files with juce.
Thans
The path should be relative to the file where the include is. For example, if you have fifo.hpp in a “farbot” folder which is in the same folder that the current file, it’s #include "farbot/fifo.hpp".
ah i see. How do you think you would go about choosing the correct length for the fifo. I have created it but when I call the constructor I need to pass a size. It seems a bit arbitrary to just choose a size for this application.
I’m not sure about that. The idea is to consider how many changes may arrive between two blocks in the limiting case of all parameters being automated. I chose a very conservative 16384, but when I’ve tested it in Cubase with heavy automation, it stayed well below 1000. That may change from one host to another though.