|go ahead... be a heretic|
Insight needed: Perl, C, Gtk2, Audio, Threads, IPC & Performance - Oh My!by Joost (Canon)
|on Jun 01, 2006 at 14:30 UTC||Need Help??|
Joost has asked for the wisdom of the Perl Monks concerning the following question:
This is a long rambling account of a project I'm working on and some of the problems I have. I'm writing this to get my head cleared up and hopefully recieve some suggestions.
Read on at your own peril ;-)
IntroductionSome time ago I was toying with writing my own "modular audio composer" / sequencer. Sort of like Buzz for Linux, only better ;-)
I started with an XS library that wraps the LADSPA API in a nice perl OO API, just so I could play with the many existing LADSPA plugins (these plugins are mostly written in C / C++ and can be loaded into the program at runtime).
Then I added a bunch of perl code to make it all easier to use and manage. I'm still finetuning it, but that part is reasonably stable and available on CPAN.
So far so good, but this project needed a GUI. After two initial attempts in Perl/Tk that got quite messy and didn't work as well as I'd hoped, I abandoned the whole thing for a two years.
Right now, I've got some spare time and I'm rewriting the interface in Gtk2. Once you get used to it, coding in Gtk is pretty easy, you can use nice OO techniques if you want, it's easy to refactor and it looks a whole lot prettier than Tk.
Though I'm still not finished, I'm mostly happy with how the code looks and the progress there.
ProblemsThis program should be able to generate audio streams that can be sequenced and changed on the fly via the GUI (and eventually also via MIDI controls). It works, with some latency, but:
Some GUI updates are a bit slow and cause breakups in the audio stream.
It's possible that I could buffer more output to offset the problem but if the delay between GUI actions and output bcomes too long, the whole thing will feel sluggish and it just won't work very well.
For larger networks the whole thing slows down.
A large network here is one with lots of connected plugins. I still need to do some work to figure out where the bottle neck is, exactly.
If needed I could rewrite some of the perl code in Audio::LADSPA::Network in C/XS, but to get real good speed I might have to break the possibility of writing plugins in Perl. That would be a shame, since I use that option right now for "special" plugins that glue the network to the GUI and add extra functionality. I could also rewrite those plugins in C/XS, which wouldn't be too bad, but I like having the option to quickly make & change prototypes in Perl first and optimize later.
I'm looking for a good audio IO library for perlAt the moment I'm using Audio::Play. Works well and it's very easy to use, but it only supports monophonic output and doesn't allow me to check the output buffer state.
Since I've got a 6 in / 6 out audio card that works very well with ALSA, I'd like to be able to use all of those inputs & outputs, either directly using the ALSA api or via Jack or both.
I might end up writing my own XS code to handle this anyway, but I would appreciate suggestions for existing solutions.
Constraints & design decisions
Currently, the highlevel design is simple: there's one perl process that initializes the Audio::LADSPA::Network code and sets up the GUI. GUI actions modify the network, and network changes are reflected in the GUI via Observer /Observable messages. (Note: due to a bug in Class::Observable, this functionality is not yet available in the CPAN release of Audio::LADSPA).
Every few milliseconds, a Gtk2 timeout callback will ask the network to generate a bit of audio, which may be send to the audio output if the network contains a Play plugin (which uses Audio::Play)
Simplified diagram of the current design
Updates are things like: adding & removing plugins, adjusting parameters etc. The run action is what actually generates the audio stream (every run action generates about 2.5 milliseconds worth of audio).
Now, because all this runs in a single thread, the timer isn't completely reliable (gui updates can delay timer calls). Also, as noted above, it seems that the network code could use a few improvements to make it faster.
What I really want to do is to seperate out the run actions from the rest of the process. I can think of three ways to do that: threading, multi-process or a combination of the two.
Now that perl has some measure of thread support (based on pthreads it seems), I might be able to use that. Basically, put the run() calls in a seperate thread.
This seems ideal, especially if I want to use the Jack audio API, which more or less requires a seperate thread for its callback routine (Jack callbacks look like they could be fairly easily mapped to network runs).
Since I haven't really played with perl's threading for ages, and I haven't done any pthread programming in C, I do have some questions:
Forking / multi-processSince the Network/Plugin API is getting pretty stable, I might be able to run the whole network in a seperate process. This would mean I need some kind of IPC mechanism. I'd prefer not to write a whole IPC library from scratch if I can help it :-)
ConclusionThis is more or less everything I can think up now. I hope it wasn't too incoherent.
I'm looking for suggestions, answers, links, anecdotes, questions, anything. In any case, if you've made it this far, thank you for reading it all. :-)