Rust VST part 4: creating a GUI
So far, we've been controlling our plugin through the dials and knobs our host provides. However, most commercial audio plugins also include a custom GUI. In this chapter, we will create a simple user interface with egui
and baseview
related crates.
Setting up logging
Before we start, it may be useful to enable logging. VST plugins don't do anything with print statements (e.g. println!()
). Instead, we must log messages to a file.
Because of Rust and fundsp
designs, the risk of introducing a panic within our process block is slim. Yet, different systems and hosts can react unpredictably, and logs are a useful diagnostic. To capture logs, we'll be adding a few more crates:
[dependencies]
"0.4"
"0.11"
"2"
In lib.rs
, implement the init
method of the Plugin
trait to set up file logging:
This code will set up a file using the plugin's name, version, and ID to which to write logs. log_panics
lets us capture any panic messages, too. We find the current home directory using dirs
, and log to a subfolder named tmp
, as simply adding a log adjacent to the plugin binary may result in issues 1. To test that this works, cargo build --release
like usual, and load up the plugin in your host. If you open ~/tmp/synthy-{bunchofnumbers}-log.txt
, it should read something like:
)
Everything looks good (except for some CanDo
s that we haven't handled. But that's fine.) We now have logging, and can check if the plugin has issues loading or is crashing.
Getting a UI to show
We need to bring in a few more crates to get our UI working. Note the specific required rev
, which matches the version used in egui-baseview
.
[]
# ...
= "0.15"
= { = "https://github.com/BillyDM/egui-baseview" }
= { = "https://github.com/RustAudio/baseview.git", = "f6e99e9aa6f5aeb6b721cb05e4d882a51d995909" }
= "0.3"
# ...
Don't worry if you're not 100% sure what's going on in the next few code blocks. This is plumbing to get our UI to show up. Once it's set up, we won't need to touch a lot of this code again.
Create a new file called editor.rs
. Add a mod editor;
line somewhere in lib.rs
to include the new editor
module. Initialize the editor.rs
file with the following code 2. Note that our code will not compile for the next few code blocks.
use crate Parameters;
use *;
use *;
use *;
use Arc;
use ;
// ------------------ //
// 1. Setting UI size //
// ------------------ //
const WINDOW_WIDTH: usize = 256;
const WINDOW_HEIGHT: usize = 256;
// --------------------------------- //
// 2. Creating `PluginEditor` struct //
// --------------------------------- //
// ------------------------ //
// 3. Implementing `Editor` //
// ------------------------ //
// ---------------------------- //
// 4. Wrapper types boilerplate //
// ---------------------------- //
use ;
;
unsafe
;
unsafe
Most of this code is boilerplate, so let's cover the important parts only:
1. Setting UI size
2. Creating PluginEditor
struct
3. Implementing Editor
4. Setting up egui
for use
5. Wrapper types boilerplate
Because our Plugin
must implement Send
, we wrap a few needed pointers in newtypes. We then implement Send
manually on the newtypes.
Right below that block, add the following boilerplate code for platform specific windowing:
// ...
unsafe
unsafe
unsafe
Adjusting our lib.rs
to support the editor
We still need to make a few more changes in our lib.rs
file to enable showing the editor. Add the following field on our Synthy
struct to keep track of our editor:
Modify the Plugin::new
method implemented on Synthy
to return the following struct:
// Plugin::new
// ...
let params: = new;
Self
// ...
Lastly, implement the Plugin::get_editor
method on Synthy
:
// Plugin
// ...
We can finally create the draw_ui
function and begin creating a UI, and get our code to compile.
Using egui
to create a user interface
The hard, boring part is over! Time to create some cool UIs. Create a new function in your editor.rs
file titled draw_ui
with the following parameters. Remember that we called this draw_ui
function earlier in our Editor::open
method.
We use a CentralPanel
as a base canvas for all our UI content. We then want to show two text labels, so we use vertical
to position a few label
s. In our second label, we get the current value of Parameter::Modulation
and display it. If we want to modify our UI, we will revisit the draw_ui
function. Almost everything else we've done so far is boilerplate that won't change much.
egui
built-in widgets
Setting parameters
Footnotes
Keep in mind that many Windows users store plugins in C:\Program Files (x86)\VstPlugins
which has strict permissions. In my testing, if the folder specified was incorrect, my host failed to even list the plugin as available.
It is important to mention the majority of the initialization is adapted from egui_baseview_test_vst2 by DGriffin91, licensed under MIT.