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

That is a very precious project. I do greatly appreciate your initiative, because this could become the long awaited, up-to-date successor of Brutefir! Time/evolution will tell...

I encountered a very first complication when I tried to install it: For a successful installation, it needed several (10 or so) cumulative iterations because of repetitive network failures downloading the crates.io dependencies. Maybe a hint in the HowTo could be useful: Never say die ...

Now CamillaDSP is ready to run on my system.

My interest in CamillaDSP would be to replace Brutefir within this quite complex audio pipe:

$ sox | writeloop | catloop | volrace | resample_soxr | brutefir | volrace | sox | writeloop | catloop | playhrt

First sox converts everything to to FLOAT64, second sox converts FLOAT64 to S32_LE. So there is max possible precision within volume, samplerate and convolution/filtering operations. Brutefir happily "does" FLOAT64.
Thus my question/wish - Could you implement FLOAT and FLOAT64 into CamillaDSP? This feature would bring it an important step closer to Brutefir's potential.
 
Many people (including me) keep hitting the problem of multiple master clocks in the chain. There are two cases:

A) All master clocks are fixed, uncontrollable by the chain.
Example: Two separate soundcards (clocked by crystal or SPDIF input).

B) At most one clock is uncontrollable, all other cards can have its clock controlled
Example: loopback snd-aloop, capture alsa device of async USB-audio gadget (the USB audio device side)

Case A) requires some form of adaptive resampling, no other option possible.

However, case B) can be solved, it just requires correct tools.

Crossovers are case B) (snd-aloop), I have case B) too (in my other projects with async USB-audio gadget, generally any use of the linux USB soundcard). Async USB-audio gadget has its feedback value telling the sender on USB host side how to modify the sending rate. Device snd-aloop has a PCM control "PCM Rate Shift" - look at this discussion [alsa-devel] Can module snd-aloop (virtual looback device) be made clock slave of a real audio device ?

The utility alsaloop discussed in that thread measures outgoing rate (clock of the real output soundcard) against the computer hardware clock (i.e. the clock snd-aloop uses). Knowing the difference it can (if told to do so with its cmdline param) continuously update the "PCM Rate Shift" value so that the loopback rate approaches that of the hardware soundcard.

So you can either implement a very similar code in your project, or put another snd-aloop loopback between the dsp software and the hardware soundcard, with alsaloop controlling the loopback. A simple script can keep reading current "PCM Rate Shift" of that device with amixer and set the very same value to the loopback device between the playback application and the DSP software. That will synchronize the two loopback devices to the final hardware soundcard.
 
The problem is most likely that the loopback runs a little slower than the dac, so the playback thread runs out of data one in a while. Does CamillaDSP print "prepare playback" in the terminal every time you get the noise?

You can probably get rid of some of it by pausing the processing between songs. This resets the timing, just set a short silence_timeout, half a second maybe.

I have a few ideas on how to improve this:
- Let the buffer underrun happen (as it's done now) but add a longer delay before restarting playback. This would mean it takes much longer until the next one.
- Watch the buffer and when it's getting dangerously low insert some interpolated samples to make the glitch less audible.
- Same as the previous one but try insert silence when the music is quiet.

Opinions?

Indeed, after monitoring, it does print the prepare playback message when a drop-out happens.
From start of a song things run smooth, pausing it helps but later on the drop-outs appear.
 
I encountered a very first complication when I tried to install it: For a successful installation, it needed several (10 or so) cumulative iterations because of repetitive network failures downloading the crates.io dependencies.

....

Thus my question/wish - Could you implement FLOAT and FLOAT64 into CamillaDSP? This feature would bring it an important step closer to Brutefir's potential.
It's strange that you had problems with cargo and crates.io, it has always been reliable for me. If this continues to be a problem it would be good to report it to the cargo maintainers.

About float 32&64, yes absolutely I'll add those!
 
On a German Forum:

Andree (Backes & Muller BM Prime 8) - Seite 8 - aktives-hoeren.de



#!/bin/bash

# define and initialize variables
#loopc_bufsize=$(cat /proc/asound/card3/pcm1c/sub0/hw_params | grep "buffer_size: " | grep -o '[[:digit:]]*')
loopc_bufsize=8192
loopc_buf_avail=0
clkrate=0
lastclk=0
buf_ratio=0
seconds=0

# set default clock to 100% (= uncorrected)
amixer -c 3 cset numid=7 100000 > /dev/null

# keep filled capture buf size in the range of 70% to 80% of defined buffer size
#while [ true ]
while [ "$(cat /proc/asound/card3/pcm1c/sub0/status)" != "closed" ]
do
# read amount of available data from loop capture device
loopc_buf_avail=$(cat /proc/asound/card3/pcm1c/sub0/status | grep "avail : " | grep -o '[[:digit:]]*')

# fill rate in percent
buf_ratio=$(($loopc_buf_avail*100/$loopc_bufsize))

# create uptime string in %06d format
up=$(printf "%06d" $seconds)

# below target (<70% of overall buffer size), speed up
if [ $buf_ratio -lt 70 ]
then
clkrate=99000
# above target (>80% of overall buffer size), slow down
elif [ $buf_ratio -gt 80 ]
then
clkrate=101000
# normal condition, default speed
else
clkrate=100000
fi

# set clock rate of snd-aloop
if [ $clkrate -ne $lastclk ]
then
amixer -c 3 cset numid=7 $clkrate > /dev/null
echo "uptime: "$up"s bufsize: "$loopc_bufsize" filled: "$buf_ratio"% clk: "$clkrate
fi

lastclk=$clkrate

seconds=$(($seconds+1))

sleep 1
done

# set default clock to 100% (= uncorrected)
amixer -c 3 cset numid=7 100000 > /dev/null

echo "Capture device closed. Clock rate control disabled (clk: 1000000)."
 
It will work for any alsa device, be it USB, loopback, or virtual alsa device of a USB gadget. It reads momentary buffer fill level from the the virtual /proc files generated by kernel modules - in this case by the core part of any alsa driver.

In my project I wanted to modify alsalib to report average data rate, but reading momentary buffer fill is WAY better - it is how USB async sound devices work, reporting back the fill level of their small buffer behind the USB receiver.
 
To make this ''clockscript'' work, is it as easy as replacing cardnumbers (card0 in my case) and make it executable?
I haven't tried it yet, but I think it will need some modification to work with CamillaDSP. The script is monitoring the buffer level of the capture device, but CamillaDSP reads ahead as far as it can so the capture device buffer will mostly be empty. If you want to give it a try, set the script to monitor the buffer of the playback device instead.


I will integrate this solution inside CamillaDSP as soon as I can. Until then, maybe it's enough to just use the amixer command to speed up the loopback device a little?
 
How does the chain actually work? There are two independent devices - capture, playback. Which one has the "clock master" role in your code?

IIUC correctly there are options:

A) each side uses blocking read/write in separate threads, with execution thread in between, reading from some buffers. Then the "xrun" would occur in these buffers, not in the alsa drivers.

B) one side (master) uses blocking operation, the other one non-blocking ("slave"). The thread is timed by the blocking side, hoping the non-blocking side will produce/consume in the same rate.

C, D - other variants I do not know

Please which workflow do you use?
 
I haven't tried it yet, but I think it will need some modification to work with CamillaDSP. The script is monitoring the buffer level of the capture device, but CamillaDSP reads ahead as far as it can so the capture device buffer will mostly be empty. If you want to give it a try, set the script to monitor the buffer of the playback device instead.


I will integrate this solution inside CamillaDSP as soon as I can. Until then, maybe it's enough to just use the amixer command to speed up the loopback device a little?

I tried with card 1.

Card 1 is the Hifiberry dac.

/proc/asound/card1/ lists 2 subdirectories : pcm0p and pcm0c.


Output with pcm0p (playback) :

pi@realtimepi:~ $ sh softclock.sh
Invalid card number.
Invalid card number.
uptime: 000000s bufsize: 8192 filled: 712% clk: 101000
 
cube75: Look at the script, the key parts are quite simple.

There are two operations involved - reading current buffer fill, and adjusting the rate.

The reading operation uses the /proc interface. /proc/asound/card3/pcm1c - card number 3, device 1, capture ('c' - output of arecord -l, 'p' - playback - output of aplay -l)

The adjustment uses amixer -c 3 - card ID 3.

The rate adjustment works only for the loopback (regular cards do not have the "PCM Rate Shift" parameter, set by the amixer command - using parameter ID, not its name). I think the script would work correctly even if the /proc soundcard were a different one - depends on how the chain is configured - my previous question to Henrik.

I would recommend using card names instead of IDs - look at ls -l /proc/asound - the card names are symlinks to their card IDs. IDs can change at boot, while the names are hard-coded in drivers.

Code:
hestia@hestia:~$ ls -l /proc/asound/
Total 0
-r--r--r-- 1 root root 0 bře  9 10:48 cards
dr-xr-xr-x 6 root root 0 bře  9 10:48 card0
dr-xr-xr-x 3 root root 0 bře  9 10:48 card1
-r--r--r-- 1 root root 0 bře  9 10:48 devices
lrwxrwxrwx 1 root root 5 bře  9 10:48 HDMI -> card1
-r--r--r-- 1 root root 0 bře  9 10:48 hwdep
lrwxrwxrwx 1 root root 5 bře  9 10:48 Intel -> card0
-r--r--r-- 1 root root 0 bře  9 10:48 modules
dr-xr-xr-x 2 root root 0 bře  9 10:48 oss
-r--r--r-- 1 root root 0 bře  9 10:48 pcm
dr-xr-xr-x 2 root root 0 bře  9 10:48 seq
-r--r--r-- 1 root root 0 bře  9 10:48 timers
-r--r--r-- 1 root root 0 bře  9 10:48 version

ls /proc/asound/Intel/pcm0p
amixer -c Intel
 
Very interesting topic, this struggle for servicing out of sync hardware. Either by tweaking directly into the timing of the slave hardware, or by cheating something useful into the data stream. Useful in terms of matching between different hardware devices, and also in terms of irrelevance to psychoacoustic perception.

As intersting as such a functionality is; in my oppinion this functionality has not to be implemented into a convolver. I like a convolver (as any other program) as lean (and as precise) as possible.

So why not opening another, new thread in order to start a completely new project of such a most welcome "anyclocks-anydatastream-harmonizer" which by parameters allows it
- to work either as a HW master's receptor, or a HW slave's supplyier
- to provide either datastream modification or hardware tweaking

This would indeed be a completely different function set than a convolver.