←Back to Redwood Audio DSP home
JUCE 4.x for VST Plugin Development (old JUCE 3.x Tutorial)
Need Help with this Tutorial? (Contact Us)
Was this useful? (Consider a Contribution)
Download Tutorial Source Code including built VST or the Source using AudioParameterX Classes
Summary using AudioParameter Classes
(Show summary of tutorial using ValueTree)
In our tutorial, we stepped through a few illustrative details for a Stereo Width Controller plug-in using the ValueTreeState to save parameters. Here, we aim to summarize the key steps to reference as a more generic task list for new projects. In this version, we will directly use the AudioParameterFloat as an example of the direct parameter classes. The StereoWidthCtrl project source reworked with these classes is available form from the links at the top of the page. You may wish to also refer back to the tutorial sections for Juce 4 and Juce 3.x or see the additional notes for some more practical implementations and additional features.
Note that you would replace references to YourProjectName with your actual project name.
As a reminder - any changes to the PluginEditor.h/.cpp must be in designated areas (marked by comments in the files). Anything outside of these areas will be lost the next time you save the file from the Jucer GUI editor. On the other hand, PluginProcessor.h/.cpp are not as closely auto-managed and any changes can be made as long as you don't remove any of the default functions.
1. Setup (Getting Started with Free Development)
Install development software (Microsoft Visual Studio 2015 Community or Apple Xcode= free!)
Download the VST SDK3.x from Steinberg's development portal and install to c:\SDKs\VST3 SDK
For AU in OSX Download the CoreAudioUtility Classes see OSX Setup tab for details
Download the JUCE library and install to c:\juce
Make a shortcut to Projucer.exe, use it to make new projects, edit the GUI and select/update the JUCE Modules to use in your project.
Make an Environment variable pointing to your VST Plugin Folder (used by VST host software) - For example, VSTPluginFolder = c:\VST (Not needed in OSX)
2. Starting a New VST Plug-In Project
Create your project with Projucer.exe, select type as AudioPlug-In and configure at least the properties for project/plug-in names and Plugin Channel Configuration.
Add any additional exporter targets or configurations (Visual Studio 2015 should be setup by default) and save.
Add Post-Build Command (release mode): copy /Y "$(TargetPath)" "$(VSTPluginFolder)" for quick testing of your plugin (Not needed in OSX).
Use the Projucer.exe "Files" tab to create a replacement PluginEditor.h/.cpp (source files for VST GUIs in JUCE).
Right-click the file list and "Add a new GUI component" - saving over the existing PluginEditor.h in you project's source folder... Click the new PluginEditor.cpp in the file list and select the "Class" tab to configure as follows:
Class Name: YourProjectNameAudioProcessorEditor
Parent Class: public AudioProcessorEditor, public AudioProcessorListener, public Timer
Constructor Parameters: YourProjectNameAudioProcessor& ownerProc
Initializers: AudioProcessorEditor(ownerProc), processor(ownerProc)
Save this new GUI class (File→Save "PluginEditor.cpp").
Edit your project in Visual Studio/XCode and complete the basic code setup:
PluginProcessor.h
Add an inherited class for monitoring parameter changes by modifying the class declaration to be:
class YourProjectNameAudioProcessor
: public AudioProcessor,
public AudioProcessorListener
{...
Add User Parameter Support
//overrides for AudioProcessorListener
void
audioProcessorChanged(AudioProcessor* callingProcessor) override {
MajorParamChange = true; };
void
audioProcessorParameterChanged(AudioProcessor* callingProcessor, int
parameterIndex, float newValue) override
{
/*you could add your own handling to check
for particular parameters, but it is important to not do any significant
handling here */
MajorParamChange = true;
};
//Params as Public Data indexed by enums
enum{FP_Param1 = 0, /*...*/ NUM_FLOAT_PARAM};
AudioParameterFloat* FloatParam[NUM_FLOAT_PARAM];
/*Repeat for other Params types...,*/
private:
//Private Data, helper methods etc.
bool MajorParamChange;
float lastStateData_Float[NUM_FLOAT_PARAM];
PluginProcessor.cpp
Initialize UserParams in the Constructor:
YourProjectNameAudioProcessor::YourProjectNameAudioProcessor()
{
//add each of your parameters
addParameter(FloatParam[FP_Param1] = new AudioParameterFloat("StrID","Name", MinVal, MaxVal, defaultVal));
//Once all parameters are added, you can start listening
addListener(this);
MajorParamChange = true;//start with a deeper update
}
Destruct remove your listener (parameters are unloaded automatically)
YourProjectNameAudioProcessor::~YourProjectNameAudioProcessor()
{
removeListener(this);
}
All of your custom processing implementation will live in the "processBlock" call - but this already has a reasonable default implementation for the quick setup.
Finally, you may wish to implement the ability to save/load state information such as your user parameters. When not using a ValueTree, it is recommended to use the xml method. For an example of manual or semi-automated xml formatted state data, see our source code, notes on string tokenizers or our Juce 3.x tutorial.
PluginEditor.h
Correct the included headers - they should be:
//[Headers]
#include "JuceHeader.h"
#include "PluginProcessor.h"
//[/Headers]
Add timerCallback and AudioProcessorListener overrides
//[UserMethods] -- You can add your own custom methods in this section.
void
audioProcessorChanged(AudioProcessor* callingProcessor) override {
UpdateFromProcessor = true; };
void
audioProcessorParameterChanged(AudioProcessor* callingProcessor, int
parameterIndex, float newValue) override { UpdateFromProcessor = true;
};
void timerCallback()
override;
//[/UserMethods]
Add processor reference and attachments for each parameter that has a GUI element
//[UserVariables] -- You can add your own custom variables in this section.
YourProjectNameAudioProcessor& processor;
bool UpdateFromProcessor;
//[/UserVariables]
PluginEditor.cpp
Set the editor as a listener in the constructor and start your update timer to check for changes
//[Constructor] You can add your own custom stuff here..
processor.addListener(this);
UpdateFromProcessor= true;//start with a deeper update
startTimer(200);//starts timer with interval of 200mS
// any control setup, such as setting range of sliders etc.
//[/Constructor]
Make sure to remove the listener in the destuctor or it will cause access violations
//[Destructor_pre]. You can add your own custom destruction code here..
stopTimer();
processor.removeListener(this);
//[/Destructor_pre]
Implement the timerCallback features as needed to update the state of the controls from the plugin/DSP. Similarly, add asignment when a control is modified which will call the needed notifications for the plugin and it's listeners. For example:
If Param1 is a slider:
//[UserSliderCode_WidthCtrlSld] -- add your slider handling code here..
*processor.FloatParam[YourProjectNameAudioProcessor::FP_Param1]
= (float)sliderThatWasMoved->getValue();
//[/UserSliderCode_WidthCtrlSld]
Implement the timerCallback function:
//[MiscUserCode] You can add your own
definitions of your custom methods or any other code here...
void YourProjectNameAudioProcessorEditor::timerCallback()
{
if(UpdateFromProcessor)
{
//update your controls from the parameters
yourSlider->setValue(*processor.FloatParam[YourProjectNameAudioProcessor::FP_Param1] , dontSendNotification);
}
}
//[/MiscUserCode]
3. Work-Flow
The code above has setup a solid template/framework for your project. In this section, we will recap the JUCE work-flow and how to go about updating it with your custom code and graphical interface.
Adding GUI Components (Getting user parameters to the user)
Updates to the GUI for your plug-in are accomplished by selecting your PluginEditor.cpp in the Projucer.exe. It will play nice back and forth with editing in the IDE (Visual Studio), in that you can have it open in both at any time. However, care should be taken to ensure you are only making unsaved changes in one editor at a time.
Use of the Projucer for editing is relatively straight forward. There is a "Class settings" tab which we have already configured everything but the size settings. You can specify a size and prevent the user from resizing the window if desired.
The UI components are added on the "Subcomponents" tab. Here you can add all the typical controls you might want - buttons, combobox, sliders etc. As you add components you can configure their properties, position/resize them on the window etc. The Graphics tab allows you to change the background color, load images for skins etc. They will be shown with any components you have added as an overlay.
Once it looks the way you want (and you have named components appropriately) you can save your work. And tie in your controls to code. You can return to the here at any time and change properties (such as slider range), layout, etc. Note, most - if not all properties for components can also be modified programmatically in the code.
If you are not using the AudioProcessorStateValueTree class, There are two manual code updates to PluginEditor.cpp needed for each UI component you add with the Projucer (everything else is handled automatically).
In the timerCallback() function we created, add code to initialize the UI component from the AudioProcessor's parameter. This will be called on the timer interval, but you may wish to only update when there has been a change.
The second change depends on the component type, as each component type has its own message handler - such as when Buttons are clicked, or sliders are moved. Within the message handler, the Projucer will have automatically separated cases for each component of each type. So, all you have to do is call your "processor" with an appropriate setParameter() targeted at the AudioParameter type you have registered - reading the value from the UI component. Alternatively, you can call any other methods you created for your AudioProcessor.
Adding Source Code/Libraries or other Build/Project Settings
To maintain the JUCE work-flow (which allows you to pull JUCE updates and add additional build targets to your source code) you must make all updates in Projucer.exe (not the IDE - no matter how tempting the solution explore may appear). When you need to make any of the following changes, it is best to save your files in Visual Studio/XCode and close the project. Then open the project file (*.jucer) in the Projucer.exe. Make your changes, save, and open the resulting build project back in Visual Studio/XCode. The things you can do here are:
Add existing Source Code using the source file list on the "Files" tab of the Projucer. It works about as you would expect the solution explorer to function in the IDE. you can add folders and source - the results will be reflected in the IDE when you load your project. Note that to get back to the regular project settings (VST settings) you have to select the main project (with the orange slice Icon) on the "Config" tab.
If your source files are not in the Source folder, you will need to add additional include directories. This works much the way it would in the IDE. On the "Config" tab of the Projucer are both debug and release settings for each build target. You would simply add the needed include directories to the corresponding "Header Search Paths". Similarly, you can add library directories using the "Extra Library Search Paths" option in the configuration.
Some settings like Preprocessor Definitions can be added both at the configuration level (debug or release) and the project level (applied to both debug and release).
Other build settings you may find useful include your pre/post build commands, optimization settings, and binary names.
For additional details, consider skimming through our tutorial and/or additional notes or look straight from the source... There is good class documentation for all of the JUCE library classes available on their website as well as a few forums which contain a wealth of information.