Well, right now I’m lying back, listening to the fruits of my labours.
When it comes to time-shifting, there are quite a few solutions that allows a user to time-shift television. MythTV, Tivo, Foxtel IQ… just to name three. However, I’ve looked around, and found very little for radio.
My needs are basic… I have an old stereo FM tuner plugged into the line-in socket of an old laptop. It’s in a spot where it can get good reception. The machine has a 20GB HDD, Apache web server, and enough CPU grunt to push the data. I thought it’d be nice if I could stream my radio around the house, in a manner that allowed me to time shift backwards up to a few hours or so. Therefore… if I wanted to hear a radio programme, but missed the first 15 minutes — no problem, just tell it to start playing from 15 mins ago. Done.
It’d also be nice if it could be in lossless form. I’m on a 100Mbit LAN — I don’t need compression, but some extra CPU cycles could be nice.
Icecast did the basic need of streaming radio quite well… but there were a few problems:
- It couldn’t timeshift
- I found it would frequently drop the feed mysteriously
- It used Vorbis — not so much a problem, but for my needs, lossless was better
So seeing few alternatives, I set about designing my own system. The concept is simple:
- Create a capture programme, that records audio to a series of 1sec raw blockfiles, stored in a stream directory.
- Make a CGI application that scoops up a number of these blockfiles, concatenates them, adds an appropriate header and pushes them out to the client
- Construct a shell script that can clean up old files (to keep the disk from getting full)
After looking around at a suitable capture API, I settled on Portaudio v19. I had done some apps with v18… thus I had some familiarity with it. Doing this allowed my app to be cross-platform, should I decide to develop it further.
It was simply a case of writing a program that would write blockfiles, one for each second, identified by a Unix timestamp. As time ticks forward, it closes one block file and opens the next. My capture app is basically 137 lines of pure C code. Very tight and efficient.
For the CGI app, I again did it in pure C. It picks up its parameters from the PATH_INFO environment variable, allowing nice clean URLs, and parsing is as simple as calling getenv. I ended up writing two versions of this app.
The first version used sox in a child process, to convert the raw audio to a RIFF stream. My problem was that I used popen to execute sox. It worked well, except that disconnecting the player left loads of sox instances running, hogging the CPU. I started looking at writing my own RIFF header library, only to discover how complicated it was.
Looking around, I stumbled across the humble Sun Audio format. Apart from the minor inconvenience of having to pass everything through atonl/atons… everything works fine. I specify a URL like http://blackbox.local/cgi-bin/stream/linein/2 (the laptop, an IBM T20 is called blackbox), which gives me the stream “linein” from 2 seconds ago. I find it’s a bit glitchy any closer than that… but for maybe 6 hours coding, I’m quite happy with the result.
The problem has been clients… Amarok seems to know what to do with the stream. mplayer tries despairately to seek in the stream (why?) then drops out. And don’t even ask what Audacious does — I’m at a loss there. That said, good ol’e sox and wget or curl works fine, and is lightweight — just the interface is a tad annoying.
I’ll probably wind up writing my own client anyway — so I can time-shift somewhat more conveniently (using a slider to set the offset).
Still on the TODO list, is to make the capture app daemonise itself (it runs in the foreground for now), and to write an app that can be run from cron, and will record a radio programme for later listening (e.g. on a portable music player for instance) — perhaps optionally encoding it in a compressed format.
I’m tossing up whether to release the sources or not… if there’s demand for such a project, I’ll look into cleaning things up and releasing it. That said, if anyone knows of something that does similar to the above… I’d be interested to hear about it.