How to run CamillaDSP with Multiple DACs
I use two 4-channel DACs working together with CamillaDSP to provide 6 active speaker channels. Here’s what I do to get it working in Ubuntu on a Raspberry PI.
Use Two of the Same DAC
The issue with USB audio is that it is (generally) asynchronous. Data is sent via USB in a block then queued up to stream the audio device. Depending on processor speeds and block size and queueing methods the latency through this varies. Two of the same DAC will have the same streaming software and hardware so they are likely to have the same latency.
I started with a 2-channel DAC (SMSL DO-100) and a 4–channel DAC (Motu M4) but the stereo image seemed very muddy. I switched to a PreSonus Studio 26c 4-channel DAC instead of the SMSL to do one device per speaker and realized there was a clear echo — the latency difference was that large between the Motu and the PreSonus.
Investigation
I bought another PreSonus 26c to investigate what was going on. Here’s what I found.
Using the Motu with the PreSonus. When driven by a simple sine wave the phase difference between the two would add/subtract 360 every second or so — so the latency difference was wildly changing and large. When looking at a burstier output the bursts were roughly synchronized but the latency difference was visible.
Using two PreSonus Studio 26c DACs. On a sine wave stream… The starting phase difference was random somewhere between -180 and 180. That difference would pretty much not change during the entire signal duration (10 minutes) so it looked like a function of when the first data bytes of the stream hit the device. A very simple analysis (which does not currently exist) would make that error near-zero.
For numerical results, I created a sound file that was repeating: 10 seconds of a low-volume wave (to fill buffers) and then a second of high-volume square waves. Playing the file on the aggregate device (two PreSonus DACs) produced this capture on a scope:
I expanded the time scale and measured the delay: .005 mS.
Retrying the test gave varying results
Even in the worst case, though, the delay was about 256uS (1/4mS).
So, my decision is simple -> use two PreSonus 26c DACs. Currently the left DAC runs the left speaker and the right DAC the right speaker. I tried other configurations, and the signal was subjectively too muddy, while the crossover had to assume one driver at random phase/delay.
Configuration
̶I̶ ̶w̶a̶s̶ ̶n̶e̶v̶e̶r̶ ̶a̶b̶l̶e̶ ̶t̶o̶ ̶g̶e̶t̶ ̶C̶a̶m̶i̶l̶l̶a̶D̶s̶p̶ ̶t̶o̶ ̶t̶a̶l̶k̶ ̶t̶o̶ ̶a̶ ̶c̶o̶n̶s̶t̶r̶u̶c̶t̶e̶d̶ ̶a̶l̶s̶a̶ ̶d̶e̶v̶i̶c̶e̶.̶ ̶I̶ ̶d̶o̶n̶’̶t̶ ̶k̶n̶o̶w̶ ̶w̶h̶y̶.̶ ̶ Postscript (Jan 2024) — so I finally found out how to use a constructed device with CamillaDsp. The device must be created in the system-level /etc/asound.conf instead of the user-level ~/.asoundrc. Then CamillaDsp has access and works fine.
Use the constructed Alsa device if possible. The alsa loopback does work and I’ve left the text on how to use it, but I would avoid it because it adds significant latency.
Create the Merged alsa Device
In etc create a file named asound.conf . This will be autoscanned at start to create alsa devices.
Content to build a 32/96 merged 8 channel device:
-> File: /etc/asound.conf
# create a virtual eight-channel device with two 4-channel devices:
# This is in fact two interleaved quad streams in
# different memory locations, so JACK will complain that it
# cannot get mmap-based access. see below.
pcm.both {
type route;
slave.pcm {
type multi;
slaves.a.pcm "hw:S26c_1,0";
slaves.b.pcm "hw:S26c,0";
slaves.a.channels 4;
slaves.b.channels 4;
bindings.0.slave a;
bindings.0.channel 0;
bindings.1.slave a;
bindings.1.channel 1;
bindings.2.slave a;
bindings.2.channel 2;
bindings.3.slave a;
bindings.3.channel 3;
bindings.4.slave b;
bindings.4.channel 0;
bindings.5.slave b;
bindings.5.channel 1;
bindings.6.slave b;
bindings.6.channel 2;
bindings.7.slave b;
bindings.7.channel 3;
hint { description "The Combo Hw" }
}
ttable.0.0 1;
ttable.1.1 1;
ttable.2.2 1;
ttable.3.3 1;
ttable.4.4 1;
ttable.5.5 1;
ttable.6.6 1;
ttable.7.7 1;
}
ctl.both {
type hw;
card S26c;
}
# The "plug" plugin converts channels, rate and format on request.
# In our case it converts the 32 format to whatever the application request.
pcm.convert {
type plug
slave {
pcm both
format S32_LE
channels 8
rate 96000
}
hint { description "The Combo Converted" }
}
Here the both device is the merged device with a control channel from one of the DACs. The convert device is a convenience that will auto-convert stuff to 32/96 for the merged device.
Save the above file then reboot or use
sudo alsa force-reload
Using the Merged Device
To tell CamillaDsp to use the merged both device on output just write the name alone:
Current System
Here’s my current system:
The top contains a Roku, two 4 channel amplifiers configured as 2.1 (one pair can be in parallel), and an i5 Mac Mini running Ubuntu 24. The middle has two 4 channel DACs and a Wiim Pro.
Amplifiers
The amplifiers (each box is 4 channels of 70WRMS @ 4 ohms) are my layout and construction. PCBs were built in China with the surface mount parts but some specific parts were inserted here. The boxes weren’t deep enough so I 3d printed a 1" extension for the cable connectors — 3 XLR, 2 Speak-On and one Power-Con.
Here’s one amp at 5WPC
DACs
The DACs are Presonus 26c units. Inexpensive good quality 4-ch dac w 2ch adc.
Wiim Pro
There’s a Wiim Pro that supports bit-perfect Chromecast and bit-perfect Amazon music (my provider) as well as supporting my tv via optical cable so I use that with an optical->usb converter for the Mac Mini as input.
Power Supply
Currently this is a linear power supply with a toroidal transformer.
Speakers
Home built speakers with Dayton Audio drivers. The top is a sealed 2-way and the bottom is a ported 8" woofer. Sides are 3/4" MDF with cherry veneer and front is 1" mahogany. Tung oil and lacquer.
Using the Alsa Loopback
Get the Alsa Loopback Device
To use the alsa loopback as output and continuously stream that to the constructed alsa device.
(from RPi4 + CamillaDSP Tutorial | Audio Science Review (ASR) Forum)
On the raspberry pi the loopback device has to be installed.
sudo apt install linux-modules-extra-raspi
sudo nano /etc/modules-load.d/snd-aloop.conf
enter this line in snd-aloop.conf and save
snd-aloop
Tell CamillaDsp to use the loopback device on output:
then start a permanent stream device from loopback to the both device
alsaloop -fS32_LE -c8 -r96000 -d --sync=simple -l 512 -T 1 -Chw:Loopback,1 -Pboth
with the -d option the above line will run a daemon in the background that streams from Loopback,1 (the Loopback output channel when 0 is input) to both — the merged device. If this fails to start run it without the -d to get errors on-screen.