Joost has asked for the wisdom of the Perl Monks concerning the following question:
Read on at your own peril ;-)
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.
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.
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.
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.
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
+--------------------------------------------------------+ | Main process | | +--------------+ +----------------------+ | | | GUI (Gtk2) | ---update--> | Network with plugins | | | | | <--update--- | | | | +--------------+ | +------------------+ | | | +--------------+ | | audio output | | | | | Timer (Gtk2) | ---- run --> | +------------------+ | | + +--------------+ +----------------------+ | +--------------------------------------------------------+
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:
More questions:
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. :-)
Cheers, Joost.
|
---|