Streaming to an Airport Express in Ruby
In the current Apple frenzy, what’s better than a post about an old Apple product: the Airport Express. Small and versatile, this device can be used for several different applications but it’s most popular to send music over wi-fi (to your good ole’ amplifier located on the other side of your apartment for example). It’s also a pretty cheap thing.
I mentioned in my last post that I was using my little last.fm reader script to feed an Airport Express and somebody asked me how. First, I have to say that there isn’t much to do, thanks to the nice raop client created by Aaron Patterson. So like many other Ruby hacks, it starts with a:
gem install raop-client
Playing an MP3 file to your Airport is now just a small script and a command away:
[source:ruby]
require ‘raop’
raop = Net::RAOP::Client.new(“192.168.1.63″)
raop.connect
# Closer to 0 is louder
raop.volume=5
raop.play $stdin
[/source]
What looks like an IP address above (192.168.1.63) really is an IP address. It’s the one of your Airport Express on your local network, I’m sure you now how to find it (hint: your router probably knows). Save this script in a file (aex.rb for example). It takes a stream of raw wav as input, so we’ll need to summon the power of LAME (Lame Ain’t an MP3 Encoder) to produce the stream:
lame --decode "Joe Dassin - Les Champs-Élysées.mp3" - | ruby aex.rb
Mind the dash after the file name, it’s important. If you’re not a Linux happy user ™, you’ll need to get LAME. If you’re a Windows user, good luck with the pipe. Divide & Conquer with Cygwin would probably work though.
Note that making open source software that uses MP3 files isn’t an easy task. The Fraunhofer Society owns a patent on the MP3 format and has enforced it in several occasions (generating a good deal of revenue). Everything that encodes, decodes or reads MP3s have to pay and yes, that includes your iPod. I don’t think it ever went directly against open source developers but then it never said it wouldn’t either. A very reasonable move would be to authorize GPL licensed software to use their patent. I’m sure they use open source software as well, so they should really stop leaching.
Anyway you’re now happily playing songs to your Airport Express but you’re still missing the last.fm part. As I said, raop client accepts any wav stream so it’s just a matter of piping the last.fm stream (obtained in my last installment) through LAME and redirecting that to raop client.
To glue this with the last.fm script I’ve shown you last time, just do:
lastfm = LastFM.login("mriou", "****")
lastfm.tune("lastfm://user/mriou/playlist")
aex = AirportListener.new("192.168.1.65")
lastfm.play(aex)
If you want to see the whole script plus a few additional niceties, it’s available on Rubyforge. You can checkout the whole thing and then run bin/lastfm.rb.
svn co svn://rubyforge.org/var/svn/dubya/trunk dubya
Happy listening!
Matthieu Riou on July 9th 2007 in Uncategorized

responded on 12 Jul 2007 at 4:40 pm #
Thanks for the great work. Unfortunately, I can’t get it running. I grabbed the script from Rubyforge (svn wasn’t working) but executing it in Ruby does nothing. Is it supposed to prompt for user/pass or is there somewhere in that script that I’m supposed to put it? I’m not an expert at Ruby and I couldn’t tell.
responded on 12 Jul 2007 at 5:05 pm #
Er, nevermind. I was able to get the svn and all is good now. Nifty little piece of code!
responded on 12 Jul 2007 at 5:35 pm #
A buffer/cache would be nice to clear up stuttering…
responded on 12 Jul 2007 at 7:08 pm #
Actually I already had some buffering logic in there but it was giving me some difficulties with mpg123 (when you want to play on your local machine instead of going to your Airport). I’ve reintroduced it and improved it a bit.
Just update from Subversion (svn up) and you should be good. You can increase the buffer size by editing bin/lastfm.rb and changing the value passed to multiplex.bufferize(100). But unless lastfm gets very slow, the current value should be enough.
responded on 15 Jul 2007 at 5:48 pm #
Wonderful, I’d set up pretty much the same thing just without knowing to lame decode the stream from lastfm before sending it off to the airport. I was getting a lot of clicks but no music. But yours is way better what with the caching and the writing files to disk.
Aaron Patterson responded on 31 Jul 2007 at 10:21 am #
I’ve just released a gem called icanhasaudio that will keep you from having to execute lame on the command line. The only drawback is that I don’t have a win32 build ready.
http://seattlerb.rubyforge.org/icanhasaudio/
Matthieu Riou responded on 31 Jul 2007 at 7:35 pm #
Cool! I’ll check it out. And any plan to add Airport detection using the Apple RendezVous protocol? I’ve tried a few stuff here and there but haven’t had time to dig deep enough yet.
Aaron Patterson responded on 31 Jul 2007 at 8:29 pm #
I’m not going to add Airport detection to my library, but you can use net-mdns to detect Bonjour addresses:
http://rubyforge.org/projects/dnssd/
http://dnssd.rubyforge.org/net-mdns/
Off The Lip » Getting Sound Out of Flash responded on 04 Dec 2007 at 6:43 am #
[...] From there you can do a lot of things. Write the sink to a classic file that you can keep around with a cat /tmp/music.output > myfile for example. Note that this is raw music but you can probably send it to lame to encode to mp3. Personally, I pipe the file sink to an Airport Express using raop-ruby. [...]
Cody A. Ray responded on 18 Mar 2009 at 12:20 pm #
Hi Aaron,
I realize this thread (and the accompanying library on RubyForge) are long dead, but I was wondering if you still use anything for streaming last.fm to ruby? I’ve played with the code from SVN and the IP address returned from our call to tune() [which subsequently calls ws.audioscrobbler.com/radio/adjust.php] is no longer listening for port 80 connections. Any ideas?
- Cody