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

Thanks for the updates. I'll have to check out the 0.4 beta and the gui which I've never played with. (Silly me I didn't realize you had created a web gui already.)

A format conversion question for you. Using alsa and camilladsp if I specify in the config that the loopback interface is S16LE and the playback interface is S32LE, what will camilla do in converting from 16 to 32 bit?

Will it apply <<16 gain? Or no gain? I have a 32 bit DAC (which only accepts 32 bit packets via USB so it has to be opened as S32LE) and I would like to use some of the headroom that comes with going from 16 bit input to 32 bit output for filter gain and clipping avoidance (including the peaks that occur when the DAC does it's own internal upsampling before applying it's output filter).

I'm under the impression, but not certain, that most programs (or maybe alsa itself) just shifts the low bit depth data into the higher bits so volume is the same for files of different bit depths.
 
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.

Any reason that FFTW wasn't used? Also it's possible to develop FIR on GPU too - clFFT for example. I've written GPU FIR image processing - the system would simply process the entire file ahead of time).

Edit: I've just found the comment on the FFTW in the developers notes.
 
Last edited:
Thanks for the updates. I'll have to check out the 0.4 beta and the gui which I've never played with. (Silly me I didn't realize you had created a web gui already.)

A format conversion question for you. Using alsa and camilladsp if I specify in the config that the loopback interface is S16LE and the playback interface is S32LE, what will camilla do in converting from 16 to 32 bit?

Will it apply <<16 gain? Or no gain? I have a 32 bit DAC (which only accepts 32 bit packets via USB so it has to be opened as S32LE) and I would like to use some of the headroom that comes with going from 16 bit input to 32 bit output for filter gain and clipping avoidance (including the peaks that occur when the DAC does it's own internal upsampling before applying it's output filter).

I'm under the impression, but not certain, that most programs (or maybe alsa itself) just shifts the low bit depth data into the higher bits so volume is the same for files of different bit depths.
If you leave the pipeline empty, then in practice CamillaDSP will do this too. But let's elaborate a bit.
In the capture thread all input samples are converted to 64-bit float in the range -1.0 to +1.0. This is done by casting to float, and then dividing by 2^(nbr_of_bits - 1), which is 32768 for 16 bits. This conversion is bit-perfect, since the value bits of the number aren't changed by the power-of-two division. Only the exponent changes.
At the playback thread then, the values are converted to the format the playback device wants. To get an integer the float number is multiplied with 2^(nbr_of_bits - 1). Again, a power-of-two multiplication only changes the exponent. Then it's rounded and cast to an integer.
So let's do a 16 to 32 bit check. For an input value of 8192 (16-bit), this gets converted to float and divided by 2^15 (32768) giving a value of 0.25. Then this is multiplied by 2^31 (2147483648) and cast to int, giving a final value of 536870912. This is the same as just bit-shifting the number.




Any reason that FFTW wasn't used? Also it's possible to develop FIR on GPU too - clFFT for example. I've written GPU FIR image processing - the system would simply process the entire file ahead of time).

Edit: I've just found the comment on the FFTW in the developers notes.
Is the comment a complete answer to your question?

Note that the text you quoted is outdated, I use segmented convolution now so the IR can be longer than the chunksize.
 
I have now released version 0.3.2, and 0.4.0-beta1.


The changes of 0.4.0 are:
New features:
- New commands to get more playback information from the websocket server.
- Changed all websocket commands to use Json input and output.
- Added support for secure websocket connections (wss)

Bugfixes:
- Better handling of input device errors, fixes 100% cpu usage after panic.



0.4.x introduces a new format of the websocket commands where both commands and responses are in json format. Because of this, it also requires a new version of pyCamillaDSP, which is also version 0.4.0. This is the version currently in the develop branch of pyCamillaDSP.


For the gui, there is a new version as well with various fixes. It also displays the current buffer level and number of clipped samples since the processing started. I will replace this ina later version with a Clipping indicator and some kind of VU meter instead of showing the signal level just as a number.

I tried to install all the new stuff but got stuck on this. Is there a lib missing?

root@DietPi:/home/tc# sudo /home/tc/DSP_Engine/camilladsp -V
/home/tc/DSP_Engine/camilladsp: error while loading shared libraries: libssl.so.1.0.0: cannot open shared object file: No such file or directory
 
I tried to install all the new stuff but got stuck on this. Is there a lib missing?

root@DietPi:/home/tc# sudo /home/tc/DSP_Engine/camilladsp -V
/home/tc/DSP_Engine/camilladsp: error while loading shared libraries: libssl.so.1.0.0: cannot open shared object file: No such file or directory
Yes the new websocket server needs openssl for the secure connections. You can probably install it with "apt install openssl".
If this turns out to be a problem I can make the secure connections an optional feature.
 
Thanks. I just tried apt install openssl which installed ok. But same error message running camilladsp. BTW I got logging working from command line but couldn't get it to work within Jespers CamillaDSP.sh shell. I left it running idle all day from the command line but it didn't fall over so I couldn't capture the fault in the error log.
 
Last edited:
Thanks. I just tried apt install openssl which installed ok. But same error message running camilladsp. BTW I got logging working from command line but couldn't get it to work within Jespers CamillaDSP.sh shell. I left it running idle all day from the command line but it didn't fall over so I couldn't capture the fault in the error log.
I checked on my raspberry with raspbian and I see the same problem. I think the issue is that the binary is build on some other system (a docker image from rustembedded), where libssl.1.0.0 is the one to use. If you build it from source it's probably fine. I'll just make it an optional feature and build the binaries without ssl.



Logging from within Jespers script is probably tricky yes. But there should be no difference for CamillaDSP, so just keep running it in the terminal until you catch the error :)
 
Yes the new websocket server needs openssl for the secure connections. You can probably install it with "apt install openssl".
If this turns out to be a problem I can make the secure connections an optional feature.

Just a quick question - which version of openssl ("openssl -v") are you using?

If it's 1.1.1d I'd advise moving given it has security flaws and updating (note that Debian's docker image upgrade to 1.1.1g is "unstable" but works for example). Alternatively if you can upgrade to v3 which is an alpha.
 
Just a quick question - which version of openssl ("openssl -v") are you using?

If it's 1.1.1d I'd advise moving given it has security flaws and updating (note that Debian's docker image upgrade to 1.1.1g is "unstable" but works for example). Alternatively if you can upgrade to v3 which is an alpha.
I don't specify any particular version. When compiling it uses pkgconfig to find the installed version, and it works with OpenSSL versions 1.0.1 through 1.1.1 and LibreSSL versions 2.5 through 2.8.
 
What else would you suggest? IMO that is the only logical way.
I don't suggest anything else. I agree that is the correct default behavior. I was just putting some context to my question and I wasn't certain that's what all
programs do. This is my first > 16 bit DAC and I just got it. It seems alsa itself doesn't do anything for you automatically as I tried to use aplay this morning to send a 16 bit wav file directly to the DAC hardware and aplay simply reported S16_LE wasn't supported. I'm happy to see that behavior as well.

There's probably a plug device available in alsa that will do the described conversion.

If you leave the pipeline empty, then in practice CamillaDSP will do this too. But let's elaborate a bit.
In the capture thread all input samples are converted to 64-bit float in the range -1.0 to +1.0. This is done by casting to float, and then dividing by 2^(nbr_of_bits - 1), which is 32768 for 16 bits. This conversion is bit-perfect, since the value bits of the number aren't changed by the power-of-two division. Only the exponent changes.
At the playback thread then, the values are converted to the format the playback device wants. To get an integer the float number is multiplied with 2^(nbr_of_bits - 1). Again, a power-of-two multiplication only changes the exponent. Then it's rounded and cast to an integer.
So let's do a 16 to 32 bit check. For an input value of 8192 (16-bit), this gets converted to float and divided by 2^15 (32768) giving a value of 0.25. Then this is multiplied by 2^31 (2147483648) and cast to int, giving a final value of 536870912. This is the same as just bit-shifting the number.
Thanks for the details. Exactly what I was hoping for. Just wanted to make sure you weren't doing some post processing peak detection before you scaled the output. Probably a stupid question as I didn't figure you applied a running AGC and couldn't think of how it would clip if you didn't used fixed scaling in/out, but it seemed harmless to confirm the behavior.

So I can just use your gain filter to scale down inside CamillaDSP to give my self some head room right? I assume it will go as follows.

(16 bits integer in) => Floating Point / 2^15 => Multiply by Gain in FP => Multiply by 2^31 and round => (32 bit integer out)

Is that right?
 
Perfect. Thanks again.

Regarding headroom for the DAC I mentioned, according to Benchmark Media (audio company) most DAC and SRC chips don't leave enough headroom for their own internal upsampling. (You can get a peak of ~3dB between samples when you upsample/interpolate.) Fortunately either the DAC chips have been improved since the time they wrote their article or the designer who made my finished DAC did the right attentuation before feeding the actual DAC chip as it worked fine on my sinusoidal test signal built for the purpose of exposing any overflow/underflow/clipping caused by the interpolation peaks of a signal already at full scale. So at least I don't have to worry about that.
 
I finally managed to capture an error from the putty console :

root@DietPi:/home/tc# sudo /home/tc/DSP_Engine/camilladsp /home/tc/DSP_Engine/filters/null_96000.yml -p 3011
Buffer frames 16384
[2020-09-26T11:00:57Z INFO ws] Listening for new connections on 127.0.0.1:3011.
[2020-09-26T11:00:57Z INFO camillalib::alsadevice] Capture device supports rate adjust
[2020-09-26T11:00:57Z INFO camillalib::alsadevice] Starting playback from Prepared state
[2020-09-26T11:00:58Z INFO ws::io] Accepted a new tcp connection from 127.0.0.1:37678.
[2020-09-26T11:01:42Z INFO ws::io] Accepted a new tcp connection from 127.0.0.1:37680.
Buffer frames 8192
[2020-09-26T11:01:42Z INFO ws::io] Accepted a new tcp connection from 127.0.0.1:37682.
[2020-09-26T11:01:42Z INFO camillalib::alsadevice] Capture device supports rate adjust
[2020-09-26T11:01:42Z INFO camillalib::alsadevice] Starting playback from Prepared state
[2020-09-26T11:02:34Z INFO ws::io] Accepted a new tcp connection from 127.0.0.1:37688.
Buffer frames 16384
[2020-09-26T11:02:35Z INFO ws::io] Accepted a new tcp connection from 127.0.0.1:37690.
[2020-09-26T11:02:35Z INFO camillalib::alsadevice] Capture device supports rate adjust
[2020-09-26T11:02:35Z INFO camillalib::alsadevice] Starting playback from Prepared state
thread 'AlsaCapture' panicked at 'called `Result::unwrap()` on an `Err` value: SystemTimeError(893.27409ms)', src/alsadevice .rs:345:46
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Could this be because I am concurrently running HTOP in another putty session to monitor CPU usage?
 
Ok thanks a bunch. I tried to move to V4 today but I am wondering if it is compatible with Jespers script? I think I really need auto sample rate switching. Seemed like the websocket stuff wasn't working anymore with V4 and I got 404 errors on the web GUI. When you come up with a fix can you maybe back port it to V3.2? BTW I traced the error back to the source file and line in your rust code and had a geek at it. Pretty cool error messaging to provide for that!
 
The warning message should of course say something like WARN: Temporal anomaly detected :)

It's acually very simple to update Jespers script to work with 0.4.x, I think this is both easier and faster than backporting the fix. I'll post the changes you need to do as soon as I can.
Errors on the web gui probably means you need to update the gui itself and/or pycamilladsp.
 
Lol Temporal anomaly detected haha. Yes I updated the web gui. pycamilladsp to 4.0. Verified using pip list. Is websocket or websocket-client required? I get errors about importing with websocket when starting Python3 main.py. Is there any error in the backend documentation for debian in this respect? Python3 main.py starts ok though when websocket-client is installed but 404 error and no html files are present in the gui build folder. Maybe my folder structure is wrong?
 
Last edited: