Saturday, April 25, 2009

the law of unintended consequences

So last week I bolted my mp3 player to twitter, via @steves_choonz. (Why? I've no idea, because I could I suppose --- I've given up following myself though.) Next thing I know I'm getting all these emails indicating I'm being followed by gentlemen, who I think it's safe to say, like their gangsta rap.

And all because I played a track with the line "Beat that bitch with a bat." (OK I played it twice to see what would happen.) And guess what? As usual, you're miles ahead of me.

It's still a great song though. Not mysogynistic at all I hear.

Wednesday, February 11, 2009

Nerd alarmclock

I bought an Asus Eee 901 recently. One of the first things I thought of having it do was to wake me up in the mornings with an Internet radio station. In principle, this is a really trivial problem:
  1. set an alarm
  2. go to sleep
  3. make a loud noise
The first step is the stumbling block. I'd no idea whether PCs could be woken by their hardware clock. (I had heard of wake-on-LAN: boot on reception of an Ethernet packet.) So I parked the idea pending further information.

Another common use of Linux boxes is as media centres, so it should have come as no surprise when I discovered that a similar problem had been solved by MythTV for an eco-friendly PVR function. (See here.)

The simplest thing that could possibly work began to look a bit like this:
wake=$(date --date="$1" +%s)
sudo sh -c "echo $wake > /sys/class/rtc/rtc0/wakealarm"
sudo /etc/acpi/sleepbtn.sh
/usr/bin/realplay rtsp://live1.rte.ie/redundant/1516.ra
Slightly noteworthy here is:
  1. The rtc driver's wake-up parameter is "seconds since the epoch".
  2. sleepbtn.sh is a Debian-ism; you may have an equivalent.
  3. GNU date has an awesome parser.
  4. (Don't you love that "redundant" in RTE's URL? What are they trying to say, exactly?)
Naturally it wasn't as simple as all that: "in theory there's no difference between theory and practice."

The first problem is that networking is disabled when the machine wakes up which upsets Real Player. (It doesn't fail, it pops up a dialog box, which isn't of very much use to a sleeping user.) A simple sleep works around this one.

A more insidious problem arises from Ubuntu's default ALSA configuration. If a program has the default audio device open when the machine goes to sleep, Real Player will fail to open it when it wakes up. Again it doesn't return a failure status, just prints an error on the console:
ALSA lib pcm_dmix.c:996:(snd_pcm_dmix_open) unable to open slave
ALSA snd_pcm_open error: Device or resource busy

The solution, described here, is to configure a software mixer sitting over the real device. (This also allows you to hear incoming Skype calls while listening to the radio, which is nice.)

So with the addition of some error-checking and a configuration file, setting an alarm clock is now as simple as:
$ /usr/local/bin/wakeup "tomorrow 07:00" radio4
(Admittedly the UI could use some work. Feel free to contribute!)

$ cat ~/.wakeuprc
radio4=http://www.bbc.co.uk/radio4/realplayer/media/fmg2.ram
newstalk=http://newstalk.fmstreams.com:8080
rte1=rtsp://live1.rte.ie/redundant/1516.ra

$ cat /usr/local/bin/wakeup
#!/bin/bash

RC=${HOME}/.wakeuprc

[ $# -ne 2 ] && echo "Usage: " && exit -1
[ ! -f $RC ] && echo "$RC: Not found" && exit -1

. $RC
name=$2
[ "${!name}" == "" ] && echo "Failed to find $name in $RC" && exit 1

wake=$(date --date="$1 +1" +%s)
wake_str=$(date -d @$wake)
now=$(date +%s)
[ $now -gt $wake ] && echo "$wake_str is past!" && exit 1

echo "Will wake at $wake_str"
sudo sh -c "echo > /sys/class/rtc/rtc0/wakealarm"
sudo sh -c "echo $wake > /sys/class/rtc/rtc0/wakealarm"
sudo /etc/acpi/sleepbtn.sh

sleep 45
/usr/bin/realplay ${!name}

Wednesday, January 28, 2009

Signal Processing 101

Stumbling across the Old Computers site the other day I was pleasantly reminded of the era of the first wave of home computers which began, roughly, with the Commodore PET and ended, rudely, with the the IBM PC. (Rather astonishingly, this site even has a description of the Compukit UK101, a machine which came into my possession, in kit form, in 1981 or 82.)

A characteristic of these machines was that they used compact cassettes as mass storage. Although floppy disk drives were available (for the Apple II, for instance) their price made them unaffordable to most nerdy teenage boys on a pocket-money budget. Cassette tapes however were not something teenage boys, whether nerdy or not, were short of in those days, for reasons embarrassingly obvious to anyone who's read High Fidelity.

So, one day in 1997 the idea of building a software emulator of the UK101 occurred to me. The first step would be to attempt to recover various programs written and bought for it and naturally stored on aging cassette tapes. In those days, the best source of information on old computers was, of course, the USENET group alt.folklore.computers.My query for information about the Kansas City tape standard led to this thread. (Nowadays of course, one's first port of call is Google, and indeed without Google's USENET archive to prompt my memory, I doubt I would have remembered enough about this effort to write anything about it at all.)

The Kansas City tape standard specifies that 0s are encoded as 4 cycles of a 1200Hz sine-wave while 1s are encoded as 8 cycles of 2400Hz. Bytes are transmitted in little-endian order, framed by a single 0 start-bit and two 1 stop-bits. This gives a data-rate of 300 baud. (A subsequent hardware modification to the UK101 doubled the above frequencies with no loss of fidelity.)

Armed with a walkman, a jack-to-jack cable, a PC with a sound-capturing sound-card and a program such as wavrec, lossless WAV files corresponding to the audio tracks containing the original programs can be prepared relatively easily. Typically these are of CD quality: 16-bit samples at 44.1kHz.

With a theoretically-perfect 300 baud signal, each bit is encoded in 147 samples of the WAV file. (This is 44100/1200*4 or 44100/2400*8.) In other words, we'd expect the first and last samples to be of zero amplitude and to see 7 zero-crossings between them for a 0-bit. Observing a 1-bit we'd expect 15 zero-crossings. The crucial insight therefore (prompted by a line in a response to the original request on alt.folklore.computers) was simply to count the zero-crossings, in software rather than hardware.

Heartened by the fact that no FFTs were required, a small C program was written to test the theory out. In practice, however, it was discovered that cassette tapes can undergo a couple of types of damage which cause deviations from the ideal: `drop-outs' which cause a diminution of signal amplitude and creasing which tends to compress or stretch the tape, increasing or decreasing the frequency of the signal. The first made it harder to determine zero-transitions, while the second caused 1s to be mistaken for 0s and vice-versa.

(A more `complete' solution to the same problem can be found here. Judging by the quality of the output, its error-correction was much better than the primitive approach just described.)

For BASIC programs, which were in the majority, single (or even multiple) bit-errors didn't prove fatal since the program was saved in ASCII format and thus they could usually be spotted by eye. The relatively low bit-rate also helped to keep errors short. (Assembler programs or those saved in a custom binary format were less tractable unfortunately.) Where possible, errors were corrected manually, using Emacs as a binary editor.

A fragment of a successfully-recovered program is shown below. (The line-terminating NUL characters provide a delay, allowing the interpreter to scan the line it's just read --- the language is Microsoft BASIC.)
^M^@^@^@^@^@^@^@^@^@^@
0 PRINTCHR$(26):DIMD(70),B(70):Q=226:GOTO31^M^@^@^@^@^@^@^@^@^@^@
1 G$=STR$(G):L=LEN(G$)^M^@^@^@^@^@^@^@^@^@^@
2 FORI=1TOL:POKE53364-I,ASC(MID$(G$,L-I+1,1)):NEXTI^M^@^@^@^@^@^@^@^@^@^@
3 S=53463:BP=1:DP=1^M^@^@^@^@^@^@^@^@^@^@
4 FC=0^M^@^@^@^@^@^@^@^@^@^@
5 IFPEEK(S-65)=QTHENFC=FC+1^M^@^@^@^@^@^@^@^@^@^@
6 IFPEEK(S-64)=QTHENFC=FC+1^M^@^@^@^@^@^@^@^@^@^@
7 IFPEEK(S-63)=QTHENFC=FC+1^M^@^@^@^@^@^@^@^@^@^@
8 IFPEEK(S-1)=QTHENFC=FC+1^M^@^@^@^@^@^@^@^@^@^@
9 IFPEEK(S+1)=QTHENFC=FC+1^M^@^@^@^@^@^@^@^@^@^@
Ironically the program which produced this is now lost; however I remember a lot of tweaking to get it to produce such output, which probably caused considerable deviation from the idealised description above. Such highly-specialised (to be charitable) software is one-shot, indicative of craftsmanship rather than engineering, rather like bespoke tools used in restoration as opposed to industrial-strength engineering disciplines.

Incidentally this project provides a good example of the important lesson that the best is the enemy of the good. Without the insight that counting zero-crossings of the sample might be just good enough, and certainly simple enough to try, this project might never have been started in the first place, and I'd have saved several hours producing this pointless rambling. (Actually I've just been moved to wonder whether that saying, a favourite of my father's, is a quotation, and have discovered that it's Voltaire. Nice.)