Check out my first novel, midnight's simulacra!
KEF LS60
I acquired a pair of KEF LS60 active wireless speakers in January 2024. The speakers are awesome, but getting them working well with Linux was not completely trivial, and is not quite complete.
At the time, I had the following versions of crap:
- Linux 6.7
- Pipewire 1.0.1
- Pulseaudio 16.1
- KEF firmware 2.0
Requirements
I was using MPD 0.23.14 for my music, but I needed a solution at the audio device level, so that video, system sounds, and everything else would use the speakers. So I was looking at pulseaudio or pipewire. I wanted to create a virtual device into which I could dump digital data (probably PWM). The speakers have a line in, but I did not want to do the digital-to-analog conversion on my machine, but rather to use the DAC of the $7,000 speakers. They also have an HDMI input, which could happily accept stereo PCM at up to 24 bits at 192 kHz, but I wanted wireless.
I might end up using the HDMI if I can't eliminate the latency of my wireless solution.
An ideal solution would involve:
- no cables between computer and speakers
- lossless transfer to speakers (either as unpacked PCM or packed FLAC/AAC/etc.)
- DAC at speakers
- minimal latency
Wireless
The specs list the following "wireless streaming features":
- AirPlay 2
- Google Chromecast
- Roon Ready
- UPnP Compatible
- Bluetooth 4.2
As far as I'm aware, Bluetooth audio is always going to involve a lossy compression.
Running avahi-browse -a -t showed numerous zeroconf services:
- ax200 IPv6 ls60w-eb17fe57-5688-4f03-b2a9-4ef78be95abf _spotify-connect._tcp local
- ax200 IPv6 8417151A1273@lilypad speaker AirTunes Remote Audio local
- ax200 IPv6 lilypad speaker AirPlay Remote Video local
- ax200 IPv6 ls60w-eb17fe57-5688-4f03-b2a9-4ef78be95abf _suegrouping._tcp local
- ax200 IPv6 LS60 Wireless-490b3a50ff76a5f16a56b25fa9ca18fe _tidalconnect._tcp local
- ax200 IPv6 8417151A1273@LS60W _kef-info._tcp local
- ax200 IPv6 ls60w-eb17fe57-5688-4f03-b2a9-4ef78be95abf _sues800device._tcp local
- ax200 IPv6 lilypad speaker Web Site local
adding -r for resolution generated more details:
+ ax200 IPv6 ls60w-xxx _spotify-connect._tcp local + ax200 IPv6 xxx@lilypad speaker AirTunes Remote Audio local + ax200 IPv6 lilypad speaker AirPlay Remote Video local + ax200 IPv6 ls60w-xxx _suegrouping._tcp local + ax200 IPv6 LS60 Wireless-xxx _tidalconnect._tcp local + ax200 IPv6 xxx@LS60W _kef-info._tcp local = ax200 IPv6 ls60w-xxx _spotify-connect._tcp local hostname = [ls60w-xxx.local] address = [192.168.88.77] port = [80] txt = ["VERSION=2.9.0" "CPath=/api/stream/spotify:zeroconf"] + ax200 IPv6 ls60w-xxx _sues800device._tcp local + ax200 IPv6 lilypad speaker Web Site local = ax200 IPv6 8417151A1273@lilypad speaker AirTunes Remote Audio local hostname = [ls60w-xxxx.local] address = [192.168.88.77] port = [7000] txt = ["pk=xxx" "vs=366.0" "vn=65537" "tp=UDP" "sf=0x4" "am=LS60 Wireless" "md=0,1,2" "fv=p20.2.0.66.0x187653b" "ft=0x445F8A00,0x1C340" "et=0,4" "da=true" "cn=0,1"] = ax200 IPv6 lilypad speaker AirPlay Remote Video local hostname = [ls60w-xxx.local] address = [192.168.88.77] port = [7000] txt = ["pk=xxx" "gcgl=0" "gid=xxx" "pi=xxx" "srcvers=366.0" "protovers=1.1" "serialNumber=xxx" "manufacturer=KEF" "model=LS60 Wireless" "flags=0x4" "fv=p20.2.0.66.0x187653b" "rsf=0x0" "features=0x445F8A00,0x1C340" "deviceid=xxx" "acl=0"] = ax200 IPv6 ls60w-xxx _suegrouping._tcp local hostname = [ls60w-xxx.local] address = [192.168.88.77] port = [80] txt = ["ip=192.168.88.77" "groupTimestamp=0" "groupName=" "groupMembers=0" "spotify=true" "transcoder=true" "manufacturer=KEF" "uuid=ls60w-xxx" "name=lilypad speaker"] = ax200 IPv6 LS60 Wireless-xxx _tidalconnect._tcp local hostname = [ls60w-xxx.local] address = [192.168.88.77] port = [2019] txt = ["fn=lilypad speaker" "ve=1" "id=xxx" "ca=2" "mn=LS60 Wireless"] = ax200 IPv6 xxx@LS60W _kef-info._tcp local hostname = [ls60w-xxx.local] address = [192.168.88.77] port = [80] txt = ["groupRole=N/A" "groupName=N/A" "groupId=N/A" "kefId=xxx" "version=2.0.66.0x187653b" "serialNumberSlave=" "serialNumber=xxx" "manufacturer=KEF" "modelName=LS60 Wireless" "model=SP4017" "mac=xxx" "name=lilypad speaker"] = ax200 IPv6 ls60w-xxx _sues800device._tcp local hostname = [ls60w-xxx.local] address = [192.168.88.77] port = [80] txt = ["ip=192.168.88.77" "manufacturer=KEF" "uuid=ls60w-xxx" "serial=xxx" "name=lilypad speaker"] = ax200 IPv6 lilypad speaker Web Site local hostname = [ls60w-xxx.local] address = [192.168.88.77] port = [80] txt = [] = ax200 IPv6 Music Player @ schwarzgerat[1574275] Music Player Daemon local hostname = [schwarzgerat.local] address = [fe80::3e9c:fff:fe4f:34e5] port = [6600] txt = [] = ax200 IPv4 Music Player @ schwarzgerat[1574275] Music Player Daemon local hostname = [schwarzgerat.local] address = [192.168.88.100] port = [6600] txt = []
I first tried pipewire+AirPlay using the raop-discover module. This was able to find the speakers, and created a device visible in pavucontrol, but I was unable to produce any audible output using it. The monitor showed signal going to the sink, but I heard nothing.