BOOST XML JUCE

Look for a member of type AudioProcessorValueTreeState in the AudioProcessor subclass. If it’s not there, then you need to add it yourself. (Projucer does not add it by default because there are multiple ways to deal with the plugin parameters/states.) But if you are following the AudioProcessorValueTreeState plugin tutorial, the tutorial class should have it…

1 Like

The AudioProcessorValueTreeState is an optional class, that is intended to enhance the AudioProcessor’s handling of Parameters exposed to the host, as well as providing a public ValueTree member, where your additional parameters, that are not automated by the host (e.g. because they are of a kind the host wouldn’t understand, like in your case).
This makes it easy to serialise the plugin’s state in one go for the host to save and restore it later using getStateInformation and setStateInformation.

To use the AudioProcessorValueTreeState, create one member of this type in your AudioProcessor (this procedure is called aggregation).

The way, how the automated parameters are added was recently changed, have a look in this thread: “AudioProcessorValueTreeState Improvements”.

After that to add your own information to the state member of that AudioProcessorValueTreeState, like @Xenakios explained.

1 Like

Thanks Senseis @xenakios and @daniel !
That should keep me going for a while!

I have been on tour away from all this …
At the moment my constructor looks like this

WayloChorder3AudioProcessor::WayloChorder3AudioProcessor()
#ifndef JucePlugin_PreferredChannelConfigurations
     : AudioProcessor (BusesProperties()
                     #if ! JucePlugin_IsMidiEffect
                      #if ! JucePlugin_IsSynth
                       .withInput  ("Input",  AudioChannelSet::stereo(), true)
                      #endif
                       .withOutput ("Output", AudioChannelSet::stereo(), true)
                     #endif
                       )
#endif
{
    AudioProcessorValueTreeState parameters(*this, nullptr);
    parameters.state.addChild(makeChordValueTree("Cmaj", {60,64,67}), -1, nullptr);
    parameters.state.addChild(makeChordValueTree("Cmin", {60,63,67}), -1, nullptr);
   
}

WayloChorder3AudioProcessor::~WayloChorder3AudioProcessor()
{
}

It uses a method I added :

ValueTree WayloChorder3AudioProcessor::makeChordValueTree(String name, std::initializer_list<int> notes)
{
    ValueTree result(name);
    for (size_t i=0;i<notes.size();++i)
        result.setProperty("note_"+String(i), *(notes.begin()+i), nullptr);
    return result;
}

I just instantiated that member class directly in the constructor without declaring it in the header file.

I assume I will now be able to see that data structure from inside the process block and use it.

Do I need a pointer to it in the editor class to access it from over there ?

Is that what you folks suggested ?

The tutorial on the AudioProcessorValueTreeState didn’t help much …

Thanks for all your help !
Sean

No. How would that work? The AudioProcessorValueTreeState you added is a local variable in the constructor. For object scoped variables (that is, to be visible in the class’s methods) you need to have them as class members.

Thanks Xeniakos,
I cant figure out how to make that object a class member.
More of a C++ question than a JUCE question.
Perhaps I should head over to stackexchange !
I looked at this
https://www.learncpp.com/cpp-tutorial/103-aggregation/
It’s not clear at all to me how I would initialize the AudioProcessorValueTreeState as a member of
the AudioProcessor class.

How does this look?

WayloChorder3AudioProcessor::WayloChorder3AudioProcessor()

     //: AudioProcessorValueTreeState parameters(*this, nullptr);
#ifndef JucePlugin_PreferredChannelConfigurations
     : AudioProcessor (BusesProperties()
                     #if ! JucePlugin_IsMidiEffect
                      #if ! JucePlugin_IsSynth
                       .withInput  ("Input",  AudioChannelSet::stereo(), true)
                      #endif
                       .withOutput ("Output", AudioChannelSet::stereo(), true)
                     #endif
                       )
     , parameters(*this, nullptr)
#endif

{
    

    for (int i = 0; i < 127 ; i++ ){
        playing[i]= 0;
    }
 
    ValueTree vt("chords");
    parameters.state.addChild(vt, -1,nullptr);
    vt.addChild(makeChordValueTree("Cmaj", {60,64,67}), -1, nullptr);
    vt.addChild(makeChordValueTree("Cmin", {60,63,67}), -1, nullptr);
    
   
}

My PluginProcessor.h has this in it …

private:
    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WayloChorder3AudioProcessor)
    AudioProcessorValueTreeState parameters;

Do I also need to declare a pointer to it in the editor ?

Sean

I have really no idea how I would use this data structure in my plugin. When not using the ValueTree I had a 2D Vector and a position index.
In the tutorial these things are single memory locations. In the tutorial a pointer is pointed to the memory location in the Audio Processor constructor and dereferenced to find the value.

How to do this with a dynamic structure is a mystery to me.
I don’t see how I can dynamically create pointers to all the memory locations in a ValueTree …

It’s also not clear to me how I would access members in the Value tree stored in this way. It’s all very different to the JUCE examples.

For example in the code above how would I access the value of the 2nd note in the 2nd chord ( i.e Cmin 63 ) .

It seems like the way to access the data members of the valuetree is via the string name. Perhaps I can create a queue with the chord names in it and iterate through that to get to the next chord in the sequence or something …

Thanks for your replies.

Sean

It’s kind of beginning to look like it might be better if you just use your initial std::vector<std::vector<int>> to store your chords…The ValueTree doesn’t really necessarily give you any benefits for your use case but complicates things a lot. On the other hand, without the ValueTree you would need to figure out a way to serialize and deserialize the chords anyway…Well, programming is complicated, what else can I say?

Anyway, here’s a code example that gets the chord notes from the ValueTree :

void testValueTreeChords()
{
	ValueTree chords("chords");
	chords.addChild(makeChordValueTree("cmajor",{60, 64, 67}),-1,nullptr);
	chords.addChild(makeChordValueTree("cminor", { 60, 63, 67 }), -1, nullptr);
	for (int i = 0; i < 8; ++i)
	{
		ValueTree chord = chords.getChild(i % chords.getNumChildren()); // cycle through the chords
		for (int j = 0; j < chord.getNumProperties(); ++j)
		{
			std::cout << (int)chord.getProperty("note_" + String(j)) << " ";
		}
		std::cout << "\n";
	}
}

That is, because it is mixing two things:

  • AudioProcessorValueTreeState:
    a class that is aggregated to the AudioProcessor, that handles the state of the plugin, so it can be easily saved and restored. The state is made up by two things: the parameters, that are exposed to the host and many other data, that is not needed in the host, but still should be restored

  • ValueTree:
    Is a tree of data, similar to XML. The AudioProcessorValueTreeState offers a ValueTreeState, that you can use to add your own data. It is not visible to the host, but still it is saved and easy to restore in the getStateInformation().

Most of the examples only talk about the parameters exposed to the host.

Thanks for your replies,
Programming is indeed challenging but fun.

That nice code Xeniakos posted gets the chord values but it’s still hard for me to see how to dynamically create pointers to all the values in the audioprocessor class .

Just thinking aloud can I just somehow employ BOOST and have a button in the GUI that loads an xml file and parses it to populate the 2d std:vector ?
I have the boost file working standalone.

I see other folks have used BOOST

I would assume it’s possible for a callback in the GUI to start a process of overwriting the std:vector member in the audioprocessor class .
Is that idea too naughty ?
The thing is that none of this stuff needs to happen while the audio thread is running . You just need to be able to create chord sequences and load em up and play them with the midi callbacks.
Shouldn’t it be relatively easy for a JUCE plugin to at least run some C++ code pretty much in the background ?
Sean