Turning Synology NAS into a geeky media server: PulseAudio

This post was written in pre-docker era. Back in the time it was necessary to perform many things that are no longer required on modern devices (like installing Debian Chroot package which, expectedly, is no longer offered).

Modern devices with Docker support or equivalent features (FWIW, my choice is Linux Station from QNAP) are much more friendly towards linux enthusiasts

Required files can be downloaded from https://github.com/edio/synology-mediaserver

Previously we’ve covered ALSA setup and Debian chroot.

In this post I’ll describe how to set up PulseAudio in Debian chroot, so that it’s possible to stream any sound from linux-powered pc to a sound card connected to Synology NAS.

Installing PulseAudio

Although Synology already uses PulseAudio for sound playback in Synology AudioStation, an old version of PulseAudio is used for that purpose (2.1 as per DSM5.1). Debian chroot, on the other hand, provides PulseAudio 5.0.

Install PulseAudio and some dependencies and add pulse user to audio group

(chroot)# apt-get install pulseaudio pulseaudio-module-zeroconf dbus
(chroot)# gpasswd -a pulse audio

PulseAudio theory

Modes of operation

PulseAudio can operate in 2 modes: system mode and per-user mode.

Configuration file for per-user mode is /etc/pulse/default.pa. If PulseAudio works in system mode, it reads from /etc/pulse/system.pa.

Although system mode worked pretty well for me, it is considered unsafe and is said to have bad performance.

I’ll describe modifications I made to configuration script. They may be applied to both default.pa and system.pa with the same effect.

Working with remote clients

When it comes to playing audio from remote clients, PulseAudio provides 2 options: RTP and native TCP protocol.

In case of RTP, client broadcasts a signal and capable receivers on network receive it. RTP is extensively used and is widely supported by applications, so theoretically not only PulseAudio clients can play, but any RTP sender that can send uncompressed (RTP provides plenty of options, but PulseAudio doesn’t support all) audio.

RTP can be easily enabled or disabled with paprefs utility without even changing configuration.

I noticed 2 downsides of RTP:

  1. It gives noticeable delay when streaming from PC. Though it may be compensated manually in pavucontrol utility.

  2. PulseAudio server died frequently for me when acted as RTP receiver.

Native TCP, on the other hand, is for PulseAudio clients only. It is as if PulseAudio client had been talking to a local PulseAudio server, only it is not local. Thus, no extensive support, no 3-rd party apps playing…​

Also there’s 1 thing to keep in mind: native TCP is extremely sensitive to time synchronisation. Client and server must be almost perfectly in sync. To the extent, that even having different NTP servers on the client and server breaks playback completely.

As a benefit you’ll get perfectly synced audio and video when playing movies or youtube videos on your PC.

Also PulseAudio seems to support it much better and works without crashes for me.

PulseAudio server configuration

(chroot)# vi /etc/pulse/default.pa
  1. Devices auto-detection via udev never worked for me, no matter, how I tried, so I loaded audio drivers statically: ` load-module module-alsa-card device_id=0` and removed auto-detection completely by removing module-udev-detect and module-detect

  2. Anyone on group audio should be able to use PulseAudio ` load-module module-native-protocol-unix auth-group=audio`

  3. Anyone on local network (192.168.0.1 through 192.168.0.255 in my case) should be able to use PulseAudio ` load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;192.168.0.0/24;`

  4. PulseAudio should be discoverable via zeroconf, so clients are not required to specify static IP ` load-module module-zeroconf-publish`

That’s it. Also I cleaned up some modules I do not need, for example the one that corks audio, when phone stream is active, the one that displays information about playing application or pans notification sound depending on widget position on the screen (whoa, PulseAudio is a beast, really), bluetooth, etc..

### Automatically restore the volume of streams and devices
load-module module-device-restore
load-module module-stream-restore
load-module module-card-restore

### Should be after module-*-restore but before module-*-detect
load-module module-switch-on-port-available

### Load audio drivers statically
load-module module-alsa-card device_id=0

### Allow access for everyone in audio group
load-module module-native-protocol-unix auth-group=audio

### Allow access for everyone on local network
load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;192.168.0.0/24;
load-module module-zeroconf-publish

### Load the RTP receiver module (can be configured via paprefs)
#load-module module-rtp-recv

### Load the RTP sender module (can be configured via paprefs)
#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'"
#load-module module-rtp-send source=rtp.monitor

load-module module-default-device-restore
load-module module-rescue-streams
load-module module-always-sink
load-module module-intended-roles
load-module module-suspend-on-idle

If you do not need network streaming, remove module-zeroconf-publish. It might be a good idea to keep module-native-protocol-tcp though, as it’ll let you to connect to PulseAudio from remote PC with pavucontrol and paprefs:

(pc)$ PULSE_SERVER=<nas ip> pavucontrol
(pc)$ PULSE_SERVER=<nas ip> paprefs

Clients on the NAS

(chroot)# vi /etc/pulse/client.pa

We need all local applications to try to connect to local PulseAudio server only. Also I turned auto-spawn off as I intend to start PulseAudio manually from pulse user

default-server = 127.0.0.1
autospawn = yes

Giving it a spin

Start all the systems PulseAudio depends on

(chroot)# service start dbus
(chroot)# service start avahi-daemon
(chroot)# su pulse -s /bin/bash -c 'pulseaudio -D'

If it doesn’t start, try examining its output

(chroot)# su pulse -s /bin/bash
(chroot)$ pulseaudio

Else, if it starts successfully, try playing some sound through it

(chroot)# aplay -D pulse /media/path/to/some.wav

Volume can be adjusted with alsamixer

(chroot)# alsamixer

Configure NTP

As I mentioned streaming via native TCP protocol won’t work if system clocks on NAS and your PC are our of sync. DSM6.0 has ntp installed, and by default it uses Google servers for time synchronization:

(nas)$ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*time1.google.co 71.79.79.71      2 u  768 1024  377  126.551    1.670   1.470
+time2.google.co 71.79.79.71      2 u  684 1024  377  135.111    0.921   0.836
+time3.google.co 71.79.79.71      2 u  449 1024  377   31.924    0.433   1.520
+time4.google.co 71.79.79.71      2 u  561 1024  377  292.364    5.348   4.849

I just added the same servers to /etc/ntp.conf on my pc and never had a problem with playback again:

(pc)# cat /etc/ntp.conf | grep server
server time1.google.com minpoll 6 maxpoll 10 iburst prefer
server time2.google.com minpoll 6 maxpoll 10 iburst prefer
server time3.google.com minpoll 6 maxpoll 10 iburst prefer
server time4.google.com minpoll 6 maxpoll 10 iburst prefer

PulseAudio client configuration

Just load module-zeroconf-discover in your /etc/pulse/default.pa:

(pc)# vi /etc/pulse/default.pa

Uncomment or add line and restart PulseAudio

load-module module-zeroconf-discover

After PulseAudio restart new tunnel sink should become available in PulseAudio

(pc)$ pacmd list-sinks
  ...
    index: 2
        name: <tunnel.sigma.local.alsa_output.0.analog-stereo>
        driver: <module-tunnel.c>
        flags: NETWORK HW_MUTE_CTRL HW_VOLUME_CTRL LATENCY
        state: IDLE
        suspend cause:
        priority: 0
        volume: front-left: 42185 /  64%,   front-right: 42185 /  64%
                balance 0.00
        base volume: 65536 / 100%
        volume steps: 65537
        muted: no
        current latency: 312.65 ms
        max request: 0 KiB
        max rewind: 0 KiB
        monitor source: 3
        sample spec: s16le 2ch 44100Hz
        channel map: front-left,front-right
                     Stereo
        used by: 0
        linked by: 2
        fixed latency: 250.00 ms
        module: 24
        properties:
                device.description = "Audinst HUD-mini Analog Stereo on pulse@sigma"
                tunnel.remote.server = "[2a02:a310:20:5f80:211:32ff:fe3d:74f5]:4713"
                tunnel.remote.sink = "alsa_output.0.analog-stereo"
                device.icon_name = "audio-card"
                tunnel.remote_version = "29"
                tunnel.remote.user = "pulse"
                tunnel.remote.fqdn = "sigma"
                tunnel.remote.description = "Audinst HUD-mini Analog Stereo"
  ...

This is how it looks in PulseAudio Volume Control utility

pavucontrol

Starting PulseAudio on NAS automatically

We need to create startup scripts to run dbus, avahi-daemon and pulseaudio itself. The approach was described in the previous part.

Just refer to the git repo for details.