Sunday, May 31, 2009

Command of the day: git svn (Using git over a subversion repository)

This may be the end of a long quest for a tool to do 'offline commits on subversion'. That is, being able to work with small commits when you are not able to commit because you are off-line (on-flight or on-train hacking) or you are not a commiter yet (menthorship).

The fact is that, we can benefit of some nice features of git without having to migrate from subversion. I found a very interesting blog entry explaining that. I compiled my own notes for CLAM:

Install the required packages:
sudo apt-get install git-svn

Configure your user:
git config --global user.name 'David García Garzón'
git config --global user.email 'yo@alli.com'

Create a git clone of the svn:
git svn clone http://dgarcia@clam-project.org/clam --stdlayout -r HEAD
Notice that i skiped the 'trunk' part of the url we normally use. '--stdlayout' is to interpret trunk/ branches/ tags/ as such. '-rHEAD' avoids cloning the whole history which is faster.

Work with the repository as with a normal git one, avoiding remote operations (push/pull). Git can fustrate SVN users with a lack of shortcuts (ci, di, up...). You can define them at will:
git config --global alias.ci "commit -a"
git config --global alias.di diff
git config --global alias.stat status
git config --global alias.up "svn rebase"
git config --global alias.rci "svn dcommit"

To update your git repository to server changes:
git svn rebase
If you have uncommited local changes you have to 'stash' them first.

To apply all the local commits to subversion, once rebased:
git svn dcommit


To generate, instead, an incremental set of patches:
git format-patch -M -C -s --inline --stdout remotes/trunk > mypatch.mbox

Note that this is not a regular patch file but an mbox file containing several mails, one per commit, each with the patch attached and some aditional information such as the commit message.

Those commits can be sent to the menthor and applied to git sandbox with svn write access.
git am mypatch.mbox

An then the menthor can commit using previous command 'dcommit'. That easy... or not so.

Some other highlights i would do on git: Nice programmer oriented subcomands such as git bisect, to help bug hunting by doing a dicotomic search on revisions, or git stash, to discard temporarily uncommited changes and recovering them later, or 'git grep', which executes grep ignoring git control files and generated files. Tools like StGit and guilt that allows to edit the order of the local commits considering them a serie of patches similarly to quilt.

Monday, April 6, 2009

VST plugins with Qt user interface

I recently did an spike on what we need to make VST plugins first class CLAM citizens. CLAM allows to visually build JACK and PortAudio based applications with Qt interfaces as well as GUI-less VST and LADSPA plugins. The more flashy feature of VST is user interfaces that are mostly built using VSTGUI. We are using Qt as interface for JACK and Portaudio based apps because we are using the nice features of Qt toolkit to dynamically bind the UI elements and the underlaying processing. Moreover, Qt styling features enables shinning designer-made interfaces. Why not being able to reuse the same interface for VST and JACK? That has been a long standing TODO in CLAM so now is time to address it.

In summary, we fully solved croscompiling vst's from linux and we even started using qt interfaces as vst gui. In that last point, there still is a lot of work to do, but the basic question on whether you can use qt to edit a vst plugin is now out of any doubt.

To make the spike simpler, and in order not to collide with other CLAM developers, currently working on it, i just left apart all the CLAM wrapping part, just addressing vst crosscompiling and Qt with the sdk examples.

Cross compilation was pretty easy. This time I found lot more documentation on mingw and even scons. Just by adding the crossmingw scons tool we are already using for the apps and i managed to get Linux cross-compiled plugins running on Wine.

Adding a regular vstgui user interface is just a matter of compiling vstgui sources along with the example editor that comes in the sdk.

Once there, we should address Qt. VSTGUI is just a full graphical toolkit implementing the 'editor interface' plus a toolkit with some provided widgets and, i guess, a way of automating the binding of controls to processing. So what we need for qt is to implement the AEffEditor interface using the qt toolkit instead. The first problem is about the graphical loop. You have to create a QApplication and calling qApp::processEvents() on the editor's idle method so that qt widgets get responsive. The problem then is that, if you don't provide a QWidget as parent to your interface, it becomes a top level window ignoring the host provided window that still appears as an empty one.

VST host provides such window as a native Windows handle. How do you create a widget on an existing window handle? Months ago trolls redirected me to a commercial solution. Not such a 'solution' for us, a FLOSS project. So i was digging in windows qt source code for a hack when i found the answer just at the public and multiplatform QWidget api. QWidget::create works like a charm. The following simple class is a native window wrapper you can use as a regular QWidget.


class QVstWindow : public QWidget
{
Q_OBJECT
public:
QVstWindow(WId handle) {create(handle);}
QVstWindow::~QVstWindow() {}
};

Still there are some issues: focus handling, reopening, drag&drop... But the basic mouse clicking and resizing works

Once i got that, loading a designer ui file was very easy.

As I said there are still many caveats to solve. A matter of playing with it and refining things. Here is a list of TODO's:

  • Communicate controls from and to the interface
  • Handle focus and other events properly
  • Build a CLAM network wrapper which reensembles more the one for LADSPA
  • Wiki documentation on how to build your own plugin
  • One button plugin generator like the one we have for LADSPA ;-)

I feel that there is more people around other projects interested in using Qt for VST plugins so this is also a call for collaborative research on pending issues, at least the generic ones. Contact us on the CLAM development list or for a broader audience in the Linux Audio Developers list.

Sunday, April 5, 2009

Command of the day: interdiff

Often, when working in code projects, I have to keep some local changes uncommited because I am offline or because i am doing some experiment and i am not sure that it will be successful. In those cases you end up doing a big commit with has very low grain to rollback any regressive change. Sure new distributed VCS allow offline commits but we are working with subversion. So I was glad when i recently discovered how to use interdiff command.

If you plan to make a set of offline changes, on each point you would commit, just take an 'svn diff' on a file. Given those accomulative diffs respect the BASE revision, interdiff knows how to extract the incremental ones from one state to the next one so. When you are back online, you revert all changes, apply and commit each patch separately.

Other interesting commands to manage patches (available in the patchutils package):

  • combinediff: the reverse, joins two patches together
  • recountdiff: very useful when you want to reapply a patch but the state of the code has changed.
  • flipdiff: exchanges the order of two pages
  • filterdiff: remove the changes affecting a set of files from an existing patch
No excuses for coarse commits after on-flight coding to Parma!

Wednesday, November 19, 2008

Refactoring script for CLAM networks

Now that the CLAM NetworkEditor has become such a convenient tool to define audio processing systems, we started to use it massively instead of C++ code to integrate processings. Even thought, we started to find that whenever we change a configuration structure, a port/control name, or even a processing class name, the networks xml had to be edited by hand because they didn't load. That problem led us to avoid such kind of changes, which is not a sane option. 'Embrace change', agile gurus said, and so we did.

We have developed a python tool to support network refactoring. It can be used, both as a python module, or as a command line tool, to batch modify CLAM network files using high level operations such as:


  • Renaming a processing class name
  • Renaming a processing name
  • Renaming processing ports or controls
  • Renaming a processing configuration parameter
  • Removing/adding/reorder configuration parameters
  • Setting configuration parameters values

The script just does XPath navigation and DOM manipulations in order to know which pieces need to be changed. Each high level command is just a couple of lines in python.

We are starting to use it to:

  • Adapt to changes in the C++ code
  • Change network configuration parameters in batch
  • Provide migration scripts for user's networks

About the last point, we plan the next release to provide network migration scripts containing a command set such as:
  
ensureVersion 1.3
renameClass OutControlSender NewClassName
renameConnector AudioMixer inport "Input 0" "New Port Name 0"
renameConfig AudioMixer NumberOfInPorts NInputs
setConfigByType AudioMixer NInputs 14
upgrade 1.3.2


Still some short term TODO's:

  • Include the clam version in the network xml so that the ensureVersion and upgrade commands work.
  • Integrating also Qt Designer UI files in the refactorings
  • Add some other commands as they are needed

Happy CLAM networks refactoring!

Friday, November 14, 2008

Managing Audio Back2Back Tests

So long since last post, and a lot of things to explain (GSoC results, GSoC Mentor Submit, QtDevDays, CLAM network refactoring script, typed controls...). But, let's start explaining some work we did on a Back-to-back system we recently deployed for CLAM and our 3D acoustic project in Barcelona Media.

Back-to-back testing background



You, extreme programmer, might want to have unit tests (white box testing) for every single line of code you write. But, sometimes, this is a hard thing to achieve. For example, canonical test cases for audio processing algorithms that exercise a single piece of code are very hard to find. You might also want to take control of a piece of untested code in order to refactor it without introducing new bugs. In all those cases back-to-back tests are your most powerful tool.

Back-to-back tests (B2B) are black box tests that compare the output of a reference version of an algorithm with the output of an evolved version, given the same set of inputs. When a back-to-back test fails, it means that something changed but normally it doesn't give you any more information than that. If the change was expected to alter the output, you must revalidate the new output again and make it the new reference. But if the alteration was not expected, you should either roll-back the change or fix it.

In back-to-back tests there is no truth to be asserted. You just rely on the fact that the last version was OK. If b2b tests get red because an expected change of behaviour but you don't validate the new results, you will loose any control on posterior changes. So, is very important to keep them green or validating any new correct result. Because of that, B2B tests are very helpful to be used in combination of a continuous integration system such as TestFarm, that can point you to the guilty commit even if further commits have been done.

CLAM's OfflinePlayer is very convenient to do back2back testing of CLAM networks. It runs them off-line specifying some input and outputs wave files. Automate the check by subtracting the output with a reference file and checking the level against a threshold, and you have a back-to-back test.

But still maintaining the outputs up-to-date is hard. So, we have developed a python module named audiob2b.py that makes defining and maintaining b2b test on audio very easy.

Defining a b2b test suite


A test suite is defined by defining back-to-back data path, and a list of test cases, each one defining a name, a command line and a set of outputs to be checked:

#!/usr/bin/env python
# back2back.py
from audiob2b import runBack2BackProgram
data_path="b2b/mysuite"
back2BackTests = [
("testCase1",
"OfflinePlayer mynetwork.clamnetwork b2b/mysuite/inputs/input1.wav -o output1.wav output2.wav"
, [
"output1.wav",
"output2.wav",
]),
# any other testcases there
]
runBack2BackProgram(data_path, sys.argv, back2BackTests)




Notice that this example uses OfflinePlayer but, as you write the full command line, you are not just limited to that. Indeed for 3D acoustics algorithms we are testing other programs that also generate wave files.

Back-to-back work flow


When you run the test suite the first time (./back2back.py without parameters) there is no reference files (expectation) and you will get a red. Current outputs will be copied into the data path like that:
b2b/mysuite/testCase1_output1_result.wav
b2b/mysuite/testCase1_output2_result.wav
...

After validating that the outputs are OK, you can accept a test case by issuing:
$ ./back2back.py --validate testCase1
The files will be moved as:
b2b/mysuite/testCase1_output1_expected.wav
b2b/mysuite/testCase1_output2_expected.wav
...

And the next time you run the tests, they will be green. At this point you can add and commit the 'expected' files on the data repository.

Whenever the output is altered in a sensible way and you get a red, you will have again the '_result' files and also some '_diff' files so that you can easily check the difference. All those files will be cleaned as soon you validate them or you get back the old results.
So the main benefit of that is that the expectation files management is almost automated so it is easier to maintain them in green.

Supporting architecture differences


Often the same algorithm provides slightly different values depending on the architecture you are running on, mostly because different precision (ie. 32 vs. 64 bits) or different implementations of the floating point functions.

Having back-to-back tests changing all the time depending on which platform you run them is not something desirable. The audiob2b module generate platform dependant expectations by validating them with the --arch flag. Platform dependant expectations are used instead the regular ones just if the ones for the current platform are found.

Future


The near future of the tool is just being used. We should extend the set of controlled networks and processing modules in CLAM. So I would like to invite other CLAM contributors to add more back2back's. Place your suite data in 'clam-test-data/b2b/'. We should decide where the suite definitions themselves should be placed. Maybe somewhere in CLAM/test but it won't be fair because dependencies on NetworkEditor and maybe in plugins.

Also a feature that would extend the kind of code we control with back-to-back, would be supporting file types other than wave files such as plain text files, or XML files (some kind smarter than just plain text). Any ideas? Comments?

Tuesday, July 22, 2008

Exterminating signals and slots

I recently posted at the clam devel wiki some advices, conventions, traps and tips on programing with Qt within CLAM addressed to the GSoC students. The most painful trap is the one of gratuitous signals and slots usage. Signals and slots is a very powerful way of designing independent components that comunicate each other with low coupling. Is that powerful that it is very tempting for novices to use them everywhere and this can turn very harmful for you.

If a connected slot doesn't exist, you just get an error console on run-time, checks on slots are that soft, and this is bad. Also the signal slot resolution is more expensive than just a method call. But the main reason to avoid them is that they make the code very hard to follow. When you see a signal 'emit' you have to find where such signal is connected in order to find which are the objects and slots that will be called. Multiply this process by nearly 200 signal emisions that were done at the vmqt library Annotator is using and you'll understand why such components although very smartly designed in structure, they are very hard to maintain and use.

You should emit signals just when you don't know which is the receiver of an event. If you can guess the receiver, there is no use for signals. On the other side, if you cannot access the emitter code, or you don't want to couple it, then there is room for a 'connect'.

A bad smell for noticing that you are over using sigslots is having in a class you are writting a connection like this:

connect(this, signal, knownWidget, slot)

and later, maybe in a different method:
emit signal()

When that known, and later forgotten, object is the only one you are connecting to the slot you might want to just keep a reference to it and just call the slot instead of emitting the signal.
knownWidget->slot()

which is faster, compile time checked and much more traceable.

Given that bad smell locator at hand i decided to do a fast review of vmqt module. vmqt is the successor of the many Visualization Modules we had in CLAM, just annother definitive VM rewrite. I tried to use it several times, but it is very hard to have a global view of what it does because all the program flow is driven by signal connections. No joke: 180 signal emissions and a similar number of 'connect's in 30 classes.

After a first review, 110 signals emisions have been avoided just by having a pointer to the Plot2D at the Renderers (the objects that represent drawing layers of the plot such as the lines, the playhead, the grid...). Having a pointer to it, Renderers can do a direct call to the Plot2D slots (now regular functions) instead of emitting a signal whose only receiver is the plot.

70 'emit's are still wandering around. Some of them communicate the plot with the wplot (a widget containing the plot and other elements such as the sliders, the rulers...) so they are used to syncronize. Those are likely to be removed in a similar way than for renderers.

The rest are specific renderer signals that are connected and propagated by the specific wplot. Those are harder to remove and indeed they are very convenient to keep.

As the extermination goes on, one can better see how to really take profit of the nice vmqt module structure and which aspects can be enhanced.

Wednesday, May 21, 2008

A configuration idiom for Python scripts

For our Python scripts we have used a fair number of idioms and variations such as importing a config.py file with the parameters as module global vars or importing the content of a dictionary. A different one we used lately is very useful and simple:


# This should be your program using the config

import os
class config :
#Here we define the default parameters
paramA = "value A"
paramC = "value C"
class category1 :
paramC="value sub C"

#Here we load new values from files, if they exist
for configFile in [
"/usr/share/myprog/defaultconfig",
"/etc/myprog.conf",
"~/.myprog/config",
"myconfig.conf",
] :
if not os.access(configFile,os.R_OK) : continue
execfile(configFile)

# That's the config params usage. Pretty!
print config.paramA
print config.paramB
print config.paramC
print config.category1.paramC
print "This is a template: %(paramC)s" % config.category1.__dict__


If you write in the config file this:

# myconfig.conf
paramA="new value A"
paramB="new value B"
category1.paramC="new subCvalue"
paramB+=" extension " + paramA

you'll get this

new value A
new value B extension new value A
value C
new subCvalue
This is a template: new subCvalue


The config file is read as you were adding code at the same indentation level you have on the 'execfile' call.

Notice the advantages:

  • Your config file looks like var assignations

  • You can use inner classes to build up categories

  • You can have a list of configuration locations with different precedence

  • You can include almost whatever python code

  • You can do templating with the params getting a dictionary like config.__dict__ or config.category1.__dict__

  • You can put config checking code after loading.


Be carefull on unreliable contexts:

  • Malicious config files can include almost whatever python code

  • Config syntax errors crash the program (i guess that can be solved)

  • Config files may add any new attribute, category, or method you didn't have


But if you are just managing your own utility scripts like us, that idiom is fantastic.