tisdag 11 februari 2014

Raspberry Pi project: music server

Some months ago I bought an Arduino. Arduino is a really fun platform to experiment with, but I've also been wanting to get a Raspberry Pi. And two weekends ago I decided it was time, so off I went to Akihabara and bought one.

The nice thing with Raspberry Pi is that it is more of a full-fledged computer. Install Linux on it and boom! you got yourself an ultra-cheap server. Or programming platform. Or a board to tinker with by connecting LEDs and whatnot to. And so on.

The first thing I did with my Raspberry Pi was to install Raspian (http://www.raspbian.org). And I hooked it up to my TV and an old Bluetooth keyboard. I also had an old WiFi dongle lying around that I connected to it so that it could join my WiFi network at home.

Next I installed NodeJS it, to be able to set up a simple server. (If you want to do to, I recommend to use NVM. See https://github.com/creationix/nvm)

I have an old USB hard drive with a lot of music files on, which I rarely bother to connect to my ordinary computer. But now that drive is being put to good use, connected to the Pi.

(If you're keeping count of how many things I've connected to Pi this far, let me say that I also bought a powered USB hub in order to get more ports than the two on-board ones.)

Next I wrote a simple NodeJS based server using ExpressJS that serves the music files from the hard drive to clients over http. And I implemented functions for getting lists of files residing on the drive, and playing back files on the Pi's audio-out port.

* For getting the list of files on the hard drive from NodeJS, I'm using the ordinary Node FS APIs. See http://nodejs.org/api/fs.html
* Achieving client-side playback is really simple. The web page has an HTML audio element, and all I need to do on client side is to set the src of that element to the URL that points to the file on my hard drive, as hosted via ExpressJS.
* For playing back audio on Pi from the Node script I'm using child_process.spawn. See http://nodejs.org/api/child_process.html. This is a convenient way of spawning a child process where you can do potentially long-running Linux shell operations (like "start music playback" in my case) in parallel. NodeJS is normally single-threaded so for your script to be responsive to new requests you can not do any long-running operations in the NodeJS thread itself.

Then I wrote a simple web client (using Angular) that basically just calls the server on Pi and lists the music files on a web page. And I added buttons for playing back music either on the client (typically my smartphone or my laptop with headphones), or on the audio-out port of Pi which is connected to my living room stereo.

I created a Linux upstart conf script that makes sure my NodeJS server starts whenever Raspberry Pi is finished booting up. (Or actually it starts when the network is up-and-running after Pi is powered on.)

And I realized that for some reason the network connection sometimes goes down after a while. This is of course not good if I'm listening to music away from home, so I added a script that checks the network state now and then and restarts it in case it goes down. This seems to work fine. I created one more upstart script that starts the network checking script as soon as Pi has booted so that everything is automagically started whenever I power up Pi.

So with this setup I can easily browse my music collection and play music, either on my smartphone while I'm out and about, or on the stereo at home just using my smartphone as a remote controller.

All in all the amount of code and work for this to happen was not very much, just a few evenings of time. And it's really nice to revisit my music collection that had been collecting digital dust for a few years.

There may be more simple ways of achieving the same set-up, but I thought this was a good project for getting started experimenting with Raspberry Pi. And it's so much more satisfying to build something on your own.

Some things to improve are the web page UI (which currently looks awful) and adding things like playlists and nicer volume control.

I'll try to share all code and scripts using Github with a link here soon.