UPDATE: I appeared and presented my wireless music player on Gear Live's Bleeding Edge video podcast show: Check it out! (I'm 17 minutes in)
It's a problem many of us are familiar with. We have an extensive music library on our computers and iPods/portable music players, and we would like to listen to it at home, but our computers are too far from the stereo, or the rest of the family wants to listen to music while you go out with your iPod. Or perhaps you'd like to listen to Shoutcast radio stations on your stereo at home (something portable music players are at the moment largely incapable of).
These are all very sensible reasons for wanting a networked music player.
Now many companies have products for sale that fill this need, but this
page is not
about
buying
things
.
This page is about hacking a do-it-yourself wireless networked music player
for less than half the cost of a retail model, and with (potentially) more
features.
When I began this project, I knew I could not settle for anything less than:
Once again I turn to an old friend of an operating system, OpenWrt, only this
time on a new hardware platform, the excellent Netgear WGT634U. Unfortunately
Netgear has discontinued this router because of the flaky software they wrote
for it (or other business reasons). Luckily this means that refurbished
WGT634U units go for about $40 (or they did, until supply ran out just a few days
before this article was published). (UPDATE:
Justdeals.com
seems to get them in and out of stock sometimes) What's so great about these routers is
that they have a USB 2.0 port on the back, allowing connection of any number
of accessories, the most important of which in this case is the generic USB audio
device.
I wanted the best audio quality for my dollar. So naturally, I headed over
to eBay
and found the cheapest USB audio adapter
I could find. (As a side comment, don't be afraid of eBay for the cheap
sub-$50 stuff. No one scams you for such small amounts of money.)
Fortunately USB audio devices are standardized, so pretty much
every USB audio device you can get your hands on should work great with the
Advanced Linux Sound Architecture (ALSA). The little green one I got ended up
having a C-Media chipset, and actually has excellent sound quality. In order
to hear the background noise typical of a desktop computer system, I have to
set the volume down to 5 (out of 100) and use my in-ear headphones. It
might also help that the Netgear router's power supply doesn't have things
like clicking hard drives to inject noise into it. I'm quite satisfied with
this little green adapter - despite the garish color its sound quality and price ($8) are superb.
I had wanted to play with LCDs for a while now, but I had never really done in-depth searching for a cheap serial-based LCD module. $40 and up was just too much to pay. But for this project I really wanted to have the music player display its current song and artist information. I remembered that electronics kits are always cheaper than their prebuilt counterparts. So I fired up the good ol' eBay search again (for "serial lcd kit") and lo and behold, a kit from Hong Kong for about $12. If you're afraid you'll screw it up (like I did at first), you might want to go with non-kit LCDs. Note that the kit I bought ("Serial 1 pin LCD Ready Kit for Basic Stamp, ATMEL, PIC") only included a perfboard, a bag of parts, and a schematic. This would perhaps be too advanced for a beginner kit builder.
The visibility on the little display is great. The backlight is very bright and the display is trans-reflective, so it's viewable even in direct sunlight and so on.
Here I've loosely affixed the LCD to the router with a large rubber band (though I could easily have used magnets) for a simple, easy, non-permanent bond. The firmware inside the Atmel chip provided with the kit provides a very simple interface - there are numbered commands that can be entered even with a keyboard (for example, "1 [enter]" means clear the display). A couple simple shell scripts provided a quick interface to the LCD:
File: /usr/sbin/lcdinit rwx
#!/bin/sh
setbaud /dev/tts/1 9600 # Set the baud rate to 9600
lcdcmd 1 7 0 "Hello" # Clear the display, hide the cursor,
# and say Hello
File: /usr/sbin/lcdcmd rwx
#!/bin/sh
while test -n "$1" # While we have commands left
do # Loop
echo -ne "$1\\r" > /dev/tts/1; # Echo the command and a Carriage Return
shift # Move to the next command
done # End loop
|
|
As a test of my skills at commanding this new device,
I made a small LCD text show to force it to sing my allegiance.
File: /usr/sbin/lcdshow rwx
#!/bin/sh
lcdcmd 1 7 0 "Nate is awesome " 0 "Clap your hands"
usleep 500000
lcdcmd 3 0 " and you know it" 0 " Clap your hands"
usleep 500000
scrollright () {
lcdcmd 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
}
scrollright
usleep 750000
lcdcmd 1 0 "Nate is awesome " 0 "Clap your hands"
usleep 500000
lcdcmd 3 0 " and you know it" 0 " Clap your hands"
usleep 500000
scrollright
usleep 750000
lcdcmd 1 0 "Nate is awesome"
usleep 500000
lcdcmd 1 3 0 " and you know it"
usleep 500000
lcdcmd 1 0 "And he's not af-"
usleep 100000
lcdcmd 3 0 " raid to show it"
usleep 900000
lcdcmd 1 0 "Nate is awesome " 0 "Crap your pants"
usleep 500000
lcdcmd 3 0 " and you know it" 0 " Clap your hands"
usleep 500000
scrollright
I don't care what anyone else says.
You don't control anything until you can make it sing for you.
|
While not a necessary part of this project, I figured I'd mention the speakers I'm using. These are Virgin Boomtube speakers which I got off of Woot for something like $20. They are of course no longer available through Woot but eBay has a few.
|
|
These speakers have excellent sound quality. The internal bass boost really makes the small speakers kick, and the fact that you can pop 4 AA batteries in the center unit and take the speakers anywhere is just plain awesome. They are, of course, no replacement for a large 5.1 surround setup in your home. If you want true big sound, that's definitely the way to go. |
While the connection for the USB audio adapter is obvious (for the slower among you, it goes into the USB port in the back of the router), the serial LCD takes a little more finesse. Next to the serial console port on the WGT634U, there is a headerless second serial port to which the LCD must be attached.
|
| For power, I piggybacked the LCD power supply onto the USB port supply which is always at 5 volts. The LCD draws about 120mA with backlight on (20mA without), but the WGT634U doesn't mind and has no trouble powering both the USB audio adapter and the LCD at once. Here the wires are running down below the circuit board and are soldered to the pins on the USB connector. |
What good is a great hardware hack without software to back it up? Well, it's still great, but it's even better with good software.
Enter OpenWrt, the fabulicious Linux distribution designed to run on Broadcom-based routers. The latest version, codenamed Kamikaze, is as yet unreleased and still considered unstable, however the build I'm running (r3186) is working superbly. For USB-audio support, make sure to build the kmod-usb-core, kmod-usb-ohci, kmod-alsa, and kmod-soundcore kernel modules (see below for other modules you'll need), as well as mpd, the music player daemon. Watch out for kmod-usb-ehci - it adds USB2 support but can get buggy when you're trying to run the USB audio device through a USB hub.
Software for this hack relies extensively on the excellent music player package, mpd. It runs as a server which accepts commands that manipulate and play playlists. With a great many command-line, GUI, and web-based clients, mpd is sure to meet your remote playability needs. I was thinking about creating an AJAXy frontend for mpd to run on the router (since PHP is too heavyweight for such a tiny router), and I might still do it in the future.
Creating the scripts for the Mobile Wi-Fi Access Point really gave me a chance to hone my shell scripting skills. And while I can't say I'm one of the bash elite (hell, I'm far from it), it is quite easy for me now to create small useful scripts. In addition to the three scripts for initializing/displaying/singing on the LCD display, I've made a few others:
This one sends a single command to mpd and waits for a reply. It's much much simpler than any of the clients listed at musicpd.org and suits my purposes just fine. Thanks to Stefano Cazzulani for his connection-closing fix.
File: /usr/sbin/mpc rwx #!/bin/sh echo -e "$*\nclose" | nc 127.0.0.1 6600 # Usage: mpc (command) (parameters) # For example: mpc play # mpc stop # mpc add music.mp3 # mpc load playlist # mpc setvol 100
This is an important one. It can display up to 40 characters of song title and artist, one on each line. Overflow is handled by scrolling left and right.
File: /usr/sbin/lcdsong rwx
#!/bin/sh
spaces=" "
while test 1
do
art="`mpc currentsong | grep Artist | sed 's/Artist: //g'`"
if [ -z "$art" ]; then art="No Artist"; fi
ttl="`mpc currentsong | grep Title | sed 's/Title: //g'`"
if [ -z "$ttl" ]; then ttl="No Title"; fi
if [ ${#ttl} -gt 40 ]; then ttl=`echo "$ttl" | sed 's/ //g'`; fi
art="`expr substr "$art" 1 40`"
ttl="`expr substr "$ttl" 1 40`"
al=${#art}
tl=${#ttl}
if [ $al -gt $tl ]
then
maxl=$al
ttl="`expr substr "$spaces" 1 $(( ( $al - $tl ) / 2 ))`$ttl"
else
maxl=$tl
art="`expr substr "$spaces" 1 $(( ( $tl - $al ) / 2 ))`$art"
fi
if [ "${art}" != "${oldart}" -o "${ttl}" != "${oldttl}" ]
then
lcdcmd 1 0 "`expr substr "$art" 1 20`" 0 "`expr substr "$art" 21 20`"
lcdcmd 3 0 "`expr substr "$ttl" 1 20`" 0 "`expr substr "$ttl" 21 20`"
fi
i=$maxl
while [ $i -gt 16 ]
do
lcdcmd 5
i=$(( $i - 1 ))
done
sleep 1
while [ $i -lt $maxl ]
do
lcdcmd 4
i=$(( $i + 1 ))
done
oldart="$art"
oldttl="$ttl"
done
Used to mount the remote filesystem and prepare for mpd loading. It also loads mpd.
File: /usr/sbin/gompd rwx #!/bin/sh #Usage: gompd (computer IP address) # You must edit this file to work with your remote filesystem. # In this configuration, gompd will connect to the share called # mp3 on the specified computer, with the username "Nate True". # It will ask for a password upon connect, if required. # CIFS is the file system used to access Windows shares. mkdir /tmp/.mpd mkdir /tmp/music mount.cifs //$1/mp3 /tmp/music -o "user=Nate True" #This line reduces latency for pause and setvol commands echo mpd 5 16384 > /proc/asound/card0/pcm0p/oss mpd
This one was a fun one to make. Just type in the kind of music you want (ex. "trance", "symphonic classic", or "hindi") and it will search at ShoutCAST.com and play the first radio station it finds.
File: /usr/sbin/goradio rwx
#!/bin/sh
cd /tmp
rm -f mpdradio
q=`echo "$*" | sed 's/ /+/g'`
echo Performing search...
wget -qO mpdradio http://www.shoutcast.com/directory/?s=$q
until [ -f mpdradio ]; do sleep 1; done
url=`cat mpdradio | sed -n '/sbin[^"]*"/{p;q;}' | sed 's/^.*href="//g' | sed 's/.pls">.*$/.pls/g'`
if [ -z "$url" ]
then
echo No results found.
exit
fi
echo Downloading playlist...
rm -f mpdradio
wget -qO mpdradio http://www.shoutcast.com$url
until [ -f mpdradio ]; do sleep 1; done
radio=`cat mpdradio | sed -n '/http/{p;q;}' | sed 's/File[0-9]*=//g'`
echo Adding $radio to playlist.
mpc stop > /dev/null
mpc clear > /dev/null
mpc add $radio > /dev/null
mpc play > /dev/null
echo Done.
How would one...
This was a really fun project. I especially like the one-word radio tuning, because it makes it so easy to explore new music categories. I discovered how motivating European Trance music can be, and how fun Hindi music is to listen to.
Be sure to check out some of my other projects below, and a big thanks to HostGator for helping me survive such huge surges in traffic.