this has not been validated end-to-end by anyone including me, so be warned :-)
- try using shairport instead of squeezelite as the client
- try using sendspin instead of squeezelite (currently onnly supports one output format so not an option yet)
I have a CasaTunes music server, i like the software and the app on the iphone to control works well. The server is a CT-8x8ms but everytime someone does a good CasaTunes Home Assistant integration it gets abandoned and CasaTunes seems to have no interest.
- Intel(R) Celeron(R) CPU J1900 @ 1.99GHz)
- 8 GB of SODIMM RAM (i upgraded it from the stock 4GB ages ago)
- 1TB spinning 2.5 HDD, replaced in this project with 512GB SATA SSD i had hanging around
- Onboard ACL892 8 analog channel sound card (typically configiured for 5.1 and 2 or 3 inputs, when counting the digital input/outpu)
- Asus Xonar DSX card with 4 outs and 1 input (also a 8 channel card, but doe
The latest Music Assistant seesm to do all i want:
- Recieve Airplay Stream
- Send to Airplay Devices
- Send to Sonos Devices
- Send to a varity of other software and hardware players like squeezelite and sendspin
- Supports Apple Music, Tidal, Spotify etc (not all with HQ though
- Provided as Home Assistant Add-on (this gist set assumes it will be run on a seperate home assistant box, not on this server as it has no way to cofnigure or use the soundcards directly) - also means i can use home assistant as a single app for WAF
- Supports multiple users
- Supports mulitpkle user music acccounts (so i get my apple music, my eife gets hers)
So the question is - can i convert the box into an endpoint for Music Assistant in someway.... (answer when i finished is yes - works quite well)
- Install Debian Trixie Server without GUI - just SSH
- Install a bunch of pre-requisities for sound card and create an audio user
- Install enough X and VNC to be able to use
hdjackretaskone time to retask the blue input socket on the ALC892 onboard sound to be an output - this creates a fw file that is applied at boot and there is no CLI version of the tool AFAIK - Create an ALSA
/etc/asound.conffor both cards - Install squeezelite and conigure it run 8 instances (one per zone)
- Create USB of Debian and start install
- I prefer text mode install and always disable the installation of all items except SSH and debian tools - but you do you
- partition as you see fit
- i am bad and do things as root when i first do these things, but yeah put your user in the sudoers file / edit visduo as needed
- enable SSH for root if desired (yeah i do that, i know i shouldn't)
- i kept my first run through using default networking and dhcp, but installing
nmtuiandnetworkmanageris probably the better option these days (i will likely do that later)
So at this point you should be able to login as SSH and run commands as root or sudo as your user - if not, do not go any further and get that all working first.
Items needed for base debian manipulation
apt get update && apt get install -Y sudo
Items needed to get ALSA working with the two cards
apt get install -Y alsa-base alsa-utils libasound2 libasound2-plugins psmisc squeezelite
What each was used for:
- alsa-base
- Card detection
- Kernel module defaults
- alsa-utils
- aplay
- arecord
- amixer
- alsactl
- speaker-test ← critical
- libasound2
- Core ALSA userspace library
- libasound2-plugins
- dmix
- route
- plug
- multi
- asym
- psmisc
- fuser and other diag tools i used
- squeezelite
- all the things needed for squeezelite
Identify cards using aplay -l
root@music-server:~# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: DSX [Xonar DSX], device 0: Multichannel [Multichannel]
Subdevices: 0/1
Subdevice #0: subdevice #0
card 1: PCH [HDA Intel PCH], device 0: ALC892 Analog [ALC892 Analog]
Subdevices: 0/1
Subdevice #0: subdevice #0
Note: i am only showing the analog devices, i removed the digital ones (spdif, HDMI, etc for calrity)
This lets you know that the card indexes are in my case (may be different in your case)
plughw:0,0 for the Xonar
plughw:1,0 for the ALC892
You can now test each channel like so
speaker-test -D plughw:1,0 -c 8 -t pink -s N
Where N is a number between 1 and 8 representing either a L or R channel - this will help you build a mental map of which HW channel is which speaker and from which stero pair.
Note: some codecs or drivers may not expose all 8 channels on a given physical jack; unused channels may be silent. I had this issue with the blue connector on the ACL892 - see below for how i resovled with hdjack retask.
This creates an audio user for squeezelite and will be used later
sudo adduser --system --group --no-create-home squeezelite
sudo usermod -aG audio squeezelite
sudo install -d -o squeezelite -g squeezelite /var/log/squeezelite
sudo install -d -o root -g root /etc/squeezelite
This was needed in my system because when i was testing output to the ALC892 blue socket i was getting no sound as it was a)marked as unused and b)marked as an input. If you don't have this issue then you don't need to do these steps.
Items needed for the hdjackretask process and connecting via VNC, may be optional for other installs
apt alsa-tools-gui xorg xinit openbox xfvb x11vnc
Create a VNC password
Create a password file for x11vnc:
x11vnc -storepasswd
This will prompt for a password and save it to:
~/.vnc/passwd
Start VNC sever
Xvfb :0 -screen 0 1280x800x24 &
export DISPLAY=:0
openbox &
x11vnc \
-display :0 \
-rfbauth ~/.vnc/passwd \
-forever &
Connect to VNC
right click in window and start a terminal and run hdajackretask
Actions performed in the GUI:
- Enable advanced override
- Reassign the ALC892 blue jack from input / unused
- Mark it as line-out
- Apply the override and install boot override
in the terminal or an SSH test the output works
- speaker-test -D plughw:1,0 -c 2 -t wav
Notes:
plughw:1,0 targets the ALC892 card directly, bypasses dmix and custom PCMs
-c 2 Stereo output
-t wavUses WAV samples (very obvious if sound is present)
👉 If you hear “Front Left / Front Right” from the blue jack, the override is working.
Run sudo alsamixer
For both cards make sure the channels we will be using are unmuted and set volume to full
exit
run sudo alsactl store
Ok this is where it gets complicated and confusing - how to construct an /etc/asound.conf to create zone devices applications can connect to and to make sure the right channels are mapped into the right stereo pairs for each zone. The mapping you created earlier will be important and you can refer to my asound.conf attached to the gist as a file (see below)
ALSA route works like this:
input stream channels → ttable → hardware channels
Think of ttable as a matrix:
ttable.<input_channel>.<output_channel> = gain
For stereo input:
- Input channel 0 = Left
- Input channel 1 = Right
Hardware channels:
- 0–7 from your speaker-test discovery
These create the raw hardware defintions, one for each card based on the card index you discovered earlier
pcm.xonar_hw_raw {
type hw
card 0
device 0
}
pcm.alc892_hw_raw {
type hw
card 1
device 0
}
Mixer Section - this is a mixer that provides access to all 8 channels and sets things like frequency and bit rate
# 8ch dmix per card (dmix must slave to hw)
pcm.xonar_dmix8 {
type dmix
ipc_key 10240
ipc_perm 0666
slave {
pcm "xonar_hw_raw"
rate 96000
format S32_LE
channels 8
period_time 0
period_size 1024
buffer_size 8192
}
}
# 8ch dmix per card (dmix must slave to hw)
pcm.alc892_dmix8 {
type dmix
ipc_key 10241
ipc_perm 0666
slave {
pcm "alc892_hw_raw"
rate 96000
format S32_LE
channels 8
period_time 0
period_size 1024
buffer_size 8192
}
}
Created stero pair routes that map the channels to create a raw route (if you used this you would only be able to use a fixed frequency input, these routes are used by the plugs that create the devices you will use for applications)
# Raw route (no plug here)
pcm.zone1_raw { type route slave { pcm "xonar_dmix8"; channels 8; } ttable.0.0 1 ttable.1.1 1 }
pcm.zone2_raw { type route slave { pcm "xonar_dmix8"; channels 8; } ttable.0.6 1 ttable.1.7 1 }
pcm.zone3_raw { type route slave { pcm "xonar_dmix8"; channels 8; } ttable.0.4 1 ttable.1.5 1 }
pcm.zone4_raw { type route slave { pcm "xonar_dmix8"; channels 8; } ttable.0.2 1 ttable.1.3 1 }
# Raw route (no plug here)
pcm.zone5_raw { type route slave { pcm "alc892_dmix8"; channels 8; } ttable.0.0 1 ttable.1.1 1 }
pcm.zone6_raw { type route slave { pcm "alc892_dmix8"; channels 8; } ttable.0.2 1 ttable.1.3 1 }
pcm.zone7_raw { type route slave { pcm "alc892_dmix8"; channels 8; } ttable.0.4 1 ttable.1.5 1 }
pcm.zone8_raw { type route slave { pcm "alc892_dmix8"; channels 8; } ttable.0.6 1 ttable.1.7 1 }
so to take the first line as an example
pcm.zone1_raw { type route slave { pcm "xonar_dmix8"; channels 8; } ttable.0.0 1 ttable.1.1 1 }
this uses the ALSA route plugin to map incoming 2-channel audio so that channel 0 / left is sent to channel 0 on the card and 1 / right is sent to channel 1 on the card, both at full volume 1, via the shared Xonar mixer.
This is what applications will connect to, these appear as sound devices.
# What apps open (plug does the conversion of played format to 96k/S32)
pcm.zone1 { type plug slave.pcm "zone1_raw" }
pcm.zone2 { type plug slave.pcm "zone2_raw" }
pcm.zone3 { type plug slave.pcm "zone3_raw" }
pcm.zone4 { type plug slave.pcm "zone4_raw" }
pcm.zone5 { type plug slave.pcm "zone5_raw" }
pcm.zone6 { type plug slave.pcm "zone6_raw" }
pcm.zone7 { type plug slave.pcm "zone7_raw" }
pcm.zone8 { type plug slave.pcm "zone8_raw" }
using a play you can see if all items were created correctly, it is important you only ever connect applications to zone1 through zone8 - never connect at app to the dmix or raw variants
note: i truncated the output to focus on what we created, you will see many more entries from real hardware
root@music-server:~# squeezelite -l
Output devices:
...
xonar_dmix8
zone1_raw
zone2_raw
zone3_raw
zone4_raw
zone1
zone2
zone3
zone4
alc892_dmix8
zone5_raw
zone6_raw
zone7_raw
zone8_raw
zone5
zone6
zone7
zone8
...
Create the template unit: /etc/systemd/system/[email protected]
[Unit]
Description=Squeezelite (%i)
After=network-online.target sound.target
Wants=network-online.target
[Service]
Type=simple
User=squeezelite
Group=squeezelite
# One config file per instance name, e.g. /etc/squeezelite/zone1.conf
EnvironmentFile=/etc/squeezelite/%i.conf
Environment=EXTRA_OPTS=
ExecStart=/usr/bin/squeezelite \
-n "${NAME}" \
-m "${MAC}" \
-o "${OUTPUT}" \
-a "${ALSA_PARAMS}" \
${EXTRA_OPTS}
Restart=on-failure
RestartSec=2
# Log per zone
StandardOutput=append:/var/log/squeezelite/%i.log
StandardError=append:/var/log/squeezelite/%i.log
[Install]
WantedBy=multi-user.target
Notes:
%i will be zone1, zone2, etc.
NAME can contain spaces if quoted properly in the .conf files.
Only showing one example, make sure to set the last digit in the mac address to the zonenuber (like 1 or 2 or 3 etc). The squeezelite will discover the server if there is an mDNS brodcast path on the network, if you dont have that you can sepcify the server IPv4 address using the EXTRA_OPTS
/etc/squeezelite/zone1.conf
# Note if the zone name has a space use the format NAME="Room\ Area" or systemd parsing of conf file will fail
NAME="Room\ Area"
OUTPUT="zone1"
MAC="02:00:00:00:00:01"
ALSA_PARAMS="40:4::0"
# example if you need to specify the server
# EXTRA_OPTS="-s 192.168.1.63"
# note not sure if a \ will be needed after -s havent tested
sudo chown -R root:root /etc/squeezelite
sudo chmod 0644 /etc/squeezelite/*.conf
sudo systemctl daemon-reload
sudo systemctl enable squeezelite@zone{1..8}
sudo systemctl start squeezelite@zone{1..8}
check alll services are ok
systemctl list-units 'squeezelite@zone*'
should look like this
root@music-server:/etc# systemctl list-units 'squeezelite@zone*'
UNIT LOAD ACTIVE SUB DESCRIPTION
[email protected] loaded active running Squeezelite (zone1)
[email protected] loaded active running Squeezelite (zone2)
[email protected] loaded active running Squeezelite (zone3)
[email protected] loaded active running Squeezelite (zone4)
[email protected] loaded active running Squeezelite (zone5)
[email protected] loaded active running Squeezelite (zone6)
[email protected] loaded active running Squeezelite (zone7)
[email protected] loaded active running Squeezelite (zone8)
Legend: LOAD → Reflects whether the unit definition was properly loaded.
ACTIVE → The high-level unit activation state, i.e. generalization of SUB.
SUB → The low-level unit activation state, values depend on unit type.
8 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.