Multi-room audio with Mopidy and Snapcast

In my quest to continually improve my multi-room audio setup, one thing that has bothered me is getting Spotify to play in multiple rooms. My current solution has been to run a Windows VM with the Spotify client, capture the audio using TuneBlade, and then play it out to the various Raspberry Pi’s on the network that are running shairport-sync so they can receive AirPlay signals.

Works pretty well – but means I need to have that Windows VM working all the time. This isn’t a scalable solution to locations where the VM/Windows machine on-all-the-time isn’t an option.

So, I’m now trialling using Mopidy – an extensible music server written in Python. Mopidy will run on a Raspberry Pi, and has an extension that allows it to connect to Spotify…no Windows machine needed!

To get it up and running, I followed the installation instructions, with a few key call-outs as I was getting this running on a Raspberry Pi as the server as well:

  1. Install raspbian-lite (Jessie) on the Raspberry Pi of choice and get it all set up.
  2. Follow these instructions – specifically:
    wget -q -O - | sudo apt-key add -
    sudo wget -q -O /etc/apt/sources.list.d/mopidy.list
    sudo apt-get update
    sudo apt-get install mopidy
  3. My /etc/mopidy/mopidy.conf file looks like:
    cache_dir = /var/cache/mopidy
    config_dir = /etc/mopidy
    data_dir = /var/lib/mopidy
    config_file = /etc/mopidy/logging.conf
    debug_file = /var/log/mopidy/mopidy-debug.log
    enabled = false #Note: this is just because I wasn't setting up local music at this point
    media_dir = /var/lib/mopidy/media
    playlists_dir = /var/lib/mopidy/playlists
    enabled = true
    hostname = ::
    enabled = true
    username = **** #My Spotify username
    password = **** #My Spotify password
    enabled = true
    hostname = ::
    port = 6680
    static_dir = ""
    zeroconf = Mopidy HTTP server on $hostname
    enabled = true
    musicbox = false
    config_file = /etc/mopidy/mopidy.conf
    #output = autoaudiosink
    output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! filesink location=/tmp/snapfifo
  4. To get Mopidy working as a service, I used sudo systemctl enable mopidy but you can use sudo dpkg-reconfigure mopidy for any Debian-based system.
  5. Then, I needed to install pipbut, it needed to be the Python 2.7 version. To install that, I used apt-get install python-pip2.
  6. Using pip, I could now install the various add-ins for using and supporting Spotify, namely:
    pip2 install Mopidy-Mopify
    pip2 install Mopidy-Moped
    pip2 install Mopidy-WebSettings
    pip2 install Mopidy-Iris

To pipe the audio across the network, I needed to install Snapcast server on the same Raspberry Pi, and then Snapclient on the various clients. So, that looks like:

  1. Get the latest compiled release for ARM (RPi) with

    on the client and then on the server:

  2. Install, with dependencies:
    dpkg -i snapserver_0.11.1_armhf.deb
    sudo apt-get -f install

    on the server, and then on the client:

    dpkg -i snapclient_0.11.1_armhf.deb
    sudo apt-get -f install
  3. The addition to the mopidy.conf file of the output section will pipe the audio from mopidy to any Snapclient that is listening.
  4. snapclient -l will list the USB devices and other sound cards on the client, and then your /etc/default/snapclient should look something like
    # defaults file for snapclient
    # start snapclient automatically?
    # Allowed options:
    #   --help                          produce help message
    #   -v, --version                   show version number
    #   -h, --host arg                  server hostname or ip address
    #   -p, --port arg (=1704)          server port
    #   -l, --list                      list pcm devices
    #   -s, --soundcard arg (=default)  index or name of the soundcard
    #   -d, --daemon [=arg(=-3)]        daemonize, optional process priority [-20..19]
    #   --user arg                      the user[:group] to run snapclient as when daemonized
    #   --latency arg (=0)              latency of the soundcard
    #   -i, --instance arg (=1)         instance id
    USER_OPTS="--user snapclient:audio"

Building my own Tivo replacement

The Tivo service is coming to an end in my part of the world, so I’m trying to create an equally wife/child-friendly option for recording free-to-air, over-the-air TV and playing it back.

I already have Raspberry Pi’s connected to the various TVs in the house, and I know that Kodi has good support for tvheadend, so I figured I’d give that a go.

I bought a Hauppauge dual tuner USB stick as I thought that it is a good brand, with good Linux support, and I was keen to run the whole thing on an old Raspberry Pi (model B+, 1st generation) if I could.

Good idea, but a bit tricky in the end, because while Raspbian (and LibreElec) detected the card just fine, they would only detect one tuner on the card.

The fine people over in the LibreElec forums helped me out with patching the kernel to allow for dual tuner support.  To do this, I set up a virtual machine running Ubuntu 16.04 and got it ready to act as a cross-compiler for Raspberry Pi.  The instructions to do so are here – just make sure that you remember what kind of Raspberry Pi you are building for!

The summary of steps I took:

  1. I got the toolchain and copied the right (64-bit) bin path into my .bashrc (specifically :/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin)
  2. I then downloaded the 4.9 kernel with git clone --depth 1 -b rpi-4.9.y
  3. I then got patch from a user called Puffin Chunks and followed their instructions
  4. I applied the patch as per instructions (i.e. patch -p1 < ../hauppage_winTV_dualHD_DVB_PuffinChunks_4.9.y.diff)
  5. I then followed the rest of the Raspberry Pi instructions for cross-compiling, paying attention to when to use sudo and when not to!
  6. Because my VM (Hyper-V) won’t do USB pass-through, I put the SD card in a USB card reader and plugged it into another Linux machine on my network. I then mounted it using sshfs, specifically:
    On the machine with the USB card plugged in:
    mount /dev/sdc1 /mnt/fat32/ -o umask=000
    mount /dev/sdc2 /mnt/ext4-2
    bindfs -u -g users /mnt/ext4-2/ /mnt/ext4

    (That last bindfs was due to the fact that ext4 partitions don’t mount with read/write permissions properly.)Then, on the cross-compiling machine:
    sudo sshfs @:/mnt/ext4 mnt/ext4
    sudo sshfs @:/mnt/fat32 mnt/fat32

    This was all in the linux/ folder that I was working within.)
  7. After doing this, the modules install line worked fine:
    sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=mnt/ext4 modules_install
  8. Then, making sure that I had put KERNEL=kernel, I copied the kernel across:
    sudo cp mnt/fat32/$KERNEL.img mnt/fat32/$KERNEL-backup.img
    sudo cp arch/arm/boot/zImage mnt/fat32/$KERNEL.img
    sudo cp arch/arm/boot/dts/*.dtb mnt/fat32/
    sudo cp arch/arm/boot/dts/overlays/*.dtb* mnt/fat32/overlays/
    sudo cp arch/arm/boot/dts/overlays/README mnt/fat32/overlays/
    sudo umount mnt/fat32
    sudo umount mnt/ext4
    I actually called the kernel a new name and then added kernel=.img in /boot/config.txt.
  9. Put the card in, booted it up and we were away to the races!  I was now able to boot the RPi, run raspi-config to get things set up, then SSH on and load all the firmwares from the forum into the /lib/firmware/ folder.
  10. Installing tvheadend was pretty simple – instructions are here, but apt-get worked for me after adding deb jessie main to /etc/apt/sources.list

Accessing disks in a VMWare VM

When you have disks sitting in a VMWare vSphere/ESXi box that are formatted NTFS, and you want to access them from inside a VM, you need to look at Raw Device Mapping (RDM).

What it took more than a little time to find out is that for SATA disks (i.e. not in a SAN or RAID config), you need to create “pointers” first.  Great instructions are available here. But, in essence, you’re looking to make a couple of calls like:

vmkfstools -z /vmfs/devices/disks/[long weird name of disk got from ls -l in /dev/disks] /vmfs/volumes/[name of datastore]/[sub folder]/[name of disk].vmdk

Really simple skinning of Windows 7 logon screen

In case you’re wanting to change the logon screen wallpaper on a Windows 7 machine, details are here

In short:

  1. Update the HKEY_LOCAL_MACHINE\ SOFTWARE\ Microsoft\Windows\ CurrentVersion\ Authentication\ LogonUI\ Background\ OEMBackground value to 1
  2. Create a folder called C:\Windows\System32\Oobe\info
  3. Then create a subfolder called backgrounds
  4. Then put the wallpaper – named BackgroundDefault.jpg and no more than 256KB – in the folder.
  5. Reboot.

DD-WRT, Telecom Thomson router and New Zealand broadband

Finally!  I can’t tell you how long I have tried to get DD-WRT working as a proper internet gateway in New Zealand!  The challenge is that broadband here is PPPoA (not PPPoE), so you are left trying to work through the mess that is half bridging and other such dramas.

Anyway, I leave some notes here for anyone else who is trying, and if I need to refer back.

  1. I have the Thomson TG585 router (v 8) with the DHCP server on.
  2. I have the Linksys WRT54G with DD-WRT (micro) running and a cable from the WAN port to a LAN port on the Thomson
  3. On the Thomson, under Home Network > Devices, I have selected the Linksys and chosen the link down the bottom that says “Assign external IP address to device”.  This only worked when I had the WAN setting on the Linksys (in DD-WRT) set to be DHCP, and had to restart a couple of times. It won’t work if the Thomson thinks the connected device has a static IP.
  4. By now, the external IP address was appearing in the Linksys under WAN IP, but I still couldn’t get internet access. Turns out that there was no default gateway set.  Through some serious Googling, I found this site that gave the following commands to be added to the Firewall script section under Administration > Commands:

ip route replace $(nvram get wan_gateway)/32 dev $(nvram get wan_ifname)
ip route replace default via $(nvram get wan_gateway) dev $(nvram get wan_ifname)

SSH’ing into the Linksys after running those commands, and then running route, shows the external gateway appearing at the top of the list.

And then everything seems to be working!  Hurray!