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

That together with the the multi-rate support of the gadget will be a killer combo :D
I hope so :)

I renamed the repository to more specific https://github.com/pavhofman/gaudio_ctl and implemented support for missing capture or playback gadget sides, as often the gadget would be used for playback only. I finally plowed through rust lifetimes and removed most of the playback/capture duplicites in the main method. I like it better now :)

Today I submitted all the remaining gadget patches to the kernel mailing list. It will take a while to gain acceptance and bubble up to stable (probably 5.17).

Forked rpi kernel with all the submitted patches is in branch rpi-5.16.y https://github.com/pavhofman/linux-rpi/commits/rpi-5.16.y . Should someone want to test the setup on RPi4, with camilladsp at best, I will be glad to assist.
 
I'm playing with a simple dynamic range compressor I wrote in Python, mainly to experiment and gain some understanding. It's working quite well, and I'm considering including something like that in the next version of CamillaDSP (after 1.0.0).
Originally I had planned to make something fancy with separate building blocks. There would be a loudness estimation filter that outputs the signal loudness in dB. The output of that filter would then be connected to control the gain of variable gain filters, one for each channel that should be compressed. This is a more powerful approach that would make it possible to build for example multiband compressors. But the configuration for such a compressor will get large and complicated. I'm afraid this gets too difficult to use in practice.
So I have a few questions:
  • Are you interested in having a dynamic range compressor at all?
  • Do you need multiband compression?
  • Do you need to compress some channels while leaving others untouched?
  • Should the loudness estimation be based on the sum of all channels, or do you want to select what channels it's based on?
 
Just getting into room correction and DSP, and this project looks really promising!

Having read a bit in this thread and elsewhere about CamillaDSP on the RPi platform, it sounds like there are some issues with the HiFiBerry DAC+DSP, which I had otherwise listed as my first option for digital in/out duties (not using the DSP chip capabilities, since CamillaDSP will do that through the CPU).

Will CamillaDSP on an RPi 4 work well if I instead use an external USB DAC/ADC card for digital in and out? Are there any issues running both input and output on the same USB-connected device?

If a single USB-connected unit isn't going to work, would the option then be to get the DAC+DSP for torlink input and use an USB-connected unit for toslink output?

Any user experiences with above or similar combinations would be most welcome before I take the plunge!
 
My only use case for dynamic rang cmprsn: On a laptop or another Low-Fi system, where overall max. subjective SPL might be just a bit too low in a loudish surrounding, it sometimes migth be useful to be optionally able to establish some temporarly "loudness war" by dynamic range compression. It might then help with better audibility at the price of lower overall quality, all along avoiding digital levels > 0dB. Which would be perfectly fine. But I am not longing for such an option inside of CamillaDSP.
 
Just getting into room correction and DSP, and this project looks really promising!

Having read a bit in this thread and elsewhere about CamillaDSP on the RPi platform, it sounds like there are some issues with the HiFiBerry DAC+DSP, which I had otherwise listed as my first option for digital in/out duties (not using the DSP chip capabilities, since CamillaDSP will do that through the CPU).

Will CamillaDSP on an RPi 4 work well if I instead use an external USB DAC/ADC card for digital in and out? Are there any issues running both input and output on the same USB-connected device?

If a single USB-connected unit isn't going to work, would the option then be to get the DAC+DSP for torlink input and use an USB-connected unit for toslink output?

Any user experiences with above or similar combinations would be most welcome before I take the plunge!

I've done this with a MOTU Ultralite Mk5 (TOSLINK and SPDIF inputs) and an Okto dac8 pro (AES inputs), both work well with a RPi4 running Ubuntu Server 21.10 and CamillaDSP.

I've also done a setup where I use a miniDSP 2X4HD as my capture device and a MOTU M4 as my playback device, both being connected to a RPi4 via USB. I haven't been able to play directly to the 2X4HD via USB with such a setup but it works well for the TOSLINK input.

Michael
 
I've done this with a MOTU Ultralite Mk5 (TOSLINK and SPDIF inputs) and an Okto dac8 pro (AES inputs), both work well with a RPi4 running Ubuntu Server 21.10 and CamillaDSP.

I've also done a setup where I use a miniDSP 2X4HD as my capture device and a MOTU M4 as my playback device, both being connected to a RPi4 via USB. I haven't been able to play directly to the 2X4HD via USB with such a setup but it works well for the TOSLINK input.

Michael
Thanks, very helpful! The Motu is both DAC and ADC, so when you used that, was it for both input and output through the Motu?
 
Yes, I've used the MOTU as both a capture device and playback device with all the inputs and outputs (analog, SPDIF, TOSLINK). For example taking a stereo TOSLINK input and using CamillaDSP to route that in to a 3 way active speaker + sub on the analog outputs with a straight pass through to the TOSLINK output.

A few things to be aware of:
-If switching inputs you will need to change the clock source on the MOTU
-It does not have an ASRC so it is best if you are sending a constant sample rate to the digital inputs
-If you DO have a variable sample rate digital source you should be able to switch CamillaDSP configurations to accommodate but I have not done it

Michael
 
Hello Henrik,

I'm was really amazed when found your project, specially written in Rust - which is my current topic of interest. So thank you for your commitment for DIY and audio community :)

Is it possible and how difficult is to implement different filter topology configuration - something like tree branches, when an output from previous filter go into another channel? This was offered by Siegfried Linkwitz for his amazing LX521.4 speakers.

Here is an example and PDF with short explanation of benefits of this method :)

View attachment 1006830

Do these typologies (Improved and Ramification) offer any benefits to linear phase XO's which supposedly sum flat by definition ?

TIA
 
Here is a good illustation of what my prototype compressor can do. The track is Touch by Daft Punk.

Original:
touch_orig.png


Compressed with 25ms attack, 1s release, -25dB threshold, and a compression factor of 5.
touch_comp.png


The compression makes it a bit boring to listen to (as expected), but apart from being boring it sounds fine. This will work great for low volume TV watching etc.
 
Next preview of 1.0.0 released! Get it here: https://github.com/HEnquist/camilladsp/releases/tag/v1.0.0-alpha5

This introduces an improved Alsa backend that uses non-blocking i/o. This seems to avoid many device quirks. The options "avoid_blocking_read" and "retry_on_error" are still there, but "avoid_blocking_read" has no effect and "retry_on_error" is probably not needed any more. If this works well, these options will be removed in the next release.

Also the File backend got some changes. On Linux it now uses non-blocking read, which helps when piping audio from an apps that stops streaming data while the music is paused. On macOS and Windows it still uses blocking reads because the system calls I use are linux specific. It would not be too hard to rewrite that to use only posix calls to also support macOS. Windows on the other hand is of course completely different, there a separate implementation is needed.
 
Apologies if I have missed this, but is there a balance type filter? One which simultaneously offers to increase one channel's volume and reduce another? I guess changing left/right balance is an obvious application. In my case I've just set up my rear stereo pair and want to be able to easily change the balance between front and rear.

It's no big deal if not - I can create some simple logic in Node-RED, but thought I'd ask. I guess there may be some generally accepted protocol for the relative positive and negative gain to apply when panning, that I guess I can research. Thanks
 
Apologies if I have missed this, but is there a balance type filter? One which simultaneously offers to increase one channel's volume and reduce another?
No there is no such filter. All the filters act on a single channel, not a pair. You will need to build it using individual Gain filters.
How would you use Node-RED for this? It looks neat but I haven't used it so don't know what it can do.
 
Apologies if I have missed this, but is there a balance type filter? One which simultaneously offers to increase one channel's volume and reduce another? I guess changing left/right balance is an obvious application. In my case I've just set up my rear stereo pair and want to be able to easily change the balance between front and rear.

It's no big deal if not - I can create some simple logic in Node-RED, but thought I'd ask. I guess there may be some generally accepted protocol for the relative positive and negative gain to apply when panning, that I guess I can research. Thanks
Just had a quick look at how I might workaround, and it's very clunky - in the absence of another Volume/Loudness type filter that can be controlled via the websocket, the only option I can see would be multiple configurations, one for each incremental change in the panning front/back. Ugly. Or is there a better way?

Of course, ideally there would be another filter type controllable via the websocket.
 
No there is no such filter. All the filters act on a single channel, not a pair. You will need to build it using individual Gain filters.
How would you use Node-RED for this? It looks neat but I haven't used it so don't know what it can do.
Thanks for the reply. I may simply implement three configurations for stereo source: front only; rear only; and balanced. And then I've to work out how to take 5.1 from Roon and downmix to 4.1.

I haven't been a programmer since I did a bit of Algol/Pascal/Fortran some 40 years ago, since when I've only dabbled in support of hobbies like this. Node-RED is a cool message/flow oriented platform with JavaScript function nodes. Being flow oriented, it's a breeze to handle sending messages out, and then picking up the reply as a new flow and dealing with the next stage of the task. So after sending a message to CamillaDSP's websocket to change the configuration file, the confirmation reply triggers a second flow to reload the configuration.

I'm already using Node-RED to listen to mqtt messages triggered by button presses on my Logitech Harmony remote to change the volume and mute/unmute. I'll do something similar to map button presses on the remote to changes between the CamillaDSP configurations I define.
 
I would suggest adding four Gain filters in your config, called "left", "right", "front", "rear". Then add two for each speaker, like "front" and "left" for the front left speaker etc.
Then to change left-right balance, get the correct config as json over the websocket. Json works great with JavaScript, you can easily convert it to an object. Adjust the gain value for "left" and "right", convert it back to json format. Finally upload the modified config. Only the changes will be applied, and it will be gapless.

I'll take a look at Node-RED, could be useful. Can you export a design to some kind of file that can be shared?
 
I've done this with a MOTU Ultralite Mk5 (TOSLINK and SPDIF inputs) and an Okto dac8 pro (AES inputs), both work well with a RPi4 running Ubuntu Server 21.10 and CamillaDSP.

I've also done a setup where I use a miniDSP 2X4HD as my capture device and a MOTU M4 as my playback device, both being connected to a RPi4 via USB. I haven't been able to play directly to the 2X4HD via USB with such a setup but it works well for the TOSLINK input.

Michael

Wow! Okto dac8 pro looks very interesting - I actually was looking for DAC that can handle 8-10 channels for LX521.4 speakers. Is it gonna work fine over USB with RPi4 and can really handle 8 channels digital crossover processing with CamilaDSP?
 
The channels are always referenced by index. I built the improved topology to show:
View attachment 983837
...

Merry Christmas everyone !!!

I am trying to do a multi-step pipeline using wav/FIR filters including XO and AllPass wav filters. I am noticing when I add the 3rd step (8to8 Mixer) using AllPass wav filters, the high end rolls off to @ 7kHz from 24kHz.

The Step[1-3] Topology is:

Stereo-2-channel (AllPass or HouseCurve) -> 2to8 Mixer (apply 8 XO FIRs) -> 8to8 Mixer (AllPass or TBD individual driver corrections)

The FIR XO and AllPass filters are made with RePhase. Initial topology tests is:

Step 1 -> Step 2
AllPass -> XO # WORKS, NO HF Rolloff

Step 1 -> Step 2 -> Step 3
AllPass -> XO -> AllPass # HF Rolloff

I made the AllPass in RePhase @ 384kHz to extend it's high frequency as much as RePhase will allow (to 192kHz). All other filters, measurements and playback is @ 172.4kHz. This was after trying it at 172.4kHz. Extending it from 172.4kHz to 384kHz made no difference.

Any ideas why adding the 3rd step would cause HF rolloffs ?

Thanks much.

Code:
devices:

  samplerate: 176400
  chunksize: 4096
  queuelimit: 1

  enable_resampling:  true
  capture_samplerate: 176400     # Dynamic variable

  resampler_type: Synchronous

  capture:

    channels: 2     # Dynamic variable
    format: S32LE     # Dynamic variable
    extra_samples: 32768     # Dynamic variable

    type: File
    filename: "/dev/stdin"

  playback:

    channels: 8   # 8-channel DAC or 8-channel output file
    format: S32LE

    type: Alsa
    device: "hw:DAC8PRO"

#    type: File
#    filename: "/tmp/ramdisk/176400_S32LE_8_chan_192000_raw.s32"

mixers:

  mixer2to8:
    channels:
      in: 2
      out: 8

    mapping:
# Tweet
      - dest: 0
        sources:
          - channel: 0
            gain: 0
            inverted: false
      - dest: 1
        sources:
          - channel: 1
            gain: 0
            inverted: false
# Mid
      - dest: 2
        sources:
          - channel: 0
            gain: 0
            inverted: false
      - dest: 3
        sources:
          - channel: 1
            gain: 0
            inverted: false
# Bass
      - dest: 4
        sources:
          - channel: 0
            gain: 0
            inverted: false
      - dest: 5
        sources:
          - channel: 1
            gain: 0
            inverted: false
# Sub
      - dest: 6
        sources:
          - channel: 0
            gain: 0
            inverted: false
      - dest: 7
        sources:
          - channel: 1
            gain:  0
            inverted: false
  
  mixer8to8:
    channels:
      in: 8
      out: 8

    mapping:
# Tweet
      - dest: 0
        sources:
          - channel: 0
            gain: 0
            inverted: false
      - dest: 1
        sources:
          - channel: 1
            gain: 0
            inverted: false
# Mid
      - dest: 2
        sources:
          - channel: 2
            gain: 0
            inverted: false
      - dest: 3
        sources:
          - channel: 3
            gain: 0
            inverted: false
# Bass
      - dest: 4
        sources:
          - channel: 4
            gain: 0
            inverted: false
      - dest: 5
        sources:
          - channel: 5
            gain: 0
            inverted: false
# Sub
      - dest: 6
        sources:
          - channel: 6
            gain: 0
            inverted: false
      - dest: 7
        sources:
          - channel: 7
            gain:  0
            inverted: false
  


filters:

  ALLPASS_FIR:
    type: Conv
    parameters:
 
      filename: /tmp/ramdisk/filters/Mono_176400_L_ALLPASS.wav
      type: Wav
      channel: 0

  L_ST_IR_FIR:
    type: Conv
    parameters:
 
      filename: /tmp/ramdisk/filters/Mono_176400_L_ST_IR_D64_NoPsyc_1.40_2.0.wav
      type: Wav
      channel: 0
 

# Fullrange "DRC / house-curves"
  L_HOUSE_FIR:
    type: Conv
    parameters:
 
      filename: /tmp/ramdisk/filters/Mono_176400_L_ALLPASS.wav
      type: Wav
      channel: 0
 
  R_HOUSE_FIR:
    type: Conv
    parameters:
 
      filename: /tmp/ramdisk/filters/Mono_176400_R_ALLPASS.wav
      type: Wav
      channel: 0


# Tweet XO - Highpass
  TWEET_XO_FIR:
    type: Conv
    parameters:

      filename: /tmp/ramdisk/filters/Ribbon_1K_384_3K_24_176400.wav
      type: Wav
      channel: 0

# Mid XO - Bandpass
  MID_XO_FIR:
    type: Conv
    parameters:

      filename: /tmp/ramdisk/filters/NEO8_250_384_3K_24_176400.wav
      type: Wav
      channel: 0

# Bass XO - Bandpass
  BASS_XO_FIR:
    type: Conv
    parameters:

      filename: /tmp/ramdisk/filters/HP_50_250_176400.wav
      type: Wav
      channel: 0

# Sub XO - Lowpass
  SUB_XO_FIR:
    type: Conv
    parameters:

      filename: /tmp/ramdisk/filters/LP_50_176400.wav
      type: Wav
      channel: 0





# Misc Gains ...

  gain_INVERT:
    type: Gain
    parameters:
      gain: 0
      inverted: true

  gain_MUTE:
    type: Gain
    parameters:
      gain: -120
      inverted: false

  gain_0:
    type: Gain
    parameters:
      gain: 0
      inverted: false

# Stereo Gains

  gain_LEFT:
    type: Gain
    parameters:
      gain: 2.4
      inverted: false

  gain_RIGHT:
    type: Gain
    parameters:
      gain: 0
      inverted: false


# Driver Gains

  gain_TWEET:
    type: Gain
    parameters:
      gain: 6.0
      inverted: false

  gain_MID:
    type: Gain
    parameters:
      gain: -7.0
      inverted: false

  gain_BASS:
    type: Gain
    parameters:
      gain: 0.0
      inverted: false

  gain_SUB:
    type: Gain
    parameters:
      gain:  0.0
      inverted: false



# Driver Delays

  delay_TWEET:
    type: Delay
    parameters:
      delay: 17
      unit: ms

  delay_MID:
    type: Delay
    parameters:
      delay: 17
      unit: ms

  delay_BASS:
    type: Delay
    parameters:
      delay: 17.62
      unit: ms

  delay_SUB:
    type: Delay
    parameters:
      delay: 0
      unit: ms



pipeline:

# Stereo Input matrix

  - type: Filter
    channel: 0
    names:
      - L_HOUSE_FIR
      #- gain_0
      - gain_LEFT
  
  - type: Filter
    channel: 1
    names:
      - R_HOUSE_FIR
      #- gain_0
      - gain_RIGHT
 

# [Stereo-8] channel fanout and individual driver XO matrix

  - type: Mixer
    name: mixer2to8


# Tweet
  - type: Filter
    channel: 0
    names:
      - TWEET_XO_FIR
      - gain_TWEET
      - delay_TWEET

  - type: Filter
    channel: 1
    names:
      - TWEET_XO_FIR
      - gain_TWEET
      - delay_TWEET


# Mid
  - type: Filter
    channel: 2
    names:
      - MID_XO_FIR
      - gain_MUTE
      - delay_MID

  - type: Filter
    channel: 3
    names:
      - MID_XO_FIR
      - gain_MUTE
      - delay_MID


# Bass
  - type: Filter
    channel: 4
    names:
      - BASS_XO_FIR
      - gain_MUTE
      - delay_BASS

  - type: Filter
    channel: 5
    names:
      - BASS_XO_FIR
      - gain_MUTE
      - delay_BASS


# Sub
  - type: Filter
    channel: 6
    names:
      - SUB_XO_FIR
      - gain_SUB
      - delay_SUB

  - type: Filter
    channel: 7
    names:
      - SUB_XO_FIR
      - gain_SUB
      - delay_SUB


# DRC-FIR Individual driver PRE-filter experiments

  - type: Mixer
    name: mixer8to8


# Tweet
  - type: Filter
    channel: 0
    names:
      - ALLPASS_FIR

  - type: Filter
    channel: 1
    names:
      - ALLPASS_FIR


# Mid
  - type: Filter
    channel: 2
    names:
      - ALLPASS_FIR

  - type: Filter
    channel: 3
    names:
      - ALLPASS_FIR


# Bass
  - type: Filter
    channel: 4
    names:
      - ALLPASS_FIR

  - type: Filter
    channel: 5
    names:
      - ALLPASS_FIR


# Sub - ST_IR
  - type: Filter
    channel: 6
    names:
      - ALLPASS_FIR

  - type: Filter
    channel: 7
    names:
      - ALLPASS_FIR
 
Last edited:
Wow! Okto dac8 pro looks very interesting - I actually was looking for DAC that can handle 8-10 channels for LX521.4 speakers. Is it gonna work fine over USB with RPi4 and can really handle 8 channels digital crossover processing with CamilaDSP?
FWIW, OKTO Research just announced they are opening back up for new orders. They had stopped taking orders for @ 6 months due to numerous global supply chain issues causing production backlogs.

As for 8 channels on the DAC8 with CamillaDSP, yes it works, but I am using a PC [(8 and 10) x 256K taps @ 176.4kHz]. Never used a RPi4, so can't answer that question.