Table of Contents
Manage your plug-in parameters in an elegant and sophisticated manner. Storing and accessing parameters becomes a breeze and, in particular, makes building effective user interfaces much easier.
Platforms: Windows, Mac OS X
- The AudioProcessorValueTreeState class requires that your compiler supports C++11 lamdas. Recent versions of Xcode and Visual Studio include this support.
Download the demo project for this tutorial here: tutorial_audio_processor_value_tree_state.zip. Unzip the project and open it in your IDE.
If you need help with this step, see Tutorial: Getting started with the Projucer.
You should also know how to build an audio plug-in using JUCE and load this into your preferred audio host (such as a Digital Audio Workstation). See Tutorial: Create a basic Audio/MIDI plugin Part 1: Setting up for an introduction. Ideally, you should also have read Tutorial: The AudioParameter classes, as an introduction to audio processor parameters.
The demo project is loosely based on the GainPlugin project in the
juce/Examples/PlugInSamples directory. This plugin changes the gain of an incoming signal using a single parameter. In addition to this, it also has a phase invert parameter to invert the phase of the incoming signal.
Most of the code in the
GainProcessor.cpp file is the same as that generated by the Projucer when you use the Audio Plug-In project template. For simplicity, we have bundled the processor code into a single
.cpp, file rather than being split across a
.cpp and an
.h file. The editor for the processor is in the
There are several advantages to using the AudioProcessorValueTreeState class for managing your plug-in's parameters:
- The ValueTree class inherently provides undo support.
- ValueTree objects already have support for serialising and deserialising (to XML).
- ValueTree objects can have listeners attached to them. This means that the AudioProcessorValueTreeState class can almost automatically connect to sliders and buttons to keep the state of the UI and the processor up-to-date in a thread safe manner.
To use an AudioProcessorValueTreeState object, you can store one in your processor class:
- attached to only one processor; and
- have the same lifetime as the processor (as they will have dependencies on each other).
Storing the AudioProcessorValueTreeState object in the processor class makes it easier to ensure that you satisfy these requirements.
In this case, we use a
nullptr value for the UndoManager object as we're not going to implement undo support in this tutorial. The
nullptr value indicates that we do not want to use undo support.
In the constructor of the processor you would normally configure your parameters by using the AudioProcessorValueTreeState::createAndAddParameter() function to create and add them (to both the AudioProcessorValueTreeState and the AudioProcessor on your behalf):
The parameter ID should be a unique identifier for this parameter. Think of this like a variable name; it can contain alphanumeric characters and underscores, but no spaces. The parameter name is the name that will be displayed on the screen. The parameter label allows you to specify a suffix (for example "dB" for gain in decibels or "Hz" for frequency parameters).
The range of values that will be represented by the parameter is specified using a NormalisableRange<float> object to set the minimum and maximum values for the parameter. This may also specify a skew-factor to make the transition between minimum and maximum non-linear (see Tutorial: The AudioParameter classes).
The final two
nullptr arguments in this call to the AudioProcessorValueTreeState::createAndAddParameter() function are optional conversion functions to convert between the value and the text that you want to represent that value (and vice versa). Specifying a
nullptr value as either or both of these argument uses the default conversion functions (which simply convert a floating point value to a string and a string back to a floating point value respectively).
The second parameter is configured a little differently:
invertPhaseToText function is defined as follows:
textToInvertPhase function is defined like this:
Both of these arguments to the AudioProcessorValueTreeState::createAndAddParameter() function are actually std::function types. This means we could express them as C++11 lambdas:
This gives you some flexibility on how to express these functions. In either case, these functions provide the plug-in host with the text that it should display for any given value of the parameter. The following screenshot shows the gain plug-in with its phase invert parameter turned on. You can see in the plug-in UI that the toggle button is checked, and in the Logic Pro X UI the parameter value is displayed as "Inverted":
The final step after configuring the parameters is to initialise the ValueTree within the AudioProcessorValueTreeState. This is stored in the
state public member within the AudioProcessorValueTreeState object. This ValueTree object needs an identifier to be valid (which is used as part of the conversion to XML that we will see shortly).
At this point the processor is ready to use the AudioProcessorValueTreeState object.
To help avoid clicks in the signal, we smooth gain changes and changes in signal phase. To do this, we store the previously calculated gain value in our processor :
This is initialised in the
Here we calculate the phase inversion factor (+1 or -1) and multiply this by the gain, ready for the first processing callback. You can see that we use the AudioProcessorValueTreeState::getRawParameterValue() function to get a pointer to the
float value representing the parameter value. We dereference this to get the actual value. The processing is performed in the
Here you can see that if the value hasn't changed, then we simply apply a constant gain. If the value has changed, then we apply the gain ramp, then update the
previousGain value for next time.
In addition to providing routines for processing audio you also need to provide methods for storing and retrieving the entire state of your plug-in into a block of memory. This should include the current values of all of your parameters, but it can also include other state information if needed (for example, if your plug-in deals with files, it might store the file paths).
Restoring the state from XML is almost as straightforward:
Here we include some error checking for safety. We also check that the ValueTree-generated XML is of the correct ValueTree type for our plug-in by inspecting the XML element's tag name.
Take a look at the
GainEditor.h file in the project. You might notice that the declaration of the
GainEditor class is very simple:
You might expect that we would need to inherit from the Slider::Listener class and the Button::Listener class in order to respond to slider and button interaction. But this is again one of the benefits of using the AudioProcessorValueTreeState class. Instead we can use the attachment classes within the AudioProcessorValueTreeState class.
In fact, as the names of these classes can become very long, we have included a
typedef for each of the attachment classes we need:
GainEditor class contains a number of members, including a slider, a toggle button, and some of these attachment objects:
We also need to refer to the AudioProcessorValueTreeState object so we also keep a reference to that.
The constructor for our
GainEditor class sets up these objects:
This is called by our processor's
You may notice that we don't even need to set up the slider's value range. This is done automatically by the SliderAttachment class. All we need to do is pass the attachment constructor the AudioProcessorValueTreeState, the parameter ID and the Slider object that it should attach to.
- We still retain ownership of the Slider object. You should ensure that the attachment class has the same lifetime as the Slider object.
The ButtonAttachment class still requires us to provide the button name. (And the AudioProcessorValueTreeState::ComboBoxAttachment class, which can attach to a ComboBox object, requires us to populate the ComboBox manually.)
- Change the plug-in to support only a 2-channel main bus and add a channel swap parameter that can optionally swap the left and right channels.
- Again using a 2-channel only plug-in, add a balance parameter to balance the left and right channel levels.
In this tutorial we have introduced the AudioProcessorValueTreeState class and shown how it can help you to:
- Manage your plug-in parameters.
- Store and retrieve your plug-in state using XML.
- Connect your plug-in parameters to buttons and sliders in a threadsafe manner.
Generated on Thu Feb 8 2018 15:19:30 for JUCE by 1.8.13