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

...

I provide a table of the Q values for up to 8th order.

...

Hmm... why not calculate Q values for those 2nd order Butterworth sections RT in code:

Code:
% Nth order Butterworth (N even or odd) you will have N/2 biquad
% sections ((N-1)/2 for odd N or floor(N/2) for either even or odd N),
% each will have the same resonant frequency w0 and will have Q:
%
%      Q = 1/( 2*sin((pi/N)*(n + 1/2)) )
%
%                    where  0 <= n < (N-1)/2
%

Octave/Matlab example:

Code:
function Qn = Qsos(N)
% N = filter order 
    for n=0:(N/2-1)
        Q = 1/( 2*sin((pi/N)*(n + 1/2)))
    end
endfunction
 
Last edited:
Time to take some mesurements.

For measurements I think of buying a second card with ADC from Hifiberry(DAC+ADC) which also has an alsamixer.
For passive crossover measurements I use a Steinberg UR12 with Holmimpulse.

Am I right that this DAC+ADC can be used with Camilladsp as capture device for the mesurementsignal (capture analog in from UR12) and playback simultaneously without additional software ?
 
Time to take some mesurements.

For measurements I think of buying a second card with ADC from Hifiberry(DAC+ADC) which also has an alsamixer.
For passive crossover measurements I use a Steinberg UR12 with Holmimpulse.

Am I right that this DAC+ADC can be used with Camilladsp as capture device for the mesurementsignal (capture analog in from UR12) and playback simultaneously without additional software ?
Just checking that I understand it right. You want to run Holmimpulse on a PC. This is connected to the Steinberg interface. Measurement signal goes from analog out on Steinberg to analog in on hifiberry. CamillaDSP captures this, processes, and plays back on hifiberry. Analog out from hifiberry goes to amp+speakers. Sound from speakers goes to a microphone, via the Steinberg back to Holmimpulse. Correct?
If so, yes that will work just fine! You need no other software.
 
Just checking that I understand it right. You want to run Holmimpulse on a PC. This is connected to the Steinberg interface. Measurement signal goes from analog out on Steinberg to analog in on hifiberry. CamillaDSP captures this, processes, and plays back on hifiberry. Analog out from hifiberry goes to amp+speakers. Sound from speakers goes to a microphone, via the Steinberg back to Holmimpulse. Correct?
If so, yes that will work just fine! You need no other software.

Exactly.
Ordered.
Thanks !
 
BTW, yuo have #ToDo flag there in :
...
beqdesigner/iir.py at master * 3ll3d00d/beqdesigner * GitHub - to save people referring to lookup tables

this is in python but seems like it reads pretty similarly to your code to me

Code:
class FirstOrder_HighPass(BiquadWithQ):
    '''
    A one pole high pass filter.
    '''
...

maybe you could use this kind of implementation there:

Code:
               b0 =   cos(w0) + 1
                 b1 = -(cos(w0) + 1)
                 a0 =   cos(w0) + sin(w0) + 1
                 a1 =   sin(w0) - cos(w0) - 1

where w0 = 2*pi*(fc/fs);

... though, not as fast as exp() based implementation could be ... .

If you need very fast low frequency range 1st order HPF implementation then here's one I made some time ago (Octave/Matlab source code):

Code:
% This 1st order (Butterworth) HPF coefficient calculation function which
% uses low order approximations and is meant to be used with cut-off frequencies 
% below 1 kHz
%
% Usage: [b,a] = HPF1_approx(44100, 350, 2)
%
% Brought to you by [email]jiiteepee@yahoo.se[/email] (02/2020)
% ------------------------------------------------------------------------------
function [b, a] = HPF1_approx(fs, fc, order)
% fs    = sample rate in Hz
% fc    = cut off frequency in Hz
% order = approximation order; 3 = 3rd order, otherwise 2nd order

x = 2.0 * pi * fc/fs;

switch order
  case 3 % 3rd order
    sx = -5.382643908e-8 + x * (1.00000755295 + x * (-1.627621479421707e-4 + x * (-0.1657084756226908)));
    cx = 1.99999946747854 + x * (8.3526171644607921e-5 + x * (-0.50208137392503 + x * 1.664302325330385905e-2));
  otherwise % 2nd order
    sx = -4.205541129472213e-5 + x * (1.003746378622 + x * (-4.991453468087422e-2));
    cx = 2.00000427449401 + x * (-3.1321682298812e-4 + x * (-0.4969816314888122));
end

% return
a = [cx+sx sx-cx];
b = [cx -cx];
 
I'am trying to switch filters through python, it is working fine.

But if i take a filter like this one :
And write samplerate: 96000 instead of the "default" 44100 i cannot start the engine.
---
devices:
samplerate: 44100
buffersize: 8192
target_level: 4096
adjust_period: 5
...
Instead camilla gives me this :
pi@SuperBPI:~/socket_camilla/camilladsp/target/release $ /home/pi/socket_camilla/camilladsp/target/release/camilladsp /home/pi/Cfilters/testyml.yml
[2020-03-25T19:08:06Z INFO ws] Listening for new connections on 127.0.0.1:3011.
[2020-03-25T19:08:06Z ERROR camilladsp] Capture error: ALSA function 'snd_pcm_hw_params_set_rate' failed with error 'EINVAL: Invalid argument'

Is it not possible to shift the samplerate?
Or did i miss something? :):)

Case is, i have hacked the squeezelite player to paste-out the played samplerate (e.g. 44100, ... 96000, ...192000). It's working fine grabbing this with a pythonscript and send/reload camilladsp filter also works, but only with 44100 for now?

Ugly script for now i know :eek: ::
from subprocess import *
import time
infile = "rate.txt"

from websocket import create_connection
ws = create_connection("ws://127.0.0.1:3011")

fin = open(infile, 'r');
old = fin.readlines();

while 1: #While true
fin = open(infile, 'r');
current = fin.readlines();
if(current != old ):
if int(current[0]) == 44100:
ws.send("setconfigname:/home/pi/Cfilters/rate_44100.yml")
ws.send("reload")
print("Changed to 44100")
elif int(current[0]) == 48000:
ws.send("setconfigname:/home/pi/Cfilters/rate_48000.yml")
ws.send("reload")
print("Changed to 48000")
elif int(current[0]) == 88200:
ws.send("setconfigname:/home/pi/Cfilters/rate_88200.yml")
ws.send("reload")
print("Changed to 88200")
elif int(current[0]) == 96000:
ws.send("setconfigname:/home/pi/Cfilters/rate_96000.yml")
ws.send("reload")
print("Changed to 96000")
elif int(current[0]) == 176400:
ws.send("setconfigname:/home/pi/Cfilters/rate_176400.yml")
ws.send("reload")
print("Changed to 176400")
elif int(current[0]) == 192000:
ws.send("setconfigname:/home/pi/Cfilters/rate_192000.yml")
ws.send("reload")
print("Changed to 192000")
elif int(current[0]) == 352800:
ws.send("setconfigname:/home/pi/Cfilters/rate_352800.yml")
ws.send("reload")
print("Changed to 352800")
else:
raise ValueError('Unknown rate detected')
old = current
time.sleep(0.05)

Jesper.
 
That should work. I wonder if perhaps the squeezelite player stays connected to the loopback and this makes it impossible to change the sample rate.
Does it work if you kill squeezelite and write manually to the rate file?

Edit: could you post you .asoundrc?
 
Last edited:
That should work. I wonder if perhaps the squeezelite player stays connected to the loopback and this makes it impossible to change the sample rate.
Does it work if you kill squeezelite and write manually to the rate file?

Well i think not (will try tomorrow to be sure)
I allready tried without pythonscript or anything, just plain:

If i start squeezelite and then start camilla with samplerate:44100 its working.
(I use the simple highpass filter you gave me on page 9 to keep it simple)
If i change samplerate to 96000 camilla wont start.
Even if squeezelite has been stopped its the same case...

I cant find much regarding the samplerate, except you mention in readme that there is a default.

My hack on squeezelite are cool btw. more to come when i solve this prob.

Jesper.
 
There is no default sample rate. CamillaDSP should work with anything the capture/playback devices support. Could post you r .asoundrc? Wondering if you maybe locked the sample rate somewhere.

# Loopback device 0
pcm.squeeze {
type hw
card "Loopback"
device 0
format S32_LE
channels 2
}

# Loopback device 1
pcm.camilla_in {
type hw
card "Loopback"
device 1
format S32_LE
channels 2
}

pcm.sound_out {
type hw
card 1
device 0
}

ctl.sound_out {
type hw
card 1
}
 
We are already in contact! Check a few pages back in this thread. Based on his input I already implemented the config reloading on SIGHUP, and I'm also working on a websocket server so you can pass more commands to the running process.

Nice to hear that there is so much progress!
Yeah, atm it's just so much "coding" and plain text for the average user I think.
Personally, I really like way of Crossover Rack, which is also very similar to something like SigmaStudio (for programming DSP's).

That way people are a lot more flexible in implementing filters.
Like, splitting, parallel, summing and subtracting, which can be extremely useful in some cases
 
Will to tomorrow...
Remember that when i write samplerate:44100 i mean the text in the .yml file not the actual played samplerate.
Camilla wont start with eg. Samplerate:96000 in first line.

Jesper


Hmm it works fine when I try it. Running between two Loopbacks at 96kHz:

Code:
[henrik@localhost]~/rustfir/rustfir% ./target/release/camilladsp kladd/simpleconfig.yml -v 
[2020-03-25T21:55:59Z DEBUG camilladsp::filters] Read file: filter.txt, number of coeffs: 1024 
[2020-03-25T21:55:59Z DEBUG camilladsp::filters] Read file: filter.txt, number of coeffs: 1024 
[2020-03-25T21:55:59Z DEBUG camilladsp::filters] Build new pipeline 
[2020-03-25T21:55:59Z DEBUG camilladsp::filters] Build from config 
[2020-03-25T21:55:59Z DEBUG camilladsp::filters] Read file: filter.txt, number of coeffs: 1024 
[2020-03-25T21:55:59Z DEBUG camilladsp::fftconv] Conv lowpass_fir is using 1 segments 
[2020-03-25T21:55:59Z DEBUG camilladsp::filters] Build from config 
[2020-03-25T21:55:59Z DEBUG camilladsp::filters] Read file: filter.txt, number of coeffs: 1024 
[2020-03-25T21:55:59Z DEBUG camilladsp::alsadevice] Opened audio output "hw:Loopback,0,5" with parameters: HwParams { channels: Ok(2), rate: "Ok(96000) Hz", format: Ok(S32LE), access: Ok(RWInterleaved), period_size: "Ok(128) frames", buffer_size: "Ok(2048) frames" }, SwParams(avail_min: Ok(128) frames, start_threshold: Ok(896) frames, stop_threshold: Ok(2048) frames)
[2020-03-25T21:55:59Z DEBUG camilladsp::fftconv] Conv lowpass_fir is using 1 segments 
[2020-03-25T21:55:59Z DEBUG camilladsp] Playback thread ready to start 
[2020-03-25T21:55:59Z DEBUG camilladsp::alsadevice] Opened audio output "hw:Loopback,0,1" with parameters: HwParams { channels: Ok(2), rate: "Ok(96000) Hz", format: Ok(S16LE), access: Ok(RWInterleaved), period_size: "Ok(128) frames", buffer_size: "Ok(2048) frames" }, SwParams(avail_min: Ok(128) frames, start_threshold: Ok(896) frames, stop_threshold: Ok(2048) frames) 
[2020-03-25T21:55:59Z DEBUG camilladsp::processing] build filters, waiting to start processing loop 
[2020-03-25T21:55:59Z DEBUG camilladsp] Capture thread ready to start 
[2020-03-25T21:55:59Z DEBUG camilladsp::alsadevice] Starting playback loop
BTW I just noticed a typo in the debug messages. Both the capture and playback device print "Opened audio output"...
 
Loopback device locks the params (samplerate, sample size) used by the first opening side, be it playback or capture. Both sides must use same params.

LMS opens at 48k, camilladsp has only 48k available. It opens the capture device at 48k, and from that on LMS cannot change the rate (the loopback playback device offers only 48k). The player (or the alsa plug plugin if used) must be resampling on the fly to play other rates.

The same for sample format.

Just check current params in the hw_params files of each side in /proc when the devices are opened.

That is why building a chained solution using snd-aloop is quite tricky. Unlike e.g. being part of alsa-lib chain (alsa plugin) which starts up with correct params every time the device is opened by the player. snd-aloop breaks that chain and requires some side-band channel for communicating the changes and reloading the other side.
 
So i'am been making some progress now regarding the fault clearance in my setup.

If i do this without squeezelite started i can change the samplerate at camilla without problems:
pi@SuperBPI:~/Cfilters $ camilladsp -v -p3011 rate_44100.yml
[2020-03-26T06:07:30Z DEBUG camilladsp::biquad] a1=-1.985802922748258 a2=0.9858533119988809 b0=0.9929140586867847 b1=-1.9858281173735695 b2=0.9929140586867847
[2020-03-26T06:07:30Z DEBUG camilladsp::biquad] a1=-1.985802922748258 a2=0.9858533119988809 b0=0.9929140586867847 b1=-1.9858281173735695 b2=0.9929140586867847
[2020-03-26T06:07:30Z DEBUG camilladsp::socketserver] Start websocket server on port 3011
[2020-03-26T06:07:30Z DEBUG camilladsp::filters] Build new pipeline
[2020-03-26T06:07:30Z DEBUG camilladsp::filters] Build from config
[2020-03-26T06:07:30Z DEBUG camilladsp::biquad] a1=-1.985802922748258 a2=0.9858533119988809 b0=0.9929140586867847 b1=-1.9858281173735695 b2=0.9929140586867847
[2020-03-26T06:07:30Z DEBUG camilladsp::filters] Build from config
[2020-03-26T06:07:30Z DEBUG camilladsp::biquad] a1=-1.985802922748258 a2=0.9858533119988809 b0=0.9929140586867847 b1=-1.9858281173735695 b2=0.9929140586867847
[2020-03-26T06:07:30Z DEBUG camilladsp::processing] build filters, waiting to start processing loop
[2020-03-26T06:07:30Z INFO ws] Listening for new connections on 127.0.0.1:3011.
[2020-03-26T06:07:30Z DEBUG camilladsp::alsadevice] Opened audio output "camilla_in" with parameters: HwParams { channels: Ok(2), rate: "Ok(44100) Hz", format: Ok(S32LE), access: Ok(RWInterleaved), period_size: "Ok(1024) frames", buffer_size: "Ok(16384) frames" }, SwParams(avail_min: Ok(1024) frames, start_threshold: Ok(7168) frames, stop_threshold: Ok(16384) frames)

And changing to 96000:
pi@SuperBPI:~/Cfilters $ camilladsp -v -p3011 rate_96000.yml
[2020-03-26T06:07:20Z DEBUG camilladsp::biquad] a1=-1.9934657011950088 a2=0.9934763754602268 b0=0.996735519163809 b1=-1.993471038327618 b2=0.996735519163809
[2020-03-26T06:07:20Z DEBUG camilladsp::biquad] a1=-1.9934657011950088 a2=0.9934763754602268 b0=0.996735519163809 b1=-1.993471038327618 b2=0.996735519163809
[2020-03-26T06:07:20Z DEBUG camilladsp::socketserver] Start websocket server on port 3011
[2020-03-26T06:07:20Z DEBUG camilladsp::filters] Build new pipeline
[2020-03-26T06:07:20Z DEBUG camilladsp::filters] Build from config
[2020-03-26T06:07:20Z DEBUG camilladsp::biquad] a1=-1.9934657011950088 a2=0.9934763754602268 b0=0.996735519163809 b1=-1.993471038327618 b2=0.996735519163809
[2020-03-26T06:07:20Z INFO ws] Listening for new connections on 127.0.0.1:3011.
[2020-03-26T06:07:20Z DEBUG camilladsp::filters] Build from config
[2020-03-26T06:07:20Z DEBUG camilladsp::biquad] a1=-1.9934657011950088 a2=0.9934763754602268 b0=0.996735519163809 b1=-1.993471038327618 b2=0.996735519163809
[2020-03-26T06:07:20Z DEBUG camilladsp::processing] build filters, waiting to start processing loop
[2020-03-26T06:07:20Z DEBUG camilladsp::alsadevice] Opened audio output "camilla_in" with parameters: HwParams { channels: Ok(2), rate: "Ok(96000) Hz", format: Ok(S32LE), access: Ok(RWInterleaved), period_size: "Ok(1024) frames", buffer_size: "Ok(16384) frames" }, SwParams(avail_min: Ok(1024) frames, start_threshold: Ok(7168) frames, stop_threshold: Ok(16384) frames)

So camilladsp is working as expected :)
Also the samplerate is detected at my DAC... so that's good.

If i change samplerate when squeezelite is started, i got errors allmost every time; i also had wild kernel panic and crashes doing this so it's nogo for now i think:
2020-03-26T06:15:16Z INFO ws] Listening for new connections on 127.0.0.1:3011.
[2020-03-26T06:15:16Z ERROR camilladsp] Capture error: ALSA function 'snd_pcm_hw_params_set_rate' failed with error 'EINVAL: Invalid argument'
[2020-03-26T06:15:16Z DEBUG camilladsp] Exiting

So it seems that squeezelite is locking the samplerate somehow (my guess!)
Right now i donno howto solve this, need to go thinking.
- One solution is to script squeezelite to stop when filter is changed, but it's bad while LMS needs to see the player attached, could give a lot of problems iguess.
- Attached picture of LMS when player is "attached"

Jesper.
 

Attachments

  • IMG_6465.jpg
    IMG_6465.jpg
    601 KB · Views: 237
  • IMG_6466.jpg
    IMG_6466.jpg
    591.1 KB · Views: 239
  • Screenshot from 2020-03-26 07-22-23.png
    Screenshot from 2020-03-26 07-22-23.png
    49.4 KB · Views: 232