*BSD News Article 21391


Return to BSD News archive

Newsgroups: comp.os.386bsd.apps
Path: sserve!newshost.anu.edu.au!munnari.oz.au!constellation!convex!convex!cs.utexas.edu!math.ohio-state.edu!howland.reston.ans.net!agate!doc.ic.ac.uk!uknet!zaphod.axion.bt.co.uk!sol!news
From: msmith@rheya.lssec.bt.co.uk (Martin Smith)
Subject: VoxClock SoundBlaster Clock (part 1 of 3)
Message-ID: <1993Sep24.082103.23741@lssec.bt.co.uk>
Sender: msmith@rheya (Martin Smith)
Organization: BT D&P, London Engineering Centre
Date: 24 Sep 93 09:21:03 GMT
Lines: 1177

#!/bin/sh
#
# -------------------------------------------------------------------
# This is a speaking / chiming clock for NetBsd / 386BSD with a 
# soundblaster card and suitable device driver.
#
# This is part 1 of 3 and contains the source code
#
# Written by Martin Smith (msmith@lssec.bt.co.uk)
# -------------------------------------------------------------------
#
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	voxclock.txt
#	clock.c
#	loader.c
#	main.c
#	voxclock.h
#	Makefile
#
echo x - voxclock.txt
sed 's/^X//' >voxclock.txt << 'END-of-voxclock.txt'
X		VOXCLOCK - NetBSD / SoundBlaster Speaking Clock
X
X	   Copyright 1993 By Martin H. Smith (msmith@lssec.bt.co.uk)
X			      All Rights Reserved
X       Non Profit Redistribution In Source And Binary Form Is Permitted
X
XWhat Is It?
X-----------
X
XBasically a silly program that acts like a cuckoo clock and chimes and
Xannounces the time on the hour and half hour.
X
XWhy Do I Need It?
X-----------------
X
XWell, you'll have to answer that one for yourself.
X
XWhat Do I Need To Install It?
X-----------------------------
X
X1.) A NetBSD System. VOXCLOCK was written and tested under NetBSD 0.9
X2.) A SoundBlaster card in your system
X3.) The sb device driver by Steve Haehnichen <shaehnic@ucsd.edu> in your
X    kernel.
X4.) A Shrubbery. No, that's not really a requirement. Just as long as it
X    looks nice.
X
XHow Do I Do It?
X---------------
X
X1.) Create yourself a directory /usr/lib/voxclock and set the permissions
X    appropriate to your current paranoia level.
X
X2.) Compile the code and put the binary file and sample files in the above
X    directory. If you have NetBSD0.8 you may have to edit the path to the
X    sblast.h include file. I have not confirmed this.
X
X3.) Make a link called chime (or make a physical copy) to which of the chime
X    samples you want as your default. The 3 supplied are:
X
X	- cuckoo.voc		A cuckoo clock
X	- bigben.voc		A scratchy big ben sample
X	- clock.voc		A chiming clock
X
X4.) Make sure the voice file is in the above directory.
X
X5.) run 'voxclock &' with the appropriate options (described below). I run it
X    with no options, I picked them to match what I wanted.
X
X6.) Crack open a can of Guinness and pat yourself on the back. Wait to see if
X    any noises will occur.
X
X7.) Jump alarmingly when staring at your code at midnight and the chime goes
X    off and you'd forgotten about it (I did)
X
XWhat Are The Options?
X---------------------
X
XWell basically -take_it or -leave_it. No let's be serious for a microsecond
Xshall we. This is what you can select:
X
X-o	Announce the time as, for example, 11 o'clock.
X-a	Announce the time as, for example 11 A.M. This is selected by default.
X
X-0	Only chime and speak on the hour.
X-3	Chime and speak on the half hours as well as hours. This is the
X	default.
X
X-n	Do not speak the time, chime only.
X-v	Speak as well as chime. This is the default.
X
X-1	Chime once no matter what the hour is
X-c	Chime out the hours appopriately. This is the default.
X
X-s n	Set the chime sample rate to n Hz. You do not need to specify this if
X 	the chime is a .voc file, only if it's raw sample data.
X
XCan I Record My Own Voice?
X--------------------------
X
XIf there's a demand for this I'll convert the Atari code that builds the sample
Xbank so you can. Drop me an email if you want it.
X
XWho Is That Awful Bloke Speaking?
X--------------------------------
X
XGuess.
X
XWhy Are You Writing All This Rubbish?
X-------------------------------------
X
XI'm bored.
X
XWill It Ever End?
X-----------------
X
XYes.
X
XThanks and Things
X-----------------
X
XA big thank you to all the NetBSD / 386BSD people for letting me have a real
Xoperating system at home.
X
XThanks to Steve Haehnichen <shaehnic@ucsd.edu> for doing the hard bit of
Xwriting the device driver.
X
XThanks to Microsoft for giving me a good laugh. Good luck with NT guys
X(snigger!) 
X
XBugs?
X-----
X
XPlease report bugs, or as I prefer to call them 'Anomalies', to:
X
XMartin Smith (msmith@lssec.bt.co.uk)
X
XLast Word
X---------
X
XWhenever I post code to the net I always seem to receive nasty e-mail, flames,
Xunreasonable demands and sometimes death threats from people I've never heard
Xof. So basically:
X
X1.) I don't care if you don't like this program and I don't really care why.
X2.) I don't care if you are unable to write coherent english and cannot be
X    polite. Please save your postings for someone who does.
X
X    And after my last source posting:
X
X3.) I *really* *do* *not* *care* that you know a way I could have done it
X    using 3 less bytes of code. Get a bloody life!
X
XOn the other hand:
X
X1.) If you have a genuine problem or question I do care.
X2.) If I've made a mistake somewhere I also care and want to know.
X3.) If you've got patches or ideas from improvements I'd also like to know
X    about them. Don't be shy now. I don't bite (much.)
END-of-voxclock.txt
echo x - clock.c
sed 's/^X//' >clock.c << 'END-of-clock.c'
X/******************************************************************************
X   			        Voxclock - NetBSD Speaking Clock
X
X    	    Copyright 1993 Martin H. Smith All Rights Reserved.
X*******************************************************************************
X
XProject		: VOXCLOCK
XFilename	: clock.c
XAuthor		: M.H. Smith (msmith@lssec.bt.co.uk)
XPurpose		: Tells the time believe it or not
XCreated		: 19th September 1993
XRCSid		: $Id: clock.c,v 1.9 1993/09/22 20:34:20 msmith Exp $
X
XNotes:
X
XThis program may be freely distributed and copied in source and binary form as
Xlong as it is not sold and the header and copyright information is included in
Xall such copies.
X
XEvery care has been taken in the preparation of this software. It is used at
Xthe recipients risk without charge and I can accept no responsibility for loss
Xor damage howsoever caused.
X
X******************************************************************************/
X
X#include <fcntl.h>
X#include <time.h> 
X#include <syslog.h>
X
X/*
X * I hate having to hard code this but if I use an include dir in the makefile
X * it will pick up /sys/sys/time.h and things screw up.
X */
X
X#include "/sys/sys/sblast.h"
X
X#include "voxclock.h"
X
Xextern boolean want_ampm;						  /* all defined in main.c */
Xextern boolean half_hour;
Xextern boolean chime_all;
Xextern boolean speaking;
Xextern boolean test_mode;
X
Xextern int chime_play_rate;
Xextern int chime_data_len;
Xextern unsigned char *chime_data_ptr;
X
Xextern int voice_play_rate;
X
X
X/*
X * Work out how many seconds until we need to wake up and think about doing
X * something
X */
X
Xstatic int time_to_event()
X
X{
X	time_t timenow = time(NULL);				  /* what time is it? */
X	struct tm * tp = localtime(&timenow);		  /* what time is it really? */
X	int target_h = tp -> tm_hour;
X	int target_m;
X	int res = 0;
X	
X	if (tp -> tm_min < 30 && half_hour)
X		target_m = 30;							  /* chime on half hour ? */
X	else
X	{
X		if (tp -> tm_min)						  /* might be due now */
X			++target_h;
X		
X		target_m = 0;							  /* must be on the hour */
X	}
X
X	res = (target_h * 3600 + target_m * 60) -
X		(tp -> tm_hour * 3600 + tp -> tm_min * 60 + tp -> tm_sec);
X
X	return res;									  /* no of seconds to go */
X}
X
X
X/*
X * Wait until we can open the soundblaster device and return the file
X * descriptor we got
X */
X
Xstatic int open_soundblaster(void)
X
X{
X	int fd = -1;
X	int tries = 0;
X
X	while (fd < 0 && tries++ < SB_OPEN_TRIES)
X	{
X		fd = open(SB_DSP_DEV, O_RDWR, 0);
X
X		if (fd < 0)
X			sleep(SB_OPEN_SLEEP);
X	}
X
X	if (fd < 0)
X		syslog(LOG_ERR, "Failed to open soundblaster device");
X	
X	return fd;
X}
X
X
X/*
X * Initialise soundblaster
X */
X
Xstatic void init_soundblaster(int fd, int speed)
X
X{
X	int arg = 0;
X	
X	if (ioctl(fd, DSP_IOCTL_STEREO, &arg) < 0)
X		syslog(LOG_ERR, "Failed to select mono with ioctl!\n");
X
X	arg = speed;
X
X	if (ioctl(fd, DSP_IOCTL_SPEED, &arg) < 0)
X		syslog(LOG_ERR, "Failed to select speed with ioctl!\n");
X}
X
X
X/*
X * Play either one or more chimes on the soundblaster depending on the
X * selection
X */
X
Xstatic void big_ben(int fd)						  /* apologies for the name */
X
X{
X	time_t thetime = time(NULL);
X	struct tm * tp = localtime(&thetime);
X	int bong;
X
X	int chimes = chime_all ? (tp -> tm_hour % 12) : 1;
X
X	if (!chimes)
X		chimes = 12;							  /* midnight */
X
X	if (tp -> tm_min == 30)
X		chimes = 1;								  /* once on the half hour */
X
X	for (bong = 0; bong < chimes; ++bong)		  /* here is the news */
X		write(fd, chime_data_ptr, chime_data_len);
X}
X
X
X/*
X * Speak one particular word, bung out the sample to the DSP device
X */
X
Xstatic void speak_word(int fd, word_enum what)
X
X{
X	int length;
X	String ptr = get_sample_data(what, &length);
X
X	write(fd, ptr, length);
X}
X
X	
X/*
X * Say the time in a nice friendly sort of way!
X */
X
Xstatic void speak_the_time(int fd)
X
X{
X	time_t thetime = time(NULL);
X	struct tm * tp = localtime(&thetime);
X	int hour = tp -> tm_hour % 12;
X	word_enum ampm_word = (tp -> tm_hour >= 12) ? W_PM : W_AM;
X	
X	if (!hour)
X		hour = 12;								  /* midnight, not zero */
X
X	speak_word(fd, W_ITS);						  /* Monty Pythons Flying
X													 Circus? */
X	speak_word(fd, W_1 + hour - 1);				  /* say the hour */
X
X	if (want_ampm)								  /* say AM or PM */
X	{
X		if (tp -> tm_min == 30 && half_hour)	  /* half hour */
X			speak_word(fd, W_30);
X		
X		speak_word(fd, ampm_word);				  /* say right word */
X	}
X	else
X	{
X		if (tp -> tm_min != 30)
X			speak_word(fd, W_OCLOCK);			  /* don't say it half hour */
X	}
X}
X
X
X/*
X * Announce the time, open the DSP device on the soundblaster and select the
X * correct speed. Then play the right number of chimes. Then select the VOX
X * speed and announce the time if necessary
X */
X
Xstatic void announce_time(void)
X
X{
X	int fd = open_soundblaster();
X
X	if (fd > 0)
X	{
X		init_soundblaster(fd, chime_play_rate);	  /* ready to chime */
X	
X		big_ben(fd);							  /* do chimes */
X		
X		ioctl(fd, DSP_IOCTL_FLUSH, 0);
X		close(fd);
X	}
X	
X	if (speaking)								  /* want an announcement */
X	{
X		fd = open_soundblaster();
X
X		if (fd > 0)
X		{
X			init_soundblaster(fd, voice_play_rate);	/* ready to speak */
X			
X			speak_the_time(fd);
X
X			close(fd);
X		}
X	}
X}
X
X
X/*
X * Main clock entrypoint, an infinite loop. This is of course totally
X * deliberate and I never write them by accident. It was always the person on
X * holiday.
X */
X
Xvoid clock_go(void)
X
X{
X	while (1)									  /* forever is a long time */
X	{
X		int sleepfor = time_to_event();			  /* wait interval */
X
X		if (test_mode)
X			sleepfor = 5;						  /* impatience rules */
X
X		sleep(sleepfor);						  /* wait for time */
X
X		announce_time();						  /* make a song and dance */
X	}
X}
X
X/* file identification */
X
Xstatic char RCSid[] = "$Id: clock.c,v 1.9 1993/09/22 20:34:20 msmith Exp $";
END-of-clock.c
echo x - loader.c
sed 's/^X//' >loader.c << 'END-of-loader.c'
X/******************************************************************************
X			      Voxclock - NetBSD Speaking Clock
X
X    	    Copyright 1993 Martin H. Smith All Rights Reserved.
X*******************************************************************************
X
XProject		: VOXCLOCK
XFilename	: loader.c
XAuthor		: M.H. Smith (msmith@lssec.bt.co.uk)
XPurpose		: Reads in sample files and voice files
XCreated		: 18th September 1993
XRCSid		: $Id: loader.c,v 1.3 1993/09/22 19:31:29 msmith Exp $
X
XNotes:
X
XThis program may be freely distributed and copied in source and binary form as
Xlong as it is not sold and the header and copyright information is included in
Xall such copies.
X
XEvery care has been taken in the preparation of this software. It is used at
Xthe recipients risk without charge and I can accept no responsibility for loss
Xor damage howsoever caused.
X
X******************************************************************************/
X
X#include <stdlib.h>
X#include <syslog.h>
X
X#include "voxclock.h"
X
Xstatic word_struct_ptr word_table = NULL;		  /* word sample data */
Xstatic int word_base = 0;						  /* awful hacky thing */
X
XString voc_header = "Creative Voice File";		  /* what's a VOC file? */
X
XString words_needed[] =							  /* must have these words */
X
X{
X	"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
X	"30",
X	"its", "oclock", "a.m.", "p.m.",
X
X	NULL
X};
X
X
X/*
X * Voice file was created on my Atari which has a sensible ordering of bytes
X * within a word. Bloody intel
X */
X
Xint32 get32(unsigned char *x)
X
X{
X	int res = 0;
X	int f;
X
X	for (f = 0; f < 4; ++f)
X		res = res * 256 + x[f];
X
X	return res;
X}
X
X
Xint32 get16(unsigned char *x)
X
X{
X	int res = 0;
X	int f;
X
X	for (f = 0; f < 2; ++f)
X		res = res * 256 + x[f];
X
X	return res;
X}
X
X
X/*
X * Read in a voicebank file. The file format was done on a 68000 so we have
X * to scramble the bytes into the stupid order this silly thing expects!
X */
X
XString read_voicebank(String fname)
X
X{
X	Stream fd = fopen(fname, "r");
X	String res = NULL;
X	
X	if (fd)
X	{
X		int32 flen;
X		
X		fseek(fd, 0, 2);
X		flen = ftell(fd);
X		fseek(fd, 0, 0);
X
X		res = malloc(flen);
X
X		if (res)
X		{
X			fread(res, flen, 1, fd);
X		}
X		else
X			fprintf(stderr, "No memory to load samples\n");
X		
X		fclose(fd);
X	}
X	else
X		fprintf(stderr, "voxclock: Can't open voicebank file '%s'\n", fname);
X
X	return res;
X}
X
X
X/*
X * Some fields in the voicebank must make sense before we try and process it
X * this is sample rate, file size, file id etc
X */
X
Xboolean validate_voicebank(String ptr, int *speed)
X
X{
X	boolean res = FALSE;
X	
X	if (strncmp(ptr, VOX_FILE_ID, strlen(VOX_FILE_ID)))
X		fprintf(stderr, "File does not seem to be valid voicebank file\n");
X	else
X	{
X		int stereo = get16(ptr + 12);
X		int bits = get16(ptr + 14);
X		int rate = get32(ptr + 22) &0xffffff;
X		
X		if (stereo)
X			fprintf(stderr, "Stereo samples not supported, sorry (%d)\n",
X					stereo);
X		else
X		{
X			if (bits != 8)
X				fprintf(stderr, "Only 8 bit samples supported (%d)\n", bits);
X			else
X			{
X				*speed = rate;
X				
X				res = TRUE;
X			}
X		}
X	}
X
X	return res;
X}
X
X
X
X/*
X * Fill in the initial word table, create structures and mark them all as
X * unseen
X */
X
Xstatic void init_word_table(void)
X
X{
X	int f = 0;
X	word_struct_ptr w;
X	int x;
X
X	while (words_needed[f])
X		++f;
X
X	w = (word_struct_ptr) malloc(sizeof(word_struct) * (f + 1));
X
X	if (w)
X	{
X		for (x = 0; x <= f; ++x)
X		{
X			w[x].name = words_needed[x];		  /* copy name of word */
X			w[x].found = FALSE;					  /* not seen it */
X			w[x].ptr = NULL;					  /* have no pointer to it */
X			w[x].length = 0;
X		}
X
X		word_table = w;
X	}
X	else
X		fprintf(stderr, "voxclock: No memory for word table\n");
X}
X
X
X/*
X * Given a string, look it up in the word table
X */
X
Xstatic word_struct_ptr find_word(String name)
X
X{
X	int f = 0;
X	word_struct_ptr res = NULL;
X
X	for (f = 0; word_table[f].name && !res; ++f)
X		if (!strcmp(name, word_table[f].name))
X			res = &word_table[f];
X	
X	return res;
X}
X
X
X/*
X * Look through the word list in the file, work out how many words we have
X * and build appropriate data structures for them that match the ones
X * we are expecting. It is an error if we can't find all the words we want
X * or there are duplicate words
X */
X
Xboolean extract_words(String ptr)
X
X{
X	boolean res = FALSE;
X	int f;
X
X	String oldptr = ptr;
X	
X	init_word_table();
X	ptr += 128;									  /* skip header */
X	
X	while (*ptr)
X	{
X		word_struct_ptr w;
X		
X		w = find_word(ptr);
X
X		if (!w)
X			fprintf(stderr, "voxclock: Unknown word '%s'\n", ptr);
X		else
X		{
X			if (w -> found)
X				fprintf(stderr, "voxclock: Duplicate word '%s' ignored\n",
X						ptr);
X			else
X			{
X				w -> found = TRUE;
X				w -> length = get32(ptr + 16);
X				w -> ptr = (String) (get32(ptr + 12) + (int) oldptr); 
X			}
X		}
X		
X		ptr += 20;
X	}
X
X	word_base = ptr - oldptr;
X
X	res = TRUE;
X	
X	for (f = 0; word_table[f].name; ++f)
X	{
X		if (!word_table[f].found)
X		{
X			fprintf(stderr, "voxclock: Word %s was not found in voicebank\n",
X					word_table[f].name);
X			res = FALSE;
X		}
X	}
X	
X	return res;
X}
X
X
X/*
X * Test whether a file is a Creative Labs .VOC file or not
X */
X
Xboolean is_voc_file(String ptr, int *offset)
X
X{
X	boolean res = (strncmp(ptr, voc_header,
X						   strlen(voc_header))) ? FALSE : TRUE;
X
X	if (res)									  /* looks ok so far */
X	{
X		int vers;
X		int vers2;
X		*offset = ptr[20] + (ptr[21] * 256);
X
X		vers  = ptr[22] + (ptr[23] * 256);
X		vers2 = ptr[24] + (ptr[25] * 256);
X
X		if (((~(vers) + 0x1234) & 0xffff) != vers2)
X		{
X			syslog(LOG_ERR, "Version check mismatch - bad .VOC format");
X			res = FALSE;
X		}
X	}
X
X	return res;
X}
X
X
X/*
X * Read in a sample file
X */
X
XString read_sample_file(String name, int *len)
X
X{
X	String res = NULL;
X	Stream fd = fopen(name, "r");
X
X	if (fd)
X	{
X		int32 flen;
X		
X		fseek(fd, 0, 2);
X		flen = ftell(fd);
X		fseek(fd, 0, 0);
X
X		*len = flen;
X		
X		res = malloc(flen);
X		
X		if (res)
X			fread(res, flen, 1, fd);
X		else
X			fprintf(stderr, "No memory to load sample\n");
X		
X		fclose(fd);
X	}
X	else
X		fprintf(stderr, "voxclock: Can't open sample file '%s'\n", name);
X
X	return res;
X}
X
X
X/*
X * We know it's a VOC file, extract the sample information from it and
X * set up the pointers. Should really handle multiple blocks and stuff but
X * for now expects a single one.
X */
X
Xint setup_voc_block(unsigned char * start, int *len, int *speed)
X
X{
X	int res = 0;
X	
X	if (*start == 1)							  /* type 1 = voice block */
X	{
X		int length = start[1] + (256 * start[2]) + (65536 * start[3]);
X		int rate = -1000000 / (start[4] - 256);
X
X		res = 4;								  /* skip header */
X
X		*len = length;
X		*speed = rate;
X	}
X
X	return res;
X}
X
X
X/*
X * Find out the start and length of a word sample
X */
X
XString get_sample_data(word_enum w, int *length)
X
X{
X	*length = word_table[w].length;
X
X	return word_table[w].ptr + word_base;
X}
X
X/* file identification */
X
Xstatic char RCSid[] = "$Id: loader.c,v 1.3 1993/09/22 19:31:29 msmith Exp $";
END-of-loader.c
echo x - main.c
sed 's/^X//' >main.c << 'END-of-main.c'
X/******************************************************************************
X  			        Voxclock - NetBSD Speaking Clock
X
X    	    Copyright 1993 Martin H. Smith All Rights Reserved.
X*******************************************************************************
X
XProject		: VOXCLOCK
XFilename	: main.c
XAuthor		: M.H. Smith (msmith@lssec.bt.co.uk)
XPurpose		: Main program
XCreated		: 18th September 1993
XRCSid		: $Id: main.c,v 1.4 1993/09/22 19:31:29 msmith Exp $
X
XNotes:
X
XThis program may be freely distributed and copied in source and binary form as
Xlong as it is not sold and the header and copyright information is included in
Xall such copies.
X
XEvery care has been taken in the preparation of this software. It is used at
Xthe recipients risk without charge and I can accept no responsibility for loss
Xor damage howsoever caused.
X
X******************************************************************************/
X
X#include <stdio.h>
X#include <syslog.h>
X
X#include "voxclock.h"
X
Xboolean want_ampm = TRUE;						  /* say 11 AM or 11 o'clk */
Xboolean half_hour = TRUE;						  /* half hours? */
Xboolean chime_all = TRUE;						  /* announce or chime ? */
Xboolean speaking  = TRUE;						  /* announce time ? */
Xboolean test_mode = FALSE;						  /* chime very soon to test */
X
Xint chime_play_rate = 0;						  /* chime sample speed */
Xint chime_data_len = 0;							  /* how long is chime */
Xunsigned char * chime_data_ptr = NULL;			  /* where is data for it? */
X
Xint voice_play_rate = 0;						  /* speed to play voice at */
X
XString vox_file_name = DEFAULT_VOICE;			  /* standard voice file */
XString chime_file_name = DEFAULT_CHIME;			  /* standard chime file */
X
X/*
X * Go through the options and work out what is wanted. Sets up various flags
X * and variables to be used by the program.
X */
X
Xstatic boolean process_options(int argc, String argv[])
X
X{
X	int arg = 1;
X	int files = 0;
X	boolean res = TRUE;
X	
X	while (arg < argc)							  /* more arguments */
X	{
X		if (argv[arg][0] == '-')				  /* an option */
X		{
X			switch (argv[arg][1])
X			{
X			case 'o':
X				want_ampm = FALSE;				  /* say o'clock */
X				break;
X				
X			case 'a':
X				want_ampm = TRUE;				  /* say am or pm */
X				break;
X				
X			case '3':
X				half_hour = TRUE;				  /* say half hours */
X				break;
X				
X			case '0':
X				half_hour = FALSE;				  /* don't say half hour */
X				break;
X				
X			case 'v':
X				speaking = TRUE;				  /* say the time */
X				break;
X				
X			case 'n':
X				speaking = FALSE;				  /* don't say the time */
X				break;
X				
X			case 'c':
X				chime_all = TRUE;				  /* chime all hours */
X				break;
X				
X			case '1':
X				chime_all = FALSE;				  /* chime once */
X				break;
X				
X			case 's':
X				if (isdigit(argv[arg][2]))
X					chime_play_rate = atoi(argv[arg] + 2);
X				else
X				{
X					if (arg == argc)
X						fprintf(stderr, "-s option needs sample rate\n");
X					else
X					{
X						++arg;
X						chime_play_rate = atoi(argv[arg]);
X					}
X				}
X				break;
X
X			case 't':							  /* select test mode */
X				test_mode = TRUE;
X				break;
X				
X			default:
X				
X				fprintf(stderr, "Unknown option '%s'\n", argv[arg]);
X				res = FALSE;
X				break;
X			}
X		}
X		else									  /* a filename */
X		{
X			if (files == 0)
X				chime_file_name = argv[arg];
X			else
X				vox_file_name = argv[arg];
X			
X			++files;
X		}
X		
X		++arg;
X	}
X	
X	return res;
X}
X
X
X/*
X * Print out a message giving the usage options
X */
X
Xstatic void give_out_usage(void)
X	
X{
X	fprintf(stderr, "VOXCLOCK %s by Martin H. Smith. Built on %s\n\n",
X			VOX_VERSION, __DATE__);
X	fprintf(stderr, 
X			"Usage voxclock [options] [chime_sample_file] [voice_file]\n\n");
X	fprintf(stderr, "Options are:\n\n");
X	fprintf(stderr, "-o         Announce time as e.g. 11 o'clock\n");
X	fprintf(stderr, "-a         Announce time as e.g. 11 A.M. (default)\n");
X	fprintf(stderr, "-0         Chime / announce only on the hour\n");
X	fprintf(stderr, "-3         Chime / announce half hours too (default)\n");
X	fprintf(stderr, "-n         Do not speak the time, chime only\n");
X	fprintf(stderr, "-v         Speak the time as well as chime (default)\n");
X	fprintf(stderr, "-1         Chime once on the hour\n");
X	fprintf(stderr, "-c         Chime the hours appropriately (default)\n");
X	fprintf(stderr, "-s <speed> Specify chime playback speed\n\n");
X	fprintf(stderr, "Note: you do not need -s if chime is a .VOC file\n");
X	fprintf(stderr, 
X			"Voice file should be sample file bank built by makebank\n");
X}
X
X
X/*
X * This is where we came in I believe ...
X */
X
Xint main(int argc, String argv[])
X
X{
X	int exit_status = 0;						  /* all ok? */
X	int offset;
X	int clength;								  /* chime sample length */
X
X	openlog("voxclock", LOG_PID, LOG_USER);		  /* set up syslog */
X	
X	if (process_options(argc, argv))			  /* options ok ? */
X	{
X		String ptr = read_voicebank(vox_file_name);
X		String ptr2 = read_sample_file(chime_file_name, &clength);
X		
X		if (!ptr || !validate_voicebank(ptr, &voice_play_rate))
X			exit_status = 1;					  /* rubbishy file */
X		else
X			if (!extract_words(ptr))			  /* find words */
X				exit_status = 1;
X		
X		if (!exit_status && ptr2 && is_voc_file(ptr2, &offset))
X		{
X			chime_data_ptr = ptr2 + setup_voc_block(ptr2 + offset,
X													&chime_data_len,
X													&chime_play_rate);
X			chime_data_ptr += offset;
X		}
X		else										  /* use all of file raw */
X		{
X			chime_data_ptr = ptr2;
X			chime_data_len = clength;
X		}
X		
X		if (chime_data_ptr && chime_data_len && chime_play_rate &&
X			voice_play_rate)
X		{
X			clock_go();
X		}
X		else
X			fprintf(stderr, "voxclock: Initialisation failed. Exiting.\n");
X	}
X	else
X	{
X		exit_status = 1;
X		give_out_usage();
X	}
X	
X	return exit_status;
X}
X	
Xstatic char RCSid[] = "$Id: main.c,v 1.4 1993/09/22 19:31:29 msmith Exp $";
END-of-main.c
echo x - voxclock.h
sed 's/^X//' >voxclock.h << 'END-of-voxclock.h'
X/******************************************************************************
X                    Voxclock - NetBSD Speaking Clock
X
X    	    Copyright 1993 Martin H. Smith All Rights Reserved.
X*******************************************************************************
X
XProject		: VOXCLOCK
XFilename	: VOXCLOCK.H
XAuthor		: M.H. Smith (msmith@lssec.bt.co.uk)
XPurpose		: Definitions for voxclock program
XCreated		: 10th June 1993
XRCSid		: $Id: voxclock.h,v 1.4 1993/09/22 19:31:29 msmith Exp $
X
XNotes:
X
XYou need to have a SoundBlaster installed and the device driver written by
XSteve Haehnichen <shaehnic@ucsd.edu> built into the kernel to make this program
Xwork.
X
XThis program may be freely distributed and copied in source and binary form as
Xlong as it is not sold and the header and copyright information is included in
Xall such copies.
X
XEvery care has been taken in the preparation of this software. It is used at
Xthe recipients risk without charge and I can accept no responsibility for loss
Xor damage howsoever caused.
X
X******************************************************************************/
X
X#ifndef VOXCLOCK_H_INC
X#define VOXCLOCK_H_INC
X
X#include <stdio.h>
X
X#define VOX_VERSION "V1.01"
X
Xtypedef char * String;
Xtypedef FILE * Stream;
Xtypedef enum { FALSE, TRUE } boolean;
X
Xtypedef int int32;
Xtypedef unsigned int Uint32;
X
X#define DEFAULT_CHIME "/usr/lib/voxclock/chime"
X#define DEFAULT_VOICE "/usr/lib/voxclock/voice"
X
X#define VOX_FILE_ID "VOXB"						  /* file id string */
X
X#define SB_DSP_DEV "/dev/sb_dsp"				  /* DSP device file */
X#define SB_OPEN_SLEEP 5							  /* seconds to wait */
X#define SB_OPEN_TRIES 60						  /* no of goes to have */
X
Xtypedef struct word_struct
X
X{
X	String name;								  /* ascii representation */
X	boolean found;								  /* seen it before? */
X	String ptr;									  /* pointer to sample */
X	int32 length;								  /* length of sample */
X} word_struct, *word_struct_ptr;
X
X/*
X * NOTE: KEEP THESE IN THE SAME ORDER AS IN LOADER.C !!!!
X */
X
Xtypedef enum
X
X{
X	W_1, W_2, W_3, W_4, W_5, W_6, W_7, W_8, W_9, W_10, W_11, W_12,
X	W_30, W_ITS, W_OCLOCK, W_AM, W_PM
X} word_enum;
X
X/* prototypes */
X
X/* loader.c */
X
Xextern String read_voicebank(String);
Xextern boolean validate_voicebank(String, int *);
Xextern boolean extract_words(String);
Xextern boolean is_voc_file(String, int *);
Xextern String read_sample_file(String, int *);
Xextern int setup_voc_block(unsigned char *, int *, int *);
Xextern String get_sample_data(word_enum, int *);
X
X#endif /* VOXCLOCK_H_INC */
END-of-voxclock.h
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
X#
X# Makefile for Voxclock (msmith@lssec.bt.co.uk)
X#
X# $Id: Makefile,v 1.3 1993/09/22 20:50:59 msmith Exp msmith $
X#
X
XOBJ=main.o loader.o clock.o
XSRC=main.c loader.c clock.c
X
XCFLAGS=-g -ansi
X
Xall: voxclock
X
X.c.o:
X	cc -c $(CFLAGS) $<
X
Xvoxclock: $(OBJ)
X	cc $(CFLAGS) $(OBJ) -o voxclock
X
X#
X# All this rubbish is for putting the distribution together
X#
X
Xsamples: voice.Z.uue clock.voc.Z.uue cuckoo.voc.Z.uue bigben.voc.Z.uue
X	
Xvoice.Z.uue: voice
X	compress -c < voice | uuencode voice.Z > voice.Z.uue
X
Xclock.voc.Z.uue: clock.voc
X	compress -c < clock.voc | uuencode clock.voc.Z > clock.voc.Z.uue
X
Xcuckoo.voc.Z.uue: cuckoo.voc
X	compress -c < cuckoo.voc | uuencode cuckoo.voc.Z > cuckoo.voc.Z.uue
X
Xbigben.voc.Z.uue: bigben.voc
X	compress -c < bigben.voc | uuencode bigben.voc.Z > bigben.voc.Z.uue
X
Xshar: voxclock samples
X	shar voxclock.txt *.[ch] Makefile > voxclock1.shar
X	shar *.voc.Z.uue > voxclock2.shar
X	shar voice.Z.uue > voxclock3.shar
X
X#
X# Keep your filesystem tidy
X#
X
Xclean:	
X	rm voxclock *.o *.uue *.shar #* *~
X
END-of-Makefile
exit

-------------------------------------------------------------------------------
Martin Smith  | A superlative suggestion sir, with only two flaws ... 
              | I know that, technically, that's only one flaw, but it was
BT D&P nodnoL | such a big one I thought I'd mention it twice - Kryten. 
-------------------------------------------------------------------------------