←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 of Tutorial using ValueTree

(show summary using AudioParameter Classes)

 

In our tutorial, we stepped through a few illustrative details for a Stereo Width Controller plug-in.  Here, we aim to summarize the key steps to reference as a more generic task list for new projects.  For details, refer back to the tutorial sections 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 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 AudioProcessorValueTreeState::Listener

{...

 

Add User Parameter Support

//Custom Methods, Params and Public Data

#define PARAM_SETNAME "ParamSetName"

#define PARAM_MASTERBYPASS "Bypass"

 /*OtherParams...,*/

AudioProcessorValueTreeState& getState() { return *mState; };
void parameterChanged(const String& parameterID, float newValue)

{ MajorParamChange = true; }; 

private:

//Private Data, helper methods etc.

bool MajorParamChange;
ScopedPointer<AudioProcessorValueTreeState> mState;
ScopedPointer<UndoManager> mUndoManager; 

 

 

PluginProcessor.cpp

Initialize UserParams in the Constructor:

YourProjectNameAudioProcessor::YourProjectNameAudioProcessor()

{

mUndoManager = new UndoManager();
mState = new AudioProcessorValueTreeState(*this, mUndoManager);
 

//add each of your parameters

mState->createAndAddParameter(PARAM_BYPASS, "Bypass", "", NormalisableRange<float>(0, 1, 1), 0, GetBypassStr, nullptr);

 

//Once all parameters are added, it is safe to create the actual ValueTree

mState->state = ValueTree(PARAM_SETNAME);

 

//add any controls you want to trigger a deeper update timed with the call to process

mState->addParameterListener(PARAM_MAJOR , this);

MajorParamChange = true;//start with a deeper update

}

 

Destruct the undomanager and valuetreestate.

YourProjectNameAudioProcessor::~YourProjectNameAudioProcessor()

{

mState = nullptr;
mUndoManager = nullptr;);

}

 

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.  Our recommended way of doing this is with the new StateValueTree export functions.  For an example of manual or semi-automated xml formatted state data, see our Juce 3.x tutorial.  Please refer back to either for sample code. 

 

PluginEditor.h

Correct the included headers - they should be:

//[Headers]

#include "JuceHeader.h"

#include "PluginProcessor.h"

//[/Headers]

 

Add timerCallback override

//[UserMethods] -- You can add your own custom methods in this section.

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;

ScopedPointer<AudioProcessorValueTreeState::ButtonAttachment> mBypassAttachment;

//... as many attachements as needed using the type Button, Slider or ComboBox Radio etc.

//[/UserVariables] 

 

PluginEditor.cpp

Create the attachments in the constructor

//[Constructor] You can add your own custom stuff here..

mBypassAttachment = new AudioProcessorValueTreeState::ButtonAttachment(processor.getState(), PARAM_BYPASS, *BypassBtn);

// etc. repeat for each attachment / parameter

//[/Constructor] 

Destroy the attachments in the destructor_pre

//[Destructor_pre]. You can add your own custom destruction code here..

mBypassAttachment = nullptr;

// etc. repeat for each attachment / parameter

//[/Destructor_pre] 

 

 

Implement the timerCallback features as needed (all Timer/timerCallback functions are now optional!

Initialize the timer in the Constructor:

//[Constructor] You can add your own custom stuff here..

startTimer(200);//starts timer with interval of 200mS

//[/Constructor]

 

Implement the timerCallback function:

//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...

void YourProjectNameAudioProcessorEditor::timerCallback()

{

//if you want any display updates with a refresh timer

}

//[/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.