Monday, February 24, 2014

Linux VST Plug-in for Ardour

I now have a functioning template for a working VST effect in Ardour. I'm still using 2.x format, since my version of Ardour (v3.0) doesn't recognize VST v3.0 (at least not on a Linux platform). The video below is just a screen capture of the plug in in action. The effect algorithm is just a signal-finding method I used at the cosmic ray experiment I worked at during grad school called the Generalized Second Difference (Mariscotti 1995).

Despite the controls going from zero to one, the parameter 'z' really ranges from 0 to 30 and 'm' goes from 1 to 17. The filter kernel will have 3 + 2*m*z elements, so this works well to test the limits of the DSP (it actually performed way better than I expected). 'm' determines the "width" of the filter while 'z' affects the "smoothness".

Some notes:

I compiled everything but the examples in VST3 SDK into a static library. I copy vstplugmain.cpp into whatever project I'm working at and add -I${VST_HOME} to my include flags for compiling. I add the static library directly so the compiler can pull whatever it needs from it when creating the plugin .so file. With that "template", all I really need to use for reference when writing the code for the plugin are the files in public.sdk/sources/vst2.x.

Still can't get my own GUI working. When I try to use Qt, Ardour seg-faults before the recent projects window pops up. When I try skeleton code Ardour crashes when I try to open the controls for the plugin. I presume the latter case occurs because I'm not handling signals from the Host program. But I don't understand what's going on in the first case. I can't run a proper "debugging" environment because I didn't build debugging versions of any of the dependent packages for Ardour. Not saying I can't, just looking at a few day's work to get that going and I feel like the solution probably isn't all that complicated.

Friday, February 7, 2014

Ardour and Linux VST

I have Ardour 3 compiled and working. I don't have a realtime kernel, so I can't run Jack in realtime mode and there is some significant latency (~1/4-1/2 second, didn't time exactly). I looked up the interwebs to see what was involved in getting a realtime kernel set up and found this gem:

http://askubuntu.com/questions/72964/how-can-i-install-a-realtime-kernel 

I followed the instructions exactly on a VM running the same OS as my notebook and it worked without a hitch. I decided it may be better to use the same kernel version (3.2.0). The closest match with a corresponding RT patch was 3.2.52. It took about two hours to compile everything but so far it's working perfectly. Just one hitch, not sure it's totally related to the kernel update: Firefox and emacs look-and-feel reverted to some ugly state and Firefox stopped recognizing the printer. I did some ridiculous things like reinstalling gtk3 and cups. Eventually, I just rebooted and everything was back to normal (but I have and RT kernel now).

I tried running Ardour with Jack in RT mode, but it wouldn't open my project. Unsure if I needed to rebuild everything (looking back, I don't think I had to) I felt it better to do that anyway, since originally I was adding packages kind of willy-nilly as I was building the dependencies. The new build of Ardour still didn't work. After scanning the interwebs for people with similar problems, I found a solution that worked for me: Simply add my username to the audio group with sudo usermod -a -G audio ${username}. The latency is much more manageable now (~30-40ms).  Not good for real production stuff, but good enough as a sandbox for plugin development.

I tried compiling the examples that come with the VST3 SDK. The following define flags will get most to compile:

-DPTHREADS -DBYTEORDER=kLittleEndian -DFORMAT_INT64A="\"%ld\"" -DFORMAT_UINT64A="\"%lu\"" -DENDLINE_A="\"\\n\""
You will also need to define things like -DRELEASE/-DDEBUG/-DDEVELOPMENT and -DPLATFORM_64.

Even with all these, I still had to hack my way through to get some of the code to compile. I won't reproduce the code here but if you've been programming C/C++ for a while you should find the required mods to be pretty straightforward.

I skipped trying to compile the examples, especially because a bunch are in a subdirectory with a space in the name (pain in the butt!). I copied one of the examples into QtCreator, tinkered with the configuration a bit and got it to build a nice SO.

When Linux VST's are enabled in Ardour, it searches the standard shared object library path for a folder named lxvst (I think status quo is to put it in /usr/local/lib/lxvst). I opened the plugin manager by right clicking on the mixer pane. There wasn't anything obviously wrong with the SO itself, so I searched through the Ardour source code to find out where the load was failing. Turns out Ardour 3 isn't equipped to read Linux VSTs compiled according to VST version 3. It wasn't clear to me how to mod Ardour to get this to work. I also haven't been able to figure out how to get the example code to build in 2.x format.

Instead, I created a new dummy plugin called JustVisiting. It took a little bit of tinkering, but eventually I got Ardour to recognize it. Here's the skeleton code:

justvisiting.h:

#include <stdio>
#include "public.sdk/source/vst2.x/audioeffectx.h"

class JustVisiting : public AudioEffectX {
public:
    JustVisiting(audioMasterCallback iAudioMaster, 
        VstInt32 iNumPrograms, VstInt32 iNumParams);
    void processReplacing(float **inputs, float **outputs, VstInt32 sampleFrames);
    bool getVendorString(char *text);
    VstInt32 getVendorVersion();
    VstInt32 canDo(char *text);
    VstPlugCategory getPlugCategory();
};

justvisiting.cpp:

#include "justvisiting.h"

AudioEffect *createEffectInstance(audioMasterCallback audioMaster) {
    JustVisiting *effect = new JustVisiting(audioMaster,1,1);
    return effect;
}

JustVisiting::JustVisiting(audioMasterCallback iAudioMaster, 
    VstInt32 iNumPrograms, VstInt32 iNumParams)
    : AudioEffectX(iAudioMaster,iNumPrograms,iNumParams){
    // nothing to allocate
    this->setNumInputs(2);
    this->setNumOutputs(2);
    this->setBlockSize(1);
}

void JustVisiting::processReplacing(float **inputs, float **outputs, VstInt32 sampleFrames) {
    // TODO: do something
}

bool JustVisiting::getVendorString(char *text) {
    sprintf(text,"CamelNote");
}

VstInt32 JustVisiting::getVendorVersion() {
    return 0;
}

VstInt32 JustVisiting::canDo(char *text) {
    if ( strcasecmp(text,"sendVstEvents") )
        return -1;
    else if ( strcasecmp(text,"sendVstMidiEvent") )
        return -1;
    else if ( strcasecmp(text,"receiveVstEvents") )
        return -1;
    else if ( strcasecmp(text,"receiveVstMidiEvent") )
        return -1;
    else if ( strcasecmp(text,"receiveVstTimeInfo") )
        return 0;
    else if ( strcasecmp(text,"offline") )
        return -1;
    else if ( strcasecmp(text,"midiProgramNames") )
        return -1;
    else if ( strcasecmp(text,"bypass") )
        return -1;
    return 0;
}

VstPlugCategory JustVisiting::getPlugCategory() {
    return kPlugCategEffect;
}
Be sure to build this in with vstplugmain.cpp from the VST SDK so that the VSTPluginMain symbol gets compiled in. Ardour uses this as the entry point for the Linux VST (It actually looks for 'main' first, and if it can't find it, then looks for VSTPluginMain. When if finds that one, it will complain that the version is too new and may not be compatible with Ardour). At the bare minimum, you'll need to define processReplacing, but there are a number of other member functions you should override for safety. I haven't gone through them all yet.

Monday, February 3, 2014

Building Ardour from Source

I want to develop a VST plugin.  Problem is I'm working on a Linux platform.  Audacity is apparently popular, but it only supports VST plugins on its Windows and Mac editions.  I recently discovered Ardour, which is really nice.  And, because I don't seem to like doing anything the easy way, decided to build it from the source.  It has eleventy-billion dependencies which are listed on their download page.  I grabbed the sources from all of them and built them as well.

I'm not including all of the details, but I will say that in most cases, I was able to cd into the package directory, run configure-make-make install without problem.  Some of these use 'waf', which until the other day I was completely unaware of.  In these cases, you run waf configure-waf-waf install instead, so it's very similar (and you get colorful output text).

I'm not sure if there was a "proper" order for building the packages.  I just kind of winged it and I got Ardour working.  I started by installing jack with apt-get jackd2 jackd2-dev.  I also kept everything segregated by installing the dependencies in /opt/ardour-deps.  Of course, this requires making sure LD_LIBRARY_PATH=/opt/ardour-deps/lib before running ardour, but no biggie.  I just aliased the executable with 'env LD_LIBRARY_PATH=/opt/ardour-deps/lib /usr/local/bin/ardour3'.

Here's a brief transcript of my notes while putting everything together:
  • libsndfile - got source, no probs building
  • gnomecanvas - got source, required intltool and libgail to build
  • libsigc++ - source from ardour site, no probs building
  • libcairo - source from ardour site, no probs building
  • libgtk+ - source from ardour site, no probs building
  • lrdf - source from ardour site, no probs building
  • boost 1.49.0 - got source, no probs building
  • fftw 3.3.1 - provided link broken, only found 3.3.3, apparently incompatible with other dependencies.   ultimately had to get with apt-get fftw3 fftw3-dev
  • glibmm 2.32.0 - got source, no probs building
  • cairomm 1.10.0 - got source, no probs building
  • pangomm 2.28.4 - got source, no probs building
  • atkmm 2.22.6 - got source, no probs building
  • gtkmm 2.24.2 - got source, no probs building
  • libart_lgpl 2.3.21 - got source, no probs building
  • libgnomecanvasmm 2.26.0 - got source, no probs building
  • liblo 0.26 - got source, no probs building
  • raptor2 2.0.6 - got source, no probs building
  • rasqal 0.9.28 - got source, no probs building
  • redland 1.0.15 - got source, no probs building
  • libogg 1.3.0 - got source, no probs building
  • flac 1.2.1 - got source, couldn't make examples with fftw 3.3.3, had to use alternate
  • libvorbis 1.3.2 - got source, no probs building
  • libsamplerate 0.1.8 - requires libsndfile
  • aubio 0.3.2 - requires alternate fftw
  • rdflib 3.2.0 - link broken, had to find elsewhere.  requires python_setuptools to install.
  • lv2 1.2.0 - 'waf' install, no probs building
  • serd 0.18.2 - 'waf' install, no probs building
  • sord 0.12.0 - 'waf' install, no probs building
  • sratom 0.4.2 - 'waf' install, no probs building
  • lilv 0.16.0 - 'waf' install, gave some message about python bindings disabled, haven't come across any consequences yet.
  • suil 0.6.10 'waf' install, no probs building
  • curl 7.25.0 - found source, long time building.  seemed to hang at 'check recvfrom', but waited ~5 minutes and it worked.
  • taglib 1.9.1- found source, requires cmake
  • vamp 2.5 - had to do some hunting from link, eventually found newer version.  built & installed and this apparently works.
  • rubberband 1.8.1 - can't confirm that I grabbed right version.  required alternate fftw3 lib.
I skipped the freetype "patch", but I think the text renders just fine on my platform.

Here's more detail on building some of the packages:
  • Building boost
    • ./bootstrap.sh --prefix=/opt/ardour-deps
    • long wait w/o messages after ./b2 install
      • ~2-5 minutes with no output, then 10+ minutes to compile
    • accidentally followed steps for alternative build.  No matter bc files put in /tmp directory.
  • Want to do cairo next, received complaints about no libpng
  • libsig++ - easy compile with config/make/make install
  • back to cairo
    • apt-get install libpng12-dev
    • apt-get install libpixman1-dev
    • apt-get install libfontconfig1 libfontconfig1-dev libfreetype6 libfreetype6-dev
  • building glibmm
    • needs glib-2.0
    • apt-get install libglib2.0-0 libglib2.0-dev
  • building pangomm
    • note: accidentally had build cairo instead of cairomm
    • apt-get install libcairo2-dev
    • apt-get install libpango1.0-dev
  • libatkmm
    • required apt-get install libatk1.0-0 libatk1.0-dev
  • aubio had issues, wouldn't accept my install of fftw3.  had to run apt-get install libfftw3-dev and it worked.
  • building ardour (final step)
    • if you get "unexpected version of vamp header" errors, then in waf configure: also had to include ALL vamp subdirs to "--also-includes"
    • had to add @aubio - rtprio 99 line to /etc/security/ldconfig (NOTE: this didn't work)
    • had to configure jack to not run in rtmode.
 This literally took me all day last Saturday, but Ardour appears to run fine.  I haven't experienced any crashes yet (although I haven't really pushed my luck).  I set user memory lock limit of 2048000 kB and max files of 16384 (no matter what, program complains of limits, but I think these are fair, since I'm not planning on doing anything fancy).

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.