MPD with double wireless remote pulse bluetooth sink

MPD with double wireless remote pulse bluetooth sink

I had the beautifully stupid idea of firing my mpd with a central hub that connects to multiple bluetooth devices all over my appartment.

o ------------ o      o ------------------ o      o ----------------- o
| MPD (Docker) | ---> | Pulse Server (RPi) | ---> | Bluetooth Speaker |
o ------------ o      o ------------------ o      o ----------------- o

MPD

The config for the mpd is fairly straight forward:

audio_output {
        type    "pulse"
        name    "PulseAudio Edifier Wohnraum"
        server  "192.168.178.48"
        sink    "bluez_sink.FC_E8_06_57_A9_8D.a2dp_sink"
}

The server address is the address of the Raspberry Pi, which runs the pulseaudio server and the sink is the audio device local to the Raspberry Pi. To find out what device you have to enter here just run pactl list short sinks on the Pi and choose the right device (this will be explained later).

Pulse Server

This is the hard part, there will be problems and you have to debug a lot.

First of all configure a headless Raspberry Pi to run a pulseaudio server and bluetoothd. On Arch Linux for Arm I installed the following packages:

pacman -S pulseaudio-bluetooh bluez dbus bluez-tools

Make sure /etc/pulse/system.pa contains the following:

### Network shit
load-module module-native-protocol-tcp auth-ip-acl=192.168.0.0/16

### Blutooth shit
load-module module-bluetooth-policy
load-module module-bluetooth-discover
load-module module-switch-on-connect
/etc/pulse/system.pa

Create Systemd Unit files to run the pulseaudio server in system mode:

# systemd service spec for pulseaudio running in system mode -- not recommended though!
# on arch, put it under /etc/systemd/system/pulseaudio.service
# start with: systemctl start pulseaudio.service
# enable on boot: systemctl enable pulseaudio.service
[Unit]
Description=Pulseaudio sound server
After=avahi-daemon.service network.target

[Service]
Type=forking
ExecStart=/usr/bin/pulseaudio --realtime --no-cpu-limit --system --disallow-exit --daemon
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target
/usr/lib/systemd/system/pulseaudio.service

And enable it with systemctl enable pulseaudio.service.

Now you have to create the pulse user: adduser -m pulse and add it to the respective groups:

usermod -a -G bluetooth pulse # I haven't had this group, works anyway
usermod -a -G audio pulse
usermod -a -G pulse-access root

To allow it being configured by the buetoothd create the following file (the whole path might not preexist on your system): /etc/dbus-1/system.d/pulseaudio.conf

<!DOCTYPE busconfig PUBLIC
 "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
        <policy user="pulse">
            <allow own="org.pulseaudio.Server"/>
            <allow send_destination="org.pulseaudio.Server"/>
            <allow receive_sender="org.pulseaudio.Server"/>
        </policy>
</busconfig>
/etc/dbus-1/system.d/pulseaudio.conf

Adding the bluetooth device (headless)

You can either use the bt-device tool but I didn't do that. I can only explain how the procedure works with bluetoothctl:

[root@alarm]# bluetoothctl
Agent registered
[CHG] Controller B8:27:EB:3E:68:DC Pairable: yes

[bluetooth]# scan on
Discovery started
[CHG] Controller B8:27:EB:3E:68:DC Discovering: yes
[NEW] Device FC:E8:06:57:A9:8D EDIFIER R1380DB
[CHG] Device FC:E8:06:57:A9:8D TxPower: 4
[CHG] Device FC:E8:06:57:A9:8D UUIDs: 0000110d-0000-1000-8000-00805f9b34fb
[CHG] Device FC:E8:06:57:A9:8D UUIDs: 0000110b-0000-1000-8000-00805f9b34fb
[CHG] Device FC:E8:06:57:A9:8D UUIDs: 0000110e-0000-1000-8000-00805f9b34fb
[CHG] Device FC:E8:06:57:A9:8D UUIDs: 0000110f-0000-1000-8000-00805f9b34fb
[CHG] Device FC:E8:06:57:A9:8D RSSI: -70

[bluetooth]# devices
Device FC:E8:06:57:A9:8D EDIFIER R1380DB
[CHG] Device FC:E8:06:57:A9:8D RSSI: -78
[CHG] Device FC:E8:06:57:A9:8D RSSI: -69

[bluetooth]# pair FC:E8:06:57:A9:8D
Attempting to pair with FC:E8:06:57:A9:8D
[CHG] Device FC:E8:06:57:A9:8D Connected: yes
[CHG] Device FC:E8:06:57:A9:8D Bonded: yes
[CHG] Device FC:E8:06:57:A9:8D UUIDs: 00001101-0000-1000-8000-00805f9b34fb
[CHG] Device FC:E8:06:57:A9:8D UUIDs: 0000110b-0000-1000-8000-00805f9b34fb
[CHG] Device FC:E8:06:57:A9:8D UUIDs: 0000110c-0000-1000-8000-00805f9b34fb
[CHG] Device FC:E8:06:57:A9:8D UUIDs: 0000110d-0000-1000-8000-00805f9b34fb
[CHG] Device FC:E8:06:57:A9:8D UUIDs: 0000110e-0000-1000-8000-00805f9b34fb
[CHG] Device FC:E8:06:57:A9:8D UUIDs: 0000110f-0000-1000-8000-00805f9b34fb
[CHG] Device FC:E8:06:57:A9:8D UUIDs: 00001800-0000-1000-8000-00805f9b34fb
[CHG] Device FC:E8:06:57:A9:8D ServicesResolved: yes
[CHG] Device FC:E8:06:57:A9:8D Paired: yes
Pairing successful
[CHG] Device FC:E8:06:57:A9:8D ServicesResolved: no
[CHG] Device FC:E8:06:57:A9:8D Connected: no

[bluetooth]# trust FC:E8:06:57:A9:8D
[CHG] Device FC:E8:06:57:A9:8D Trusted: yes
Changing FC:E8:06:57:A9:8D trust succeeded

[bluetooth]# connect FC:E8:06:57:A9:8D
Attempting to connect to FC:E8:06:57:A9:8D
[CHG] Device FC:E8:06:57:A9:8D Connected: yes
[NEW] Endpoint /org/bluez/hci1/dev_FC_E8_06_57_A9_8D/sep5
[NEW] Endpoint /org/bluez/hci1/dev_FC_E8_06_57_A9_8D/sep1
[NEW] Transport /org/bluez/hci1/dev_FC_E8_06_57_A9_8D/sep1/fd0
Connection successful
[CHG] Transport /org/bluez/hci1/dev_FC_E8_06_57_A9_8D/sep1/fd0 State: active
[CHG] Transport /org/bluez/hci1/dev_FC_E8_06_57_A9_8D/sep1/fd0 Volume: 0x007f (127)
[CHG] Device FC:E8:06:57:A9:8D ServicesResolved: yes
[CHG] Transport /org/bluez/hci1/dev_FC_E8_06_57_A9_8D/sep1/fd0 State: idle
[CHG] Transport /org/bluez/hci1/dev_FC_E8_06_57_A9_8D/sep1/fd0 State: active

There are basically three commands that you have to run:

[bluetooth]# scan on
[bluetooth]# pair FC:E8:06:57:A9:8D
[bluetooth]# trust FC:E8:06:57:A9:8D
[bluetooth]# connect FC:E8:06:57:A9:8D

After that do a reboot. If you are lucky, everything should come up automatically and pactl list short sinks displays something like this:

0       alsa_output.platform-bcm2835_audio.stereo-fallback      module-alsa-card.c      s16le 2ch 44100Hz       SUSPENDED
1       bluez_sink.FC_E8_06_57_A9_8D.a2dp_sink  module-bluez5-device.c  s16le 2ch 44100Hz       SUSPENDED

This is the device you need to enter in your mpd.conf

Debugging

Next to output in bluetoothctl I'd recommend an open terminal with journalctl -f running at all times.

The Raspberry Pi is the worst platform for playing with bluetooth. There is a long standing problem with RPI 2,3 and 4. When wifi is actively used as I'm doing here, the bluetooth audio will be distorted. Crackling audio resulting from adaptively down- and uprading the codec.

Therefore you have to use an USB bluetooth dongle.

But that's not the only problem. Even with an USB dongle you might suffer interruptions. You should try changing the power supply, if the audio suddenly stops playing after 5-15 minutes and your journal looks something like this:

Oct 11 11:46:28 alarm kernel: Bluetooth: hci1: Opcode 0x c52 failed: -110
Oct 11 11:46:28 alarm kernel: Bluetooth: hci1: command 0x0c52 tx timeout
Oct 11 11:46:25 alarm kernel: Bluetooth: hci1: Opcode 0x c13 failed: -110
Oct 11 11:46:25 alarm kernel: Bluetooth: hci1: command 0x0c13 tx timeout
Oct 11 11:46:23 alarm kernel: Bluetooth: hci1: Opcode 0x c52 failed: -110
Oct 11 11:46:23 alarm kernel: Bluetooth: hci1: command 0x0c52 tx timeout