CamillaDSP - Cross-platform IIR and FIR engine for crossovers, room correction etc

I would like to announce CamillaDSP, a general purpose tool for routing and filtering sound. It can be used for example for building crossovers for active speakers, or for performing room correction.

You can find the source code here: GitHub - HEnquist/camilladsp

The documentation for all published versions can be found here: CamillaDSP

There is a second repository for configuration help here: GitHub - HEnquist/camilladsp-config: Help for setting up CamillaDSP, example config files etc

Quick summary
  • For Linux, macOS and Windows
  • Written in Rust
  • IIR filters (BiQuad)
  • FIR filters (Convolution via FFT)
  • Built-in sample rate converter
  • Filters can be chained freely
  • Flexible routing
  • Alsa, PulseAudio, Wasapi, CoreAudio, File and stdio input/output
  • Simple YAML configuration
  • All calculations done with 64-bit floats

I have been using BruteFIR for crossovers for quite some time, but there were two main things I wanted to improve upon. Firstly BruteFIR only supports FIR filters, and I wanted the ability to also use BiQuad without having to make an overly complicated setup. Secondly when BruteFIR encounters a buffer underrun it always exits with a "broken pipe" error instead of just trying again. This can get somewhat annoying. I also thought that the BruteFIR configuration is unnessecarily complicated.
I have also been using the excellent tool EqualizerAPO that does all I want, but it's for Windows only.

When using Alsa for both input and output, CamillaDSP can work almost as a drop-in replacement for BruteFIR. I run a 2048 tap stereo 2-way crossover at 44.1kHz, and this consumes less than 2% of a single cpu core on my HTPC (dual core Intel Skylake).

To help with configuration CamillaDSP checks the configuration and tries to give helpful error messages when there is a problem. In addition there is a Python script to visualize the whole pipeline from a config file.

Sample output:
pipeline.png


To try it, download a pre-built binary for your system from the "Releases" page:. Click "Assets" to view the available files.

Instead of using a pre-built binary it can also be built from source. The "Cargo" tool makes this very easy as it will download and compile all dependencies automatically. See more instructions in the README.
 
Last edited:
Wow, very impressive! That really looks like a nice piece of work.

Couple of questions:
How are you implementing the FIR convolution (e.g. using what library, it was handcoded, etc.).?
How do you account for different latencies of different FIR filters, and if you have many filter blocks on one path from input to output versus another path?
 
  • Like
Reactions: Jcris
Thanks!
The convolution is done with the overlap-add method and it's using RustFFT for fft/ifft: GitHub - awelkie/RustFFT: A mixed-radix FFT library written in pure Rust

The processing is done on chunks of audio data. The capture device captures let's say 1024 frames, and then passes this chunk to the processing thread. This then applies all filtering, mixing etc before passing it on to the playback device. This means that there will always be a delay of 1024 samples, plus a little bit more for the time the processing takes. In this case the fir filter can be up to 1024 taps but no more. For longer filters a larger chunk size is needed.

If there are different FIR filters for different channels, they need to be matched so they have the same latency. It would also be ok to add a simple delay to a channel to make up for a shorter FIR latency. I guess that in most cases a crossover will be designed in matched pairs, so there is no relative delay between the high- and lowpass filters. But if you then add a FIR room correction to only the LF channels, the HF will be too early and needs a delay. This isn't done automatically.
 
Got it running on a headless Debian / Mopidy / Mopidy-Spotify & File player, easy to configure and handle! Doing convolution with rephase-generated filters.

Great, thank you for this nice piece of software!

Jo
Thanks for the feedback!

Just out of curiosity, how long filters do you have, what samplerate, and many channels are you outputting? And are you using the Alsa backend?
 
I have just updated the master branch. The new version is 0.0.3.

New features:

  • File input/output backend (also supports stdin/stdout)
  • Updated readme to include the Alsa and Pulse packages needed to build.

Bugfixes:

  • It's now possible to have a pipeline without any filters or mixers. (For example, stereo in - stereo out doesn't need a mixer but it wouldn't run if the "mixers" section was empty).

Other changes:

  • Added CI Tests for BiQuad and Convolution filters
  • Automatic testing with Github Actions
 
Last edited:
This seems brilliant 🙂

Could you recommend a compact Linux distro to build and run this on?
I just moved house and am now streaming the music from the main file server to a MacBook Air and listening to it via an older simple sub/sat system, when I used to have one of my work computers (in windows 10) handle x-overs and media ingestion and feed it via an USB soundcard to the power amps of the main system.

As I have a spare low-end NUC just lounging ATM, I though this could be used to feed the music system. I'd like to configure it as a very simple DLNA endpoint - no server or media database - so the video part of the system (also almost 100% streaming based) can share the main sound. I'd output the "x-overed" and room-corrected sound either over HDMI or the old USB soundcared.
 
Could you recommend a compact Linux distro to build and run this on?
I just moved house and am now streaming the music from the main file server to a MacBook Air and listening to it via an older simple sub/sat system, when I used to have one of my work computers (in windows 10) handle x-overs and media ingestion and feed it via an USB soundcard to the power amps of the main system.


For a NUC I'd suggest Linux Mint, IMHO the most refined linux distro for x86 based systems there is.
 
@HenrikEnquist I'm the author of Pulseaudio crossover rack. Currently I'm using LADSPA plugins to insert them into the pulseaudio chain. This has several drawbacks including
a) left/right channel plugin parameters can not be controlled independently

b) LADSPA plugins cannot accept parameters other than numbers which makes loading FIR filter responses somewhat ugly
c) pulseaudio does not permit to mix/split/combine channels in an arbitrary manner like you woud need for say a 2.1 setup, not to speak of any kind of multichannel setup with say a pair of three way front speakers, 2 subs and a bunch of two way satellites.


Can your software do mixing/spliting/routing for all input channels to all output channels independently? Can you change parameters on the fly or do you have to restart it? Can you route audio output to channels on different pulseaudio sinks to (an example only) route highs to soundcard A and lows to soundcard B?


From all the questions asked you probably already deducted that I'm searching for a more flexible & powerful filtering and routing engine to do the DSP stuff in the background...
 
This seems brilliant 🙂

Could you recommend a compact Linux distro to build and run this on?
I just moved house and am now streaming the music from the main file server to a MacBook Air and listening to it via an older simple sub/sat system, when I used to have one of my work computers (in windows 10) handle x-overs and media ingestion and feed it via an USB soundcard to the power amps of the main system.

As I have a spare low-end NUC just lounging ATM, I though this could be used to feed the music system. I'd like to configure it as a very simple DLNA endpoint - no server or media database - so the video part of the system (also almost 100% streaming based) can share the main sound. I'd output the "x-overed" and room-corrected sound either over HDMI or the old USB soundcared.
A NUC should work really well for this. I use Fedora for my media computer and that works great. I have not tried Mint so unfortunately I can't compare.
 
@HenrikEnquist I'm the author of Pulseaudio crossover rack. Currently I'm using LADSPA plugins to insert them into the pulseaudio chain. This has several drawbacks including
a) left/right channel plugin parameters can not be controlled independently

b) LADSPA plugins cannot accept parameters other than numbers which makes loading FIR filter responses somewhat ugly
c) pulseaudio does not permit to mix/split/combine channels in an arbitrary manner like you woud need for say a 2.1 setup, not to speak of any kind of multichannel setup with say a pair of three way front speakers, 2 subs and a bunch of two way satellites.


Can your software do mixing/spliting/routing for all input channels to all output channels independently? Can you change parameters on the fly or do you have to restart it? Can you route audio output to channels on different pulseaudio sinks to (an example only) route highs to soundcard A and lows to soundcard B?


From all the questions asked you probably already deducted that I'm searching for a more flexible & powerful filtering and routing engine to do the DSP stuff in the background...
I have also looked at LADSPA plugins and it seemed quite difficult to implement FIR filtering that way. Not impossible, but definitely a challenge. The limitations on passing parameters will definitely lead to some ulgy hacks. That's part of the reason I chose to do something else.

> Can your software do mixing/spliting/routing for all input channels to all output channels independently?
Yes!

> Can you change parameters on the fly or do you have to restart it?
You have to restart. For changing the config on the fly I would still have to rebuild the pipeline, so it would probably take as long as just restarting. But it starts in a fraction of a second, so that may not be a big problem.

> Can you route audio output to channels on different pulseaudio sinks to (an example only) route highs to soundcard A and lows to soundcard B?
No, but you can maybe join the two soundcards with alsa. Read here: Alsa Opensrc Org - Independent ALSA and linux audio support site
Synchronization might be a problem. I would guess that the relative delay between the two can be a little different every time playback starts, and unless they share the clock source they will slowly drift apart while playing. Not so nice for a crossover..


If you have any ideas for improvements or new features, please let me know!
 
No, but you can maybe join the two soundcards with alsa. Read here: Alsa Opensrc Org - Independent ALSA and linux audio support site
Synchronization might be a problem. I would guess that the relative delay between the two can be a little different every time playback starts, and unless they share the clock source they will slowly drift apart while playing. Not so nice for a crossover..

If you have any ideas for improvements or new features, please let me know!


OK, I know the implications of using two soundcards. Personally I wouldn't go that route anyway but users seem to do it already. So I wouldn't want to take away that functionality from them without reason.


Here's an imporvement you might want to consider: add an interface to camilladsp to set parameters of filters in real time (change biquad coeffs, fir responses etc). How (http, tcp, sockets, ...) I couldn't care less, just something to update params for a running process would be superb. If you do this I'm pretty sure that I might give it a try and replace the whole LADPSA insanity in version 2.0...
 
OK, I know the implications of using two soundcards. Personally I wouldn't go that route anyway but users seem to do it already. So I wouldn't want to take away that functionality from them without reason.


Here's an imporvement you might want to consider: add an interface to camilladsp to set parameters of filters in real time (change biquad coeffs, fir responses etc). How (http, tcp, sockets, ...) I couldn't care less, just something to update params for a running process would be superb. If you do this I'm pretty sure that I might give it a try and replace the whole LADPSA insanity in version 2.0...


Adding a tcp socket or such would require a lot of changes but maybe it's not needed... It would be quite easy to let it watch for changes in the config file, and just reload it when needed. If only filters and mixers changed, I can then build the new pipeline with all filters while the processing is still running, and then swap once it's ready. There would only be a small interruption on the order of one buffer length.
What do you think?
 
One thing I definitely don't want to loose is instant parameter changing without any interruption of the sound. I have sliders on the filter configuration dialogs which of course can spit out lots of updates per second. Currently I have a memory-mapped-file interface in my LADSPA plugins to achieve seamless, uninterrupted updates of parameters.

Please don't feel bothered by me, just stating my needs here. If it can't be done easily in your design then I won't (and can't) beat you to it 😉


PS: it's just a curious coincidence that I thought about moving the DSP processing to a monolythic process instead of LADSPA plugins since a few weeks. It just offers so much greater flexiblity. So then I stumbled upon your project and it immediately seemed like a good candidate to just plug in there. And FIR filters are on my todo list also, so your software would tick that box "for free" 🙂
 
Last edited: