# 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:
#
#	READ.ME
#	install.bsd
#	spkr.7
#	Makefile
#	spkr.c
#	spkr.h
#	interp.c
#	Files
#	Install
#	Master
#	Name
#	Node
#	Remove
#	Size
#	System
#	playtest
#
echo x - READ.ME
sed 's/^X//' >READ.ME << 'END-of-READ.ME'
X		Console Speaker Driver Package (v1.1)
X
X		by Eric S. Raymond (esr@snark.thyrsus.com)
X
XThis package gives 80386 machines running SVr3.2 or later the ability to play
Xtunes on the console speaker.  It has been extended to 386BSD (and possibly
XBSDI) by Andrew A. Chernov, and to SCO UNIX 3.2.4 (and possibly other VPIX
Xsystems) by Andreas Arens.
X
XThe following files are contained in the kit:
X
XDocumentation and examples:
XREAD.ME		-- this file
Xspeaker.7	-- man page for the driver
Xplaytest	-- test script exercising familiar tunes
X
XInstallable driver kit parts, for SVr3.2 or later:
XFiles		-- list of driver package file locations
XInstall		-- installation script for driver kit
XMaster		-- mdevice entry for speaker driver
XName		-- name entry foe speaker driver
XNode		-- /dev node specification file
XRemove		-- Driver removal script
XSize		-- installation size data
XSystem		-- sdevice entry for speaker driver
X
XDriver source code, for SVr3.2 or later and 386BSD:
XMakefile	-- Makefile for driver code
Xspkr.c		-- the driver source
Xspeaker.h	-- ioctl interface file
X
XCommon source code:
Xinterp.c	-- play string interpretation code
X
XFor SVr3.2 or later, simply type `make' and wait. Then type ./Install
Xand follow its instructions. You will have to install the man pages by hand.
XBe aware that the speaker.7 man page uses tbl(1) constructs.
X
XFor 386BSD, follow the installation instructions in install.bsd.
X
XFor SCO UNIX 3.2.4, no new kernel drivers are needed, and you need only
Xcopy interp.c to your src directory and proceed with making NetHack, with
XVPIX_MUSIC set in unixconf.h.
X
XInteresting tunes mailed to the author will be periodically posted in batches
Xand added to the test script for future versions.
X
X			Revision notes
X
X1.1 -- fixed minor bug in M[LSN] interpretation, added octave-tracking.
X       Tweaked the playtest examples.
END-of-READ.ME
echo x - install.bsd
sed 's/^X//' >install.bsd << 'END-of-install.bsd'
XCopy spkr.c and interp.c to /sys/i386/isa
XCopy spkr.h to /sys/sys
X
X-----------------------------------------------------------------------------
X
XFile /sys/i386/conf/YOUR_MACHINE_NAME
Xadd following line:
X
Xpseudo-device   speaker
X
X-----------------------------------------------------------------------------
X
XFile /sys/i386/conf/files.i386
Xadd following line:
X
Xi386/isa/spkr.c         optional speaker
X
X-----------------------------------------------------------------------------
X
XFile /sys/i386/i386/conf.c
X[major number 20 (hex) is registered for spkr driver, don't change it]
Xadd following code:
X
X#include "speaker.h"
X#if NSPEAKER > 0
Xint     spkropen(),spkrclose(),spkrwrite(),spkrioctl();
X#else
X#define spkropen  enxio
X#define spkrclose enxio
X#define spkrwrite enxio
X#define spkrioctl enxio
X#endif
X	...
X
Xstruct cdevsw	cdevsw[] =
X{
X	...
X
X	{ spkropen,     spkrclose,      enxio,          spkrwrite,      /*20*/
X	  spkrioctl,    enxio,          enxio,          NULL,
X	  enxio,        enxio,          enxio },
X	...
X
X-----------------------------------------------------------------------------
X
XMake corresponding device:
X
X	mknod /dev/speaker c 32 0
X
X[major number 32 (20 hex) is registered for spkr driver, don't change it]
X
X-----------------------------------------------------------------------------
X
XGo to /sys/i386/conf and type
X	config YOUR_MACHINE_NAME
Xthen go to /sys/compile/YOUR_MACHINE_NAME and type
X	make depend
X	make
X
END-of-install.bsd
echo x - spkr.7
sed 's/^X//' >spkr.7 << 'END-of-spkr.7'
X.TH SPKR 7
X.SH NAME
Xspkr \- console speaker device driver
X.SH DESCRIPTION
XThe speaker device driver allows applications to control the PC console
Xspeaker on an IBM-PC-compatible machine running UNIX.
X.PP
XOnly one process may have this device open at any given time; open() and
Xclose() are used to lock and relinquish it. An attempt to open() when
Xanother process has the device locked will return -1 with an EBUSY error
Xindication. Writes to the device are interpreted as 'play strings' in a
Xsimple ASCII melody notation. An ioctl() for tone generation at arbitrary
Xfrequencies is also supported.
X.PP
XSound-generation does \fInot\fR monopolize the processor; in fact, the driver
Xspends most of its time sleeping while the PC hardware is emitting
Xtones. Other processes may emit beeps while the driver is running.
X.PP
XApplications may call ioctl() on a speaker file descriptor to control the
Xspeaker driver directly; definitions for the ioctl() interface are in
Xsys/spkr.h. The tone_t structure used in these calls has two fields,
Xspecifying a frequency (in hz) and a duration (in 1/100ths of a second).
XA frequency of zero is interpreted as a rest.
X.PP
XAt present there are two such ioctls. SPKRTONE accepts a pointer to a
Xsingle tone structure as third argument and plays it. SPKRTUNE accepts a
Xpointer to the first of an array of tone structures and plays them in
Xcontinuous sequence; this array must be terminated by a final member with
Xa zero duration.
X.PP
XThe play-string language is modelled on the PLAY statement conventions of
XIBM BASIC 2.0. The MB, MF and X primitives of PLAY are not useful in a UNIX 
Xenvironment and are omitted. The `octave-tracking' feature is also new.
X.PP
XThere are 84 accessible notes numbered 1-83 in 7 octaves, each running from
XC to B, numbered 0-6; the scale is equal-tempered A440 and octave 3 starts
Xwith middle C. By default, the play function emits half-second notes with the
Xlast 1/16th second being `rest time'.
X.PP
XPlay strings are interpreted left to right as a series of play command groups;
Xletter case is ignored. Play command groups are as follows:
X.PP
XCDEFGAB -- letters A through G cause the corresponding note to be played in the
Xcurrent octave. A note letter may optionally be followed by an \fIaccidental
Xsign\fR, one of # + or -; the first two of these cause it to be sharped one
Xhalf-tone, the last causes it to be flatted one half-tone. It may also be
Xfollowed by a time value number and by sustain dots (see below). Time values
Xare interpreted as for the L command below;.
X.PP
XO <n> -- if <n> is numeric, this sets the current octave. <n> may also be one
Xof 'L' or 'N' to enable or disable octave-tracking (it is disabled by default).
XWhen octave-tracking is on, interpretation of a pair of letter notes will
Xchange octaves if necessary in order to make the smallest possible jump between
Xnotes. Thus "olbc" will be played as "olb>c", and "olcb" as "olc<b". Octave
Xlocking is disabled for one letter note following by >, < and O[0123456].
X.PP
X> -- bump the current octave up one.
X.PP
X< -- drop the current octave down one.
X.PP
XN <n> -- play note n, n being 1 to 84 or 0 for a rest of current time value.
XMay be followedv by sustain dots.
X.PP
XL <n> -- sets the current time value for notes. The default is L4, quarter
Xnotes. The lowest possible value is 1; values up to 64 are accepted. L1 sets
Xwhole notes, L2 sets half notes, L4 sets quarter notes, etc..
X.PP
XP <n> -- pause (rest), with <n> interpreted as for L. May be followed by
Xsustain dots. May also be written '~'.
X.PP
XT <n> -- Sets the number of quarter notes per minute; default is 120. Musical
Xnames for common tempi are:
X
X.TS
Xa a a.
X        	Tempo    	Beats Per Minute
Xvery slow	Larghissimo	
X        	Largo    	40-60
X         	Larghetto    	60-66
X        	Grave       	
X        	Lento       	
X        	Adagio       	66-76
Xslow    	Adagietto    	
X        	Andante   	76-108
Xmedium   	Andantino	
X        	Moderato	108-120
Xfast    	Allegretto	
X        	Allegro   	120-168
X        	Vivace    	
X        	Veloce    	
X        	Presto    	168-208
Xvery fast	Prestissimo	
X.TE
X.PP
XM[LNS] -- set articulation. MN (N for normal) is the default; the last 1/8th of
Xthe note's value is rest time. You can set ML for legato (no rest space) or
XMS (staccato) 1/4 rest space.
X.PP
XNotes (that is, CDEFGAB or N command character groups) may be followed by
Xsustain dots. Each dot causes the note's value to be lengthened by one-half
Xfor each one. Thus, a note dotted once is held for 3/2 of its undotted value;
Xdotted twice, it is held 9/4, and three times would give 27/8. 
X.PP
XWhitespace in play strings is simply skipped and may be used to separate
Xmelody sections.
X.SH BUGS
XDue to roundoff in the pitch tables and slop in the tone-generation and timer
Xhardware (neither of which was designed for precision), neither pitch accuracy
Xnor timings will be mathematically exact. There is no volume control.
X.PP
XIn play strings which are very long (longer than your system's physical I/O
Xblocks) note suffixes or numbers may occasionally be parsed incorrectly due
Xto crossing a block boundary.
X.SH FILES
X/dev/speaker -- speaker device file
X.SH AUTHOR
XEric S. Raymond (esr@snark.thyrsus.com) Feb 1990
END-of-spkr.7
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
X#
X# Speaker driver package makefile
X#
XCFLAGS = -I. -O # -DDEBUG
XLDFLAGS = -s
X
Xall: Driver.o
X
Xinstall:
X	./Install
X
XDriver.o: spkr.c
X	$(CC) $(CFLAGS) -c spkr.c
X	mv spkr.o Driver.o
X
Xclean:
X	rm -f Driver.o *~ speaker.shar
X
XDSP =  Files Install Master Name Node Remove Size System 
Xshar:
X	shar READ.ME install.bsd spkr.7 Makefile spkr.[ch] \
X		interp.c $(DSP) playtest >speaker.shar
END-of-Makefile
echo x - spkr.c
sed 's/^X//' >spkr.c << 'END-of-spkr.c'
X/*
X * spkr.c -- device driver for console speaker on 80386
X *
X * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
X *      modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
X */
X
X#ifdef __386BSD__
X#include "speaker.h"
X#endif
X#if !defined(__386BSD__) || (NSPEAKER > 0)
X
X#ifdef __386BSD__
X#include "types.h"
X#include "param.h"
X#include "errno.h"
X#include "buf.h"
X#include "uio.h"
X
X#define CADDR caddr_t
X#define err_ret(x) return(x)
X#else /* SYSV */
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/dir.h>
X#include <sys/signal.h>
X#include <sys/errno.h>
X#include <sys/ioctl.h>
X#include <sys/user.h>
X#include <sys/sysmacros.h> 
X#include <limits.h>
X
X#define CADDR char *
X#define err_ret(x) u.u_error = (x)
X#endif
X
X#include "spkr.h"
X
X/**************** MACHINE DEPENDENT PART STARTS HERE *************************
X *
X * This section defines a function tone() which causes a tone of given
X * frequency and duration from the 80x86's console speaker.
X * Another function endtone() is defined to force sound off, and there is
X * also a rest() entry point to do pauses.
X *
X * Audible sound is generated using the Programmable Interval Timer (PIT) and
X * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The
X * PPI controls whether sound is passed through at all; the PIT's channel 2 is
X * used to generate clicks (a square wave) of whatever frequency is desired.
X *
X * The non-BSD code requires SVr3.2-compatible inb(), outb(), timeout(),
X * sleep(), and wakeup().
X */
X
X/*
X * PIT and PPI port addresses and control values
X *
X * Most of the magic is hidden in the TIMER_PREP value, which selects PIT
X * channel 2, frequency LSB first, square-wave mode and binary encoding.
X * The encoding is as follows:
X *
X * +----------+----------+---------------+-----+
X * |  1    0  |  1    1  |  0    1    1  |  0  |
X * | SC1  SC0 | RW1  RW0 | M2   M1   M0  | BCD |
X * +----------+----------+---------------+-----+
X *   Counter     Write        Mode 3      Binary
X *  Channel 2  LSB first,  (Square Wave) Encoding 
X *             MSB second
X */
X#define PPI		0x61	/* port of Programmable Peripheral Interface */
X#define PPI_SPKR	0x03	/* turn these PPI bits on to pass sound */
X#define PIT_CTRL	0x43	/* PIT control address */
X#define PIT_COUNT	0x42	/* PIT count address */
X#define PIT_MODE	0xB6	/* set timer mode for sound generation */
X
X/*
X * Magic numbers for timer control. 
X */
X#define TIMER_CLK	1193180L	/* corresponds to 18.2 MHz tick rate */
X
Xstatic int endtone()
X/* turn off the speaker, ending current tone */
X{
X    wakeup((CADDR)endtone);
X    outb(PPI, inb(PPI) & ~PPI_SPKR);
X}
X
Xstatic void tone(hz, ticks)
X/* emit tone of frequency hz for given number of ticks */
Xunsigned int hz, ticks;
X{
X    unsigned int divisor = TIMER_CLK / hz;
X    int sps;
X
X#ifdef DEBUG
X    printf("tone: hz=%d ticks=%d\n", hz, ticks);
X#endif /* DEBUG */
X
X    /* set timer to generate clicks at given frequency in Hertz */
X#ifdef __386BSD__
X    sps = spltty();
X#else
X    sps = spl5();
X#endif
X    outb(PIT_CTRL, PIT_MODE);		/* prepare timer */
X    outb(PIT_COUNT, (unsigned char) divisor);  /* send lo byte */
X    outb(PIT_COUNT, (divisor >> 8));	/* send hi byte */
X    splx(sps);
X
X    /* turn the speaker on */
X    outb(PPI, inb(PPI) | PPI_SPKR);
X
X    /*
X     * Set timeout to endtone function, then give up the timeslice.
X     * This is so other processes can execute while the tone is being
X     * emitted.
X     */
X    timeout((CADDR)endtone, (CADDR)NULL, ticks);
X    sleep((CADDR)endtone, PZERO - 1);
X}
X
Xstatic int endrest()
X/* end a rest */
X{
X    wakeup((CADDR)endrest);
X}
X
Xstatic void rest(ticks)
X/* rest for given number of ticks */
Xint	ticks;
X{
X    /*
X     * Set timeout to endrest function, then give up the timeslice.
X     * This is so other processes can execute while the rest is being
X     * waited out.
X     */
X#ifdef DEBUG
X    printf("rest: %d\n", ticks);
X#endif /* DEBUG */
X    timeout((CADDR)endrest, (CADDR)NULL, ticks);
X    sleep((CADDR)endrest, PZERO - 1);
X}
X
X#include "interp.c"	/* playinit() and playstring() */
X
X/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
X *
X * This section implements driver hooks to run playstring() and the tone(),
X * endtone(), and rest() functions defined above.  For non-BSD systems,
X * SVr3.2-compatible copyin() is also required.
X */
X
Xstatic int spkr_active;	/* exclusion flag */
X#ifdef __386BSD__
Xstatic struct  buf *spkr_inbuf; /* incoming buf */
X#endif
X
Xint spkropen(dev)
Xdev_t	dev;
X{
X#ifdef DEBUG
X    printf("spkropen: entering with dev = %x\n", dev);
X#endif /* DEBUG */
X
X    if (minor(dev) != 0)
X	err_ret(ENXIO);
X    else if (spkr_active)
X	err_ret(EBUSY);
X    else
X    {
X	playinit();
X#ifdef __386BSD__
X	spkr_inbuf = geteblk(DEV_BSIZE);
X#endif
X	spkr_active = 1;
X    }
X#ifdef __386BSD__
X    return(0);
X#endif
X}
X
X#ifdef __386BSD__
Xint spkrwrite(dev, uio)
Xstruct uio *uio;
X#else
Xint spkrwrite(dev)
X#endif
Xdev_t	dev;
X{
X#ifdef __386BSD__
X    register unsigned n;
X    char *cp;
X    int error;
X#endif
X#ifdef DEBUG
X#ifdef __386BSD__
X    printf("spkrwrite: entering with dev = %x, count = %d\n",
X		dev, uio->uio_resid);
X#else
X    printf("spkrwrite: entering with dev = %x, u.u_count = %d\n",
X		dev, u.u_count);
X#endif
X#endif /* DEBUG */
X
X    if (minor(dev) != 0)
X	err_ret(ENXIO);
X    else
X    {
X#ifdef __386BSD__
X	n = MIN(DEV_BSIZE, uio->uio_resid);
X	cp = spkr_inbuf->b_un.b_addr;
X	error = uiomove(cp, n, uio);
X	if (!error)
X		playstring(cp, n);
X	return(error);
X#else
X	char	bfr[STD_BLK];
X
X	copyin(u.u_base, bfr, u.u_count);
X	playstring(bfr, u.u_count);
X	u.u_base += u.u_count;
X	u.u_count = 0;
X#endif
X    }
X}
X
Xint spkrclose(dev)
Xdev_t	dev;
X{
X#ifdef DEBUG
X    printf("spkrclose: entering with dev = %x\n", dev);
X#endif /* DEBUG */
X
X    if (minor(dev) != 0)
X	err_ret(ENXIO);
X    else
X    {
X	endtone();
X#ifdef __386BSD__
X	brelse(spkr_inbuf);
X#endif
X	spkr_active = 0;
X    }
X#ifdef __386BSD__
X    return(0);
X#endif
X}
X
Xint spkrioctl(dev, cmd, cmdarg)
Xdev_t	dev;
Xint	cmd;
XCADDR   cmdarg;
X{
X#ifdef DEBUG
X    printf("spkrioctl: entering with dev = %x, cmd = %x\n", dev, cmd);
X#endif /* DEBUG */
X
X    if (minor(dev) != 0)
X	err_ret(ENXIO);
X    else if (cmd == SPKRTONE)
X    {
X	tone_t	*tp = (tone_t *)cmdarg;
X
X	if (tp->frequency == 0)
X	    rest(tp->duration);
X	else
X	    tone(tp->frequency, tp->duration);
X    }
X    else if (cmd == SPKRTUNE)
X    {
X#ifdef __386BSD__
X	tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
X	tone_t ttp;
X	int error;
X
X	for (; ; tp++) {
X	    error = copyin(tp, &ttp, sizeof(tone_t));
X	    if (error)
X		    return(error);
X	    if (ttp.duration == 0)
X		    break;
X	    if (ttp.frequency == 0)
X		rest(ttp.duration);
X	    else
X		tone(ttp.frequency, ttp.duration);
X	}
X#else
X	tone_t	*tp = (tone_t *)cmdarg;
X
X	for (; tp->duration; tp++)
X	    if (tp->frequency == 0)
X		rest(tp->duration);
X	    else
X		tone(tp->frequency, tp->duration);
X#endif
X    }
X    else
X	err_ret(EINVAL);
X#ifdef __386BSD__
X    return(0);
X#endif
X}
X
X#endif  /* !defined(__386BSD__) || (NSPEAKER > 0) */
X/* spkr.c ends here */
END-of-spkr.c
echo x - spkr.h
sed 's/^X//' >spkr.h << 'END-of-spkr.h'
X/*
X * spkr.h -- interface definitions for speaker ioctl()
X *
X * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
X *      modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
X */
X
X#ifndef _SPKR_H_
X#define _SPKR_H_
X
X#ifdef __386BSD__
X#ifndef KERNEL
X#include <sys/ioctl.h>
X#else
X#include "ioctl.h"
X#endif
X
X#define SPKRTONE        _IOW('S', 1, tone_t)    /* emit tone */
X#define SPKRTUNE        _IO('S', 2)             /* emit tone sequence*/
X#else /* SYSV */
X#define	SPKRIOC		('S'<<8)
X#define	SPKRTONE	(SPKRIOC|1)	/* emit tone */
X#define	SPKRTUNE	(SPKRIOC|2)	/* emit tone sequence*/
X#endif
X
Xtypedef struct
X{
X    int	frequency;	/* in hertz */
X    int duration;	/* in 1/100ths of a second */
X}
Xtone_t;
X
X#endif /* _SPKR_H_ */
X/* spkr.h ends here */
END-of-spkr.h
echo x - interp.c
sed 's/^X//' >interp.c << 'END-of-interp.c'
X/*
X * interp.c -- device driver for console speaker on 80386
X *
X * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
X *
X * this is the part of the code common to all 386 UNIX OSes
X *
X * playinit() and playstring() are called from the appropriate driver
X */
X
X#ifdef __386BSD__
X#include "param.h"
X#else
X#include <sys/param.h>
X#endif
X
X#ifndef HZ
X#define HZ 60
X#endif
X
X
X/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
X *
X * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
X * M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
X * Requires tone(), rest(), and endtone(). String play is not interruptible
X * except possibly at physical block boundaries.
X */
X
Xtypedef int	bool;
X#ifndef TRUE
X#define TRUE	1
X#endif
X#ifndef FALSE
X#define FALSE	0
X#endif
X
X#define toupper(c)	((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
X#define isdigit(c)	(((c) >= '0') && ((c) <= '9'))
X#define dtoi(c)		((c) - '0')
X
Xstatic int octave;	/* currently selected octave */
Xstatic int whole;	/* whole-note time at current tempo, in ticks */
Xstatic int value;	/* whole divisor for note time, quarter note = 1 */
Xstatic int fill;	/* controls spacing of notes */
Xstatic bool octtrack;	/* octave-tracking on? */
Xstatic bool octprefix;	/* override current octave-tracking state? */
X
X/*
X * Magic number avoidance...
X */
X#define SECS_PER_MIN	60	/* seconds per minute */
X#define WHOLE_NOTE	4	/* quarter notes per whole note */
X#define MIN_VALUE	64	/* the most we can divide a note by */
X#define DFLT_VALUE	4	/* default value (quarter-note) */
X#define FILLTIME	8	/* for articulation, break note in parts */
X#define STACCATO	6	/* 6/8 = 3/4 of note is filled */
X#define NORMAL		7	/* 7/8ths of note interval is filled */
X#define LEGATO		8	/* all of note interval is filled */
X#define DFLT_OCTAVE	4	/* default octave */
X#define MIN_TEMPO	32	/* minimum tempo */
X#define DFLT_TEMPO	120	/* default tempo */
X#define MAX_TEMPO	255	/* max tempo */
X#define NUM_MULT	3	/* numerator of dot multiplier */
X#define DENOM_MULT	2	/* denominator of dot multiplier */
X
X/* letter to half-tone:  A   B  C  D  E  F  G */
Xstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
X
X/*
X * This is the American Standard A440 Equal-Tempered scale with frequencies
X * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
X * our octave 0 is standard octave 2.
X */
X#define OCTAVE_NOTES	12	/* semitones per octave */
Xstatic int pitchtab[] =
X{
X/*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
X/* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
X/* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
X/* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
X/* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
X/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
X/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
X/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
X};
X
Xstatic void playinit()
X{
X    octave = DFLT_OCTAVE;
X    whole = (HZ * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
X    fill = NORMAL;
X    value = DFLT_VALUE;
X    octtrack = FALSE;
X    octprefix = TRUE;	/* act as though there was an initial O(n) */
X}
X
Xstatic void playtone(pitch, value, sustain)
X/* play tone of proper duration for current rhythm signature */
Xint	pitch, value, sustain;
X{
X    register int	sound, silence, snum = 1, sdenom = 1;
X
X    /* this weirdness avoids floating-point arithmetic */
X    for (; sustain; sustain--)
X    {
X	snum *= NUM_MULT;
X	sdenom *= DENOM_MULT;
X    }
X
X    if (pitch == -1)
X	rest(whole * snum / (value * sdenom));
X    else
X    {
X	sound = (whole * snum) / (value * sdenom)
X		- (whole * (FILLTIME - fill)) / (value * FILLTIME);
X	silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
X
X#ifdef DEBUG
X	printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
X			pitch, sound, silence);
X#endif /* DEBUG */
X
X	tone(pitchtab[pitch], sound);
X	if (fill != LEGATO)
X	    rest(silence);
X    }
X}
X
Xstatic int abs(n)
Xint n;
X{
X    if (n < 0)
X	return(-n);
X    else
X	return(n);
X}
X
Xstatic void playstring(cp, slen)
X/* interpret and play an item from a notation string */
Xchar	*cp;
Xsize_t	slen;
X{
X    int		pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
X
X#define GETNUM(cp, v)	for(v=0; isdigit(cp[1]) && slen > 0; ) \
X				{v = v * 10 + (*++cp - '0'); slen--;}
X    for (; slen--; cp++)
X    {
X	int		sustain, timeval, tempo;
X	register char	c = toupper(*cp);
X
X#ifdef DEBUG
X	printf("playstring: %c (%x)\n", c, c);
X#endif /* DEBUG */
X
X	switch (c)
X	{
X	case 'A':  case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
X
X	    /* compute pitch */
X	    pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
X
X	    /* this may be followed by an accidental sign */
X	    if (cp[1] == '#' || cp[1] == '+')
X	    {
X		++pitch;
X		++cp;
X		slen--;
X	    }
X	    else if (cp[1] == '-')
X	    {
X		--pitch;
X		++cp;
X		slen--;
X	    }
X
X	    /*
X	     * If octave-tracking mode is on, and there has been no octave-
X	     * setting prefix, find the version of the current letter note
X	     * closest to the last regardless of octave.
X	     */
X	    if (octtrack && !octprefix)
X	    {
X		if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
X		{
X		    ++octave;
X		    pitch += OCTAVE_NOTES;
X		}
X
X		if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
X		{
X		    --octave;
X		    pitch -= OCTAVE_NOTES;
X		}
X	    }
X	    octprefix = FALSE;
X	    lastpitch = pitch;
X
X	    /* ...which may in turn be followed by an override time value */
X	    GETNUM(cp, timeval);
X	    if (timeval <= 0 || timeval > MIN_VALUE)
X		timeval = value;
X
X	    /* ...and/or sustain dots */
X	    for (sustain = 0; cp[1] == '.'; cp++)
X	    {
X		slen--;
X		sustain++;
X	    }
X
X	    /* time to emit the actual tone */
X	    playtone(pitch, timeval, sustain);
X	    break;
X
X	case 'O':
X	    if (cp[1] == 'N' || cp[1] == 'n')
X	    {
X		octprefix = octtrack = FALSE;
X		++cp;
X		slen--;
X	    }
X	    else if (cp[1] == 'L' || cp[1] == 'l')
X	    {
X		octtrack = TRUE;
X		++cp;
X		slen--;
X	    }
X	    else
X	    {
X		GETNUM(cp, octave);
X		if (octave >= sizeof(pitchtab) / OCTAVE_NOTES)
X		    octave = DFLT_OCTAVE;
X		octprefix = TRUE;
X	    }
X	    break;
X
X	case '>':
X	    if (octave < sizeof(pitchtab) / OCTAVE_NOTES - 1)
X		octave++;
X	    octprefix = TRUE;
X	    break;
X
X	case '<':
X	    if (octave > 0)
X		octave--;
X	    octprefix = TRUE;
X	    break;
X
X	case 'N':
X	    GETNUM(cp, pitch);
X	    for (sustain = 0; cp[1] == '.'; cp++)
X	    {
X		slen--;
X		sustain++;
X	    }
X	    playtone(pitch - 1, value, sustain);
X	    break;
X
X	case 'L':
X	    GETNUM(cp, value);
X	    if (value <= 0 || value > MIN_VALUE)
X		value = DFLT_VALUE;
X	    break;
X
X	case 'P':
X	case '~':
X	    /* this may be followed by an override time value */
X	    GETNUM(cp, timeval);
X	    if (timeval <= 0 || timeval > MIN_VALUE)
X		timeval = value;
X	    for (sustain = 0; cp[1] == '.'; cp++)
X	    {
X		slen--;
X		sustain++;
X	    }
X	    playtone(-1, timeval, sustain);
X	    break;
X
X	case 'T':
X	    GETNUM(cp, tempo);
X	    if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
X		tempo = DFLT_TEMPO;
X	    whole = (HZ * SECS_PER_MIN * WHOLE_NOTE) / tempo;
X	    break;
X
X	case 'M':
X	    if (cp[1] == 'N' || cp[1] == 'n')
X	    {
X		fill = NORMAL;
X		++cp;
X		slen--;
X	    }
X	    else if (cp[1] == 'L' || cp[1] == 'l')
X	    {
X		fill = LEGATO;
X		++cp;
X		slen--;
X	    }
X	    else if (cp[1] == 'S' || cp[1] == 's')
X	    {
X		fill = STACCATO;
X		++cp;
X		slen--;
X	    }
X	    break;
X	}
X    }
X}
END-of-interp.c
echo x - Files
sed 's/^X//' >Files << 'END-of-Files'
X/usr/include/sys/spkr.h
END-of-Files
echo x - Install
sed 's/^X//' >Install << 'END-of-Install'
X#
X# Speaker driver installation script
X#
XTMP=/tmp/speaker.err
XERR1=" Errors have been written to the file $TMP."
XERR2=" The Speaker Driver software was not installed."
X
Xecho "Installing Speaker Driver Software Package"
X
X/etc/conf/bin/idcheck -p speaker 2>$TMP
Xif [ $? != 0 ]
Xthen
X	echo "The speaker package is already at least partly installed.
X	Removing the old version now..."
X	/etc/conf/bin/idinstall -d speaker
Xfi
X
X/etc/conf/bin/idinstall -a -k speaker 2>>$TMP
Xif [ $? != 0 ]
Xthen
X	message "There was an error during package installation. $ERR1 $ERR2"
X	exit 1
Xfi
X
X/etc/conf/bin/idbuild 2>>$TMP
Xif [ $? != 0 ]
Xthen
X	message "There was an error during kernel reconfiguration. $ERR1 $ERR2"
X	exit 1
Xfi
X
Xrm -f $TMP
X
Xcp spkr.h /usr/include/sys/spkr.h
X
Xecho "Performing shutdown..."
Xcd /; exec /etc/shutdown -g0 -y
END-of-Install
echo x - Master
sed 's/^X//' >Master << 'END-of-Master'
Xspeaker	ocwi	iocH	spkr	0	0	1	1	-1
END-of-Master
echo x - Name
sed 's/^X//' >Name << 'END-of-Name'
X386 UNIX Speaker Device Driver Package
END-of-Name
echo x - Node
sed 's/^X//' >Node << 'END-of-Node'
Xspeaker	speaker	c	0
END-of-Node
echo x - Remove
sed 's/^X//' >Remove << 'END-of-Remove'
X#
X# Speaker driver remove script
X#
XTMP=/tmp/speaker.err
XRERR="Errors have been written to the file $TMP."
X
Xecho "Removing Speaker Driver Software Package"
X
X/etc/conf/bin/idinstall -d speaker 2>$TMP
Xif [ $? != 0 ]
Xthen
X	message "There was an error during package removal. $RERR"
X	exit 1
Xfi
X
X/etc/conf/bin/idbuild 2>>$TMP
Xif [ $? != 0 ]
Xthen
X	message "There was an error during kernel reconfiguration. $RERR"
X	exit 1
Xfi
X
Xrm -f /dev/speaker $TMP /usr/include/sys/spkr.h
X
Xexit 0
END-of-Remove
echo x - Size
sed 's/^X//' >Size << 'END-of-Size'
XROOT=1400
XUSR=100
END-of-Size
echo x - System
sed 's/^X//' >System << 'END-of-System'
Xspeaker	Y	1	0	0	0	0	0	0	0
END-of-System
echo x - playtest
sed 's/^X//' >playtest << 'END-of-playtest'
X:
X# Test script for the speaker driver
X#
X# v1.0 by Eric S. Raymond (Feb 1990)
X#      modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
X#
Xreveille="t255l8c.f.afc~c.f.afc~c.f.afc.f.a..f.~c.f.afc~c.f.afc~c.f.afc~c.f.."
Xcontact="<cd<a#~<a#>f"
Xdance="t240<cfcfgagaa#b#>dc<a#a.~fg.gaa#.agagegc.~cfcfgagaa#b#>dc<a#a.~fg.gga.agfgfgf."
Xloony="t255cf8f8edc<a.>~cf8f8edd#e.~ce8cdce8cd.<a>c8c8c#def8af8."
X
Xcase $1 in
Xreveille) echo  $reveille >/dev/speaker;;
Xcontact)  echo  $contact >/dev/speaker;;
Xdance)  echo  $dance >/dev/speaker;;
Xloony)  echo  $loony >/dev/speaker;;
X*)
X	echo "No such tune. Available tunes are:"
X	echo
X	echo "reveille -- Reveille"
X	echo "contact -- Contact theme from Close Encounters"
X	echo "dance -- Lord of the Dance (aka Simple Gifts)"
X	echo "loony -- Loony Toons theme"
X	;;
Xesac
END-of-playtest
exit
