--- /dev/null
+# 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