In Part 3, we created a fully functioning VST2/VST3 plugin which was capable of switching between 2 inputs. This post features some notes about the remaining of the implementation to be fully code complete (without a pretty UI).
Setup
This project is available on github: vst-ab-switch. Make sure you checkout the blog-part4-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
Creating a UI only text edit field
The plugin allows you to give a name to each input. This field is purely UI (the processor does not care about it). But it still needs to be saved as part of the state so that the names are preserved. It turned out to be a much more complicated task that anticipated. Adding a “Text Field” view in the editor (class CTextEdit
) and equivalent parameter in the controller like we did for the Audio Switch control actually does not work! A parameter must represent a value (between 0 and 1) not a pure string.
In order to achieve this, here are the steps I ended up following (check the source code for details):
- using the editor, added a “Text Field” view (class
CTextField
) for each input - assign the view to a tag so that we can get a hold of it in the code
- changed the controller to inherit from
VSTGUI::VST3EditorDelegate
in order to override theverifyView
method which is the place where we can get a hold of the views - implemented the
getState
andsetState
method which deal with the UI state only (setComponentState
deals with the processor state!) in order to save/restore the text.
Although it does make sense in hindsight, the views are actually not created when the plugin is loaded. The processor and controller classes are instantiated and initialized (via the various set*State
methods) when the plugin gets loaded. The views are actually only created when the UI portion of the plugin is displayed (which usually happens when double clicking on the loaded plugin in a DAW). And when the UI portion is closed they get destroyed!
This is the reason why I ended up creating a StringTextEdit
class whose lifespan matches the controller (the controller owns them): this class is in charge of keeping the actual text of the field. When a view is created (when the UI is opened) it gets assigned to this class (assignTextEdit
) at which point the view gets initialized with the text value and a couple of listeners are registered:
valueChanged
so that it gets notified if the user modifies the stringviewWillDelete
so that it gets notified when the view is closed (and can deregister the listeners)
As you can see this is a lot more involved than simply adding a parameter!
String
, ConstString
, UTF8String
, char8
, tchar
, STR16
(macro), USTRING
(macro), and much more… Here is an example: CTextEdit.getText()
returns a UTF8String
string, but the method IBStream.writeStringUtf8
expects a const tchar* ptr
which are not compatible…Soften/Cross fade feature
Adding this feature was not too hard: from a UI point of view it is just another On/Off button and is tied to a “standard” parameter. From the processor point of view, it requires to keep track of the previous state of the switch since cross fading happens only when transitioning from one input to the next.
The actual implementation is simply doing a linear interpolation between the 2 inputs for the duration of the frame. I reused the concept of templated code (introduced in the again
sample coming with the SDK) and enhanced it a little bit to avoid dealing with void **
pointers… Templates can be pretty powerful!
Adding a processor controlled parameter
The plugin has an LED light which shows whether there is sound or not going on, or in other words a very simplistic VU meter. The again
sample provided with the SDK has a full VU meter so it was easy to follow the steps which are not too hard.
- added another On/Off button in the editor
- this component gets registered as a parameter in the controller but this time with a flag of
ParameterInfo::kIsReadOnly
since it is only modified by the code not the user - the processor determines if there is sound or not in the frame being rendered and communicates the result to the UI using the
data.outputParameterChanges
concept. Note that the value is communicated only when it changes! - since this is a transient and dynamic/computed value it does not get saved with the state of the plugin
Check the code for more details.
Conclusion
At this stage, the plugin pretty much reimplements all the features of the rack extension (minus CV handling which does not exist in VST). The next step is to make the UI look decent ;)
Last edited: 2018/03/24