Monday, July 9, 2012

Writing Lv2 GUI's: Making it look snazzy

So we know how to build a basic "amp" plugin, but there's no GUI. We like shiny gui's right? Of course we do :) This tutorial will explain how to make one.

Gooeys - Or GUI's:

The GUI part of a plugin can be viewed as a totally separate entity to the "DSP" or audio part of the plugin. We will call these the "halves" of our plugin. Theres a little bit of theory to cover before we start looking at code. The next communication section describes how the DSP and GUI parts of the plugin can inform eachother about whats going on.


So the GUI runs in the "GUI" part of the host program, and the DSP runs in the audio part of the host program. We can't communicate directly between the halves due to details that don't really concern us. (threading and thread safety if you must know..)

How it works: The two parts of our plugin are loaded into the two parts of the host program. How we communicate between the two "halves" of our plugin is by using Lv2 ports. These ports send communicate data between the host and the plugin. The host can then use that same data to update the GUI.

Similarly the GUI can write data, which will then be sent to the audio half by the host. That's all there is to it!

Choosing a toolkit

So there's a lot of GUI toolkits out there. The most prominent open-source ones are GTK and QT. This tutorial will explain how to create a custom GTK interface for an Lv2 plugin. My reason for choosing GTK? Its what I know. I also like it. If somebody would contribute code for doing the same in QT, I will gladly link to it from here.

Please note that I'm using the C++ wrapper around GTK (called GTKmm), and I use version 2, not the new stable GTK 3. I know that GTKmm 2 works, and have no reason to update to GTKmm 3.

Side note (skip if you want to get hands-on GUI writing): The loading of GUI plugins into hosts is done using the SUIL library. It is useful to know that it has certain toolkits that it supports, currently X11, GTK2 and QT. If you want to use a different toolkit please read the SUIL library page.

Making a GUI

So what do we actually do to get a GUI for an Lv2? We write a widget. If you're not at all familiar with GUI programming, I will suggest you look at the GTKmm2 Drawing Area tutorial code: Drawing a custom widget using a GTK::DrawingArea. When we have a widget created, we need to pass that widget to the Lv2 host, which will display it for us.


Yes. Where is it? There is a copy of the Lv2 repo on my Github, where I will publish the code for this tutorial. Chances are that these tutorials will be merged into the main Lv2 examples from the Github page.

Communication in code

So we talked about the theory of communication between the halves, but how do we actually do that in code? The Lv2 UI extension that we use to load the GUI gives us two "things" that we need to write port events from the GUI to the host. What these things represent is not really important: we just need to use them. (if you must know: LV2UI_Controller is a pointer to the host's own stuff, and LV2UI_Write_Function is a function pointer that will do the work)

Github Lv2 repo: SinSynth folder at commit while writing this tutorial

Writing port events in the GUI

So we have these two things: LV2UI_Controller and LV2UI_Write_Function, how do we use them? More theory first. Events happen in the Widget, ie: if the user clicks, our Widget gets a function called. That function must decide what to do, and then call the write_function().

What this means in practical terms, is that the Widget class needs access to the LV2UI_Controller and LV2UI_Write_Function.

The way that I solve that, is by adding the LV2UI_Controller and LV2UI_Write_Function to the Widget class. Some people might say this is ugly design: but its simple and works well.

Look at Widget::on_button_press_event to see the code in action.


Testing the plugin

There's a program called Jalv, which is a great little host to test out your Lv2 plugins with: I've used it to develop this tutorial and the GUI for the tutorial. Grab it here:

Running jalv.gtk
gives me this screenshot, clicking it will change the frequency!


That's all!

You've now made a custom GUI, that should work as a Lv2 plugin gui. Sure its not UI artwork yet, but I'll leave you to explore Cairo and its cool graphics functions on your own for a while ;)