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

:)

Working like this :

squeezelite -n XioPlayX -o - -a 32 -z (n=name, -o - =stdout, -a32=buf -z=daemonize)
camilladsp -v /home/pi/Cfilters/rate_44100.yml



But no sound, and squeezelite is consuming like 100% CPU...
also squeezelite acting very unstable like this... not camilladsp's fault ;)
I created an issue on the squeezelite git for this a couble of hours ago allready.

Let's see if there will be any correction on that?

Jesper.
Did you mean this command?:
Code:
squeezelite -n XioPlayX -o - -a 32 -z | camilladsp -v /home/pi/Cfilters/rate_44100.yml
 
Did you mean this command?:
Code:
squeezelite -n XioPlayX -o - -a 32 -z | camilladsp -v /home/pi/Cfilters/rate_44100.yml

This for starting squeezelite and send stream to stdout:
squeezelite -n XioPlayX -o - -a 32 -z
squeezelite name=XioPlay output (-o -)=stdout

And this for starting camilladsp:
camilladsp -v /home/pi/Cfilters/rate_44100.yml

With this config...
capture:
type: File
channels: 2
filename: "/dev/stdin"
format: S32LE
playback:
type: Alsa
channels: 2
device: "sound_out"
format: S32LE

Jesper.
 
I just updated the "develop" branch to version 0.0.12.


New features:

  • New filter type "BiquadCombo" for making Butterworth and LR high/lowpass filters (no more tables!)
  • Option to use FFTW instead of RustFFT. FFTW runs about a factor 2 faster, but increases the complexity a lot. Use only if you have problems with high cpu usage!
  • More options for Dither filters
  • Added first order allpass, first order low/high shelf Biquads
  • New filter type DiffEq for making filters with arbitrary poles/zeros. Give a0..aN and b0..bN, for H(z) = (b0 + b1z^-1 + .. + bnz^-n)/(a0 + a1z^-1 + .. + anz^-n)
  • Improved readme
  • Minor fixes
 
This for starting squeezelite and send stream to stdout:
squeezelite -n XioPlayX -o - -a 32 -z
squeezelite name=XioPlay output (-o -)=stdout

And this for starting camilladsp:
camilladsp -v /home/pi/Cfilters/rate_44100.yml

With this config...


Jesper.
Alright. If you use stdin/stdout you need to start them together with the pipe operator "|" in between. Otherwise they don't get connected. Whe you type "some_command | another_command", the system connects stdout of the first one to stdin of the second one.
 
Alright. If you use stdin/stdout you need to start them together with the pipe operator "|" in between. Otherwise they don't get connected. Whe you type "some_command | another_command", the system connects stdout of the first one to stdin of the second one.

A little delayed i know, but it's working allright... I havent tried to mkfifo named pipe's but i guess it will work nearly with same performance.
When piping using "|" between output / input, i have an 40sec. delay before music starts, but didn't try anything to fix that now.
The camilladsp & squeezelite are using very little cpu when using stdio pipes (when using them the correct way Jesper... sighhh!@%?) :eek:

Jesper.
 
Henrik... :)

Is this still the right way of building ?
git clone GitHub - HEnquist/camilladsp: A flexible linux IIR and FIR engine for crossovers, room correction etc.
git checkout develop
git pull

Build with ::
cargo build --release --no-default-features --features alsa-backend --features 32bit --features FFTW

Anyway i actually found a way making switching filters "on the fly".
It's working as proto pretty well, for now on RPI4 with Raspbian OS ;)

Right now i am listning to different sample rates (44100 & 96000) with the simple highpass+-gain filter i created.

I had to hack the squeezelite code for making it possible and set the right switch on it when executed.

What is does is as follows.

LogitechMediaServer (LMS) send stream to squeezelite, which is started with this syntax:
sudo /home/pi/DSP_Engine/squeezelite/squeezelite -n SuperPlayer -o squeeze -r 44100-192000:3000 -z
This stream is then captured by camilladsp from the alsa ~alias 'squeeze'(Card1,device0 on my setup)... Camilla send the stream after filter are invoked to the real hardware (alsa ~alias 'sound_out')
Then if sample_rate is changed in squeezelite, it sends a through system call (snip from squeezelite C sourcecode :
LOG_INFO("Player detected sample rate change ! %u", sample_rate);

if(sample_rate == 44100) system("/usr/bin/python3 /home/pi/DSP_Engine/filters/exec_44100.py");
if(sample_rate == 96000) system("/usr/bin/python3 /home/pi/DSP_Engine/filters/exec_96000.py");

LOG_INFO("Sample rate filter changed ! %u", sample_rate);

The python script connect to camilladsp and change the filter to e.g. 96000, the switch '-r' i use for executing squeezelite makes a delay when sample_rate change before music is started again, so there are no hickups when sample_rate are changing.

I can provide some more information if anyone like?, but it's only some uglyhack setup, but hey it's working very good indeed!

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1167 root 20 0 16960 13924 6788 S 2.3 0.3 1:13.57 squeezelite
1154 pi 20 0 32036 6788 5752 S 2.0 0.2 1:17.94 camilladsp

Jesper.
 
I did some benchmarking of CamillaDSP as a convolver and compared it vs. Brutefir. Very first the results in words:

- Camilladsp precision is equal with Brutefir and as mathematically adequate as possible.
- Camilladsp requires a bit more CPU ressources than Brutefr (due to inbuilt supervisor mechanisms).
- Camilladsp, unlike Brutefir, preserves datastream integrity till the very end, even along with linear phase filters (Brutefir stops convolution as soon as the incoming data stream ends. Because of the time delay introduced by the linear phase filters this will truncate the data stream at it's end).

Camilladsp (and Brutefir) were tested within an audiopipe. Input was a multitone signal of 16/24/32/64 Bit depth. This input was normalized by sox to FLOAT64. The convolution was performed using a set of FLOAT64 mixed linear-phase and minimum-phase filters, summing up to a delay of 128k. Filters were arranged to an input shelving filter, then a 4-/5-way xover, then summing and a final inverse shelving filter. Summing up to a (cpu-intense, but useless) 1:1 in-out-setup. The output was sampled as FLOAT64.

Filters were generated by Acourate, and so the analysis of the resulting output files.

1. Filters setup:

A_Filters+20-285.png



2. 16Bit input file - Comparing input file (red), sox conversion (green, -10dB), brutefir convolution result (brown, -20dB) and camilladsp convoltution result (blue, -30dB)

A_16_Orig-Sox-Bfir-Cam.png



3. 24Bit input file - Comparing input file (red), sox conversion (green, -10dB), brutefir convolution result (brown, -20dB) and camilladsp convoltution result (blue, -30dB)

A_24_Orig-Sox-Bfir-Cam.png



4. 32Bit input file - Comparing input file (red), sox conversion (green, -10dB), brutefir convolution result (brown, -20dB) and camilladsp convoltution result (blue, -30dB)

A_32_Orig-Sox-Bfir-Cam.png


5. Behavior of Camilladsp vs. Brutefir in case of the linear phase filters used in this setup (2*128k, equalling to a delay of 120k). Brutefir will truncate the 128k samples initial delay at the end. Camilladap does not.

16-Time.png


Because of this latter improvement over brutefir , my vote goes to Camilladsp, despite of the higher CPU usage. Because I am using linear phase filters within my active loudspeakers projects.
 

Attachments

  • A_Filters+20-285.png
    A_Filters+20-285.png
    14 KB · Views: 294
  • A_16_Orig-Sox-Bfir-Cam.png
    A_16_Orig-Sox-Bfir-Cam.png
    38 KB · Views: 295
  • A_24_Orig-Sox-Bfir-Cam.png
    A_24_Orig-Sox-Bfir-Cam.png
    42.4 KB · Views: 282
  • A_32_Orig-Sox-Bfir-Cam.png
    A_32_Orig-Sox-Bfir-Cam.png
    42 KB · Views: 273
  • 16-Time.png
    16-Time.png
    34.6 KB · Views: 288
Just a note - sox uses signed int32 internally, therefore any work with larger formats (e.g. float64) reduces precision ...

OK. This is it! These are the corresponding graphs with an input from a file formatted as FLOAT64. Note that the multitone does not reach 64Bit precision. But such as it is, sox does set the resolution level at -210dB (sox graph is attenuated by -10dB), which is slightly worse than the input signal.

64-64-bit.png

Does anybody know a bitdepth converter which will gracefully handle conversions to FLOAT64 and back?
 

Attachments

  • 64-64-bit.png
    64-64-bit.png
    43.8 KB · Views: 107
Is this still the right way of building ?
Yes that looks correct! I take it you're not using the websocket server?


Anyway i actually found a way making switching filters "on the fly".
It's working as proto pretty well, for now on RPI4 with Raspbian OS ;)

Right now i am listning to different sample rates (44100 & 96000) with the simple highpass+-gain filter i created.

I had to hack the squeezelite code for making it possible and set the right switch on it when executed.

What is does is as follows.

LogitechMediaServer (LMS) send stream to squeezelite, which is started with this syntax:
sudo /home/pi/DSP_Engine/squeezelite/squeezelite -n SuperPlayer -o squeeze -r 44100-192000:3000 -z
This stream is then captured by camilladsp from the alsa ~alias 'squeeze'(Card1,device0 on my setup)... Camilla send the stream after filter are invoked to the real hardware (alsa ~alias 'sound_out')
Then if sample_rate is changed in squeezelite, it sends a through system call (snip from squeezelite C sourcecode :


The python script connect to camilladsp and change the filter to e.g. 96000, the switch '-r' i use for executing squeezelite makes a delay when sample_rate change before music is started again, so there are no hickups when sample_rate are changing.

I can provide some more information if anyone like?, but it's only some uglyhack setup, but hey it's working very good indeed!

Jesper.
This is brilliant! I had a feeling you would get it working eventually :)

Having squeezelite somehow signal when it wants a new samplerate makes perfect sense. Perhaps your solution could be made a bit more generic, so you specify the command to run as a config parameter or something?

There is actually an open pull request on squeezelite that has some similarities, for controlling an external amplifier: Support control of an external amplifier. by mjagdis * Pull Request #81 * ralph-irving/squeezelite * GitHub
If you could make a nice generic solution and send it as a pull request, there should be a good chance to get it accepted. Are you familiar with the process of making pull requests? If not, just ask and I'll try to help you.
 
Yet another set of benchmarks. Camilladsp comes in different flavors, 64Bit, 32Bit, RustFFT, FFTW ... So there is a nice playround to compare 64Bit, 64BitFFTW, 32Bit, 32BitFFTW

16BitIn -> sox -> Float64 -> Camilladsp (all 4 datasets the same, blue-4 hiding brown-3 hiding green-2 hiding red-1)
16-64.png

24BitIn -> sox -> Float64 -> Camilladsp (gn-2 hiding red-1, blue-4 hiding brown-3)
24-64.png

32BitIn -> sox -> Float64 -> Camilladsp (gn-2 hiding red-1, blue-4 hiding brown-3)
32-64.png

64BitIn -> sox -> Float64 -> Camilladsp (gn-2 hiding red-1, blue-4 hiding brown-3)
64-64.png

And Brutefir 32Bit as a reference, formatted differently with all 16/24/32/64 inputs on the same graph
1664-64_Bfir32.png

No surprizes (still with sox as a bitdepth shifter - was done before phofman made his objection about the problematic float64 conversion by sox).

Actually, you can use CamillaDSP for that :)
Just compile it with the default features (meaning without 32bit mode) and run it with file in/out with the formats you want, and an empty pipeline. You could also add some dither if you like.

Oh my ... this calls for another set of benchmarks ...
 

Attachments

  • 16-64.png
    16-64.png
    29.1 KB · Views: 88
  • 24-64.png
    24-64.png
    36.7 KB · Views: 81
  • 32-64.png
    32-64.png
    38 KB · Views: 70
  • 64-64.png
    64-64.png
    39.5 KB · Views: 89
  • 1664-64_Bfir32.png
    1664-64_Bfir32.png
    39.9 KB · Views: 95
Last edited:
Actually, you can use CamillaDSP for that :)
Just compile it with the default features (meaning without 32bit mode) and run it with file in/out with the formats you want, and an empty pipeline. You could also add some dither if you like.
This is very exciting news ... because it's really working !!! Three piped instances of camilladsp's within a same testbed:

The first instance to read in an audio.wav (S16LE) and transforming the bitdepth and format of the datastream to F64, could also perform -1dB .. -2dB to prevent intersample clipping in the following stages
(Then a samplerate conversion 44.1k->96k)
The second instance to perform the convolution, in this example 10x 128k Filters in full F64
The third instance to transform from F64 to S32, along with some dithering.
(Then aplay or playhrt)

and this looks like

camilladsp /home/privat/_camilladsp/BitXform/S16-F64.yml | \
resample_soxr ... --inrate=44100 --outrate=9600 | \
camilladsp /home/privat/_camilladsp/4way/4way.yml | \
camilladsp /home/privat/_camilladsp/BitXform/F64-S32.yml | \
aplay -c 2 -D hw:0,0 --mmap -f S32_LE -r 96000

My linux is a slim Arch, the hardware is an outdated Lenovo T500 with a (by today's standards weak) Core2Duo Mobile processor. On this machine, the first and the last instance of camilladsp generate each a mere 0.3% cpu load. The convolution instance makes up for some 60% ... 65% cup load. Playback is stable and rock solid.

This is really great news, and it's getting absolutely interesting ... No more sox needed, no more brutefir needed ...