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

Thanks for the explanation and suggestions, easy enough to just to expand the relative path. I assume the path is relative to the config file directory or maybe the back end working directory ?
If the path is relative to the config file, would it make sense to have the read_config_file(path) function expand the relative path using the provided path ?
 
Looks like most of my questions were already covered in the Fiters FIR section of the README, should have checked first.
If a relative path is given it will first try to find the file relative to the config file path. If it's not found there, the path is assumed to be relative to the current working directory. Note that this only applies when the config is loaded from a file. When a config is supplied via the websocket server only the current working dir of the CamillaDSP process will be searched.
 
If the path is relative to the config file, would it make sense to have the read_config_file(path) function expand the relative path using the provided path ?
I prefer to not put in automatic stuff like this. But the gui backend has this functionality. I could move that over to pycamilladsp, so you get a helper function to expand all paths of a config file to absolute.
 
On a different topic, the convolution filters take about 20 sec to plot for my setup running both camillacdsp and the backend on a rpi4 with raspberry pi OS 64 bit. The full size graph, which is really nice feature btw, takes about 30 sec to display. Numpy is installed so I think it is being used, is this what can be expected with an rpi4 running the backend ?
20 seconds sounds like a lot, but if the filters are very long then it may not be unreasonable. Did you also run the browser on the pi? How long are the filters?
 
20 seconds sounds like a lot, but if the filters are very long then it may not be unreasonable. Did you also run the browser on the pi? How long are the filters?
Yes the filter is very long,131072 taps, 96k. Not running the browser on the rpi though.

I prefer to not put in automatic stuff like this. But the gui backend has this functionality. I could move that over to pycamilladsp, so you get a helper function to expand all paths of a config file to absolute.
I have added a few lines of code to my program to handle the relative path so OK as is for me now.
 
HI,
@HenrikEnquist
I've decided to give CamillaDSP a try - with the intent of building a self contained source + crossover unit with the capability of outputing over HDMI
This evening:

  • I've installed ubuntu64 on a raspberry pi 3B
  • I've installed CamillaDSP on it
  • I've Installed the alsa plugins and loopback device
Where I'm a bit confused is how to configure the HDMI out of the rpi3 . As I mentioned above, I'd like to split the stereo input into 2 or 3 way stereo i.e. 6 or 8 output channels output over HDMI that I can output using a spare HT receiver .
1. Is there a simple example for my use case . Most of the configs I see are for USB DACS with crossovers built in . Ideally, I'd like to configure the crossovers using the front end .
2. I have questions on the front end config but for now, I'd like to get Camilladsp running and outputting some sound .
3. The answers might be buried in this thread but I haven't found them yet via search.

Which of these configs (if any) would serve my use case . https://github.com/HEnquist/camilladsp/tree/master/exampleconfigs

Thanks


1674366238641.png


Code:
ubuntu@pi3:~/camilladsp$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Loopback [Loopback], device 0: Loopback PCM [Loopback PCM]
  Subdevices: 8/8
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
  Subdevice #4: subdevice #4
  Subdevice #5: subdevice #5
  Subdevice #6: subdevice #6
  Subdevice #7: subdevice #7
card 0: Loopback [Loopback], device 1: Loopback PCM [Loopback PCM]
  Subdevices: 8/8
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
  Subdevice #4: subdevice #4
  Subdevice #5: subdevice #5
  Subdevice #6: subdevice #6
  Subdevice #7: subdevice #7
card 1: b1 [bcm2835 HDMI 1], device 0: bcm2835 HDMI 1 [bcm2835 HDMI 1]
  Subdevices: 4/4
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
card 2: Headphones [bcm2835 Headphones], device 0: bcm2835 Headphones [bcm2835 Headphones]
  Subdevices: 4/4
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
 
Last edited:
I messed around a bit more . I took a simple nofilters config and modified it as below and managed to start CamillaDSP. I don't have anything connected to my HDMI out at the moment. Is this config correct ?

Code:
ubuntu@pi3:~/camilladsp/configs$  aplay -L
.
.
hw:CARD=b1,DEV=0 ==============================> choose this
    bcm2835 HDMI 1, bcm2835 HDMI 1
    Direct hardware device without any conversions
plughw:CARD=b1,DEV=0
    bcm2835 HDMI 1, bcm2835 HDMI 1
    Hardware device with all software conversions
default:CARD=b1
    bcm2835 HDMI 1, bcm2835 HDMI 1
    Default Audio Device
sysdefault:CARD=b1
    bcm2835 HDMI 1, bcm2835 HDMI 1
    Default Audio Device
dmix:CARD=b1,DEV=0
    bcm2835 HDMI 1, bcm2835 HDMI 1
    Direct sample mixing device
.
.


Code:
ubuntu@pi3:~/camilladsp/configs$ aplay -v -D hw:b1 /dev/zero --dump-hw-params
Playing raw data '/dev/zero' : Unsigned 8 bit, Rate 8000 Hz, Mono
HW Params of device "hw:b1":
--------------------
ACCESS:  MMAP_INTERLEAVED RW_INTERLEAVED
FORMAT:  U8 S16_LE ========================> use this
SUBFORMAT:  STD
SAMPLE_BITS: [8 16]
FRAME_BITS: [8 128]
CHANNELS: [1 8]
RATE: [8000 192000]
PERIOD_TIME: [10000 16384000]
PERIOD_SIZE: [80 131072]
PERIOD_BYTES: [1024 524288]
PERIODS: [1 128]
BUFFER_TIME: (416 16384000]
BUFFER_SIZE: [80 131072]
BUFFER_BYTES: [1024 131072]
TICK_TIME: ALL
.
.


Code:
ubuntu@pi3:~/camilladsp/configs$ cat nofilters.yml 
---
devices:
  samplerate: 44100
  chunksize: 1024
  capture:
    type: Alsa
    channels: 2
    device: "hw:Loopback,0,0"
    format: S16LE
  playback:
    type: Alsa
    channels: 2
    device: "hw:b1" ================> use b1 from above
    format: S16LE =================> use S16LE from above

mixers:
  mono:
    channels:
      in: 2
      out: 2
    mapping:
      - dest: 0
        sources:
          - channel: 0
            gain: -6
            inverted: false
          - channel: 1
            gain: -6
            inverted: false
      - dest: 1
        sources:
          - channel: 0
            gain: -6
            inverted: false
          - channel: 1
            gain: -6
            inverted: false

pipeline:
  - type: Mixer
    name: mono


Resulting in
Code:
ubuntu@pi3:~/camilladsp/configs$ /home/ubuntu/camilladsp/camilladsp -g-40 -p1234 -v /home/ubuntu/camilladsp/configs/nofilters.yml 
2023-01-22 07:39:55.634244 INFO [src/bin.rs:711] CamillaDSP version 1.0.3
2023-01-22 07:39:55.634681 INFO [src/bin.rs:712] Running on linux, aarch64
2023-01-22 07:39:55.635008 DEBUG [src/bin.rs:754] Read config file Some("/home/ubuntu/camilladsp/configs/nofilters.yml")
2023-01-22 07:39:55.636511 DEBUG [src/bin.rs:773] Config is valid
2023-01-22 07:39:55.636778 DEBUG [src/socketserver.rs:260] Start websocket server on 127.0.0.1:1234
2023-01-22 07:39:55.637457 DEBUG [src/bin.rs:857] Wait for config
2023-01-22 07:39:55.637766 DEBUG [src/bin.rs:890] Config ready
2023-01-22 07:39:55.638903 DEBUG [src/filters.rs:450] Build new pipeline
2023-01-22 07:39:55.638904 DEBUG [src/bin.rs:213] Using channels [true, true]
2023-01-22 07:39:55.639234 DEBUG [src/processing.rs:19] build filters, waiting to start processing loop
2023-01-22 07:39:55.639240 DEBUG [src/alsadevice.rs:917] Buffer frames 2048
2023-01-22 07:39:55.644250 DEBUG [src/alsadevice.rs:413] Playback: supported channels, min: 1, max: 8, list: [1, 2, 3, 4, 5, 6, 7, 8]
2023-01-22 07:39:55.644988 DEBUG [src/alsadevice.rs:414] Playback: setting channels to 2
2023-01-22 07:39:55.645467 DEBUG [src/alsadevice.rs:418] Playback: supported samplerates: Range(8000, 192000)
2023-01-22 07:39:55.645796 DEBUG [src/alsadevice.rs:419] Playback: setting rate to 44100
2023-01-22 07:39:55.646121 DEBUG [src/alsadevice.rs:423] Playback: supported sample formats: [S16LE]
2023-01-22 07:39:55.646401 DEBUG [src/alsadevice.rs:424] Playback: setting format to S16LE
2023-01-22 07:39:55.647354 DEBUG [src/alsadevice.rs:455] Opening audio device "hw:b1" with parameters: HwParams { channels: Ok(2), rate: "Ok(44100) Hz", format: Ok(S16LE), access: Ok(RWInterleaved), period_size: "Ok(444) frames", buffer_size: "Ok(2048) frames" }, SwParams(avail_min: Ok(1024) frames, start_threshold: Ok(580) frames, stop_threshold: Ok(2048) frames)
2023-01-22 07:39:55.647661 DEBUG [src/alsadevice.rs:460] Audio device "hw:b1" successfully opened
2023-01-22 07:39:55.648188 DEBUG [src/bin.rs:323] Playback thread ready to start
2023-01-22 07:39:55.649913 DEBUG [src/alsadevice.rs:413] Capture: supported channels, min: 1, max: 32, list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
2023-01-22 07:39:55.650141 DEBUG [src/alsadevice.rs:414] Capture: setting channels to 2
2023-01-22 07:39:55.650417 DEBUG [src/alsadevice.rs:418] Capture: supported samplerates: Range(8000, 192000)
2023-01-22 07:39:55.650767 DEBUG [src/alsadevice.rs:419] Capture: setting rate to 44100
2023-01-22 07:39:55.651068 DEBUG [src/alsadevice.rs:423] Capture: supported sample formats: [S16LE, S24LE, S24LE3, S32LE, FLOAT32LE]
2023-01-22 07:39:55.651351 DEBUG [src/alsadevice.rs:424] Capture: setting format to S16LE
2023-01-22 07:39:55.652167 DEBUG [src/alsadevice.rs:455] Opening audio device "hw:Loopback,0,0" with parameters: HwParams { channels: Ok(2), rate: "Ok(44100) Hz", format: Ok(S16LE), access: Ok(RWInterleaved), period_size: "Ok(512) frames", buffer_size: "Ok(4096) frames" }, SwParams(avail_min: Ok(1024) frames, start_threshold: Ok(0) frames, stop_threshold: Ok(4096) frames)
2023-01-22 07:39:55.652566 DEBUG [src/alsadevice.rs:460] Audio device "hw:Loopback,0,0" successfully opened
2023-01-22 07:39:55.653099 DEBUG [src/bin.rs:333] Capture thread ready to start
2023-01-22 07:39:55.653526 DEBUG [src/bin.rs:336] Both capture and playback ready, release barrier
2023-01-22 07:39:55.653840 DEBUG [src/bin.rs:338] Supervisor loop starts now!
2023-01-22 07:39:55.653970 DEBUG [src/processing.rs:21] Processing loop starts now!
2023-01-22 07:39:55.654280 DEBUG [src/alsadevice.rs:863] Starting playback loop
2023-01-22 07:39:55.654646 DEBUG [src/alsadevice.rs:958] Starting captureloop
2023-01-22 07:39:55.656013 INFO [src/alsadevice.rs:592] Capture device supports rate adjust
2023-01-22 07:39:55.656267 DEBUG [src/alsadevice.rs:249] Starting capture from state: SND_PCM_STATE_PREPARED, Ready to start
2023-01-22 07:39:55.683789 INFO [src/alsadevice.rs:161] Starting playback from Prepared state
 
CamillaDSP running on a Linux UAC2 gadget host can easily be configured to automatically adapt to capture samplerate changes. This post describes a robust solution where CamillaDSP, while running inside a loop of a bash script, can toggle on demand between 44.1/16 and 48/16.

Ingredients:

The newer (as for v.6.1, v5.15 does not work yet) g_audio kernel module of the UAC2 audio gadget allows for several capture frequencies, e.g.:
$ vi /etc/modprobe.d/g_audio.conf
options g_audio c_chmask=3 p_chmask=0 c_srate=44100,48000 c_ssize=2 iProduct="UAC2Gadget"

The actual capture frequency of a running USB gadget can then basically be read out e.g. with arecord:
$ arecord -D hw:UAC2Gadget --dump-hw-params

CamillaDSP can be set to react/stop on sampling rate changes. Within the configuration file set:
stop_on_rate_change: true

Now bake it ... :

Before starting CamillaDSP, the actual samplerate of the incoming audio stream gets compared to the setting in the CamillaDSP configuration file. In case of a mismatch, the preset value in the configuration file gets updated. Wrapping this functionality in a bash script may look like that

Code:
...
camilla_config_file="/home/foo/_camilladsp/configs/cdsp.yml"

while true; do

  ###  First check for values match or mismatch ... ###

  # read the samplerate from the gadget
  capt_rate=$(arecord -D hw:UAC2Gadget --dump-hw-params 2> >(grep -i "rate:") | cut -d ":" -f2 | tr -d ' ')
  echo "script: info - capture samplerate: $capt_rate Hz"

  # check wheter the $capt_rate does match with the configuration file
  grep -R "capture_samplerate: $capt_rate" $aiop_cnfg_1 &> /dev/null  # returns successful in case of matching values
  if [ $? -eq 1  ] ; then  # the grep search was not successful, so there is a values mismatch
    # update the configuration file
    sed -i "/capture_samplerate/c\  capture_samplerate: \\$capt_rate" $camilla_config_file
  fi

  ### And then start CamillaDSP

  camilladsp $camilla_config_file ...

done
...

To make the loop functional, you will have to configure CamillaDSP to stop in case of a sample rate mismatch. Inside the configuration file:
Code:
...
devices
  ...
  # sample rates
  samplerate: 96000
  capture_samplerate: 44100
  stop_on_rate_change: true
  rate_measure_interval: 1
  ...
...
In this specific example, audio then gets resampled to 96k later on in order to have only one singe set of 96k FIR filters (and for other reasons)

... and taste it:

In my setup along with a RPi3A+ clocked at 700MHz, there is a latency of some 4 seconds from a samplerate change to a new functional streaming. CamillaDSP needs some time (around 3.5 seconds) to assess the change and stop processing.

I am well aware that there might be more elegant solutions. One of the main drawbacks of this approach consists in the fact that it only works within a Linux gadget host. But it works very well. And it is straightforward also: there is no need for further ALSA tweaks.
 
Last edited:
  • Like
Reactions: 1 user
Another option for a Linux/Camilladsp setup - the homebrew CamillaDSP CPU frequency scheduler.

Problem:
While CamillaDSP (as any other program) may need a peak processing power to initialize everything flawlessly, the CPU frequency might be throttled back for the constant streaming regime requiring much less ressources. The standard on-demand scheduler accounts for this very well, but can cause some latencies when eventually continuing switching frequencies during playback. This can lead to xruns. Setting the CPU to a fixed low frequency does not just make the startup process a wee wee bit slower, but might altogether completely clash with the higher ressorces demands during startup in case of a more complex setup. Instead, setting the CPU to a high frequency would be ok for startup/initializing, but is not necessary and also unwanted for the steady state while playback. As a compromise, the CPU frequency might be set to an intermediate, fixed value. But there is another, better solution, joining the best of all worlds.

Solution:
CamillaDSP might be started inside of a bash script. A bivalent scheduler function will be run as a daemon (e.g. in the background), initially switching the CPU to a high frequency, just before starting CamillaDSP. Acting in the background while CamillaDSP is starting up, this function keeps the CPU frequency high, letting CamillaDSP initualize with decent CPU ressources. After one second, the function reduces the CPU frequency to a sufficient and economic regime matching the steady-state demands for playback. And then exits completely.

Code:
...
function host_CPUf_set() {

  # Set higher CPU frequency for startup/initializing

  sudo cpufreq-set --freq $1
  sudo cpufreq-set --governor userspace --max=$1 --min=$1

  sleep 1  # Let CamillaDSP start/initialize at increased power for one second, then throttle back for the continous streaming

  # Set lower CPU frequency for continuous streaming

  sudo cpufreq-set --freq $2
  sudo cpufreq-set --governor userspace --max=$2 --min=$2
}

# Then, later on in the script, you may run this function ...
...
freq_init=1200000  # 1.2GHz (up to 1.4 GHz in case of the Rpi3A+)
freq_strm=700000  # 700MHz (down to 700MHz in case of the Rip3A+)
...
host_CPUf_set $freq_init $freq_strm &   # run this function in the background! So don't forget the final "&" of this command line!
sleep 0.1   # A "safety margin" to be sure that the frequency is switched high before CamillaDSP starts
camilladsp $configfile ... ...
...
 
Last edited:
  • Like
Reactions: 1 user
I messed around a bit more . I took a simple nofilters config and modified it as below and managed to start CamillaDSP. I don't have anything connected to my HDMI out at the moment. Is this config correct ?
Looks fine! Now you can change the number of channels of the playback device to 8, and add a mixer in the pipeline to copy the two incoming channels to four pairs. You can use the mixer in the step-by-step guide as a starting point.
 
  • Like
Reactions: 1 user
Addendum / Update / Erratum
Another option for a Linux/Camilladsp setup - the homebrew CamillaDSP CPU frequency scheduler.

Sorry for potentially spamming. But meanwhile, I relized that it may be better to use the still ongoingly updated linux-cpupower package than the seemingly abandoned cpufrequtils package. And I also realized that for the RpiA3+ the min CPU frequency is 600Mhz, not 700MHz. And as an additional note, also: take care to declare the frequencies in kHz (and not in MHz).

The update code snippet then looks like
Bash:
...
# CPU frequency min/max declaration
cpu_freq_lo=600000  # values in kHz!
cpu_freq_hi=1200000
...
...
function host_CPU_set() {
  # CPU frequency min/max limits system update
  sudo cpupower frequency-set --min $cpu_freq_lo --max $cpu_freq_hi &> /dev/null
 # CPU frequency toggle is performed by selecting the appropriate governors
  sudo cpupower frequency-set --governor performance &> /dev/null  # sets to max. CPU frequency
  sleep 0.3  # this is long enough
  sudo cpupower frequency-set --governor powersave &> /dev/null  # sets to min. CPU frequency
}
...
...
host_CPU_set  &
sleep 0.1
camilladsp $configfile ... ...
...
 
  • Like
Reactions: 1 users
Looks fine! Now you can change the number of channels of the playback device to 8, and add a mixer in the pipeline to copy the two incoming channels to four pairs. You can use the mixer in the step-by-step guide as a starting point.
Today, I reconfigured the mixer as a 2 in -> 8 out mono mixer and output to an HDMI-enabled 8 channel home theater receiver .

It appears that everything is as it should be on the camillaDSP / raspberry pi but I get no sound (I've verified that I'm using the correct input on the receiver and it's running in pure-direct / straight mode) .

I tried a 2in/8out mixer but have since reduced to a 2 in/2out mixer for simcity while I debug this issue.

Source for testing purposes is ipad via airplay. I can see the input and output mixers on CamillaDSP move so the signal is definitely getting in and out of the DSP stack.
I've tried plugging speakers into various channels of the receiver to no avail.

I could try a different receiver tomorrow.
For debugging purposes, I thought of outputting to the headphone jack on the raspberry pi 3 after setting the playback device to stdout but that didn't work either. Do you have any thoughts on what I could try ? There are no obvious errors on the DSP logs .

1674965636621.png


1674965807241.png
 
RPI3 Headphone out Config (I did try moving the slider all the way to the right from the default -40dB) . I also tried disconnecting HDMI in order to leave the headphone out as the only viable output

Code:
ubuntu@rpi3:~$ aplay -v -D hw:Headphones /dev/zero --dump-hw-params
Playing raw data '/dev/zero' : Unsigned 8 bit, Rate 8000 Hz, Mono
HW Params of device "hw:Headphones":
--------------------
ACCESS:  MMAP_INTERLEAVED RW_INTERLEAVED
FORMAT:  U8 S16_LE
SUBFORMAT:  STD
SAMPLE_BITS: [8 16]
FRAME_BITS: [8 128]
CHANNELS: [1 8]
RATE: [8000 192000]
PERIOD_TIME: [10000 16384000]
PERIOD_SIZE: [80 131072]
PERIOD_BYTES: [1024 524288]
PERIODS: [1 128]
BUFFER_TIME: (416 16384000]
BUFFER_SIZE: [80 131072]
BUFFER_BYTES: [1024 131072]
TICK_TIME: ALL
--------------------

1674966577477.png


1674968222732.png


Logs
Code:
2023-01-29 04:57:26.866383 INFO  [src/bin.rs:711] CamillaDSP version 1.0.3
2023-01-29 04:57:26.866755 INFO  [src/bin.rs:712] Running on linux, aarch64
2023-01-29 04:57:26.880506 INFO  [src/alsadevice.rs:592] Capture device supports rate adjust
2023-01-29 04:57:26.909800 INFO  [src/alsadevice.rs:161] Starting playback from Prepared state
2023-01-29 04:58:47.554084 WARN  [src/alsadevice.rs:157] Prepare playback after buffer underrun
 
Last edited: