1# This is a shell archive.  Save it in a file, remove anything before
2# this line, and then unpack it by entering "sh file".  Note, it may
3# create directories; files and directories will be owned by you and
4# have default permissions.
5#
6# This archive contains:
7#
8#	READ.ME
9#	install.bsd
10#	spkr.7
11#	Makefile
12#	spkr.c
13#	spkr.h
14#	interp.c
15#	Files
16#	Install
17#	Master
18#	Name
19#	Node
20#	Remove
21#	Size
22#	System
23#	playtest
24#
25echo x - READ.ME
26sed 's/^X//' >READ.ME << 'END-of-READ.ME'
27X		Console Speaker Driver Package (v1.1)
28X
29X		by Eric S. Raymond (esr@snark.thyrsus.com)
30X
31XThis package gives 80386 machines running SVr3.2 or later the ability to play
32Xtunes on the console speaker.  It has been extended to 386BSD (and possibly
33XBSDI) by Andrew A. Chernov, and to SCO UNIX 3.2.4 (and possibly other VPIX
34Xsystems) by Andreas Arens.
35X
36XThe following files are contained in the kit:
37X
38XDocumentation and examples:
39XREAD.ME		-- this file
40Xspeaker.7	-- man page for the driver
41Xplaytest	-- test script exercising familiar tunes
42X
43XInstallable driver kit parts, for SVr3.2 or later:
44XFiles		-- list of driver package file locations
45XInstall		-- installation script for driver kit
46XMaster		-- mdevice entry for speaker driver
47XName		-- name entry foe speaker driver
48XNode		-- /dev node specification file
49XRemove		-- Driver removal script
50XSize		-- installation size data
51XSystem		-- sdevice entry for speaker driver
52X
53XDriver source code, for SVr3.2 or later and 386BSD:
54XMakefile	-- Makefile for driver code
55Xspkr.c		-- the driver source
56Xspeaker.h	-- ioctl interface file
57X
58XCommon source code:
59Xinterp.c	-- play string interpretation code
60X
61XFor SVr3.2 or later, simply type `make' and wait. Then type ./Install
62Xand follow its instructions. You will have to install the man pages by hand.
63XBe aware that the speaker.7 man page uses tbl(1) constructs.
64X
65XFor 386BSD, follow the installation instructions in install.bsd.
66X
67XFor SCO UNIX 3.2.4, no new kernel drivers are needed, and you need only
68Xcopy interp.c to your src directory and proceed with making NetHack, with
69XVPIX_MUSIC set in unixconf.h.
70X
71XInteresting tunes mailed to the author will be periodically posted in batches
72Xand added to the test script for future versions.
73X
74X			Revision notes
75X
76X1.1 -- fixed minor bug in M[LSN] interpretation, added octave-tracking.
77X       Tweaked the playtest examples.
78END-of-READ.ME
79echo x - install.bsd
80sed 's/^X//' >install.bsd << 'END-of-install.bsd'
81XCopy spkr.c and interp.c to /sys/i386/isa
82XCopy spkr.h to /sys/sys
83X
84X-----------------------------------------------------------------------------
85X
86XFile /sys/i386/conf/YOUR_MACHINE_NAME
87Xadd following line:
88X
89Xpseudo-device   speaker
90X
91X-----------------------------------------------------------------------------
92X
93XFile /sys/i386/conf/files.i386
94Xadd following line:
95X
96Xi386/isa/spkr.c         optional speaker
97X
98X-----------------------------------------------------------------------------
99X
100XFile /sys/i386/i386/conf.c
101X[major number 20 (hex) is registered for spkr driver, don't change it]
102Xadd following code:
103X
104X#include "speaker.h"
105X#if NSPEAKER > 0
106Xint     spkropen(),spkrclose(),spkrwrite(),spkrioctl();
107X#else
108X#define spkropen  enxio
109X#define spkrclose enxio
110X#define spkrwrite enxio
111X#define spkrioctl enxio
112X#endif
113X	...
114X
115Xstruct cdevsw	cdevsw[] =
116X{
117X	...
118X
119X	{ spkropen,     spkrclose,      enxio,          spkrwrite,      /*20*/
120X	  spkrioctl,    enxio,          enxio,          NULL,
121X	  enxio,        enxio,          enxio },
122X	...
123X
124X-----------------------------------------------------------------------------
125X
126XMake corresponding device:
127X
128X	mknod /dev/speaker c 32 0
129X
130X[major number 32 (20 hex) is registered for spkr driver, don't change it]
131X
132X-----------------------------------------------------------------------------
133X
134XGo to /sys/i386/conf and type
135X	config YOUR_MACHINE_NAME
136Xthen go to /sys/compile/YOUR_MACHINE_NAME and type
137X	make depend
138X	make
139X
140END-of-install.bsd
141echo x - spkr.7
142sed 's/^X//' >spkr.7 << 'END-of-spkr.7'
143X.TH SPKR 7
144X.SH NAME
145Xspkr \- console speaker device driver
146X.SH DESCRIPTION
147XThe speaker device driver allows applications to control the PC console
148Xspeaker on an IBM-PC-compatible machine running UNIX.
149X.PP
150XOnly one process may have this device open at any given time; open() and
151Xclose() are used to lock and relinquish it. An attempt to open() when
152Xanother process has the device locked will return -1 with an EBUSY error
153Xindication. Writes to the device are interpreted as 'play strings' in a
154Xsimple ASCII melody notation. An ioctl() for tone generation at arbitrary
155Xfrequencies is also supported.
156X.PP
157XSound-generation does \fInot\fR monopolize the processor; in fact, the driver
158Xspends most of its time sleeping while the PC hardware is emitting
159Xtones. Other processes may emit beeps while the driver is running.
160X.PP
161XApplications may call ioctl() on a speaker file descriptor to control the
162Xspeaker driver directly; definitions for the ioctl() interface are in
163Xsys/spkr.h. The tone_t structure used in these calls has two fields,
164Xspecifying a frequency (in hz) and a duration (in 1/100ths of a second).
165XA frequency of zero is interpreted as a rest.
166X.PP
167XAt present there are two such ioctls. SPKRTONE accepts a pointer to a
168Xsingle tone structure as third argument and plays it. SPKRTUNE accepts a
169Xpointer to the first of an array of tone structures and plays them in
170Xcontinuous sequence; this array must be terminated by a final member with
171Xa zero duration.
172X.PP
173XThe play-string language is modelled on the PLAY statement conventions of
174XIBM BASIC 2.0. The MB, MF and X primitives of PLAY are not useful in a UNIX
175Xenvironment and are omitted. The `octave-tracking' feature is also new.
176X.PP
177XThere are 84 accessible notes numbered 1-83 in 7 octaves, each running from
178XC to B, numbered 0-6; the scale is equal-tempered A440 and octave 3 starts
179Xwith middle C. By default, the play function emits half-second notes with the
180Xlast 1/16th second being `rest time'.
181X.PP
182XPlay strings are interpreted left to right as a series of play command groups;
183Xletter case is ignored. Play command groups are as follows:
184X.PP
185XCDEFGAB -- letters A through G cause the corresponding note to be played in the
186Xcurrent octave. A note letter may optionally be followed by an \fIaccidental
187Xsign\fR, one of # + or -; the first two of these cause it to be sharped one
188Xhalf-tone, the last causes it to be flatted one half-tone. It may also be
189Xfollowed by a time value number and by sustain dots (see below). Time values
190Xare interpreted as for the L command below;.
191X.PP
192XO <n> -- if <n> is numeric, this sets the current octave. <n> may also be one
193Xof 'L' or 'N' to enable or disable octave-tracking (it is disabled by default).
194XWhen octave-tracking is on, interpretation of a pair of letter notes will
195Xchange octaves if necessary in order to make the smallest possible jump between
196Xnotes. Thus "olbc" will be played as "olb>c", and "olcb" as "olc<b". Octave
197Xlocking is disabled for one letter note following by >, < and O[0123456].
198X.PP
199X> -- bump the current octave up one.
200X.PP
201X< -- drop the current octave down one.
202X.PP
203XN <n> -- play note n, n being 1 to 84 or 0 for a rest of current time value.
204XMay be followedv by sustain dots.
205X.PP
206XL <n> -- sets the current time value for notes. The default is L4, quarter
207Xnotes. The lowest possible value is 1; values up to 64 are accepted. L1 sets
208Xwhole notes, L2 sets half notes, L4 sets quarter notes, etc..
209X.PP
210XP <n> -- pause (rest), with <n> interpreted as for L. May be followed by
211Xsustain dots. May also be written '~'.
212X.PP
213XT <n> -- Sets the number of quarter notes per minute; default is 120. Musical
214Xnames for common tempi are:
215X
216X.TS
217Xa a a.
218X        	Tempo    	Beats Per Minute
219Xvery slow	Larghissimo
220X        	Largo    	40-60
221X         	Larghetto    	60-66
222X        	Grave
223X        	Lento
224X        	Adagio       	66-76
225Xslow    	Adagietto
226X        	Andante   	76-108
227Xmedium   	Andantino
228X        	Moderato	108-120
229Xfast    	Allegretto
230X        	Allegro   	120-168
231X        	Vivace
232X        	Veloce
233X        	Presto    	168-208
234Xvery fast	Prestissimo
235X.TE
236X.PP
237XM[LNS] -- set articulation. MN (N for normal) is the default; the last 1/8th of
238Xthe note's value is rest time. You can set ML for legato (no rest space) or
239XMS (staccato) 1/4 rest space.
240X.PP
241XNotes (that is, CDEFGAB or N command character groups) may be followed by
242Xsustain dots. Each dot causes the note's value to be lengthened by one-half
243Xfor each one. Thus, a note dotted once is held for 3/2 of its undotted value;
244Xdotted twice, it is held 9/4, and three times would give 27/8.
245X.PP
246XWhitespace in play strings is simply skipped and may be used to separate
247Xmelody sections.
248X.SH BUGS
249XDue to roundoff in the pitch tables and slop in the tone-generation and timer
250Xhardware (neither of which was designed for precision), neither pitch accuracy
251Xnor timings will be mathematically exact. There is no volume control.
252X.PP
253XIn play strings which are very long (longer than your system's physical I/O
254Xblocks) note suffixes or numbers may occasionally be parsed incorrectly due
255Xto crossing a block boundary.
256X.SH FILES
257X/dev/speaker -- speaker device file
258X.SH AUTHOR
259XEric S. Raymond (esr@snark.thyrsus.com) Feb 1990
260END-of-spkr.7
261echo x - Makefile
262sed 's/^X//' >Makefile << 'END-of-Makefile'
263X#
264X# Speaker driver package makefile
265X#
266XCFLAGS = -I. -O # -DDEBUG
267XLDFLAGS = -s
268X
269Xall: Driver.o
270X
271Xinstall:
272X	./Install
273X
274XDriver.o: spkr.c
275X	$(CC) $(CFLAGS) -c spkr.c
276X	mv spkr.o Driver.o
277X
278Xclean:
279X	rm -f Driver.o *~ speaker.shar
280X
281XDSP =  Files Install Master Name Node Remove Size System
282Xshar:
283X	shar READ.ME install.bsd spkr.7 Makefile spkr.[ch] \
284X		interp.c $(DSP) playtest >speaker.shar
285END-of-Makefile
286echo x - spkr.c
287sed 's/^X//' >spkr.c << 'END-of-spkr.c'
288X/*
289X * spkr.c -- device driver for console speaker on 80386
290X *
291X * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
292X *      modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
293X */
294X
295X#ifdef __386BSD__
296X#include "speaker.h"
297X#endif
298X#if !defined(__386BSD__) || (NSPEAKER > 0)
299X
300X#ifdef __386BSD__
301X#include "types.h"
302X#include "param.h"
303X#include "errno.h"
304X#include "buf.h"
305X#include "uio.h"
306X
307X#define CADDR caddr_t
308X#define err_ret(x) return(x)
309X#else /* SYSV */
310X#include <sys/types.h>
311X#include <sys/param.h>
312X#include <sys/dir.h>
313X#include <sys/signal.h>
314X#include <sys/errno.h>
315X#include <sys/ioctl.h>
316X#include <sys/user.h>
317X#include <sys/sysmacros.h>
318X#include <limits.h>
319X
320X#define CADDR char *
321X#define err_ret(x) u.u_error = (x)
322X#endif
323X
324X#include "spkr.h"
325X
326X/**************** MACHINE DEPENDENT PART STARTS HERE *************************
327X *
328X * This section defines a function tone() which causes a tone of given
329X * frequency and duration from the 80x86's console speaker.
330X * Another function endtone() is defined to force sound off, and there is
331X * also a rest() entry point to do pauses.
332X *
333X * Audible sound is generated using the Programmable Interval Timer (PIT) and
334X * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The
335X * PPI controls whether sound is passed through at all; the PIT's channel 2 is
336X * used to generate clicks (a square wave) of whatever frequency is desired.
337X *
338X * The non-BSD code requires SVr3.2-compatible inb(), outb(), timeout(),
339X * sleep(), and wakeup().
340X */
341X
342X/*
343X * PIT and PPI port addresses and control values
344X *
345X * Most of the magic is hidden in the TIMER_PREP value, which selects PIT
346X * channel 2, frequency LSB first, square-wave mode and binary encoding.
347X * The encoding is as follows:
348X *
349X * +----------+----------+---------------+-----+
350X * |  1    0  |  1    1  |  0    1    1  |  0  |
351X * | SC1  SC0 | RW1  RW0 | M2   M1   M0  | BCD |
352X * +----------+----------+---------------+-----+
353X *   Counter     Write        Mode 3      Binary
354X *  Channel 2  LSB first,  (Square Wave) Encoding
355X *             MSB second
356X */
357X#define PPI		0x61	/* port of Programmable Peripheral Interface */
358X#define PPI_SPKR	0x03	/* turn these PPI bits on to pass sound */
359X#define PIT_CTRL	0x43	/* PIT control address */
360X#define PIT_COUNT	0x42	/* PIT count address */
361X#define PIT_MODE	0xB6	/* set timer mode for sound generation */
362X
363X/*
364X * Magic numbers for timer control.
365X */
366X#define TIMER_CLK	1193180L	/* corresponds to 18.2 MHz tick rate */
367X
368Xstatic int endtone()
369X/* turn off the speaker, ending current tone */
370X{
371X    wakeup((CADDR)endtone);
372X    outb(PPI, inb(PPI) & ~PPI_SPKR);
373X}
374X
375Xstatic void tone(hz, ticks)
376X/* emit tone of frequency hz for given number of ticks */
377Xunsigned int hz, ticks;
378X{
379X    unsigned int divisor = TIMER_CLK / hz;
380X    int sps;
381X
382X#ifdef DEBUG
383X    printf("tone: hz=%d ticks=%d\n", hz, ticks);
384X#endif /* DEBUG */
385X
386X    /* set timer to generate clicks at given frequency in Hertz */
387X#ifdef __386BSD__
388X    sps = spltty();
389X#else
390X    sps = spl5();
391X#endif
392X    outb(PIT_CTRL, PIT_MODE);		/* prepare timer */
393X    outb(PIT_COUNT, (unsigned char) divisor);  /* send lo byte */
394X    outb(PIT_COUNT, (divisor >> 8));	/* send hi byte */
395X    splx(sps);
396X
397X    /* turn the speaker on */
398X    outb(PPI, inb(PPI) | PPI_SPKR);
399X
400X    /*
401X     * Set timeout to endtone function, then give up the timeslice.
402X     * This is so other processes can execute while the tone is being
403X     * emitted.
404X     */
405X    timeout((CADDR)endtone, (CADDR)NULL, ticks);
406X    sleep((CADDR)endtone, PZERO - 1);
407X}
408X
409Xstatic int endrest()
410X/* end a rest */
411X{
412X    wakeup((CADDR)endrest);
413X}
414X
415Xstatic void rest(ticks)
416X/* rest for given number of ticks */
417Xint	ticks;
418X{
419X    /*
420X     * Set timeout to endrest function, then give up the timeslice.
421X     * This is so other processes can execute while the rest is being
422X     * waited out.
423X     */
424X#ifdef DEBUG
425X    printf("rest: %d\n", ticks);
426X#endif /* DEBUG */
427X    timeout((CADDR)endrest, (CADDR)NULL, ticks);
428X    sleep((CADDR)endrest, PZERO - 1);
429X}
430X
431X#include "interp.c"	/* playinit() and playstring() */
432X
433X/******************* UNIX DRIVER HOOKS BEGIN HERE **************************
434X *
435X * This section implements driver hooks to run playstring() and the tone(),
436X * endtone(), and rest() functions defined above.  For non-BSD systems,
437X * SVr3.2-compatible copyin() is also required.
438X */
439X
440Xstatic int spkr_active;	/* exclusion flag */
441X#ifdef __386BSD__
442Xstatic struct  buf *spkr_inbuf; /* incoming buf */
443X#endif
444X
445Xint spkropen(dev)
446Xdev_t	dev;
447X{
448X#ifdef DEBUG
449X    printf("spkropen: entering with dev = %x\n", dev);
450X#endif /* DEBUG */
451X
452X    if (minor(dev) != 0)
453X	err_ret(ENXIO);
454X    else if (spkr_active)
455X	err_ret(EBUSY);
456X    else
457X    {
458X	playinit();
459X#ifdef __386BSD__
460X	spkr_inbuf = geteblk(DEV_BSIZE);
461X#endif
462X	spkr_active = 1;
463X    }
464X#ifdef __386BSD__
465X    return(0);
466X#endif
467X}
468X
469X#ifdef __386BSD__
470Xint spkrwrite(dev, uio)
471Xstruct uio *uio;
472X#else
473Xint spkrwrite(dev)
474X#endif
475Xdev_t	dev;
476X{
477X#ifdef __386BSD__
478X    register unsigned n;
479X    char *cp;
480X    int error;
481X#endif
482X#ifdef DEBUG
483X#ifdef __386BSD__
484X    printf("spkrwrite: entering with dev = %x, count = %d\n",
485X		dev, uio->uio_resid);
486X#else
487X    printf("spkrwrite: entering with dev = %x, u.u_count = %d\n",
488X		dev, u.u_count);
489X#endif
490X#endif /* DEBUG */
491X
492X    if (minor(dev) != 0)
493X	err_ret(ENXIO);
494X    else
495X    {
496X#ifdef __386BSD__
497X	n = MIN(DEV_BSIZE, uio->uio_resid);
498X	cp = spkr_inbuf->b_un.b_addr;
499X	error = uiomove(cp, n, uio);
500X	if (!error)
501X		playstring(cp, n);
502X	return(error);
503X#else
504X	char	bfr[STD_BLK];
505X
506X	copyin(u.u_base, bfr, u.u_count);
507X	playstring(bfr, u.u_count);
508X	u.u_base += u.u_count;
509X	u.u_count = 0;
510X#endif
511X    }
512X}
513X
514Xint spkrclose(dev)
515Xdev_t	dev;
516X{
517X#ifdef DEBUG
518X    printf("spkrclose: entering with dev = %x\n", dev);
519X#endif /* DEBUG */
520X
521X    if (minor(dev) != 0)
522X	err_ret(ENXIO);
523X    else
524X    {
525X	endtone();
526X#ifdef __386BSD__
527X	brelse(spkr_inbuf);
528X#endif
529X	spkr_active = 0;
530X    }
531X#ifdef __386BSD__
532X    return(0);
533X#endif
534X}
535X
536Xint spkrioctl(dev, cmd, cmdarg)
537Xdev_t	dev;
538Xint	cmd;
539XCADDR   cmdarg;
540X{
541X#ifdef DEBUG
542X    printf("spkrioctl: entering with dev = %x, cmd = %x\n", dev, cmd);
543X#endif /* DEBUG */
544X
545X    if (minor(dev) != 0)
546X	err_ret(ENXIO);
547X    else if (cmd == SPKRTONE)
548X    {
549X	tone_t	*tp = (tone_t *)cmdarg;
550X
551X	if (tp->frequency == 0)
552X	    rest(tp->duration);
553X	else
554X	    tone(tp->frequency, tp->duration);
555X    }
556X    else if (cmd == SPKRTUNE)
557X    {
558X#ifdef __386BSD__
559X	tone_t  *tp = (tone_t *)(*(caddr_t *)cmdarg);
560X	tone_t ttp;
561X	int error;
562X
563X	for (; ; tp++) {
564X	    error = copyin(tp, &ttp, sizeof(tone_t));
565X	    if (error)
566X		    return(error);
567X	    if (ttp.duration == 0)
568X		    break;
569X	    if (ttp.frequency == 0)
570X		rest(ttp.duration);
571X	    else
572X		tone(ttp.frequency, ttp.duration);
573X	}
574X#else
575X	tone_t	*tp = (tone_t *)cmdarg;
576X
577X	for (; tp->duration; tp++)
578X	    if (tp->frequency == 0)
579X		rest(tp->duration);
580X	    else
581X		tone(tp->frequency, tp->duration);
582X#endif
583X    }
584X    else
585X	err_ret(EINVAL);
586X#ifdef __386BSD__
587X    return(0);
588X#endif
589X}
590X
591X#endif  /* !defined(__386BSD__) || (NSPEAKER > 0) */
592X/* spkr.c ends here */
593END-of-spkr.c
594echo x - spkr.h
595sed 's/^X//' >spkr.h << 'END-of-spkr.h'
596X/*
597X * spkr.h -- interface definitions for speaker ioctl()
598X *
599X * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
600X *      modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
601X */
602X
603X#ifndef _SPKR_H_
604X#define _SPKR_H_
605X
606X#ifdef __386BSD__
607X#ifndef KERNEL
608X#include <sys/ioctl.h>
609X#else
610X#include "ioctl.h"
611X#endif
612X
613X#define SPKRTONE        _IOW('S', 1, tone_t)    /* emit tone */
614X#define SPKRTUNE        _IO('S', 2)             /* emit tone sequence*/
615X#else /* SYSV */
616X#define	SPKRIOC		('S'<<8)
617X#define	SPKRTONE	(SPKRIOC|1)	/* emit tone */
618X#define	SPKRTUNE	(SPKRIOC|2)	/* emit tone sequence*/
619X#endif
620X
621Xtypedef struct
622X{
623X    int	frequency;	/* in hertz */
624X    int duration;	/* in 1/100ths of a second */
625X}
626Xtone_t;
627X
628X#endif /* _SPKR_H_ */
629X/* spkr.h ends here */
630END-of-spkr.h
631echo x - interp.c
632sed 's/^X//' >interp.c << 'END-of-interp.c'
633X/*
634X * interp.c -- device driver for console speaker on 80386
635X *
636X * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990
637X *
638X * this is the part of the code common to all 386 UNIX OSes
639X *
640X * playinit() and playstring() are called from the appropriate driver
641X */
642X
643X#ifdef __386BSD__
644X#include "param.h"
645X#else
646X#include <sys/param.h>
647X#endif
648X
649X#ifndef HZ
650X#define HZ 60
651X#endif
652X
653X
654X/**************** PLAY STRING INTERPRETER BEGINS HERE **********************
655X *
656X * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
657X * M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
658X * Requires tone(), rest(), and endtone(). String play is not interruptible
659X * except possibly at physical block boundaries.
660X */
661X
662Xtypedef int	bool;
663X#ifndef TRUE
664X#define TRUE	1
665X#endif
666X#ifndef FALSE
667X#define FALSE	0
668X#endif
669X
670X#define toupper(c)	((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
671X#define isdigit(c)	(((c) >= '0') && ((c) <= '9'))
672X#define dtoi(c)		((c) - '0')
673X
674Xstatic int octave;	/* currently selected octave */
675Xstatic int whole;	/* whole-note time at current tempo, in ticks */
676Xstatic int value;	/* whole divisor for note time, quarter note = 1 */
677Xstatic int fill;	/* controls spacing of notes */
678Xstatic bool octtrack;	/* octave-tracking on? */
679Xstatic bool octprefix;	/* override current octave-tracking state? */
680X
681X/*
682X * Magic number avoidance...
683X */
684X#define SECS_PER_MIN	60	/* seconds per minute */
685X#define WHOLE_NOTE	4	/* quarter notes per whole note */
686X#define MIN_VALUE	64	/* the most we can divide a note by */
687X#define DFLT_VALUE	4	/* default value (quarter-note) */
688X#define FILLTIME	8	/* for articulation, break note in parts */
689X#define STACCATO	6	/* 6/8 = 3/4 of note is filled */
690X#define NORMAL		7	/* 7/8ths of note interval is filled */
691X#define LEGATO		8	/* all of note interval is filled */
692X#define DFLT_OCTAVE	4	/* default octave */
693X#define MIN_TEMPO	32	/* minimum tempo */
694X#define DFLT_TEMPO	120	/* default tempo */
695X#define MAX_TEMPO	255	/* max tempo */
696X#define NUM_MULT	3	/* numerator of dot multiplier */
697X#define DENOM_MULT	2	/* denominator of dot multiplier */
698X
699X/* letter to half-tone:  A   B  C  D  E  F  G */
700Xstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
701X
702X/*
703X * This is the American Standard A440 Equal-Tempered scale with frequencies
704X * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
705X * our octave 0 is standard octave 2.
706X */
707X#define OCTAVE_NOTES	12	/* semitones per octave */
708Xstatic int pitchtab[] =
709X{
710X/*        C     C#    D     D#    E     F     F#    G     G#    A     A#    B*/
711X/* 0 */   65,   69,   73,   78,   82,   87,   93,   98,  103,  110,  117,  123,
712X/* 1 */  131,  139,  147,  156,  165,  175,  185,  196,  208,  220,  233,  247,
713X/* 2 */  262,  277,  294,  311,  330,  349,  370,  392,  415,  440,  466,  494,
714X/* 3 */  523,  554,  587,  622,  659,  698,  740,  784,  831,  880,  932,  988,
715X/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
716X/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
717X/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
718X};
719X
720Xstatic void playinit()
721X{
722X    octave = DFLT_OCTAVE;
723X    whole = (HZ * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
724X    fill = NORMAL;
725X    value = DFLT_VALUE;
726X    octtrack = FALSE;
727X    octprefix = TRUE;	/* act as though there was an initial O(n) */
728X}
729X
730Xstatic void playtone(pitch, value, sustain)
731X/* play tone of proper duration for current rhythm signature */
732Xint	pitch, value, sustain;
733X{
734X    register int	sound, silence, snum = 1, sdenom = 1;
735X
736X    /* this weirdness avoids floating-point arithmetic */
737X    for (; sustain; sustain--)
738X    {
739X	snum *= NUM_MULT;
740X	sdenom *= DENOM_MULT;
741X    }
742X
743X    if (pitch == -1)
744X	rest(whole * snum / (value * sdenom));
745X    else
746X    {
747X	sound = (whole * snum) / (value * sdenom)
748X		- (whole * (FILLTIME - fill)) / (value * FILLTIME);
749X	silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
750X
751X#ifdef DEBUG
752X	printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
753X			pitch, sound, silence);
754X#endif /* DEBUG */
755X
756X	tone(pitchtab[pitch], sound);
757X	if (fill != LEGATO)
758X	    rest(silence);
759X    }
760X}
761X
762Xstatic int abs(n)
763Xint n;
764X{
765X    if (n < 0)
766X	return(-n);
767X    else
768X	return(n);
769X}
770X
771Xstatic void playstring(cp, slen)
772X/* interpret and play an item from a notation string */
773Xchar	*cp;
774Xsize_t	slen;
775X{
776X    int		pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
777X
778X#define GETNUM(cp, v)	for(v=0; isdigit(cp[1]) && slen > 0; ) \
779X				{v = v * 10 + (*++cp - '0'); slen--;}
780X    for (; slen--; cp++)
781X    {
782X	int		sustain, timeval, tempo;
783X	register char	c = toupper(*cp);
784X
785X#ifdef DEBUG
786X	printf("playstring: %c (%x)\n", c, c);
787X#endif /* DEBUG */
788X
789X	switch (c)
790X	{
791X	case 'A':  case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
792X
793X	    /* compute pitch */
794X	    pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
795X
796X	    /* this may be followed by an accidental sign */
797X	    if (cp[1] == '#' || cp[1] == '+')
798X	    {
799X		++pitch;
800X		++cp;
801X		slen--;
802X	    }
803X	    else if (cp[1] == '-')
804X	    {
805X		--pitch;
806X		++cp;
807X		slen--;
808X	    }
809X
810X	    /*
811X	     * If octave-tracking mode is on, and there has been no octave-
812X	     * setting prefix, find the version of the current letter note
813X	     * closest to the last regardless of octave.
814X	     */
815X	    if (octtrack && !octprefix)
816X	    {
817X		if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
818X		{
819X		    ++octave;
820X		    pitch += OCTAVE_NOTES;
821X		}
822X
823X		if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
824X		{
825X		    --octave;
826X		    pitch -= OCTAVE_NOTES;
827X		}
828X	    }
829X	    octprefix = FALSE;
830X	    lastpitch = pitch;
831X
832X	    /* ...which may in turn be followed by an override time value */
833X	    GETNUM(cp, timeval);
834X	    if (timeval <= 0 || timeval > MIN_VALUE)
835X		timeval = value;
836X
837X	    /* ...and/or sustain dots */
838X	    for (sustain = 0; cp[1] == '.'; cp++)
839X	    {
840X		slen--;
841X		sustain++;
842X	    }
843X
844X	    /* time to emit the actual tone */
845X	    playtone(pitch, timeval, sustain);
846X	    break;
847X
848X	case 'O':
849X	    if (cp[1] == 'N' || cp[1] == 'n')
850X	    {
851X		octprefix = octtrack = FALSE;
852X		++cp;
853X		slen--;
854X	    }
855X	    else if (cp[1] == 'L' || cp[1] == 'l')
856X	    {
857X		octtrack = TRUE;
858X		++cp;
859X		slen--;
860X	    }
861X	    else
862X	    {
863X		GETNUM(cp, octave);
864X		if (octave >= sizeof(pitchtab) / OCTAVE_NOTES)
865X		    octave = DFLT_OCTAVE;
866X		octprefix = TRUE;
867X	    }
868X	    break;
869X
870X	case '>':
871X	    if (octave < sizeof(pitchtab) / OCTAVE_NOTES - 1)
872X		octave++;
873X	    octprefix = TRUE;
874X	    break;
875X
876X	case '<':
877X	    if (octave > 0)
878X		octave--;
879X	    octprefix = TRUE;
880X	    break;
881X
882X	case 'N':
883X	    GETNUM(cp, pitch);
884X	    for (sustain = 0; cp[1] == '.'; cp++)
885X	    {
886X		slen--;
887X		sustain++;
888X	    }
889X	    playtone(pitch - 1, value, sustain);
890X	    break;
891X
892X	case 'L':
893X	    GETNUM(cp, value);
894X	    if (value <= 0 || value > MIN_VALUE)
895X		value = DFLT_VALUE;
896X	    break;
897X
898X	case 'P':
899X	case '~':
900X	    /* this may be followed by an override time value */
901X	    GETNUM(cp, timeval);
902X	    if (timeval <= 0 || timeval > MIN_VALUE)
903X		timeval = value;
904X	    for (sustain = 0; cp[1] == '.'; cp++)
905X	    {
906X		slen--;
907X		sustain++;
908X	    }
909X	    playtone(-1, timeval, sustain);
910X	    break;
911X
912X	case 'T':
913X	    GETNUM(cp, tempo);
914X	    if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
915X		tempo = DFLT_TEMPO;
916X	    whole = (HZ * SECS_PER_MIN * WHOLE_NOTE) / tempo;
917X	    break;
918X
919X	case 'M':
920X	    if (cp[1] == 'N' || cp[1] == 'n')
921X	    {
922X		fill = NORMAL;
923X		++cp;
924X		slen--;
925X	    }
926X	    else if (cp[1] == 'L' || cp[1] == 'l')
927X	    {
928X		fill = LEGATO;
929X		++cp;
930X		slen--;
931X	    }
932X	    else if (cp[1] == 'S' || cp[1] == 's')
933X	    {
934X		fill = STACCATO;
935X		++cp;
936X		slen--;
937X	    }
938X	    break;
939X	}
940X    }
941X}
942END-of-interp.c
943echo x - Files
944sed 's/^X//' >Files << 'END-of-Files'
945X/usr/include/sys/spkr.h
946END-of-Files
947echo x - Install
948sed 's/^X//' >Install << 'END-of-Install'
949X#
950X# Speaker driver installation script
951X#
952XTMP=/tmp/speaker.err
953XERR1=" Errors have been written to the file $TMP."
954XERR2=" The Speaker Driver software was not installed."
955X
956Xecho "Installing Speaker Driver Software Package"
957X
958X/etc/conf/bin/idcheck -p speaker 2>$TMP
959Xif [ $? != 0 ]
960Xthen
961X	echo "The speaker package is already at least partly installed.
962X	Removing the old version now..."
963X	/etc/conf/bin/idinstall -d speaker
964Xfi
965X
966X/etc/conf/bin/idinstall -a -k speaker 2>>$TMP
967Xif [ $? != 0 ]
968Xthen
969X	message "There was an error during package installation. $ERR1 $ERR2"
970X	exit 1
971Xfi
972X
973X/etc/conf/bin/idbuild 2>>$TMP
974Xif [ $? != 0 ]
975Xthen
976X	message "There was an error during kernel reconfiguration. $ERR1 $ERR2"
977X	exit 1
978Xfi
979X
980Xrm -f $TMP
981X
982Xcp spkr.h /usr/include/sys/spkr.h
983X
984Xecho "Performing shutdown..."
985Xcd /; exec /etc/shutdown -g0 -y
986END-of-Install
987echo x - Master
988sed 's/^X//' >Master << 'END-of-Master'
989Xspeaker	ocwi	iocH	spkr	0	0	1	1	-1
990END-of-Master
991echo x - Name
992sed 's/^X//' >Name << 'END-of-Name'
993X386 UNIX Speaker Device Driver Package
994END-of-Name
995echo x - Node
996sed 's/^X//' >Node << 'END-of-Node'
997Xspeaker	speaker	c	0
998END-of-Node
999echo x - Remove
1000sed 's/^X//' >Remove << 'END-of-Remove'
1001X#
1002X# Speaker driver remove script
1003X#
1004XTMP=/tmp/speaker.err
1005XRERR="Errors have been written to the file $TMP."
1006X
1007Xecho "Removing Speaker Driver Software Package"
1008X
1009X/etc/conf/bin/idinstall -d speaker 2>$TMP
1010Xif [ $? != 0 ]
1011Xthen
1012X	message "There was an error during package removal. $RERR"
1013X	exit 1
1014Xfi
1015X
1016X/etc/conf/bin/idbuild 2>>$TMP
1017Xif [ $? != 0 ]
1018Xthen
1019X	message "There was an error during kernel reconfiguration. $RERR"
1020X	exit 1
1021Xfi
1022X
1023Xrm -f /dev/speaker $TMP /usr/include/sys/spkr.h
1024X
1025Xexit 0
1026END-of-Remove
1027echo x - Size
1028sed 's/^X//' >Size << 'END-of-Size'
1029XROOT=1400
1030XUSR=100
1031END-of-Size
1032echo x - System
1033sed 's/^X//' >System << 'END-of-System'
1034Xspeaker	Y	1	0	0	0	0	0	0	0
1035END-of-System
1036echo x - playtest
1037sed 's/^X//' >playtest << 'END-of-playtest'
1038X:
1039X# Test script for the speaker driver
1040X#
1041X# v1.0 by Eric S. Raymond (Feb 1990)
1042X#      modified for 386bsd by Andrew A. Chernov <ache@astral.msk.su>
1043X#
1044Xreveille="t255l8c.f.afc~c.f.afc~c.f.afc.f.a..f.~c.f.afc~c.f.afc~c.f.afc~c.f.."
1045Xcontact="<cd<a#~<a#>f"
1046Xdance="t240<cfcfgagaa#b#>dc<a#a.~fg.gaa#.agagegc.~cfcfgagaa#b#>dc<a#a.~fg.gga.agfgfgf."
1047Xloony="t255cf8f8edc<a.>~cf8f8edd#e.~ce8cdce8cd.<a>c8c8c#def8af8."
1048X
1049Xcase $1 in
1050Xreveille) echo  $reveille >/dev/speaker;;
1051Xcontact)  echo  $contact >/dev/speaker;;
1052Xdance)  echo  $dance >/dev/speaker;;
1053Xloony)  echo  $loony >/dev/speaker;;
1054X*)
1055X	echo "No such tune. Available tunes are:"
1056X	echo
1057X	echo "reveille -- Reveille"
1058X	echo "contact -- Contact theme from Close Encounters"
1059X	echo "dance -- Lord of the Dance (aka Simple Gifts)"
1060X	echo "loony -- Loony Toons theme"
1061X	;;
1062Xesac
1063END-of-playtest
1064exit
1065