Sample rate switcher for CamillaDSP

Interesting project! However, for my actual needs, the very elegant CDSP config autoswitch automatism is overkill. I am firstline interested only to be able to readout the sample rate of an incoming stream, within an USB gadget primarly.

If I understand things well, then it's in the line 119 of setrate_alsa.c that you get the sample rate with the following line:

rate = snd_ctl_elem_value_get_integer(elem_value, 0);

This is straightforward and lean. In contrary and until now, my simplicistic solution to get the actual rate was looping within a bash script, basically resorting to arecord. Arecord assesses and displays the rate (amongst other infos) with the following lines:

if (dump_hw_params) { fprintf(stderr, _("HW Params of device \"%s\":\n"), snd_pcm_name(handle)); fprintf(stderr, "--------------------\n"); snd_pcm_hw_params_dump(params, log); fprintf(stderr, "--------------------\n"); }

This is way too much info for my needs, so within my script, the rate gets filtered out by:

rate=$(arecord -D hw:$sndcard --dump-hw-params 2> >(grep -i "rate:") | cut -d ":" -f2 | tr -d ' ')

I think that basically your solution would be much more straightforward and elegant to assess the rate only, within a dedicated small non-sudo program that only spits out the actual incoming sample rate at a distinct audio device. Nothing but this functionality, even with no need to have camilladsp running. And no need to dig deep into the linux file system to find and extract the value.

So, could you imagine to eventually strip down camilladsp-setrate, thus creating such a new little rate-reporting program? Or alternatively, could you introduce a startup flag to on demand make camilladsp-setrate provide this only functionality? Something that would make a command like

$ anyappropriatenameforthisprogram --device=hw:xyz
or
$ camilladsp-setrate --rate --device=hw:xyz

I could imagine that such a functionality could be helpful for several different uses.
 
@Daihedz. thank you for your interest in my little project.
The aim of my project was to change the sample rate in CamillaDSP's configuration without resource-consuming polling cycles and with great timeliness so as to avoid audio quirks and loss of the initial part of a track.
If you are not interested in direct interaction with CamillaDSP, you might find @phofman's gaudio_ctl tool of interest:
https://github.com/pavhofman/gaudio_ctl
 
I have just uploaded camilladsp-setrate v2.0.0 to github:
https://github.com/marcoevang/camilladsp-setrate

This version adds the --capture command flag to have the capture_samplerate parameter updated instead of the samplerate parameter. This flag should be used when CamillaDSP is configured for resampling of the captured audio to a fixed playback rate.

Source code has been completely re-engineered. As a result, performance has improved noticeably (less than 3ms to complete a config update cycle on my Raspberry Pi 4).

Please read the CHANGELOG.md and README.md files.
 
  • Thank You
Reactions: 1 user
Bravo, Bravo, Bravissimo !!!

I finally have included camilladsp-setrate into my setup running on a RPi3A+. Rate switching is immediate. Superb! E.g. listening to albums on youtube, you may find a wildly mixed assembly of 44kHz and 48kHz tracks for one and the same album. An ideal testbed, and no problem at all for camilladsp-setrate ... What a nice, seemsess playback now!

A remark about the systemd camilladsp-setrate.service file and the "Restart=xyz" option:
Restart=no
This one did not work for me. On my setup, camillilladsp-playback is indirectly started from within a bash script which performs some system servicing tasks before then starting camilladsp. As a consequence, camilladsp starts a bit delayed related to the systemd startup sequence. This made the camilladsp-setrate service to fail, which sequentially gets immediately started after the start of the playback service. Therefore, I had to change this option to
Restart=on-failure
to ensure a consistent start of camilladsp-setrate. This lets the setrate service to fail several times before it locks into a stable operation mode along with a fully functional camilladsp playback.
 
Last edited:
A remark about the systemd camilladsp-setrate.service file and the "Restart=xyz" option:
Thank you for your interest in my little project.
I provided the camilladsp-setrate.service file just as an example. Each user should customise that file according to his/her needs, just as you did.
This is the .service file I currently use in my setup:
Code:
[Unit]
Description=Sample Rate Changer Daemon for CamillaDSP
After=camilladsp.service

[Service]
EnvironmentFile=/etc/default/camilladsp-setrate
ExecStartPre=sleep 0.5
ExecStart=/usr/local/bin/camilladsp-setrate $ARGS
Restart=always
RestartSec=1
StandardOutput=journal
StandardError=journal
SyslogIdentifier=setrate
CPUSchedulingPolicy=fifo
CPUSchedulingPriority=10
User=dietpi
Group=dietpi

[Install]
WantedBy=camilladsp.service

Note that command options are defined in the /etc/default/camilladsp-setrate file. This way, by changing only the options, there's no need to make systemd to reload the .service file.
Information about syntax and options of the .service file can be found in systemd.unit and systemd.service man pages.
systemd.unit man pages
systemd.service man pages

Have fun!
 
Hey guys,

first of all thank you to @mevang for this nice project. It's basically what I am looking for to automate the switch between samplerates coming from different sources. Somehow I can't get it to work though.

My setup is as follows. I got an WiiM Mini as source for either Streaming (Spotify, Airplay, Tidal, Qobuz) or some local FLAC files. Going from there via Toslink into an Hifime UR23 (Toslink to USB Converter, as mentioned before here) attached to a ThinClient (Fujitsu Futro S740). There CamillaDSP is applying FIR Filters I created in REW. Sending the signal out to an Sabaj A20d via USB cable (USB-A to USB-B cable).

I've entered the Vendor and ProdID into the 85-DAC.rules, as you described in your docs. Also I added the --device parameter to the systemd service and adjusted it to the UR23 name. The ExecStart looks something like this:

Code:
/usr/local/bin/camilladsp-setrate --device=hw:Rx --err --warn --user --syslog

But what I am constantly getting is this error:

Code:
camilladsp-setrate[12607]:     WAIT_RATE_CHANGE: Callback: Connection to localhost:1234 failed: HS: Rejected at CLIENT_ESTABLISHED

Not sure if it's related to the UR23, since it was mentioned that camilladsp-setrate only works with "USB gadgets" and apparently the UR23 isn't one. But the error looks like something else might be wrong on my side.

Does anyone have a clue?
 
Hi @DeBrow,
a few preliminary notes:
1) The computer running CamillaDSP must be equipped with an audio capture device. Are you sure that at least one of the USB ports of your Futro can act as a capture device (USB gadget) and is configured accordingly ?
2) The alsa sound system must be informed of the playback sample rate. Are you able to do some tests to check that this happens with your toslink+converter combo?

That said, let us concentrate on the log message. It tells us that the handshake (HS) between camilladsp (the server) and camilladsp-setrate (the client) could not be completed. Something might also be wrong on the camilladsp-setrate side due to unhandled conditions. Please allow me some time to investigate further.
In the meantime, are you able to test the websocket interface of your camilladsp server ? (see the example folder of Henrik's project pycamilladsp https://github.com/HEnquist/pycamilladsp).

I'll be back to you soon.
 
Thanks for the swift replies guys.

I think I understand now what USB gadget capability means. I had a read here https://www.kernel.org/doc/html/v4.17/driver-api/usb/gadget.html. Thank you for clarification.

I tried to get the sample rate from the UR23, but somehow couldn't. For the DAC I was able to. With this command:

Code:
cat /proc/asound/card2/pcm0p/sub0/hw_params 
access: RW_INTERLEAVED
format: S32_LE
subformat: STD
channels: 2
rate: 44100 (44100/1)
period_size: 512
buffer_size: 4096

The Futro doesn't have any USB gadget capability, as @phofman mentioned, so the tool won't work. I'll look out to get an RPi soon and it should all work there :) Thanks guys for the time you put into that!
 
I tried to get the sample rate from the UR23, but somehow couldn't. For the DAC I was able to. With this command:
It's important to realize what goes first. In playback (your DAC) you (your software) open the device and tell it to run at a specific rate. That's what you see in the hw_params file then.

For capture the same holds. But first you need to know at what rates your device can be opened. For standard ADC capture devices with internal clocks it's simple - the capture supports the same rates like playback. But for SPDIF capture (and for USB gadget) the rate is not determined by the device, but by the incoming rate, outside of control of the capture device. The receiver is slaved to the stream transmitter. So eventually your software will open the capture device at some rate and this value will be listed in hw_params. But you need to know when to open it and at what rate.

The USB gadget rate-switching functionality of the sample rate switcher (and of the other input-rate tracking tools like my gaudio-ctl) is based on monitoring changes of a dedicated alsa control element which reports momentary rate of the incoming stream. Zero if no stream is incoming, specific rate if the USB host starts playback.

An SPDIF receiver has this information too and typically offers this info via some registers. Some alsa drivers for SPDIF receivers read these registers periodically to provide a similar control element. E.g. look at 'amixer contents' (list of all alsa controls) of ESI Juli, a PCI soundcard with a properly-implemented SPDIF receiver and alsa driver coded to provide relevant alsa controls https://audiophilestyle.com/forums/topic/9468-linux-question/#comment-112977 . There is a ctl element 'IEC958 External Rate' which can be monitored for changes in the very same way https://github.com/torvalds/linux/blob/master/sound/i2c/other/ak4117.c#L483-L485

So with Juli@, any PC with PCI would do to track SPDIF input. The digital half of Juli@ would suffice, no need for the analog board. Your Futro may not have a PCI port, other Futros do (I played with Futro + Juli in https://github.com/pavhofman/measurement-station a few years ago :) ).

Actually for USB gadget you do not need an ARM device. Many newer Intel Atom devices have dwc3 USB OTG feature. I have two inexpensive chinese Atom z8350 touchscreen tablets. With a great help from a BIOS linux guru both allowed to be convinced to activate their gadget mode (one directly over USB-C, for the other I had to solder a USB-A male -> USB-A male cable). Both worked perfectly as gadgets with a great touch screen display, decent Intel performance, professional case, running stock amd64 ubuntu.
 
In the meantime, are you able to test the websocket interface of your camilladsp server ? (see the example folder of Henrik's project pycamilladsp https://github.com/HEnquist/pycamilladsp).
I forgot to tell you. I wasn't able to test that one yet since I am still on CamillaDSP 1.0.3. I tried to run and/or compile CamillaDSP 2.0.0. But I ams till on Debian 11 and it seems the glibc Version is not suitable for the new CamillaDSP. I will upgrade in the near future and test it.
 
Member
Joined 2011
Paid Member
Hello, I have built and installed this on my RPi 4 and I cannot get it to work. It is a very simple setup; RPi 4 (moOde) -->CamillaDSP-->USB-->Khadas Tone 1. I have no "capture" device, just playback.

I suspect this has something to do with USBGadget, because I have no idea what that is. I am confused why rate-switcher can't just read the current input rate (CamillaDSP already knows this) and then do the switching.

Is USBGadget a "must have"? If so, what is it and how to I set it up? I have tried to find information about it, but it all seems a lot more complicated than it needs to be for the purpose here.

moode@moode:~ $ sudo systemctl status camilladsp-setrate
● camilladsp-setrate.service - Automatic Sample Rate Changer Daemon for CamillaDSP
Loaded: loaded (/etc/systemd/system/camilladsp-setrate.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Mon 2024-01-15 10:12:30 MST; 1min 12s ago
Process: 3258 ExecStart=/usr/local/bin/camilladsp-setrate --err --warn --user --syslog (code=exited, status=255/EXCEPTION)
Main PID: 3258 (code=exited, status=255/EXCEPTION)
CPU: 12ms
 
Last edited:
Member
Joined 2011
Paid Member
If moode runs directly on your device, you do not use any usb input and have no need for the sample rate switcher.
Well then I guess I do not understand the goal of this software. If you don't mind, perhaps you can help me identify what solution I do need.

The situtation:

1) I have audio of various input samplerates from different renderers in moode (FLAC at various sample rates, spotify, etc.)

2) I have used REW to generate WAV files for room correction, at all the various sample rates that I use (my DAC supports them all) and they are on my moode server:

moode@moode:/usr/share/camilladsp/coeffs $ ls -la
total 5148
drwxrwxrwx 2 root root 4096 Jan 15 11:02 .
drwxrwxrwx 4 root root 4096 Jan 13 09:25 ..
-rw-rw-rw- 1 www-data www-data 2097196 Jan 13 16:00 Harman_Curve-192000.wav
-rw-rw-rw- 1 www-data www-data 524332 Jan 13 16:01 Harman_Curve-41000.wav
-rw-rw-rw- 1 www-data www-data 524332 Jan 13 16:01 Harman_Curve-48000.wav
-rw-rw-rw- 1 www-data www-data 1048620 Jan 13 16:01 Harman_Curve-88200.wav
-rw-rw-rw- 1 www-data www-data 1048620 Jan 13 16:00 Harman_Curve-96000.wav
moode@moode:/usr/share/camilladsp/coeffs $

3) I have configured CamillaDSP to select the appropriate wav file for the sample rate by using the $samplerate$ wildcard as per HEnquist's suggestion, here https://github.com/HEnquist/camilladsp#fir:

filters:
ir_left:
parameters:
channel: 0
filename: ../coeffs/Harman_Curve-$samplerate$.wav
type: Wav
type: Conv
ir_right:
parameters:
channel: 0
filename: ../coeffs/Harman_Curve-$samplerate$.wav
type: Wav
type: Conv

4) This works, but only at the sample rate that is defined in the config file as "samplerate", and only if I enable re-sampling. With this configuration, CamillaDSP is resampling all incoming sample rates to "samplerate" (96000 for example) and then it uses ../coeffs/Harman_Curve-96000.wav. This is not ideal, because it uses resampling for everything (except 96000Hz content in this case).

I need CamillaDSP to look at the source sample rate and set the "samplerate" parameter to the same value, and then load the appropriate room correction (filter) file accordingly.

In other words, if I am playing a 41000Hz file, then set "samplerate=41000" and CamillaDSP uses the ../coeffs/Harman_Curve-41000.wav, but if the next song in the queue is 96000Hz, then the "samplerate" parameter is changed to 96000 and the filter switches to ../coeffs/Harman_Curve-96000.wav.

I noticed that there is a "capture_samplerate" parameter as well. It is set to "0" by default. Ideally, I need "capture_samplerate" to be precisely my source samplerate and "samplerate=capture_samplerate".

When I read the description of "camilladsp-setrate" I thought it would help me achieve my goal.
 
Last edited:
Well then I guess I do not understand the goal of this software.
camilladsp-setrate automatically updates the samplerate (or capture_samplerate) parameter in the configuration of CamillaDSP according to the sample rate of the source. This may be useful if the player and CamillaDSP run on distinct machines. In your case, as @phofman highlighted, camilladsp-setrate is of no use.
 
  • Thank You
Reactions: 1 user