Monday, February 3, 2014

Effect Testbed GUI

I had to learn some Qt programming for work, so I thought I'd take advantage of this and do some of my own at home over the weekend.  I wanted to write a GUI for a DAW plugin testbed.  I'm aware that there are some more appropriate graphics toolkits, such as JUCE, but Qt is supported on multiple platforms, and seems to offer more flexibility as far as control customization.

FYI: I'm running Ubuntu 12.04 LTS on a HP Pavilion dv4 notebook.  I installed qt4 and qtcreator using apt-get.  I had never used the qtcreator IDE before, but I'm pretty happy with it so far, as I tend to rely a lot on auto-complete and that feature seems to work pretty well.

First, I sketched out my design (fig. 1).  I wanted to model it after an old effects rack that I used about 15 years ago.  I can't remember much about it beyond it having an alphanumeric LED display.  I wanted to have a dial to cycle through effects and presets and possibly to edit effect settings.  I also wanted a number pad so that I could enter specific values.  Different effects will have different parameters, so I wanted some "selector" buttons so that parameters displayed at in the LED window could be selected.

Fig. 1: Sketch of GUI before I started coding.

In Qt, the buttons and dial are easy (just QDial and QPushButton objects).  As of Qt4, stylesheets are supported in the buttons, but not QDial.  The tricky part was going to be the LED display.

For each "cell" of the LED display, I could have mapped characters to bitmaps of display characters (I'm sure a google image search would have turned up some pretty quickly).  But I didn't see the fun in that.  I decided to define my own characters.  I used pinta image editor to draw the characters pixel by pixel (fig. 2) (this actually only took about an hour or so).  Each character fits on a 5x7  cell.  To make it easier to map strings to these characters, I followed ASCII standard for the alpha-numeric characters (0x30 [space] to 0x7e [tilde]).  And I filled the remaining characters with random stuff (superscript digits, arrows, random patterns) to get a total of 128 characters.

Fig. 2: Layout of pixels for each type of text cell character.

Next, I came up with an encoding scheme to save the pixel mapping for each character to a 35-bit number.  Since I needed to use long integers anyway, I decided to encode one column of pixels in each byte.  "lit" pixels are represented by binary 1's.  I encoded each character into a 128-element lookup table (this actually took a couple of hours), then wrote a function that takes a character number, pixel row and column as arguments and returns a boolean 'true' if that pixel should be "lit" or not.

In Qt, I defined an LEDText class to represent a single character cell in an LED display.  The constructor requires an x-y offset relative to the parent container (an LEDDisplay object).  The character is constructed by first drawing a rectangle with a background color of dark blue.  Then a 5x7 array of squares is drawn on top.  If the pixel is "off", then the square is drawn with a color just slightly brighter than the background, to simulate the effect you get with you actually look at one of these things.  If the pixel is "on", then its color is set to white and a larger square is drawn on top.  Its color is set to blue and made semi-transparent.  This gives the pixel itself a blue color and gives the appearance of "glowing".  The LEDDisplay class creates an array of LEDText cells and sets an additional border to keep them centered.

In Qt creator, laying out controls is fairly straighforward if you use the UI designer tool.  The drawback to using this tool is that it is highly dependent on the Qt version and takes away some of the freedom you have in controlling GUI behavior.  However, if you want to throw together something quickly then it is very useful.

Fig. 3: Screenshot of finished GUI.
With some more work, I was able to forward key press events to the GUI.  For example, pressing the '5' key automatically fired the same function as when you click the '5' button on the GUI.  I also declared six "dummy" effects.  The GUI main class decides the number that is assigned to each one, but when you press one of the "opt#" keys the GUI enters "selection" mode and forwards the control functions to virtual functions defined by the effect classes.

Even without taking advantage of the "signals/slots" feature of Qt, the GUI works well as far as updating components as key and mouse events are fired.  However, as I develop this further I may try to lean on that a little more.

No comments:

Post a Comment