In Part 1, I described my first steps in installing the VST3 SDK and compiling a basic plugin in its own environment. Part2 focuses on the content of a plugin. For this exercise, I am reimplementing the A/B Audio Switch rack extension since the logic is fairly simple while still touching many areas of plugin development.
Setup
This project is available on github: vst-ab-switch. Make sure you checkout the blog-part2-369
tag in order to follow along with this part (or browse the code directly on github).
cmake
directly as explained in Part 1.All the source files are located under src/cpp
Processor and Controller
As described in the documentation, a plugin is made up of 2 main concepts:
- the processor in charge of doing all the actual audio processing (deal with audio samples, midi events, etc…)
- the controller in charge of the UI (vu meters, knobs, mouse events, etc…)
Each concept is represented by a main class, in this case ABSwitchProcessor
and ABSwitchController
which inherit/implement the required interfaces/classes.
Main entry point(s)
When the host/DAW loads the plugin, it needs to get a handle to the classes. This is a bit convoluted and frankly not explained at all in the documentation. So this is what I could gather from looking at the examples and SDK source code.
Class IDs
Each class defines a unique ID (actually each class in the SDK has a unique ID as well). The file ABSwitchCIDs.h
declares the unique ID for the 2 main classes:
The helloworld plugin that comes with the SDK specifies: “you can use GUID creator tools like www.guidgenerator.com” in order to generate those IDS. I generated these using java java.lang.UUID.randomUUID()
(see javadoc), but feel free to use the website suggested.
VST2 entry point
The VST3 SDK contains a helper wrapper to wrap a VST3 plugin into a VST2 plugin. The main entry point for VST2 is defined in ABSwitchVST2.cpp
:
It simply uses the ID of the processor. Note that the VST2 unique ID is set to TBDx
as I have not registered one with Steinberg yet…
VST_EXPORT AEffect* VSTPluginMain(audioMasterCallback audioMaster);
but the VST3 wrapper classes define this method which internally calls
createEffectInstance
VST3 entry point
For VST3, the main entry point is actually the following C-style export function:
Once the host gets access to the factory, it can then call it back to instantiate the processor and controller.
Thankfully the implementation of this method, the factory itself and the class registration is all done via a set of macros (see file ABSwitchVST3.cpp
):
The macro uses the IDs previously created as well as the factory methods (createInstance
) from the processor and controller.
Attaching the controller to the processor
Finally, the controller gets attached to the processor via its ID. See file ABSwitchProcessor.cpp
:
The processor
The processor (defined in ABSwitchProcessor.h
) needs to implement both IComponent
and IAudioProcessor
as per the documentation. The SDK class AudioEffect
implements the basic methods and is used as a starting point for the processor.
Initializing the processor
During the initialization phase of the processor, we define the inputs and outputs of the plugin (see file ABSwitchProcessor.cpp
):
canProcessSampleSize
needs to be overridden (see file ABSwitchProcessor.cpp
).The actual processing
The actual audio processing takes place in this method (see file ABSwitchProcessor.cpp
):
A few points:
- all the info necessary is provided in the
data
parameter data.inputs[0]
refers to the Stereo In pair defined during initializationdata.outputs[0]
refers to the Stereo Out pair defined during initializationin[0]
represents the first channel of the Stereo In pair (the left channel). It is an array of samples of sizedata.numSamples
.in[1]
represents the other channel (right).out[0]
represents the first channel of the Stereo Out pair (the left channel). It is an array of samples of sizedata.numSamples
.out[1]
represents the other channel (right).- the type of a sample (ex:
in[0][0]
is the first sample of the left input pair) is determined bydata.symbolicSampleSize
. Note how the code uses a templated function (defined inABSwitchProcess.h
) so as not to repeat the code for 32 or 64 bits.
The controller
The controller (defined in ABSwitchController.h
) needs to implement IEditController
as per the documentation. The SDK class EditController
implements the basic methods and is used as a starting point for this plugin.
Creating the main view
The controller main method is quite simple and returns an instance of VSTGUI::VST3Editor
:
This class will read the provided xml file to build the GUI (it is empty so it will render a black square…). This class has an editing mode which allows you to create the UI on the fly with an editor (by right clicking in the UI)! The documentation (section VSTGUI 4 / Inline UI Editing for VST3 (WYSIWYG)) explains the various steps to make it happen (seems complicated), but in my experience it is already built-in in the makefiles so it is quite easy:
As long as you compile in Debug
mode the editor is enabled. Simple.
Although you can start your DAW to load the plugin, the SDK comes with a simple editorhost
application (similar to the validator
application (see Part 1)) which lets you load the plugin and edit it:
What we have so far
At this stage we have a fully functioning VST3 and VST2 plugin which simply removes 3dB from the input signal (so we can verify that the plugin gets called properly) and has a blank UI with editing built-in.
Next
Now that we have a fully functioning plugin which does something trivial, let’s build the UI and add the toggle to switch between input A and input B. This is the point of Part 3.
Last edited: 2018/03/17