Journal Articles

CVu Journal Vol 17, #5 - Oct 2005
Browse in : All > Journals > CVu > 175 (15)

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: Silas's Corner

Author: Administrator

Date: 06 October 2005 05:00:00 +01:00 or Thu, 06 October 2005 05:00:00 +01:00

Summary: 

Body: 

Recycling throwaway hardware

I was recently given the challenge of setting up a system suitable for an individual to use for selecting and playing educational sound recordings. What made this particularly interesting was that the individual was an adult with very little schooling (so no complicated user interfaces allowed) and no knowledge of the English language. His accommodation was too small to set up a full PC with monitor, and there was absolutely no budget.

I was able to obtain an old PC that was being given away for free. It had 16M of RAM, a 2G hard disk, a slow CD-ROM drive and a soundcard. The system unit was not exactly small, but I planned to set it up in such a way that nothing else would be needed apart from the system unit and keyboard (and a pair of headphones). Of course the Windows 98 on it ran extremely slowly, was full of viruses and was probably unlicensed, not to mention being unsuitable for "blind" use, so I decided to erase it and install Linux.

First I used Knoppix (www.knoppix.org) to detect the sound hardware. Knoppix isn't very good at dealing with a low amount of RAM, so I had to go into "expert" mode and override some of the bootup behaviour. It helped to repartition the hard disk as soon as possible and create a swap partition. I typed lsmod and noted which kernelmodules Knoppix had loaded; this told me which modules would be needed for the soundcard.

The Linux installation to the hard disk had to be as smallas possible so as to make room for the sound recordings and also given the limited amount of RAM. Graphics was out ofthe question (but not needed for this application anyway). Knoppix was too big: I didn't want to do the drudgery required to trim it down, and anyway its installation script is broken in recent releases, and older versions of Knoppix didn't detect the soundcard at all; the unusual type of soundcard installed was supported only by the latest (2.6) version of the Linux kernel. So I needed a distribution that is up-to-date, that can give you a near-minimal installation without too much hassle, and that contains players for MP3, OGG and SPX sound files.

The obvious answer was Debian. Its most recent release (3.1 or "sarge") has a much improved installer; it probably wouldn't detect the old soundcard, but thanks to Knoppix Inow knew which module to load. I burnt myself a copy of the first CD of Debian (you don't need all the CDs) and installed a minimal system. Then I installed the 2.6 kernel, and installed the sound packages by copying the necessary package files onto a floppy disk (they weren't popular enough to be on the first CD) and finally trimmed out any remaining unnecessary packages (such as the old 2.4 kernel). Then I customised the startup scripts into /etc/init.d so as to load the correct module with modprobe, set the volume with aumix, clean out old logfiles (the disk would be very nearly full once the recordings were on it) and run my program. I also commented out potentially time-consuming startup scripts such as the filesystem check (since nowadays it uses a journalling filesystem, the check is less necessary and it doesn't matter if the user prematurely switches off the power, especially given that this application does not require saving anything).

The main part of the program was written in Python. Basically I wanted a system that would read out (in the user's native language) a list of recordings and invite him to press a number to choose one to play. Of course, I couldn't have the complication of asking him to press Enter after the number, so I used the Python curses library which interfaces with the terminal at a lower level:

import curses
curses.initscr()
curses.cbreak()

initscr() is required to initialise the curses library, and cbreak() tells it to accept one character at a time (but still allow sequences such as control-break to interrupt the program, which may be useful for debugging). Then to read a character, one would do something like:

import sys
response = sys.stdin.read(1)

That wasn't too difficult to find in the Python library documentation, and didn't require any extra libraries to be set up (it's all included with Python). We can build it up into a function that repeatedly plays a prompt while waiting for an answer like this:

def get_selection(play_prompt_command):
  pid = os.spawnlp(os.P_NOWAIT,"/bin/bash",
        "/bin/bash","-c",
        "while true; do %s; done"
        % play_prompt_command)
        curses.flushinp()
        response = sys.stdin.read(1)
    os.kill(pid,signal.SIGKILL)
    os.system("killall -9 ogg123;
    killall -9 mpg123;killall -9 speexdec")
    return response

The killall command is hacky but the alternative would be to write a function that goes through all the processes and checks each one to see if it is in the same process group as the current process but is not the same as the current process (we cannot include the current process in an uncatchable signal, and if any other signal is used then some sound-playing commands do not stop immediately). Also we use curses.flushinp() to flush the keyboard buffer (typeahead would probably be too confusing in this application).

As for what to do when something was selected (say, option 2), I decided to check for 2.mp3, 2.ogg, 2.spx or a directory called 2 (and option 0 always goes up one level).

Here's the rest of the script:

def get_play_command(filename):
  for extension,format in [
    (".mp3", "mpg123 %s.mp3"),
    (".mp2", "mpg123 %s.mp2"),
    (".ogg", "ogg123 %s.ogg"),
    (".spx", "speexdec %s.spx")]:
   try:
    if open(filename+extension):
       return format % filename
   except IOError: pass
  # by default returns None

def menu():
  play_prompt_command = get_play_command("index")
  if play_prompt_command:
    while True:
      response = get_selection(play_prompt_command)
      if response=="0": return
      play_cmd = get_play_command(response)
      if play_cmd: os.system(play_cmd)
      else:
        # it might be a directory
        try:
          os.chdir(response)
        except OSError: continue
        menu()
        os.chdir("..")
  else:
    print "\a Warning: index not found!"
    # but continue running (up one level)

def main():
  try:
    while True: menu()
  except KeyboardInterrupt: curses.endwin()if __name__ == "__main__": main()

I threw in a feature that checks for an executable script on any CD-ROM that is inserted (for future expansion) and then delivered the box. It's a shame that throwaway PCs don'tcome any smaller.

Notes: 

More fields may be available via dynamicdata ..