Introduction
This article describes how to create a software environment on your desktop computer which will make it possible to boot a second computer from the network and use that as the interface between the desktop computer –from which the audio files and the boot environment are served– and the external audio equipment. The goal is to achieve maximum audio quality using low-cost off-the-shelve equipment and free software and at the same time offering ease of configuration and use. Of course this comes with a price; reading and understanding this article.
Background and goals
I love both music and free software.
After having used the Slimdevices Squeezebox as a streaming client connected to a MSB Link DACII (review) for seven years, I got tired with the fact that I couldn’t use any other client devices or software or formats –the squeezebox client device only outputs 16bit/24KHz and can only communicate with the squeezebox server software. Furthermore, my audio equipment didn’t fit my life any more (too big and THX/surround targeted).
In September 2010 I therefore bought a new audio system consisting of small and relative cheap Opera Mezza stereo speakers, the 24 bit Pink Faun USB DAC 3.24 and a Pink Faun D-Power 140 power amplifier in which the excellent engineers of Pink Faun fitted their newly developed remote controlled volume control. The speakers are connected to the amp using Pink Faun SC-4 cable, while Pink Faun IL-2SE cable is used between the amplifier and the DAC. The power and USB-cables I use are all standard cheap ones and leave room for improvement.
After buying this set I started using xbmc on a laptop which was connected to the DAC with a cheap USB cord and really loved the sound. I couldn’t wait to play native 24bit / 96KHz music. All my digital music files where flacced EAC rips of my own CD’s. I bought some 24/96 albums from HDtracks.com but the results were disappointing. After waiting for a while (to let my rather new equipment settle) I tried again with the same results. On the ergonomic side I really liked the fact that I could use my laptop both as a media controller and as sort of a black box by using whatever software client (like banshee and rhythmbox) on my desktop PC and the DLNA/upnp features of pulseaudio.
On the Internet I learned that although the input consisted of 24bit/96KHz files, the actual audio stream received by the USB-DAC was in fact down sampled to 16bit/44.1KHz by some piece of software!
My DAC doesn’t indicate which bit depth or sample rate it uses, but watch out. Even if your DAC does indicate 24bit/96KHz when playing a high resolution file, it could also do so with other formats. That is because the software is also able to upsample, using an arbitrary and bad format conversion algorithm like libsamplerate. To complicate things further, a lot of DACs try to do the same on the hardware side.
The worst case scenario is that when you play a 24bit/96KHz file, it first gets downsampled to 16bit/44.1Khz by the software –again using a fast and sloppy algorithm– and secondly gets upsampled to 24bit/96KHz by the DAC. Instead of crispy tube like sound and depth, you get a MP3 experience.
It seems the price for convenience buys you a ticket to 1990′s ideas of sound perception.
Default pulseaudio with alsa configurations perform on the fly bit depth and sample rate conversion for ensuring a pop and crackle free “plug-n-play” experience (see pulseaudio ticket #930: Media players report 96khz, proc reports 44khz). Most audio enthusiasts won’t be thrilled by these “qualities” and would like to leave decoding of the audio stream to (more suitable) external audio equipment like a DA-converter. Sample-rate conversion should be avoided altogether when possible. Currently, one has to bypass the default pulseaudio layer altogether and alter the default alsa configuration to get multi-format bit-perfect output to the external DAC.
In short, my wanted setup had the following requirements:
- having bit-perfect audio for all digital formats accepted by my DAC
- having the freedom of only using free software
- having the freedom of only using open –non patent encumbered– file formats
- having the freedom to use any client device as a streaming digital audio device
- having the freedom to use any device for controlling the streaming digital audio device (acting like a “media controller”)
To achieve these goals I’ve made the following choices:
- create a LTSP environment using my desktop PC as the LTSP server
- installing a MPD client on the desktop PC or Android client acting as a media controller
- using an old HP t5725 thin client as the LTSP client
- this thin client will also act as a (headless) MPD server
The result is whopping! I can plug in any PXE-capable PC or laptop to my LAN and connect it to my DAC with USB. After booting it, 15 seconds later it is a bit perfect audio streaming client. I can use any PC, laptop or PDA to control the media and browse through my playlists and music library.
Preparation
Step 1: Create a working LTSP-server environment
Follow the instructions to set up a LTSP server and LTSP client image using https://help.ubuntu.com/community/UbuntuLTSP/FatClients.
This process also involves installing and/or configuring DHCP and TFTP to serve the generated LTSP image to LTSP clients.
The result should be:
- a working DHCP server which has `dhcp-boot` options filled in for PXE enabled clients
- a working TFTP server with LTSP client boot scripts in `/var/lib/tftpboot/ltsp.i386`
- a working LTSP fat client chroot environment in `/opt/ltsp/i386`
Step 2: Add the mpd-userspace package to the LTSP client image
The `mpd-userspace` package from the MPD Trunk PPA should be installed as a local application in the LTSP client chroot environment. I choose the userspace variant instead of the system daemon because it allows more easy integration in LTSP. With the system daemon one has to rebuild the LTSP client chroot image each time when changing the configuration for mpd.
After installing the package, the LTSP client image should be updated and the client device rebooted.
# enter the LTSP client chroot environment on the server
sudo ltsp-chroot -c -d -p
# add mpd trunk PPA to available packagelists in the LTSP image
add-apt-repository ppa:gmpc-trunk/mpd-trunk
# update the list with available packages
apt-get update
# install the mpd-userspace package
apt-get install mpd-userspace
# exit the LTSP client chroot environment
exit
# update the LTSP client image on the server
sudo ltsp-update-image --arch i386
Step 3: disable pulseaudio for the LTSP client and make the music library accessible
Dynamic configuration of LTSP clients can be achieved by the configuration file `/var/lib/tftboot/ltsp/i386/lts.conf` on the LTSP server.
# modify the LTSP client configuration file `/var/lib/tftboot/ltsp/i386/lts.conf` on the LTSP server
sudo gedit /var/lib/tftpboot/ltsp/i386/lts.conf
Add the following line to disable the auto configuring mechanism for redirecting pulseaudio and at the same time leave alsa intact.
SOUND = False
Add the following lines to enable the execution of mpd a local app i.e. using the resources (CPU and RAM) of the LTSP client instead of the server and make the music directory (presumably `/srv/media/music`) accessible.
LOCAL_APPS = True
LOCAL_APPS_EXTRAMOUNTS = /srv/media,/srv/media/music
References:
Step 4: Prepare the user environment
One has to configure a user account on the server which will automagically logon to the LTSP client, determine and configure the preferred sound card as exposed by the external DAC and start serving mpd on the local network.
The location of the music library (in my case consisting of a directory per album containing tracks in FLAC format) is presumed to be `/srv/media/music`. The home directory of the user one creates, will store its mpd settings in `/srv/media/mpd`. On the server, execute the following.
# generate a random password
sudo apt-get install pwgen
# pick one of the listed password and remember it somehow
pwgen
# get the group id (gid) of the audio group
getent group audio | awk -F: '{print $3}'
# fill in the returned group id in the command below
sudo adduser --system --home /srv/media/mpd --shell /bin/bash --gid $gid-of-audio-group mpd
# fill in (or paste) the password generated above when asked for by the next command
sudo passwd mpd
Next, test the LTSP client environment by booting the LTSP client and logging in as the mpd user created above. If you can login and get a proper desktop, continue with the following steps.
Step 5: Create the script to configure alsa and mpd and to run the mpd daemon
Again on the server, create the file `/srv/media/mpd/mpd-configure` which will hold a custom script to ensure mpd and alsa work just fine.
# create an empty file
sudo touch /srv/media/mpd/mpd-configure
# make the file executable
sudo chmod +x /srv/media/mpd/mpd-configure
# edit the file
sudo gedit /srv/media/mpd/mpd-configure
Paste the following in the file and save it.
#!/bin/bash
# add audio_output section to mpd.conf for
# alsa non-resampled hardware interface of preferred audio device
USBDAC="Pink Faun 24/96 USB Module"
BASEDIR="/srv/media"
MPD_DIR="~"
MPD_CONFFILE=".mpdconf"
MPD_MUSICDIR="${BASEDIR}/music"
MPD_PLAYLISTDIR="${MPD_DIR}/playlists"
MPD_DBFILE="${MPD_DIR}/tag_cache"
MPD_LOGFILE="${MPD_DIR}/mpd.log"
MPD_PIDFILE="${MPD_DIR}/pid"
MPD_STATEFILE="${MPD_DIR}/state"
# the sticker (rating) feature didn't quite work in my setup
#MPD_STICKERFILE="${MPD_DIR}/sticker.sqlite"
# aplay output is something like:
# card 0: Module [Pink Faun 24/96 USB Module], device 0: USB Audio [USB Audio]
APLAYOUTPUT=$(aplay -l | grep "${USBDAC}")
# return card id
ALSACARD=$(echo "${APLAYOUTPUT}" | awk '{ print $2}' | sed 's/://')
# return device id
ALSADEVICE=$(echo "${APLAYOUTPUT}" | awk -F, '{ print $2}'
| awk '{ print $2}' | sed 's/://')
# construct audio_output section for mpd.conf
AUDIOSECTION=$(echo -e "audio_output {"
"nttypet"alsa""
"ntnamet"${USBDAC}""
"ntdevicet"hw:${ALSACARD},${ALSADEVICE}""
"n}n")
# construct path and file entries for mpd.conf
PATHSSECTION=$(echo -e "music_directoryt"${MPD_MUSICDIR}""
"nplaylist_directoryt"${MPD_PLAYLISTDIR}""
"ndb_filet"${MPD_DBFILE}""
"nlog_filet"${MPD_LOGFILE}""
"npid_filet"${MPD_PIDFILE}""
"nstate_filet"${MPD_STATEFILE}"n")
# construct other entries for mpd.conf
ENCODINGSECTION=$(echo -e "filesystem_charsett"UTF-8""
"nid3v1_encodingt"UTF-8""
"nauto_updatet"yes""
"nzeroconf_enabledt"yes""
"nzeroconf_namet"MPD ${USBDAC}"")
echo "${PATHSSECTION}" | tee "${MPD_CONFFILE}"
echo "${AUDIOSECTION}" | tee -a "${MPD_CONFFILE}"
echo "${ENCODINGSECTION}" | tee -a "${MPD_CONFFILE}"
# set PCM control of card to 100%
amixer -c "${ALSACARD}" set PCM 100%
Make sure the mpd user can write to it’s home directory and can read the music library by executing the following.
# fix permissions on the home directory
sudo chown -R mpd:audio /srv/media/mpd
# allow access to the music library by members of the audio group
sudo chgrp -R audio /srv/media/music
Again, you should test the script by logging on to the LTSP client as user mpd, starting a terminal and executing.
./mpd-configure
Next, check the contents of the file `~/.mpdconf` on the LTSP client; it should contain a valid mpd configuration like:
cat .mpdconf
music_directory "/srv/media/music"
playlist_directory "~/playlists"
db_file "~/tag_cache"
log_file "~/mpd.log"
pid_file "~/pid"
state_file "~/state"
audio_output {
type "alsa"
name "Pink Faun 24/96 USB Module"
device "hw:0,0"
}
filesystem_charset "UTF-8"
id3v1_encoding "UTF-8"
auto_update "yes"
zeroconf_enabled "yes"
zeroconf_name "MPD Pink Faun 24/96 USB Module"
If the test above succeeds, try starting the mpd application by executing the following command (still logged on the LTSP client as the mpd user).
# start mpd on the client using the generated .mpdconf
/usr/bin/mpd
# check if the mpd process has been started
ps auxwww | grep "/usr/bin/mpd"
Step 6: Install a mpd client on the LTSP server
On the server you should now be able to connect to the mpd application running on the LTSP client with a mpd client like the Gnome Music Playing Client (gmpc). Execute the following on the server to install and run this application.
# add the mpd ppa to the server
sudo add-apt-repository ppa:gmpc-trunk/mpd-trunk
# refresh the list with available packages
sudo apt-get update
# install the gnome music playing daemon on the server
sudo apt-get install gmpc
# run the client application on the server
gmpc
In the configuration dialog which appears when you first start gmpc enter the IP address of the LTSP client in the Host section and check the option Automatically connect.
Step 7: Automate the logon process
If these tests succeed, modify `/srv/media/mpd/.profile` to make sure the script above will start automagically after the mpd user logs on on the LTSP client.
# edit the startup script of the mpd user on the server
sudo gedit /srv/media/mpd/.profile
Add the following to the bottom of the file.
# stop mpd if it's running
/usr/bin/mpd --kill
# start custom mpd config script
./mpd-configure
# start mpd daemon
/usr/bin/mpd start
Fix the permissions of the `.profile` file by executing the following.
# fix ownership of the home directory of the mpd user on the server
sudo chown -R mpd:audio /srv/media/mpd
As a final test, reboot the LTSP client and logon as the `mpd` user ; now mpd should be automatically configured and running.
Finally, modify the LTSP client configuration `/var/lib/tftboot/ltsp/i386/lts.conf` on the LTSP server.
# edit the ltsp client configuration file on the server
sudo gedit /var/lib/tftpboot/ltsp/i386/lts.conf
Add the following lines to make sure the `mpd` user logs on automatically after starting the LTSP client (replace the password with the one generated above) and save the file.
LDM_AUTOLOGIN = True
LDM_USERNAME = mpd
LDM_PASSWORD = ooGh5Ga2
Restart the LTSP client; it now should automatically logon and start the properly configured mpd. Test it by using the gmpc on the server.
References and thoughts
Drawbacks of the setup
In this setup, one has to make sure that each audio file is properly formatted according to the DAC’s native formats.
My DAC, for example doesn’t support formats of 24bit/88.2KHz as often used in DVD-audio or anything beyond 96KHz (like 176.4Khz). The designer of the DAC Matthijs de Vries has some good reasons to limit the USB interface of the DAC to 24bit/96KHz as he explains in Dutch in his White Paper “Pink Faun DAC 2 (Oktober 2010)”.
A higher data rate theoretically provides a better output. This appears still not completely true at this time. The disadvantages of 24/192 [USB DAC's] are:
- [Support for 24/192 USB-DAC's] is only possible in the newest operating software versions, and is asynchronous. As a result, one needs to fall back on self-developed drivers, which adds another layer in the software stack. 24/96 usually works with the native drivers which are already present [in current operating systems]. The user doesn’t have to install any additional software.
- The electronics are much more complex with 24/192; both DSP’s and numerous operations are necessary. The cure quickly becomes worse than the problem.
- The sampling frequency determines the resolution bandwidth, which is around 40KHz at 96kHz sampling rate. 192 kHz only gains so much in the audible range.
- [Galvanic] isolation does not currently work with 24/192, at the moment, 24/96 is the maximum achievable.
In my case, I choose to upsample 88.2 to 96KHz and downsample 176.4 to 96Khz using the following script using the Shibatch SRC in twopass non-fast mode algorithm.
#!/bin/bash
#
# Script to resample 24 bit flac files to 96KHz using the Shibatch SRC
# in twopass non-fast mode while preserving the flac metadata stored
# in the original files.
#
# Background:
# http://www.lacocina.nl/artikelen/how-to-setup-a-bit-perfect-digital-audio-streaming-client-with-free-software-with-ltsp-and-mpd
#
# 88.2 KHz DVD-audio files are upsampled to 96KHz (ratio 147:160)
# 176.4 KHz files are downsampled to 96Khz (ratio 80:147)
# 192.0 KHz files are downsampled to 96Khz (ratio 2:1)
#
# Change gcc of shiboleth Makefile to compile on Debian/Ubuntu
# $(CC) $(CFLAGS) ssrc.c fftsg_ld.c dbesi0.c -o ssrc -lm
# ^^^
# author: ronalde
# version: 0.1
# may 2011
# Shibatch sampling rate converter version 1.30
SSRC=~/ssrc-1.30/ssrc_hp
# download URL
SSRCSRC="http://shibatch.sourceforge.net/"
# Options to pass to the converter, example for 96.000 KHz using best
# profile using two passes
TARGETBITDEPTH="24"
TARGETSAMPLERATE="96000"
# use these options for ssrc
#SSRCOPTS="--rate ${TARGETSAMPLERATE} --twopass --quiet --profile standard"
# use these options for ssrc_hp
SSRCOPTS="--rate ${TARGETSAMPLERATE} --twopass --quiet"
# Filename for the tarball containing the source flac files
SRCFLACTAR="original-flacs.tar"
FLAC=$(which flac)
METAFLAC=$(which metaflac)
# Test existence of ssrc command
if ! [ -x "${SSRC}" ]; then
echo "Error: Missing shibatch sampling rate converter in \`${SSRC}'."
echo " Please make sure it is compiled from ${SSRCSRC} and " \
"marked as executable."
exit
fi
# Test existence of flac command
if ! [ -x "${FLAC}" ]; then
echo "Error: Missing flac converter in \`${PATH}'."
echo " Please make sure it is installed."
exit
fi
# Test existence of metaflac command
if ! [ -x "${METAFLAC}" ]; then
echo "Error: Missing metaflac in \`${PATH}'."
echo " Please make sure it is installed."
exit
fi
# Test existence of backup tarball
if [ -f "${SRCFLACTAR}" ]; then
echo "Error: It seems you have converted this directory before."
echo " If not, remove \`${SRCFLACTAR}' and try again."
exit
fi
# Test existence of files with .flac extension in current directory
FLACS=$(find . -maxdepth 1 -name "*.flac")
if [ -z "${FLACS}" ]; then
echo "Error: There are no flac files in the current directory"
exit
fi
# Temporary sub directory for storing intermediate files
# will be cleaned afterwards
TMPTARGET=$(mktemp -d "original.XXXXXXXXXX")
# Save internal field separator of bash,
# will be restored afterwards
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
# Process each file with .flac extension in current directory
for f in *.flac
do
SRCFLAC="$f"
# Test whether this is a real FLAC file
SRCFLACMIME=$(file -b "${SRCFLAC}" | cut -d' ' -f1)
if [ "${SRCFLACMIME}" != "FLAC" ]; then
echo "Warning: not processing \`${SRCFLAC}'; it seems not to be a FLAC-file."
else
SRCSAMPLERATE=$(${METAFLAC} --show-sample-rate "${SRCFLAC}")
SRCBITDEPTH=$(${METAFLAC} --show-bps "${SRCFLAC}")
echo "Processing file \`${SRCFLAC}'"
echo " ... will convert from ${SRCBITDEPTH}bit/${SRCSAMPLERATE}Hz to ${TARGETBITDEPTH}bit/${TARGETSAMPLERATE}Hz"
# Extract basename (ie filename without extension)
BASENAME=$(echo "${SRCFLAC}" | cut -d \. -f 1 -)
TARGETWAV="${BASENAME}.wav"
# Try to decode original flac file to wav file in temp directory
echo " ... decoding to PCM"
if $(${FLAC} -s -d -o "${TMPTARGET}/${TARGETWAV}" "${SRCFLAC}"); then
# Move original FLAC to temporary directory
mv "${SRCFLAC}" "${TMPTARGET}"
# Upsample original wav according to SSRCOPTS
echo " ... resampling"
if $(${SSRC} --rate 96000 --twopass --quiet --profile standard "${TMPTARGET}/${TARGETWAV}" "${TMPTARGET}/Upsampled ${TARGETWAV}"); then
# Encode upsampled wav file to flac
echo " ... recoding with flac"
$(${FLAC} -s "${TMPTARGET}/Upsampled ${TARGETWAV}" -o "${SRCFLAC}")
# Store flac tags from original flac file in upsampled flac file
$(${METAFLAC} --export-tags-to - "${TMPTARGET}/${SRCFLAC}" | ${METAFLAC} --import-tags-from - "${SRCFLAC}")
echo " done."
else
echo "Error: Could not convert \`${TMPTARGET}/${TARGETWAV}' to \`${TMPTARGET}/Upsampled ${TARGETWAV}'"
echo " Please review those temporary files and converter output."
fi
fi
fi
done
if [ -d "${TMPTARGET}" ]; then
# HARRY=$(tar cf "${SRCFLACTAR}" "${TMPTARGET}")
$(tar cf "${SRCFLACTAR}" "${TMPTARGET}")
# if [ $HARRY ] ; then
# echo "Done!"
# echo "... original flac files copied to tarball \`original-882000-flac.tar'"
# echo "... resulting upsampled flac files with metadata available in this directory"
# rm -rf "${TMPTARGET}"
# else
# echo "Error creating tarball of original flac files"
# echo "... please review the temporary files in \`${TMPTARGET}'"
# fi
fi
IFS=${SAVEIFS}
Determine the supported bit depths and sample rates for the preferred card
When I connect my USB-DAC to the HP t5725, it exposes two sound cards, the SiS embedded on the mainboard and the (preferred) one exposed by the Tenor USB transceiver chip in my USB DAC:
**** List of PLAYBACK Hardware Devices ****
card 0: SI7012 [SiS SI7012], device 0: Intel ICH [SiS SI7012]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: Module [Pink Faun 24/96 USB Module], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
As with any modern client (desktop, laptop or thin client) the built-in audio card will be the first (index 0) and therefore the default card for alsa to use. In this setup we need to alter this behaviour so that the sound card exposed by the external USB-DAC will be the default.
On the host connected to the USB-DAC enter the following in a terminal (and leave that terminal open):
watch -n 1 "cat /proc/asound/card1/stream0"
The output contains at least two sections: a ‘Status’ section which shows the current ‘mode’ and at least one ‘Interface’ section which describes the modes supported by your USB-DAC. Mine looks like this when playing a simple 16bit/44.1KHz CD-ripped FLAC:
Playback:
Status: Running
Interface = 3
Altset = 1
URBs = 3 [ 8 8 8 ]
Packet Size = 388
Momentary freq = 44100 Hz (0x2c.199a)
Interface 3
Altset 1
Format: S16_LE
Channels: 2
Endpoint: 3 OUT (ADAPTIVE)
Rates: 44100, 48000, 96000
Interface 3
Altset 2
Format: S24_3LE
Channels: 2
Endpoint: 3 OUT (ADAPTIVE)
Rates: 44100, 48000, 96000
When playing a 24bit/96KHz file the output changes to the following. Note that the `Altset` value has changed from `1` to `2` and `Momentary freq` from `44100` to `96000` indicating that the second interface (`Altset = 2`) is activated with 24bit resolution (`Format = S24_3LE`) and 96KHz sample rate (`Momentary freq = 96000`).
Playback:
Status: Running
Interface = 3
Altset = 2
URBs = 3 [ 8 8 8 ]
Packet Size = 582
Momentary freq = 96000 Hz (0x60.0000)
Interface 3
Altset 1
Format: S16_LE
Channels: 2
Endpoint: 3 OUT (ADAPTIVE)
Rates: 44100, 48000, 96000
Interface 3
Altset 2
Format: S24_3LE
Channels: 2
Endpoint: 3 OUT (ADAPTIVE)
Rates: 44100, 48000, 96000
Of course `card1` and `stream0` are system specific. To find which file you have to look at, you might use the following command:
grep -R S24_3LE /proc/asound/
This searches for any file in the proc filesystem which has a line with this string in it; for example in my case:
/proc/asound/Module/stream0: Format: S24_3LE
/proc/asound/card1/stream0: Format: S24_3LE
S24_3LE means 24 bits packed in 3 bytes as explained in this excellent page on the MultimediaWiki.
Quote:
Originally Posted by erniejunior 
b) is there a way to get bit-perfect sound?
|
Yes, but currently not with pulseaudio, as explained by the maintainers in ticket #930 on the pulseaudio website.
Any alsa client can do it however, when it (the client) uses hw:0,0 instead of the default resampling hwplug:0,0 interface.
Next configure mpd by making a section like this:
audio_output {
type "alsa"
name "Pink Faun 3.24 USB-DAC"
device "hw:1,0"
}
Of course you’ll have to point to right directories on your system for the audio library etc.
See http://www.computeraudiophile.com/co…4-now-possible.
Good luck