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

Banned Sock Puppet
Joined 2020
v0.4.0 is released: Release v0.4.0 * HEnquist/camilladsp * GitHub
See the changelog and the readme for details!


Got to give Henrik a tremendous vote of thanks for all the help over the last days.

It wasn't a walk in the park. :rolleyes:



I have a project which entailed prototype running Raspberry OS-x86 "live install" on a SD card via a USB reader.
It runs on an old but good IBM x86 notebook, with multi boot.
(4 different OS no less & 2 hard disks, one of which is in a draw-thanks to IBM's "meccano" boy's toys concept ).



The first test - "proof of concept" was to make DRC (if you can call it that) on an old Sennheiser headphone* which had known issues.
I have (new gen) sound engineers turning their noses up at them, - until I come out with "don't knock it 'til you tried it"!
:)

The trick bit, is having 2 sound cards in the Notebook, one of them being one of the very few rare pro sound cards in a PCMCIA reader (no USB wanted there, as it's direct to PCI - cardbus), and has balanced in/out XLR + Spdif native.


It has a wiring harness that pours out of the end of the PC card with on some of them the connectors to connect an external clock chip.

That is super, and the AKM DAC is not so bad.



I have to say after making a rough and ready (thanks henrik!), correction to the Senn's spikes, (for which we had proper data from 1987) the headphones were transformed out of all recognition.


My feeling at 2am in freezing URAL when it worked was WOW!



Being as I strongly believe in a stepwise approach,-

test, validate, then adopt,



I felt this was a very useful modular approach to optimising the ARM version of Raspberry OS with a high end DAC on the only just released CM4 boards.


FYI the new CM4 also has an external wifi antenna connector and PCI-e slot.
This makes them an ideal candidate for insertion into a DANTE system..


I have spoken to the company making Linux support for a Dante board that goes into that slot, so watch this space.


They are developing some stuff, and I have the sw engineer's card.

Headphone DSP correction in studios, you bet!


*The headphone was an ancient HD414.
It now sounds much better than many modern ones.
 
Hi, everyone, I am just learning of this wonderful program...Can someone report on the latency of processing this way....I've used equalizer api in the past the latency it ads to the system is higher than running eq on my minidsp...how does latency fair on this platform...I would like to see a 2 channel three way be be equalized very well per channel.
 
Got to give Henrik a tremendous vote of thanks for all the help over the last days.

It wasn't a walk in the park. :rolleyes:
Thanks! So now it's time to continue with setting up the gui :)



Hi, everyone, I am just learning of this wonderful program...Can someone report on the latency of processing this way....I've used equalizer api in the past the latency it ads to the system is higher than running eq on my minidsp...how does latency fair on this platform...I would like to see a 2 channel three way be be equalized very well per channel.
The total latency depends on many things, but if you go with IIR filters, or FIR where the main impulse comes near the beginning, and a not too large chunksize (1024 is usually a good start) then there should be very little latency added.

Are you using Windows? I haven't tried to measure how much delay is added by going through VB-Cable+CamillaDSP chain, so can't give a number.
I used EqualizerAPO before. I did not notive any added delay with IIR filters, and only the expected delay from FIR. How was your setup?
 
Banned Sock Puppet
Joined 2020
Just a little extra.
Latency seems to quite OK to begin with, but if left running for many happy hours will give a 2-3sec delay when run with video.


After reboot, the audio-vid sync came back to normal.
Not sure why this could be but bears looking at.


I suppose not many people imagine to use a DSP with TV type sound (mpg4) but it can be useful.
 
Just a little extra.
Latency seems to quite OK to begin with, but if left running for many happy hours will give a 2-3sec delay when run with video.


After reboot, the audio-vid sync came back to normal.
Not sure why this could be but bears looking at.


I suppose not many people imagine to use a DSP with TV type sound (mpg4) but it can be useful.
This is a mismatch in rate between the Alsa loopback and your soundcard. It's easy to fix! The trick is that the loopback allows its rate to be fine-tuned to match another device, and CamillaDSP can do that if you ask for it.

Post your config file, and I'll show you what to change.
 
Hi I'm trying to get Camilla working on a Windows PC in an attempt to move off of equalizer APO. I'm getting this error - any suggestions on how to resolve?

2020-12-05 10:02:29.176 ERROR camilladsp - Capture error: A backend-specific error has occurred: failed to build capture client: OS Error -2004287485 (FormatMessageW() returned error 15100) (os error -2004287485)
 
Banned Sock Puppet
Joined 2020
Another few days, and it's been non stop fun.


Not only have the x86 version working now (on the notebook with studio card), but got the 64 bit arm version up and running if only using the cheapy HDI/headphone output on the RPI.


This meant I could start creating working profiles for headphone corrections. It works like a dream.
Nothing the aftermarket for tweaking headphones in the normal analog way works like this.


The Senn HD650 once sorted out sounds like something totally different.
No more harshness, and a much closer approach to the original sound, and that was only the beginning...


..after that I tackled the monster hifi system with all kinds of notches (it's a big valve amps system ++)

The change in sheer clarity was literally astonishing, it just removes resonances in the system

(eg a nasty one at 5800hz-6100hz on the compression horn tweeter, which you simply cannot remove with analogue components).



I thought I would let you know how I got on with the install.

There's a few tricks, mostly revolving around Debian/Raspian OS using out of date repos, so DON'T install rustc with Apt, get it from rustrs.


On the ARM chips it's a good idea to check out exactly how "Headphones" are defined. I struggled for quite a while until figuring it out with VLC.


Funnily even on the original headphone output of the RPi, using Camilla + HD650 made it sound quite respectable..


Well done again Henrik!
 
@Henrik... nice upgrade.

Well i compiled the develop version (0.5.0), but there was an error doing this::

Code:
git clone [url=https://github.com/HEnquist/camilladsp.git]GitHub - HEnquist/camilladsp: A flexible linux IIR and FIR engine for crossovers, room correction etc.[/url]
git checkout develop
git checkout <--- To check if correct !

rustc 1.48.0 (7eac88abb 2020-11-16)

RUSTFLAGS='-C target-feature=+neon -C target-cpu=native' cargo build --release --no-default-features --features alsa-backend --features websocket --features 32bit >

  Compiling serde_yaml v0.8.14
   Compiling serde_json v1.0.60
   Compiling camilladsp v0.5.0 (/home/pi/camilladsp/camilladsp)
error[E0308]: mismatched types
   --> src/basicfilters.rs:110:39
    |
110 |                 self.current_volume = shared_vol as f64;
    |                                       ^^^^^^^^^^^^^^^^^ expected `f32`, found `f64`

error: aborting due to previous error

[B][COLOR="red"]So correcting line 110:39 till f32 make it compile :)[/COLOR][/B]
      // Volume setting changed
        if (shared_vol - self.target_volume).abs() > 0.001 {
            if self.ramptime_in_chunks > 0 {
                trace!("starting ramp {} -> {}", self.current_volume, shared_vol);
                self.ramp_start = self.current_volume;
                self.target_volume = shared_vol;
                self.ramp_step = 1;
            } else {
                self.current_volume = shared_vol as [COLOR="Red"][B]f64[/B][/COLOR];
                self.target_volume = shared_vol;
                self.ramp_step = 0;
            }

pi@raspberrypi:~/camilladsp/camilladsp $ RUSTFLAGS='-C target-feature=+neon -C target-cpu=native' cargo build --release --no-default-features --features alsa>
   Compiling camilladsp v0.5.0 (/home/pi/camilladsp/camilladsp)
    Finished release [optimized] target(s) in 6m 04s

So creating a example .yml like this ::

Code:
tc@piCorePlayer:~/camilladsp$ cat 44100.yml

devices:
  samplerate: 44100
  chunksize: 4096
  queuelimit: 1

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

filters:
  clipgain:
    type: Gain
    parameters:
      gain: 0.0
      inverted: false

  Baseboost:
    type: Biquad
    parameters:
      type: Highshelf
      freq: 80
      gain: -6
      slope: 6

  volume_l:
    type: Volume
    parameters:
      ramp_time: 200

  volume_r:
    type: Volume
    parameters:
      ramp_time: 200
                
pipeline:
- type: Filter
  channel: 0
  names:
    - clipgain
    - Baseboost
- type: Filter
  channel: 1
  names:
    - clipgain
    - Baseboost

- type: Filter
  channel: 0
  names:
    - Volume_l

- type: Filter
  channel: 1
  names:
    - Volume_r

This .yml are running, NEXT is to figure out how i actually use it through the websocket :eek::) :xmastree:

Jesper.
 
Last edited:
Thanks for testing! That only happens when building with the 32bit feature. I don't have any automatic testing for that yet, should definitely be added.

BTW you don't need to define separate Volume filters for the two channels, it's fine to use a single one. It anyway creates separate instances of each filter for each channel. It's better to keep a single definition of shared filters. That removes the risk of confusion from when you change one, but forget about the other one :)
 
:)

Good. And thank's for some .yml explanation also!

Regarding the volume_l + volume_r i have a nice "balance" this way perhaps? - My hearing is some dB better on my right ear.

I have read the README ::
Volume

The Volume filter is intended to be used as a volume control. The inital volume can be set with the gain command line parameter. The volume can then be changed via the websocket. A request to set the volume will be applied to all Volume filters. When the volume is changed, the gain is ramped smoothly to the new value. The duration of this ramp is set by the ramp_time parameter (unit milliseconds). This value will be rounded to the nearest number of chunks. To use this filter, insert a Volume filter somewhere in the pipeline for each channel. It's possible to use this to make a dithered volume control by placing the Volume filter somewhere in the pipeline, and having a Dither filter as the last step.

filters:
volumeexample:
type: Volume
parameters:
ramp_time: 200

And figured out that i start the camilladsp like this for my setup.
Code:
/usr/local/bin/camilladsp /home/tc/camilladsp/44100.yml -p 1234 -g-12 -l off > /dev/null 2>&1

But from what i read i cannot figure out how i actually send the volume change through the websocket ?

I can't find anything regarding this ::
The inital volume can be set with the gain command line parameter. The volume can then be changed via the websocket.
 
Hey..

{"SetVolume": -12.3}

I tried it with no luck.

My python looks like this now (for testing) // nevermind the mess please :p ::
#!/usr/local/bin/python3

from camilladsp import CamillaConnection

import io
import os
import stat
import subprocess
import sys
from websocket import create_connection

from subprocess import call
import time
from bluepy.btle import UUID, Peripheral

#cdsp = CamillaConnection("127.0.0.1", 1234)
#cdsp.connect()

#CN=cdsp.get_config_name()
#print (CN)
#print("Version: {}".format(cdsp.get_version()))


config_path = "/home/tc/camilladsp/" # .yml config files
ws_port="1234" # Websocket port number
sample_rate="44100"
volume="-12"

value = (0x002a)
p = Peripheral("B8:F0:09:CD:30:F2", "public") # The MAC adress of the ESP32
OLD_value = p.readCharacteristic(value) # First read value
LONG_press = b'\xa8a\x00\x00' # = 25000 from Arduino/ESP32 Serial monitor
DOUBLE_press = b'P\xc3\x00\x00' # = 50000

while 1:
raw = p.readCharacteristic(value)
#print (raw)

if (raw < OLD_value):
#call(["/usr/local/bin/pcp", "down"])
#cdsp.connect()
#print("Version: {}".format(cdsp.get_version())

print ("DOWN")
OLD_value = raw

if (raw > OLD_value):
#call(["/usr/local/bin/pcp", "up"])
send_cfg = '{"SetConfigName": "' + config_path + sample_rate + '.yml"}'
#send_cfg = '{"volume_l +20"}'
#send_cfg = '{"SetVolume": "' + volume + '"}'
send_cfg = {"SetVolume": -12.3}
print (send_cfg)
ws = create_connection("ws://127.0.0.1:" + ws_port)
ws.send(send_cfg)
ws.recv()
ws.send('"Reload"')
ws.recv()
ws.close()




print ("UP")



OLD_value = raw




if (raw == LONG_press):
call(["/usr/local/bin/pcp", "pause"])

#if (raw == DOUBLE_press):
#print ("DOUBLE")

time.sleep(0.05)

And my .yml ::
devices:
samplerate: 44100
chunksize: 4096
queuelimit: 1

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

filters:
clipgain:
type: Gain
parameters:
gain: 0.0
inverted: false

Baseboost:
type: Biquad
parameters:
type: Highshelf
freq: 80
gain: -6
slope: 6

volume_l:
type: Volume
parameters:
ramp_time: 200

volume_r:
type: Volume
parameters:
ramp_time: 200

pipeline:
- type: Filter
channel: 0
names:
- clipgain
- Baseboost
- type: Filter
channel: 1
names:
- clipgain
- Baseboost

- type: Filter
channel: 0
names:
- volume_l

- type: Filter
channel: 1
names:
- volume_r

Jesper.
 
Finally made it...

With the "new" GitHub - HEnquist/pycamilladsp at v05

There are proberly a lot of code which could be optimised, but it's working pretty well with
my ESP32...
I'am getting a batterypowered ESP32, so have to muckup a box etc... to see if it's working in real life too :)

Code:
#!/usr/local/bin/python3

#################################################
#                                               #
# SuperPlayer Bluetooth (BLE) remote controller #
#                                               #
# Build with help from different sources (www)  #
#                                               #
# Jesper Lykke [user :: Lykkedk @ diyaudio.com] #
#                                               #
#################################################

from camilladsp import CamillaConnection
import io
import os
import stat
import subprocess
import sys
from subprocess import call
import time
from bluepy.btle import UUID, Peripheral

cdsp = CamillaConnection("127.0.0.1", 1234)

GO = int(1)            # Go through loop = (1)
MUTE = int(0)          # Mute = (1) / Unmute = (0)

# '--gain <gain>': Must be a number between -120 and +20

volume = int(-12)      # We start with volume at -12dB
MIN_volume = int(-110) # Lower limit with some proof
MAX_volume = int(-3)   # Upper limit with some proof
CN = int(-3)           # Set volume default for Unmute / Mute

value = (0x002a)                              # BLE Characteristic
p = Peripheral("B8:F0:09:CD:30:F2", "public") # The MAC adress of the ESP32
OLD_value = p.readCharacteristic(value)       # 1. read value
LONG_press = b'\xa8a\x00\x00'                 # = 25000 from Arduino/ESP32 Serial monitor
DOUBLE_press = b'P\xc3\x00\x00'               # = 50000 from Arduino/ESP32 Serial monitor

while 1:

    if (GO == 1):
        raw = p.readCharacteristic(value)

        if (MUTE == 0):
            if (raw < OLD_value):
                #call(["/usr/local/bin/pcp", "down"])
                volume = (volume -1.5)
                cdsp.connect()
                CN=cdsp.get_volume()
                if (CN <= MIN_volume):
                    volume = int(MIN_volume)
                    cdsp.connect()
                    cdsp.set_volume(volume)
                    CN=cdsp.get_volume()

                cdsp.connect()
                cdsp.set_volume(volume)
                CN=cdsp.get_volume()
                print ("Volume DOWN :", CN)
                OLD_value = raw

        if (MUTE == 0):
            if (raw > OLD_value):
                #call(["/usr/local/bin/pcp", "up"])
                volume = (volume +1.5)
                cdsp.connect()
                CN=cdsp.get_volume()
                if (CN >= MAX_volume):
                    volume = int(MAX_volume)
                    cdsp.connect()
                    cdsp.set_volume(volume)
                    CN=cdsp.get_volume()

                cdsp.connect()
                cdsp.set_volume(volume)
                CN=cdsp.get_volume()
                print ("Volume UP   :", CN)
                OLD_value = raw

        if (raw == LONG_press):
            call(["/usr/local/bin/pcp", "pause"])
            print ("Pause / Play")
            raw = OLD_value
            time.sleep(1.00)

        if (raw == DOUBLE_press):
            print ("Double press")
            MUTE = (MUTE + 1) % 2
            if (MUTE == 0):
                print ("<> Unmute system <>")
                cdsp.connect()
                cdsp.set_volume(CN)
                time.sleep(1.00)
            if (MUTE == 1):
                print ("<> Mute system <>")
                cdsp.connect()
                CN=cdsp.get_volume()
                cdsp.set_volume(MIN_volume)
                time.sleep(1.00)

time.sleep(0.05)

Jesper.
 
Cool!

Could you please describe what battery powered controller consist of (HW&SW) and what is needed in the computer that runs Camille?

//

Hey TNT!

It's this one i just bought Adafruit HUZZAH32 – ESP32 Feather Board ID: 3405 - $19.95 : Adafruit Industries, Unique & fun DIY electronics and kits

There should be buildin charger etc... for easy setup.
I have some ESP32 without this functionality, and i use one of them for the setup right now.

I succesfull have it up and running with an encoder attached to the little esp32; encoder sends up/down/press & double press to my Raspberry Pi (which is just for 1hour ago crashed/burned so i'am setting a new one now :))

I attached the ESP32 / Arduino ide code here btw.

Jesper.
 

Attachments

  • SuperPlayer_Controller_v1.1.ino.zip
    2.9 KB · Views: 62

TNT

Member
Joined 2003
Paid Member
OK - its still not clear for me how to build and make this work :)

Whats should I buy?

- Adafruit HUZZAH32 – ESP32 Feather Board ID: 3405
- Rotary Encoder 20p/v with switch
- more?

And how to program the ASP32? Instruction?

Any settings server side? (a Mac)

//