{"id":1649,"date":"2018-06-09T09:51:45","date_gmt":"2018-06-09T16:51:45","guid":{"rendered":"https:\/\/partofthething.com\/thoughts\/?p=1649"},"modified":"2018-12-01T16:01:45","modified_gmt":"2018-12-02T00:01:45","slug":"multi-room-audio-over-wi-fi-with-pulseaudio-and-raspberry-pis","status":"publish","type":"post","link":"https:\/\/partofthething.com\/thoughts\/multi-room-audio-over-wi-fi-with-pulseaudio-and-raspberry-pis\/","title":{"rendered":"Multi-room audio over Wi-Fi with PulseAudio and Raspberry Pi(s)"},"content":{"rendered":"<p>NOTE\/UPDATE: After an update this kind of stopped working and I struggled with it a lot. Now I actually recommend using <a href=\"https:\/\/github.com\/badaix\/snapcast\">snapcast<\/a> instead of this solution. It works better!<\/p>\n<p>I moved to a new place and it has more than one room. Naturally, I hooked up the stereo in the living room and tested it like my dad taught me: by playing &#8220;Money For Nothing&#8221; really loudly. It worked. But wait a minute, there&#8217;s an upstairs now&#8230; how will I get it playing up there? I could always use the wifi network and raspberry pis to beam audio around. Yeah, let&#8217;s do that!<\/p>\n<p><!--more--><\/p>\n<figure id=\"attachment_1650\" aria-describedby=\"caption-attachment-1650\" style=\"width: 800px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/multi-room-audio.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-1650\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/multi-room-audio.png\" alt=\"Picture of some stereos hooked to raspberry pis all connected with a wifi router\" width=\"800\" height=\"390\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/multi-room-audio.png 800w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/multi-room-audio-300x146.png 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/multi-room-audio-768x374.png 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/a><figcaption id=\"caption-attachment-1650\" class=\"wp-caption-text\">The setup<\/figcaption><\/figure>\n<p>So first and foremost I have to tell you that this is a fairly linux-centric setup. The Raspberry Pi&#8217;s are running Raspbian and I have an Ubuntu home server and my daily-use laptop is also Linux. That said, I can run this whole setup from my android phone, and there are ways to control it with Windows, no doubt.<\/p>\n<h3>PulseAudio is the key<\/h3>\n<p><a href=\"https:\/\/www.freedesktop.org\/wiki\/Software\/PulseAudio\/\">PulseAudio<\/a> is a sound system that&#8217;s available on many linux-based machines. It is typically used to pipe audio between applications on a computer. But it natively supports sending audio through a network too, just as we want! So this post is mostly about setting up PulseAudio for a networked sound setup. The <a href=\"https:\/\/www.freedesktop.org\/wiki\/Software\/PulseAudio\/Documentation\/User\/Network\/\">official documentation for this is available here<\/a>.<\/p>\n<h3>Setting up the main server<\/h3>\n<p>I set up the main PulseAudio server on my home server which is plugged into a hard-wired ethernet cable. Notwithstanding all the warnings about running PulseAudio in system mode, <a href=\"https:\/\/www.freedesktop.org\/wiki\/Software\/PulseAudio\/Documentation\/User\/SystemWide\/\">this is the right application for it. <\/a>So we go into <code>\/etc\/pulse\/system.pa<\/code> and set a few things besides the defaults:<\/p>\n<pre class=\"\">### Enable positioned event sounds\r\nload-module module-position-event-sounds\r\n\r\n# Grant unfettered access to anyone on my LAN or VPN subnets\r\nload-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;192.168.168.0\/24;10.8.0.0\/24\r\nload-module module-zeroconf-publish\r\n\r\n# add the pi as a sink\r\nload-module module-tunnel-sink server=192.168.168.177 sink_name=pi\r\n\r\n# add a sink with all sinks\r\nload-module module-combine-sink sink_name=combined slaves=alsa_output.pci-0000_00_1f.3.analog-stereo,pi\r\n<\/pre>\n<p>The combined sink at the bottom is for when I want to play the same thing through all the clients at once, as opposed to playing different things through different rooms. By the way, I got the name of that alsa sink at the bottom (which is the sound card of the home server) using the list-sinks PulseAudio command, which required you to run it slightly oddly when in system mode:<\/p>\n<pre class=\"\">sudo PULSE_RUNTIME_PATH=\/var\/run\/pulse -u pulse pacmd list-modules<\/pre>\n<p>Anyway, then, I went ahead and disabled autospawn in client.conf because I wanted to run it at boot. I made <code>pulseaudio.service<\/code>, put it in <code>\/etc\/systemd\/system<\/code> and put this in it:<\/p>\n<pre class=\"\">[Unit]\r\nDescription=PulseAudio system-wide server\r\n\r\n[Service]\r\nType=forking\r\nPIDFile=\/var\/run\/pulse\/pid\r\nExecStart=\/usr\/bin\/pulseaudio --daemonize --system --realtime --log-target=journal\r\nExecStop=\/usr\/bin\/pulseaudio -k\r\n\r\n[Install]\r\nWantedBy=multi-user.target<\/pre>\n<p>Then activate it with:<\/p>\n<pre class=\"code-pre\"><code langs=\"\">sudo systemctl enable <span class=\"highlight\">application<\/span>.service<\/code><\/pre>\n<p>Boom.<\/p>\n<h3>Setup on the Pi<\/h3>\n<p>Setup is similar on the Pi. We do system mode again but this time we just have to edit the <code>client.conf<\/code> file and uncomment the <code>default-server<\/code> line and add in the IP address or hostname of the server and disable autospawn. Then I did the same systemd startup script and activated it. Oh I also messed around with the <code>resample-method<\/code> in <code>daemon.conf<\/code> and set it to <code>trivial<\/code> to minimize the load on the Pi but I don&#8217;t think this was necessary.<\/p>\n<h3>Troubleshooting<\/h3>\n<p>At first things were bad, the audio was really low quality and jumped around like crazy. After hours of trying to synchronize the clocks, I learned that the newer <a href=\"https:\/\/wiki.archlinux.org\/index.php\/PulseAudio\/Troubleshooting#Disabling_timer-based_scheduling_.280.2F4.29\">timer-based sampling doesn&#8217;t always work<\/a> on all hardware. So I tried disabling by adding <code>tsched=0<\/code> to the udev module lines on the server&#8217;s <code>system.pa<\/code> file:<\/p>\n<pre class=\"\">### Automatically load driver modules depending on the hardware available\r\n.ifexists module-udev-detect.so\r\nload-module module-udev-detect tsched=0\r\n.else\r\n### Use the static hardware detection module (for systems that lack udev\/hal support)\r\nload-module module-detect tsched=0\r\n.endif<\/pre>\n<p>I also gave PA access to run in real time by editing <code>\/etc\/security\/limits.conf<\/code>. Wow, what a difference. Rock solid audio now, always.<\/p>\n<p>I had another problem where libao-based things often had choppy audio over the network-sink. Adjusting the <code>\/etc\/libao.conf<\/code> file seemed to help a lot:<\/p>\n<pre class=\"\">default_driver=pulse\r\nquiet\r\nbuffer_time=50\r\ndev=combined\r\nserver=localhost<\/pre>\n<h3>Testing the setup<\/h3>\n<p>Initial testing was done on the command line by playing mp3&#8217;s with mpg321. You just run:<\/p>\n<pre class=\"\">PULSE_SERVER=server mpg321 real\\ mccoy\\ -\\ runaway.mp3<\/pre>\n<p>Then you have to set the volumes and whatnot. The easiest way to do this is through the GUI program pavucontrol, run on my laptop on the LAN via:<\/p>\n<pre class=\"\">PULSE_SERVER=server pavucontrol<\/pre>\n<figure id=\"attachment_1651\" aria-describedby=\"caption-attachment-1651\" style=\"width: 947px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-08-22-13-30.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-1651\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-08-22-13-30.png\" alt=\"Screenshot of the Pavucontrol program controlling volumes\" width=\"947\" height=\"1019\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-08-22-13-30.png 947w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-08-22-13-30-279x300.png 279w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-08-22-13-30-768x826.png 768w\" sizes=\"auto, (max-width: 947px) 100vw, 947px\" \/><\/a><figcaption id=\"caption-attachment-1651\" class=\"wp-caption-text\">Pavucontrol screenshot<\/figcaption><\/figure>\n<p>Aww yeah, it&#8217;s working. There are a lot of things on this screen but it all makes sense. The mp3 playing is via libao and is playing on the Simultaneous output sink in all room in the house. Adjust volume as needed.<\/p>\n<p>Note that <a href=\"https:\/\/github.com\/hchapman\/reverb\">there is an android app that does this too<\/a> called Reverb and it&#8217;s pretty handy when you&#8217;re away from the computer.<\/p>\n<figure id=\"attachment_1655\" aria-describedby=\"caption-attachment-1655\" style=\"width: 169px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot_2018-06-09-09-00-49.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"size-medium wp-image-1655\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot_2018-06-09-09-00-49-169x300.jpg\" alt=\"Screenshot of the Reverb android app\" width=\"169\" height=\"300\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot_2018-06-09-09-00-49-169x300.jpg 169w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot_2018-06-09-09-00-49-768x1365.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot_2018-06-09-09-00-49-576x1024.jpg 576w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot_2018-06-09-09-00-49.jpg 1152w\" sizes=\"auto, (max-width: 169px) 100vw, 169px\" \/><\/a><figcaption id=\"caption-attachment-1655\" class=\"wp-caption-text\">Screenshot of Reverb adjusting room volumes from the phone<\/figcaption><\/figure>\n<h3 class=\"\">Actually using it<\/h3>\n<p>I have a <a href=\"https:\/\/www.mopidy.com\/\">mopidy<\/a> instance <a href=\"https:\/\/github.com\/mopidy\/mopidy-spotify\">hooked to Spotify<\/a> and whatnot running on the server too, (that&#8217;s what the python@127.0.0.1 thing is). That&#8217;s fairly easy to set up and get access to your entire local and remote music collections. In the mopidy configuration (<code>\/etc\/mopidy\/mopidy.conf<\/code>) , just tell it to send its audio to the PA server, like this:<\/p>\n<pre class=\"\">[audio]\r\noutput = pulsesink server=127.0.0.1\r\n<\/pre>\n<p>On the phone, there are a few clients. I&#8217;m trying out <a href=\"https:\/\/gitlab.com\/gateship-one\/malp\">M.A.L.P.<\/a>, which is working great.<\/p>\n<figure id=\"attachment_1654\" aria-describedby=\"caption-attachment-1654\" style=\"width: 169px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot_2018-06-09-09-06-46.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1654 size-medium\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot_2018-06-09-09-06-46-169x300.jpg\" alt=\"Screenshot of MALP mopidy client running on my phone\" width=\"169\" height=\"300\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot_2018-06-09-09-06-46-169x300.jpg 169w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot_2018-06-09-09-06-46-768x1365.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot_2018-06-09-09-06-46-576x1024.jpg 576w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot_2018-06-09-09-06-46.jpg 1152w\" sizes=\"auto, (max-width: 169px) 100vw, 169px\" \/><\/a><figcaption id=\"caption-attachment-1654\" class=\"wp-caption-text\">Screenshot of MALP mopidy client running on my phone<\/figcaption><\/figure>\n<p>On the laptop, I sometimes just use the command line tool <a href=\"https:\/\/github.com\/arybczak\/ncmpcpp\">ncmpcpp<\/a>.<\/p>\n<figure id=\"attachment_1653\" aria-describedby=\"caption-attachment-1653\" style=\"width: 968px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-09-09-02-26.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-1653\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-09-09-02-26.png\" alt=\"Screenshot of a console-based music client ncmpcpp\" width=\"968\" height=\"373\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-09-09-02-26.png 968w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-09-09-02-26-300x116.png 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-09-09-02-26-768x296.png 768w\" sizes=\"auto, (max-width: 968px) 100vw, 968px\" \/><\/a><figcaption id=\"caption-attachment-1653\" class=\"wp-caption-text\">A text-based media client called ncmpcpp<\/figcaption><\/figure>\n<h3>Integration into Home Assistant<\/h3>\n<p>Last but not least, this whole setup integrates beautifully into my smart home system running with <a href=\"https:\/\/www.home-assistant.io\/\">Home Assistant<\/a>. For one thing, all noises my home makes can be made in all rooms now (including door-opening chimes, ships bell clock chimes, security alarm notifications, Seinfeld bass transitions, exercise reminders, etc.). Plus, hass <a href=\"https:\/\/www.home-assistant.io\/components\/media_player.mpd\/\">integrates very nicely with MPD<\/a> music players like Mopidy so I can control the tunes directly from the hass panels.<\/p>\n<figure id=\"attachment_1656\" aria-describedby=\"caption-attachment-1656\" style=\"width: 710px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-09-09-03-08.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1656 size-full\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-09-09-03-08.png\" alt=\"MPD integration in hass\" width=\"710\" height=\"490\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-09-09-03-08.png 710w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/Screenshot-from-2018-06-09-09-03-08-300x207.png 300w\" sizes=\"auto, (max-width: 710px) 100vw, 710px\" \/><\/a><figcaption id=\"caption-attachment-1656\" class=\"wp-caption-text\">MPD integration in hass<\/figcaption><\/figure>\n<p>Setup for hass is basically just to set the PULSE_SERVER environmental variable in the startup script for hass. So for systemd that looks like:<\/p>\n<pre class=\"\">[Service]\r\nType=simple\r\nUser=%i\r\nExecStart=\/opt\/homeassistant\/bin\/hass -c \"\/home\/homeassistant\/.homeassistant\"\r\nEnvironment=\"PULSE_SERVER=localhost\"<\/pre>\n<p>In my case, HA is running on the same server as the main PA server so I use localhost. Adjust accordingly.<\/p>\n<p>As always, this whole thing can be controlled from outside the house after I VPN in. This means I could pipe tunes from my home server to myself wherever I am in the world. I guess that&#8217;s what Spotify&#8217;s for though.<\/p>\n<h3>Cheap stereo amplifiers<\/h3>\n<p>Not being a huge audiophile, I have been at least moderately impressed that <a href=\"https:\/\/www.amazon.com\/gp\/product\/B007TUSXEY\">the\u00a0 $10\u00a0 stereo amplifier I had lying around<\/a> from another project works fine driving my extra speakers upstairs (my 5.1 setup doesn&#8217;t really work well in the living room any more so I moved the rear speakers upstairs). So for expanding this system out cheaply definitely try these things out. Not overly loud but sufficient if you&#8217;re on a budget.<\/p>\n<figure id=\"attachment_1658\" aria-describedby=\"caption-attachment-1658\" style=\"width: 660px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20180609_092306.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1658 size-large\" src=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20180609_092306-1024x576.jpg\" alt=\"Little cheap amp\" width=\"660\" height=\"371\" srcset=\"https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20180609_092306-1024x576.jpg 1024w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20180609_092306-300x169.jpg 300w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20180609_092306-768x432.jpg 768w, https:\/\/partofthething.com\/thoughts\/wp-content\/uploads\/IMG_20180609_092306.jpg 1488w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><\/a><figcaption id=\"caption-attachment-1658\" class=\"wp-caption-text\">Little cheap amp<\/figcaption><\/figure>\n<h3>See also<\/h3>\n<p>This is kind of a simpler project than my remote audio over <a href=\"https:\/\/partofthething.com\/thoughts\/remote-ham-radio-operation-through-a-raspberry-pi\/\">VPN one I did a few years ago for ham radio.<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>NOTE\/UPDATE: After an update this kind of stopped working and I struggled with it a lot. Now I actually recommend using snapcast instead of this solution. It works better! I moved to a new place and it has more than one room. Naturally, I hooked up the stereo in the living room and tested it &hellip; <a href=\"https:\/\/partofthething.com\/thoughts\/multi-room-audio-over-wi-fi-with-pulseaudio-and-raspberry-pis\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Multi-room audio over Wi-Fi with PulseAudio and Raspberry Pi(s)<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":1650,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":4,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":""},"categories":[3,75],"tags":[],"class_list":["post-1649","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-computers","category-home-automation"],"_links":{"self":[{"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/posts\/1649","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/comments?post=1649"}],"version-history":[{"count":10,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/posts\/1649\/revisions"}],"predecessor-version":[{"id":1695,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/posts\/1649\/revisions\/1695"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/media\/1650"}],"wp:attachment":[{"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/media?parent=1649"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/categories?post=1649"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/partofthething.com\/thoughts\/wp-json\/wp\/v2\/tags?post=1649"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}