Entries in Programming (2)

Saturday
Feb092008

Hotclock

I use a program on my Macintosh during the radio show to remind me when to take a commercial break - the network is pretty finicky about getting out on time. The program warns me a minute before I need to break, then counts me down 10 seconds before and says "Break!" when I'm supposed to stop talking. It mostly works.
Fullscreen.jpg
A number of people who noticed the alerts on my Ustream stream have asked me for the source, so here it is, one year to the day after I wrote it. A couple of caveats: it's in Applescript, requires Quicksilver (for the really big text), and must be saved as a "Run On Idle" script. The main "on idle" loop needs to be called again and again - if the program quits after "on run" nothing will happen. If you want to actually use this (instead of just looking at it and mocking me) you'll probably want to change the break times. I'm posting this as is and without support, mostly so that I can point people here who want to know "how I do that?"
-- Application to warn host when it's time to take a break
-- in the last minute before the break post word "wrap"
-- do a ten second countdown to end then show "break"

-- Requires Quicksilver!

-- Leo Laporte, 8 February 2007
-- v 1.0

property wraps : {"16:35", "27:35", "44:35", "56:25"}
property breaks : {"17:35", "28:35", "45:35", "57:25"}
property countdown : {"10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "Break!"}

global currentSegment, breakTime, wrapTime

on run
	-- find current segment
	set now to getTime()
	set currentSegment to 1
	set wrapTime to item currentSegment of wraps
	
	repeat until now is less than wrapTime
		-- keep going until the next segment 
		if currentSegment is greater than or equal to (length of wraps) then
			set currentSegment to 1
		else
			set currentSegment to currentSegment + 1
		end if
		set wrapTime to item currentSegment of wraps
	end repeat
end run


on idle
	-- runs until applet is quit
	set now to getTime()
	set wrapTime to item currentSegment of wraps
	
	if now is greater than or equal to wrapTime then
		-- there's a minute (or less) left
		
		tellHost("Wrap...")
		
		set breakTime to item currentSegment of breaks
		repeat until now is greater than or equal to breakTime
			set now to getTime()
		end repeat
		
		-- in final ten seconds, display countdown
		repeat with msg in countdown
			tellHost(msg)
			-- delay 1
		end repeat
		
		-- go to next break time
		if currentSegment is greater than or equal to (length of breaks) then
			set currentSegment to 1
		else
			set currentSegment to currentSegment + 1
		end if
		
		set waitTime to (10 * minutes) -- don't come back for 10 minutes
	else
		set waitTime to 5 -- check every five seconds so as not to use cycles unnecessarily
	end if
	
	return waitTime
end idle


-- subroutines

on tellHost(message)
	tell application "Quicksilver"
		show large type message as text
	end tell
end tellHost


on getTime()
	set timeStr to time string of (current date)
	
	-- Get the "hour"	
	set Pos to offset of ":" in timeStr
	set theHour to characters 1 thru (Pos - 1) of timeStr as string
	set timeStr to characters (Pos + 1) through end of timeStr as string
	
	-- Get the "minute"
	set Pos to offset of ":" in timeStr
	set theMin to characters 1 thru (Pos - 1) of timeStr as string
	set timeStr to characters (Pos + 1) through end of timeStr as string
	
	--Get the "second"
	set Pos to offset of " " in timeStr
	set theSec to characters 1 thru (Pos - 1) of timeStr as string
	
	return (theMin & ":" & theSec) as string
end getTime
Friday
Dec311999

They Might Be Finished!

I've been working here and there on setting up a Linux based Dial-A-Song answering machine for They Might Be Giants. I promised it to them in April, really buckled down in October, and I'm finally (almost) done today. If you want to follow the saga read my previous posts explaining the project and detailing some of the issues. I did finally find a voice modem that works with vgetty - an old US Robotics external Voice/Fax/Data modem. After some trial and error I discovered that if I resampled the MP3s to 16-bit, 8Khz, mono sound files they'd work with the modem. It was such a thrill to dial up and hear a TMBG song on the other end. The basics of the shell script follow - minus the extensive error checking code. If an error occurs anywhere (no CD, can't read the disc, etc.), the script falls back to a default message stored on the hard drive. #! /bin/bash # ----------------------------------------------------------- # newsong - Leo Laporte, April-December 2003 # ----------------------------------------------------------- # this shell script is designed to be run hourly by cron # it pulls a random MP3 off a CD in drive, combines it with the # outgoing message (message.mp3) on the same disc, then # converts it to the appropriate RMD format and moves it # into the outgoing message directory for vgetty # # requires: # pickrandom - a perl script to pick a random song # SoX for the MP3 to WAV conversion # vgetty's PVF utilities to convert the WAV to RMD # # Written for They Might Be Giants Dial-A-Song # ----------------------------------------------------------- cd /var/spool/voice/work/ touch /var/spool/voice/work/startrun # mount CD mount /dev/cdrom /mnt/cdrom ls /mnt/cdrom/*.mp3 >/var/spool/voice/work/files # call an external perl script to select a random title from files tune=$(/var/spool/voice/work/pickrandom) echo "$tune" >currentsong.txt cp -f "$tune" tune.mp3 cp -f /mnt/cdrom/*message.mp3 message.mp3 # convert the MP3s to raw audio (signed, 16-bit, 8khz, mono)... sox -V tune.mp3 -s -w -r 8000 -c 1 tune.raw sox -V message.mp3 -s -w -r 8000 -c 1 message.raw # combine the raw files... cat message.raw >>tune.raw # convert to RMD via SoX and PVF tools... sox -V -s -w -r 8000 -c 1 tune.raw -s -w -r 8000 -c 1 standard.wav wavtopvf -16 standard.wav standard.pvf pvftormd US_Robotics 1 standard.pvf standard.rmd # copy converted file to messages directory... mv -f standard.rmd /var/spool/voice/messages/standard.rmd touch /var/spool/voice/work/endrun The little randomizing perl script: #!/usr/bin/perl # picks a random song from the file list my $tune; open(TUNES, "files") or die 1; srand; do { rand($.) < 1 && ($tune = $_) while ; } until $tune !=~ /message.mp3/i ; print $tune; exit 0; A cron job will run the script hourly. I've been running it every ten minutes for several days and it's been working without a hitch. (Here's a cool little cron feature I've not used before. If you define a MAILTO environment variable in the crontab, cron will mail the output of each job to that address every time it runs. I've added MAILTO=root to the crontab, and now I can use pine to check the results of each run. Very handy for checking up on problems after the fact. ) Since this is a headless machine I'm planning to add audio error messages using pre-recorded WAVs on the hard drive. That way John can tell how the thing is working just by turning up the speaker. We're ordering the parts and plan to build the machine this week. I'm going to put it in a small case with a Celeron, small hard drive, and 256MB RAM. It'll probably run SuSE Linux - my current favorite. Total cost well under $500. I'll set it up so that I can SSH into the box if something goes wrong, but it won't normally be connected to the network. John and John live in Brooklyn so Joshua Brentano and I are hoping to fly out and deliver this to them for Christmas. With any luck it will run without maintenance for years.