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

In case of static filters, one could also make one IIR filter with coefficients suitable for all those common sample rates. Here's an example (Linkwitz-Riley Crossfeed using 1st order filters):

Code:
function [b, a] = LinkwiztRileyCrossfeed_HSF1(x)
b(1) = 8.92486106577848E-39*x^7 - 1.23294609205113E-32*x^6 + 6.90481427610453E-27*x^5 - 2.03708367949616E-21*x^4 + 3.45581026357451E-16*x^3 - 3.44913669956660E-11*x^2 + 4.56707825960187E-04*x + 1.06218214714401E+00;
b(2) = -8.92486106186911E-39*x^7 + 1.23294609153202E-32*x^6 - 6.90481427335710E-27*x^5 + 2.03708367874968E-21*x^4 - 3.45581026245418E-16*x^3 + 3.44913669864111E-11*x^2- 4.56707825959798E-04*x + 1.18185476145362E+00;
a(1) = 1.12357343915144E-38*x^7 - 1.55218716653502E-32*x^6 + 8.69264615528866E-27*x^5 - 2.56453640990810E-21*x^4 + 4.35060735892690E-16*x^3 - 4.34220583966637E-11*x^2+ 5.74961087866485E-04*x + 1.04668900667368E+00;
a(2) = -1.12357343917162E-38*x^7 + 1.55218716656155E-32*x^6 - 8.69264615542657E-27*x^5+ 2.56453640994445E-21*x^4 - 4.35060735897871E-16*x^3 + 4.34220583970547E-11*x^2 - 5.74961087866498E-04*x + 1.19734790193039E+00;
Code:
function [b, a] = LinkwiztRileyCrossfeed_LPF1(x)
b(1) = -3.73347936059962E-38*x^7 + 5.15879461936065E-32*x^6 - 2.88994018251960E-26*x^5 + 8.52985718947504E-21*x^4 - 1.44803393019973E-15*x^3 + 1.44677547605219E-10*x^2 - 8.31737045233532E-06*x + 2.52213714392960E-01;
b(2) = -3.73347936059962E-38*x^7 + 5.15879461936065E-32*x^6 - 2.88994018251960E-26*x^5+ 8.52985718947504E-21*x^4 - 1.44803393019973E-15*x^3 + 1.44677547605219E-10*x^2 - 8.31737045233532E-06*x + 2.52213714392960E-01;
a(1) = -4.32331264486865E-38*x^7 + 5.98574666256680E-32*x^6 - 3.36300126624330E-26*x^5+ 9.96980705947461E-21*x^4 - 1.70404536083758E-15*x^3 + 1.72154874571558E-10*x^2 - 1.00923085954559E-05*x + 2.31848477835107E+00;
a(2) = -6.23656164833149E-38*x^7 + 8.60552796982759E-32*x^6 - 4.81098393488902E-26*x^5+ 1.41562723854473E-20*x^4 - 2.39161308489201E-15*x^3 + 2.37055025416788E-10*x^2 - 1.34327675984916E-05*x - 1.60511666732906E+00;
where x = sample rate (fs).
Above polynomials returns exact (R^2=1) coefficients but, if the accuracy isn't important, most of those (above) original coefficients for each sample rate can be restored by using simple linear interpolation (a*x+c) with R^2 = ~ 0.99999999. NOTE, you may need to swap a and b coefficients to get proper response!
It should be possible to do the same with the built-in filters in CamillaDSP. The filters are a 1st order low pass, and a 1st order shelf, right? These are available in CamillaDSP, and the biquad coeffs are calculated for the given sample rate.
 
What do you use for volume control with the Evolve DAC?

I use the Yamaha xc50 as a player. And it has a pre-out. It outputs everything in 48k and has an adjustable digital output. My goal is to build a "normal user" application that can be switched on with one switch and then controlled with a reasonable app. CamillaDSP starts automatically and the player continues playing from the last setting. And everything with optimal correction. It is DIY but user friendly.


img_20200607_135207ymkag.jpg


The raspi is in the black box under the yamaha xc50 :cool:

Roland
 
I use the Yamaha xc50 as a player. And it has a pre-out. It outputs everything in 48k and has an adjustable digital output. My goal is to build a "normal user" application that can be switched on with one switch and then controlled with a reasonable app. CamillaDSP starts automatically and the player continues playing from the last setting. And everything with optimal correction. It is DIY but user friendly.


img_20200607_135207ymkag.jpg


The raspi is in the black box under the yamaha xc50 :cool:

Roland
Ok thanks! And that leads to another question :)

What do you use to get the signal from the xc50 into the pi? A hat with an spdif input?
 
Yes the DIGI IO+ Card has a spdif input and output

HiFiBerry Digi+ I/O | HiFiBerry

there is only a problem of sample rate switching :-(
Ok! The rate switching is something I want to do something about. Could you perhaps post the output of:
Code:
amixer -c 2 controls
but replacing the 2 with the card number for the IO+ card?

It's a bit strange that it tries to set the rate of that card, it should only happen for a loopback (that has a control named "PCM Rate Shift 100000"). Could it be that the IO+ has that control as well?
 
pi@musicpi:~ $ amixer -c 2 controls
numid=1,iface=MIXER,name='Tx Source'

pi@musicpi:~ $ arecord -l
**** Liste der Hardware-Geräte (CAPTURE) ****
Karte 0: Loopback [Loopback], Gerät 0: Loopback PCM [Loopback PCM]
Sub-Geräte: 8/8
Sub-Gerät #0: subdevice #0
Sub-Gerät #1: subdevice #1
Sub-Gerät #2: subdevice #2
Sub-Gerät #3: subdevice #3
Sub-Gerät #4: subdevice #4
Sub-Gerät #5: subdevice #5
Sub-Gerät #6: subdevice #6
Sub-Gerät #7: subdevice #7
Karte 0: Loopback [Loopback], Gerät 1: Loopback PCM [Loopback PCM]
Sub-Geräte: 8/8
Sub-Gerät #0: subdevice #0
Sub-Gerät #1: subdevice #1
Sub-Gerät #2: subdevice #2
Sub-Gerät #3: subdevice #3
Sub-Gerät #4: subdevice #4
Sub-Gerät #5: subdevice #5
Sub-Gerät #6: subdevice #6
Sub-Gerät #7: subdevice #7
Karte 2: sndrpihifiberry [snd_rpi_hifiberry_digi], Gerät 0: HifiBerry Digi HiFi wm8804-spdif-0 [HifiBerry Digi HiFi wm8804-spdif-0]
Sub-Geräte: 0/1
Sub-Gerät #0: subdevice #0
 
It should be possible to do the same with the built-in filters in CamillaDSP. The filters are a 1st order low pass, and a 1st order shelf, right? These are available in CamillaDSP, and the biquad coeffs are calculated for the given sample rate.

Sure it's possible but, my point in doing it "my way" was
- no need for conditional selection of correct coefficients
- calculation of coefficients is done much much faster
 
Ok no such control, as expected (would have been pretty strange if it had existed).
Would you mind posting your config files for the 4 and 8 channel setups?

here is the config for 8 channels. It is just a double 4 channel to check the output of the envolve hdmi dac. If you delete the channels 4 to 7 and adjust the chunksize, you get the 4 channel version.
Code:
# stereo to 8 ch 
devices:
  samplerate: 48000
  chunksize: 4096
#  queuelimit: 128 (*)
#  silence_threshold: -60 (*)
#  silence_timeout: 3.0 (*)
  target_level: 2048
  adjust_period: 4

  capture:
    type: Alsa
    channels: 2
    device: "hw:2,0"
    format: S16LE

  playback:
    type: Alsa
    channels: 8
    device: "hw:1,1"
    format: S16LE

mixers:
  2to8:
    channels:
      in: 2
      out: 8
    mapping:
      - dest: 0
        sources:
          - channel: 0
            gain: 0
            inverted: false
      - dest: 1
        sources:
          - channel: 0
            gain: 0
            inverted: false
      - dest: 2
        sources:
          - channel: 1
            gain: 0
            inverted: false
      - dest: 3
        sources:
          - channel: 1
            gain: 0
            inverted: false

      - dest: 4
        sources:
          - channel: 0
            gain: 0
            inverted: false
      - dest: 5
        sources:
          - channel: 0
            gain: 0
            inverted: false
      - dest: 6
        sources:
          - channel: 1
            gain: 0
            inverted: false
      - dest: 7
        sources:
          - channel: 1
            gain: 0
            inverted: false

filters:
  left_low:
    type: Conv
    parameters:
      type: File 
      filename: /media/pi/BRUTEFIR21/filter/UB/Cor1L48.dbl
      format: FLOAT64LE
  left_high:
    type: Conv
    parameters:
      type: File 
      filename: /media/pi/BRUTEFIR21/filter/UB/Cor2L48.dbl
      format: FLOAT64LE

  right_low:
    type: Conv
    parameters:
      type: File 
      filename: /media/pi/BRUTEFIR21/filter/UB/Cor1R48.dbl
      format: FLOAT64LE
  right_high:
    type: Conv
    parameters:
      type: File 
      filename: /media/pi/BRUTEFIR21/filter/UB/Cor2R48.dbl
      format: FLOAT64LE

  left_low1:
    type: Conv
    parameters:
      type: File 
      filename: /media/pi/BRUTEFIR21/filter/UB/Cor1L48.dbl
      format: FLOAT64LE
  left_high1:
    type: Conv
    parameters:
      type: File 
      filename: /media/pi/BRUTEFIR21/filter/UB/Cor2L48.dbl
      format: FLOAT64LE

  right_low1:
    type: Conv
    parameters:
      type: File 
      filename: /media/pi/BRUTEFIR21/filter/UB/Cor1R48.dbl
      format: FLOAT64LE
  right_high1:
    type: Conv
    parameters:
      type: File 
      filename: /media/pi/BRUTEFIR21/filter/UB/Cor2R48.dbl
      format: FLOAT64LE


pipeline:
  - type: Mixer
    name: 2to8

  - type: Filter
    channel: 0
    names:
      - left_low
 #     - peak1
  - type: Filter
    channel: 1
    names:
      - left_high
 #     - peak1
  - type: Filter
    channel: 2
    names:
      - right_low
  - type: Filter
    channel: 3
    names:
      - right_high

  - type: Filter
    channel: 4
    names:
      - left_low1
 #     - peak1
  - type: Filter
    channel: 5
    names:
      - left_high1
 #     - peak1
  - type: Filter
    channel: 6
    names:
      - right_low1
  - type: Filter
    channel: 7
    names:
      - right_high1
 
Ah I see. Then it's behaving as expected for the 8-channel version, and it should be displaying the same setspeed messages also for the 4-channel case.
That being said, this combination doesn't actually support rate adjust, so you can disable it by setting adjust_period = 0.
Rate adjust is only supported by either capturing from an alsa loopback, or by using an asynchronous resampler. Resampling isn't available in the master branch yet, so you would have to go for develop.


Another solution (if you end up having trouble with mismatched rates) is to use the silence_timeout and silence_threshold to pause processing whenever the music is silent. When the playback restarts after being paused, the timing is reset, meaning that any built up sample clock drift since the last restart will be reset as well.
 
Hi,

This software looks great. I have been playing around with the Jrivers DSP with FIR filters but I find it a little limited. This software with biquad and many other crossover filters types in addition to FIR is really nice.

I have installed it on raspberry pi4 without any problems however I cant seem to figure out how to get around this problem:

pi@musicpi:~/camilladsp/target/release $ camilladsp -v /home/pi/camilladsp/exampleconfigs/nofiltersV2.yml
[2020-06-11T01:59:31Z DEBUG camilladsp] Read config file Some("/home/pi/camilladsp/exampleconfigs/nofiltersV2.yml")
[2020-06-11T01:59:31Z DEBUG camilladsp] Config is valid
[2020-06-11T01:59:31Z DEBUG camilladsp] Wait for config
[2020-06-11T01:59:31Z DEBUG camilladsp] Config ready
[2020-06-11T01:59:31Z DEBUG camilladsp::filters] Build new pipeline
[2020-06-11T01:59:31Z DEBUG camilladsp::processing] build filters, waiting to start processing loop
[2020-06-11T01:59:31Z ERROR camilladsp] Playback error: ALSA function 'snd_pcm_hw_params_set_period_size' failed with error 'EINVAL: Invalid argument'
[2020-06-11T01:59:31Z DEBUG camilladsp] Exiting
[2020-06-11T01:59:31Z DEBUG camilladsp::alsadevice] Opened audio output "hw:Loopback,0,0" with parameters: HwParams { channels: Ok(2), rate: "Ok(48000) 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)



The config file is attached.

I assume the problem is with the playback device. I have used speaker-test to check the audio output at 4800 S16LE 2 channels, periods 128 and this played OK. This is what I got on the screen:

Playback device is hw:1,0
Stream parameters are 48000Hz, S16_LE, 2 channels
Using 16 octaves of pink noise
Rate set to 48000Hz (requested 48000Hz)
Buffer size range from 480 to 32768
Period size range from 480 to 32768
Using max buffer size 32768
Periods = 128
was set period_size = 480
was set buffer_size = 32768
0 - Front Left
1 - Front Right

I am fairly new to linux so am having a hard time getting my head around the ALSA stuff.


Any help would be appreciated.
 
The speaker-test param periods has a different meaning than period_size.


Henrik: Perhaps set_period_size_near should be used instead of the hard-limited set_period_size in camilladsp/alsadevice.rs at e2ccc5f61a721906d46d59f9516436d7f380a1a2 * HEnquist/camilladsp * GitHub ?

Look at the difference at handling the variable dir in snd_pcm_hw_param_set_near alsa-lib/pcm_params.c at 040cfea1778a06e2e0e0a73a3e580b5a2c19ecb4 * tiwai/alsa-lib * GitHub and snd_pcm_hw_param_set alsa-lib/pcm_params.c at 040cfea1778a06e2e0e0a73a3e580b5a2c19ecb4 * tiwai/alsa-lib * GitHub

The _near version really finds the nearest available value, using _min, _max etc., while the plain version just extends the interval by 1 down (or 1 up) alsa-lib/pcm_params.c at 040cfea1778a06e2e0e0a73a3e580b5a2c19ecb4 * tiwai/alsa-lib * GitHub

alsa::ValueOr - Rust

I do not really understand what dir as c_int does in pcm.rs.html -- source but I assume the correct value should be -1, 0, 1 for each enum item.
 
The speaker-test param periods has a different meaning than period_size.


Henrik: Perhaps set_period_size_near should be used instead of the hard-limited set_period_size in camilladsp/alsadevice.rs at e2ccc5f61a721906d46d59f9516436d7f380a1a2 * HEnquist/camilladsp * GitHub ?

Look at the difference at handling the variable dir in snd_pcm_hw_param_set_near alsa-lib/pcm_params.c at 040cfea1778a06e2e0e0a73a3e580b5a2c19ecb4 * tiwai/alsa-lib * GitHub and snd_pcm_hw_param_set alsa-lib/pcm_params.c at 040cfea1778a06e2e0e0a73a3e580b5a2c19ecb4 * tiwai/alsa-lib * GitHub

The _near version really finds the nearest available value, using _min, _max etc., while the plain version just extends the interval by 1 down (or 1 up) alsa-lib/pcm_params.c at 040cfea1778a06e2e0e0a73a3e580b5a2c19ecb4 * tiwai/alsa-lib * GitHub

alsa::ValueOr - Rust

I do not really understand what dir as c_int does in pcm.rs.html -- source but I assume the correct value should be -1, 0, 1 for each enum item.
Ah I see thanks! It wasn't clear to me that using the set_period_size with the ValueOr::Nearest wouldn't actually make a proper effort to search for the nearest available value. I'll switch to the _near version.


@ChrisPatlach: Could you just try doubling or quadrupling the chunksize to see if that solves it for now?
What sound card are you using?
 
Alsa API is really complicated :)

Please what does that dir as c_int do? Is the code actually correct (does not it yield 0, 1, 2 instead of -1, 0, 1)?
The ValueOr enum is define like this:

Code:
 pub enum ValueOr {
     /// The value set is the submitted value, or less
     Less = -1,
     /// The value set is the submitted value, or the nearest
     Nearest = 0,
     /// The value set is the submitted value, or greater
     Greater = 1,
 }
Casting that to an int gives the associated values, so it's fine.
 
Thanks for the tips and help Henrik and phofman.

Changing the chunksize to 4096 for the raspberry pi 4 audio out cleared the error for both the hdmi1 audio out (hw:1,0) and the analogue "speaker out" (hw:0,0).


pi@musicpi:~ $ camilladsp -v /home/pi/camilladsp/exampleconfigs/nofiltersV2.yml
[2020-06-12T15:58:25Z DEBUG camilladsp] Read config file Some("/home/pi/camilladsp/exampleconfigs/nofiltersV2.yml")
[2020-06-12T15:58:25Z DEBUG camilladsp] Config is valid
[2020-06-12T15:58:25Z DEBUG camilladsp] Wait for config
[2020-06-12T15:58:25Z DEBUG camilladsp] Config ready
[2020-06-12T15:58:25Z DEBUG camilladsp::filters] Build new pipeline
[2020-06-12T15:58:25Z DEBUG camilladsp::processing] build filters, waiting to start processing loop
[2020-06-12T15:58:25Z DEBUG camilladsp::alsadevice] Opened audio output "hw:1,0" with parameters: HwParams { channels: Ok(2), rate: "Ok(48000) Hz", format: Ok(S16LE), access: Ok(RWInterleaved), period_size: "Ok(512) frames", buffer_size: "Ok(8192) frames" }, SwParams(avail_min: Ok(512) frames, start_threshold: Ok(3584) frames, stop_threshold: Ok(8192) frames)
[2020-06-12T15:58:25Z DEBUG camilladsp] Playback thread ready to start
[2020-06-12T15:58:25Z DEBUG camilladsp::alsadevice] Opened audio output "hw:Loopback,0,0" with parameters: HwParams { channels: Ok(2), rate: "Ok(48000) Hz", format: Ok(S16LE), access: Ok(RWInterleaved), period_size: "Ok(512) frames", buffer_size: "Ok(8192) frames" }, SwParams(avail_min: Ok(512) frames, start_threshold: Ok(3584) frames, stop_threshold: Ok(8192) frames)
[2020-06-12T15:58:25Z DEBUG camilladsp] Capture thread ready to start
[2020-06-12T15:58:25Z DEBUG camilladsp::alsadevice] Starting playback loop
[2020-06-12T15:58:25Z DEBUG camilladsp::alsadevice] Starting captureloop
[2020-06-12T15:58:25Z INFO camilladsp::alsadevice] Capture device supports rate adjust


pi@musicpi:~ $ camilladsp -v /home/pi/camilladsp/exampleconfigs/nofiltersV2.yml
[2020-06-12T16:19:36Z DEBUG camilladsp] Read config file Some("/home/pi/camilladsp/exampleconfigs/nofiltersV2.yml")
[2020-06-12T16:19:36Z DEBUG camilladsp] Config is valid
[2020-06-12T16:19:36Z DEBUG camilladsp] Wait for config
[2020-06-12T16:19:36Z DEBUG camilladsp] Config ready
[2020-06-12T16:19:36Z DEBUG camilladsp::filters] Build new pipeline
[2020-06-12T16:19:36Z DEBUG camilladsp::processing] build filters, waiting to start processing loop
[2020-06-12T16:19:36Z DEBUG camilladsp::alsadevice] Opened audio output "hw:0,0" with parameters: HwParams { channels: Ok(2), rate: "Ok(48000) Hz", format: Ok(S16LE), access: Ok(RWInterleaved), period_size: "Ok(512) frames", buffer_size: "Ok(8192) frames" }, SwParams(avail_min: Ok(512) frames, start_threshold: Ok(3584) frames, stop_threshold: Ok(8192) frames)
[2020-06-12T16:19:36Z DEBUG camilladsp::alsadevice] Opened audio output "hw:Loopback,1,0" with parameters: HwParams { channels: Ok(2), rate: "Ok(48000) Hz", format: Ok(S16LE), access: Ok(RWInterleaved), period_size: "Ok(512) frames", buffer_size: "Ok(8192) frames" }, SwParams(avail_min: Ok(512) frames, start_threshold: Ok(3584) frames, stop_threshold: Ok(8192) frames)
[2020-06-12T16:19:36Z DEBUG camilladsp] Playback thread ready to start
[2020-06-12T16:19:36Z DEBUG camilladsp] Capture thread ready to start
[2020-06-12T16:19:36Z DEBUG camilladsp::alsadevice] Starting playback loop
[2020-06-12T16:19:36Z DEBUG camilladsp::alsadevice] Starting captureloop
[2020-06-12T16:19:36Z INFO camilladsp::alsadevice] Capture device supports rate adjust
[2020-06-12T16:19:36Z WARN camilladsp::alsadevice] Prepare playback