Sunday, November 22, 2009

Streaming Audio to your iPhone

I'm trying to hide the CDs and DVDs that are steadily taking over my house. I've managed to get rid of all of the DVDs, they've all been ripped to the server and are now happily up in the attic. The last problem are the CDs.

Yes, I have them all ripped to the server. The problem is that I don't have any way of getting at them everywhere in the house. Specifically, my wife likes to listen to murder mysteries as she cooks (me, I like cooking to the news). Since I don't have a network enabled stereo, I have to put up with a stack of CDs sitting on the counter.

I have given up waiting for a network enabled stereo system. The squeezebox just doesn't do it for me. I want something small, with wifi and wired, with a radio, clock and a streaming mp3 player. It should also have .m3u support and a "shuffle".

I decided to see what I could do with what we already have...

  • First realization. We both have iPhones.
  • Second realization. We have a WiFi network in the house.
  • Third realization. The iPhone will happily stream music off of a web page.

So the project was formed. First, I tried the SqueezeBox server (previously SlimServer) a server which serves up streams to network attached stereo component equipment. I've got a 1st generation SliMP3, it was a great device. The server just doesn't play well with the iPhone. So, I decided to go the bare bones route. I decided to setup Apache on my file server so that it will serve up music to the iPhones in the house. Then the phones can be used to stream the music/books wherever anyone is in the house. I would finally be allowed to hide all of the CDs! Perfect!

Setting up Apache was pretty easy. I followed the instructions that are easily Googled. I didn't create any HTML files, but I did configure the auto_index module (/etc/apache2/mods-available/autoindex.conf). My changed settings were:

IndexOptions SuppressDescription SuppressSize SuppressLastModified
IndexOptions SuppressHTMLPreamble
HeaderName /include/iPhone_Header.html

NOTE: HeaderName is relative to the DocumentRoot, not the filesystem.

This allowed me to keep the listing to just the filenames, and replaced the standard HTML header with one of my own:

<html>
<head>
<meta name="viewport" content="width=device-width; initial-scale=1.25"/>
</head>
<body>

This header provides a hint to the iPhone of where to set the viewport. It seems to work for my listings, making the file list usable.

Once we have that, we've got a server with a directory listing that we can scroll through and play individual MP3s. We don't have any playlists though, which is pretty unpleasant.

Bring in the Perl!

I've written a small piece of Perl code to iterate over my MP3 tree and create M3U files in each directory containing URLs for all of the MP3s that are children of that directory. Because of how I store my MP3s, that gives me album, artist and full play lists. I then randomize the playlist to give me a shuffled list.

The new iPhone release is able to play M3U files, with one problem. You can't skip to the next track, which is pretty poor. But now I've got a method of delivering music to any room in the house. I just have to get one of those 3rd party iPod speaker systems, and I can get rid of all of the CDs in the kitchen!

#!/usr/bin/perl

use File::Find;

use vars qw/*name *dir *prune/;

my $URL_BASE= "http://10.10.10.5/";

my @M3Us;

sub entering {
    print "entering Directory boundary ", $File::Find::name, "\n";

    push @M3Us, $File::Find::dir;
    return sort(@_);
}

sub leaving {
    print "leaving Directory boundary ", $File::Find::name, "\n";

    pop @M3Us;
}

sub wanted {
   print "Checking ", $File::Find::name,"\n";

   if ( $File::Find::name =~ /\.mp3/ ) {
       print "MP3 Found ", $File::Find::name, "\n";
       my $url = $File::Find::name;
       $url =~ s/ /%20/g;
       $url =~ s/\/export\///g;
       $url = $URL_BASE . $url;
       foreach my $dir (@M3Us) {
    my $m3u_file = $dir . "/PLAY.m3u";
    open (M3U, ">>$m3u_file") or die "Boom!";
    print M3U "$url\n";
    close(M3U);
       }
   }
}

sub cleaner {
    if ($File::Find::name =~ /PLAY.m3u/ ) {
 print "Removing ", $File::Find::name, "\n";
 unlink($File::Find::name);
    }
}

find ({ wanted => \&cleaner},"/export/mp3");

find ({ wanted => \&wanted , preprocess => \&entering, postprocess => \&leaving},"/export/mp3");

open(M3U, "/export/mp3/PLAY.m3u") or die "Unable to open play";

my @main_m3u = <M3U>;
close(M3U);

my @music = grep(!/AudioBooks/, @main_m3u);
my @random = sort { int(rand(3))-1 } @music;

open(M3U, ">/export/mp3/MUSIC.m3u") or die "Unable to open MUSIC.m3u";
print M3U "@music";
close(M3U);

open(M3U, ">/export/mp3/RANDOM.m3u") or die "Unable to open RANDOM.m3u";
print M3U "@random";
close(M3U);

3 comments:

Bruce Hoult said...

If your server is a Mac, or you have a Mac turned on that can mount the library from the server then you could use an airport express ($189) attached to speakers in the kitchen etc, and use your iPhone to control iTunes on the Mac.

Or for a dedicated solution, 1st gen iPod Touches are getting quite cheap on TradeMe now -- some cheaper than new Aiport Expresses, some not.

Jason Pollock said...

Nice, with home automation screens being so expensive, a cheap, used touch sounds like it could be used as a light controller/etc!

Anonymous said...
This comment has been removed by a blog administrator.