15b81b6b3SRodney W. Grimes /* 2e614aa8aSAndrey A. Chernov * spkr.c -- device driver for console speaker 35b81b6b3SRodney W. Grimes * 4e614aa8aSAndrey A. Chernov * v1.4 by Eric S. Raymond (esr@snark.thyrsus.com) Aug 1993 5e614aa8aSAndrey A. Chernov * modified for FreeBSD by Andrew A. Chernov <ache@astral.msk.su> 66f78ca60SRodney W. Grimes * 71e064bd4SAndrey A. Chernov * $Id: spkr.c,v 1.4 1993/11/09 02:32:30 ache Exp $ 85b81b6b3SRodney W. Grimes */ 95b81b6b3SRodney W. Grimes 105b81b6b3SRodney W. Grimes #include "speaker.h" 115b81b6b3SRodney W. Grimes 125b81b6b3SRodney W. Grimes #if NSPEAKER > 0 135b81b6b3SRodney W. Grimes 145b81b6b3SRodney W. Grimes #include "param.h" 151e064bd4SAndrey A. Chernov #include "systm.h" 165b81b6b3SRodney W. Grimes #include "kernel.h" 175b81b6b3SRodney W. Grimes #include "errno.h" 185b81b6b3SRodney W. Grimes #include "buf.h" 195b81b6b3SRodney W. Grimes #include "uio.h" 20e614aa8aSAndrey A. Chernov 211e064bd4SAndrey A. Chernov #include "machine/speaker.h" 225b81b6b3SRodney W. Grimes 235b81b6b3SRodney W. Grimes /**************** MACHINE DEPENDENT PART STARTS HERE ************************* 245b81b6b3SRodney W. Grimes * 255b81b6b3SRodney W. Grimes * This section defines a function tone() which causes a tone of given 265b81b6b3SRodney W. Grimes * frequency and duration from the 80x86's console speaker. 275b81b6b3SRodney W. Grimes * Another function endtone() is defined to force sound off, and there is 285b81b6b3SRodney W. Grimes * also a rest() entry point to do pauses. 295b81b6b3SRodney W. Grimes * 305b81b6b3SRodney W. Grimes * Audible sound is generated using the Programmable Interval Timer (PIT) and 315b81b6b3SRodney W. Grimes * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The 325b81b6b3SRodney W. Grimes * PPI controls whether sound is passed through at all; the PIT's channel 2 is 335b81b6b3SRodney W. Grimes * used to generate clicks (a square wave) of whatever frequency is desired. 345b81b6b3SRodney W. Grimes */ 355b81b6b3SRodney W. Grimes 365b81b6b3SRodney W. Grimes /* 375b81b6b3SRodney W. Grimes * PIT and PPI port addresses and control values 385b81b6b3SRodney W. Grimes * 395b81b6b3SRodney W. Grimes * Most of the magic is hidden in the TIMER_PREP value, which selects PIT 405b81b6b3SRodney W. Grimes * channel 2, frequency LSB first, square-wave mode and binary encoding. 415b81b6b3SRodney W. Grimes * The encoding is as follows: 425b81b6b3SRodney W. Grimes * 435b81b6b3SRodney W. Grimes * +----------+----------+---------------+-----+ 445b81b6b3SRodney W. Grimes * | 1 0 | 1 1 | 0 1 1 | 0 | 455b81b6b3SRodney W. Grimes * | SC1 SC0 | RW1 RW0 | M2 M1 M0 | BCD | 465b81b6b3SRodney W. Grimes * +----------+----------+---------------+-----+ 475b81b6b3SRodney W. Grimes * Counter Write Mode 3 Binary 485b81b6b3SRodney W. Grimes * Channel 2 LSB first, (Square Wave) Encoding 495b81b6b3SRodney W. Grimes * MSB second 505b81b6b3SRodney W. Grimes */ 515b81b6b3SRodney W. Grimes #define PPI 0x61 /* port of Programmable Peripheral Interface */ 525b81b6b3SRodney W. Grimes #define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */ 535b81b6b3SRodney W. Grimes #define PIT_CTRL 0x43 /* PIT control address */ 545b81b6b3SRodney W. Grimes #define PIT_COUNT 0x42 /* PIT count address */ 555b81b6b3SRodney W. Grimes #define PIT_MODE 0xB6 /* set timer mode for sound generation */ 565b81b6b3SRodney W. Grimes 575b81b6b3SRodney W. Grimes /* 585b81b6b3SRodney W. Grimes * Magic numbers for timer control. 595b81b6b3SRodney W. Grimes */ 605b81b6b3SRodney W. Grimes #define TIMER_CLK 1193180L /* corresponds to 18.2 MHz tick rate */ 615b81b6b3SRodney W. Grimes 621e064bd4SAndrey A. Chernov #define SPKRPRI PSOCK 631e064bd4SAndrey A. Chernov static char endtone, endrest; 645b81b6b3SRodney W. Grimes 651e064bd4SAndrey A. Chernov static int tone(thz, ticks) 661e064bd4SAndrey A. Chernov /* emit tone of frequency thz for given number of ticks */ 671e064bd4SAndrey A. Chernov unsigned int thz, ticks; 685b81b6b3SRodney W. Grimes { 691e064bd4SAndrey A. Chernov unsigned int divisor = TIMER_CLK / thz; 701e064bd4SAndrey A. Chernov int sps, error; 715b81b6b3SRodney W. Grimes 725b81b6b3SRodney W. Grimes #ifdef DEBUG 731e064bd4SAndrey A. Chernov (void) printf("tone: thz=%d ticks=%d\n", thz, ticks); 745b81b6b3SRodney W. Grimes #endif /* DEBUG */ 755b81b6b3SRodney W. Grimes 765b81b6b3SRodney W. Grimes /* set timer to generate clicks at given frequency in Hertz */ 775b81b6b3SRodney W. Grimes sps = spltty(); 785b81b6b3SRodney W. Grimes outb(PIT_CTRL, PIT_MODE); /* prepare timer */ 79e614aa8aSAndrey A. Chernov outb(PIT_COUNT, (divisor & 0xff)); /* send lo byte */ 805b81b6b3SRodney W. Grimes outb(PIT_COUNT, (divisor >> 8)); /* send hi byte */ 815b81b6b3SRodney W. Grimes splx(sps); 825b81b6b3SRodney W. Grimes 835b81b6b3SRodney W. Grimes /* turn the speaker on */ 845b81b6b3SRodney W. Grimes outb(PPI, inb(PPI) | PPI_SPKR); 855b81b6b3SRodney W. Grimes 865b81b6b3SRodney W. Grimes /* 875b81b6b3SRodney W. Grimes * Set timeout to endtone function, then give up the timeslice. 885b81b6b3SRodney W. Grimes * This is so other processes can execute while the tone is being 895b81b6b3SRodney W. Grimes * emitted. 905b81b6b3SRodney W. Grimes */ 911e064bd4SAndrey A. Chernov while ((error = tsleep((caddr_t)&endtone, 921e064bd4SAndrey A. Chernov SPKRPRI | PCATCH, "spkrtone", ticks)) == ERESTART) 931e064bd4SAndrey A. Chernov ; 941e064bd4SAndrey A. Chernov outb(PPI, inb(PPI) & ~PPI_SPKR); 951e064bd4SAndrey A. Chernov 961e064bd4SAndrey A. Chernov if (error == EWOULDBLOCK) 971e064bd4SAndrey A. Chernov error = 0; 981e064bd4SAndrey A. Chernov return error; 995b81b6b3SRodney W. Grimes } 1005b81b6b3SRodney W. Grimes 1011e064bd4SAndrey A. Chernov static int rest(ticks) 1025b81b6b3SRodney W. Grimes /* rest for given number of ticks */ 1035b81b6b3SRodney W. Grimes int ticks; 1045b81b6b3SRodney W. Grimes { 1051e064bd4SAndrey A. Chernov int error; 1065b81b6b3SRodney W. Grimes /* 1075b81b6b3SRodney W. Grimes * Set timeout to endrest function, then give up the timeslice. 1085b81b6b3SRodney W. Grimes * This is so other processes can execute while the rest is being 1095b81b6b3SRodney W. Grimes * waited out. 1105b81b6b3SRodney W. Grimes */ 1115b81b6b3SRodney W. Grimes #ifdef DEBUG 112e614aa8aSAndrey A. Chernov (void) printf("rest: %d\n", ticks); 1135b81b6b3SRodney W. Grimes #endif /* DEBUG */ 1141e064bd4SAndrey A. Chernov while ((error = tsleep((caddr_t)&endrest, 1151e064bd4SAndrey A. Chernov SPKRPRI | PCATCH, "spkrrest", ticks)) == ERESTART) 1161e064bd4SAndrey A. Chernov ; 1171e064bd4SAndrey A. Chernov if (error == EWOULDBLOCK) 1181e064bd4SAndrey A. Chernov error = 0; 1191e064bd4SAndrey A. Chernov return error; 1205b81b6b3SRodney W. Grimes } 1215b81b6b3SRodney W. Grimes 1225b81b6b3SRodney W. Grimes /**************** PLAY STRING INTERPRETER BEGINS HERE ********************** 1235b81b6b3SRodney W. Grimes * 1245b81b6b3SRodney W. Grimes * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; 125e614aa8aSAndrey A. Chernov * M[LNS] are missing; the ~ synonym and the _ slur mark and the octave- 126e614aa8aSAndrey A. Chernov * tracking facility are added. 1275b81b6b3SRodney W. Grimes * Requires tone(), rest(), and endtone(). String play is not interruptible 1285b81b6b3SRodney W. Grimes * except possibly at physical block boundaries. 1295b81b6b3SRodney W. Grimes */ 1305b81b6b3SRodney W. Grimes 1315b81b6b3SRodney W. Grimes typedef int bool; 1325b81b6b3SRodney W. Grimes #define TRUE 1 1335b81b6b3SRodney W. Grimes #define FALSE 0 1345b81b6b3SRodney W. Grimes 1355b81b6b3SRodney W. Grimes #define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z'))) 1365b81b6b3SRodney W. Grimes #define isdigit(c) (((c) >= '0') && ((c) <= '9')) 1375b81b6b3SRodney W. Grimes #define dtoi(c) ((c) - '0') 1385b81b6b3SRodney W. Grimes 1395b81b6b3SRodney W. Grimes static int octave; /* currently selected octave */ 1405b81b6b3SRodney W. Grimes static int whole; /* whole-note time at current tempo, in ticks */ 1415b81b6b3SRodney W. Grimes static int value; /* whole divisor for note time, quarter note = 1 */ 1425b81b6b3SRodney W. Grimes static int fill; /* controls spacing of notes */ 1435b81b6b3SRodney W. Grimes static bool octtrack; /* octave-tracking on? */ 1445b81b6b3SRodney W. Grimes static bool octprefix; /* override current octave-tracking state? */ 1455b81b6b3SRodney W. Grimes 1465b81b6b3SRodney W. Grimes /* 1475b81b6b3SRodney W. Grimes * Magic number avoidance... 1485b81b6b3SRodney W. Grimes */ 1495b81b6b3SRodney W. Grimes #define SECS_PER_MIN 60 /* seconds per minute */ 1505b81b6b3SRodney W. Grimes #define WHOLE_NOTE 4 /* quarter notes per whole note */ 1515b81b6b3SRodney W. Grimes #define MIN_VALUE 64 /* the most we can divide a note by */ 1525b81b6b3SRodney W. Grimes #define DFLT_VALUE 4 /* default value (quarter-note) */ 1535b81b6b3SRodney W. Grimes #define FILLTIME 8 /* for articulation, break note in parts */ 1545b81b6b3SRodney W. Grimes #define STACCATO 6 /* 6/8 = 3/4 of note is filled */ 1555b81b6b3SRodney W. Grimes #define NORMAL 7 /* 7/8ths of note interval is filled */ 1565b81b6b3SRodney W. Grimes #define LEGATO 8 /* all of note interval is filled */ 1575b81b6b3SRodney W. Grimes #define DFLT_OCTAVE 4 /* default octave */ 1585b81b6b3SRodney W. Grimes #define MIN_TEMPO 32 /* minimum tempo */ 1595b81b6b3SRodney W. Grimes #define DFLT_TEMPO 120 /* default tempo */ 1605b81b6b3SRodney W. Grimes #define MAX_TEMPO 255 /* max tempo */ 1615b81b6b3SRodney W. Grimes #define NUM_MULT 3 /* numerator of dot multiplier */ 1625b81b6b3SRodney W. Grimes #define DENOM_MULT 2 /* denominator of dot multiplier */ 1635b81b6b3SRodney W. Grimes 1645b81b6b3SRodney W. Grimes /* letter to half-tone: A B C D E F G */ 1655b81b6b3SRodney W. Grimes static int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; 1665b81b6b3SRodney W. Grimes 1675b81b6b3SRodney W. Grimes /* 1685b81b6b3SRodney W. Grimes * This is the American Standard A440 Equal-Tempered scale with frequencies 1695b81b6b3SRodney W. Grimes * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... 1705b81b6b3SRodney W. Grimes * our octave 0 is standard octave 2. 1715b81b6b3SRodney W. Grimes */ 1725b81b6b3SRodney W. Grimes #define OCTAVE_NOTES 12 /* semitones per octave */ 1735b81b6b3SRodney W. Grimes static int pitchtab[] = 1745b81b6b3SRodney W. Grimes { 1755b81b6b3SRodney W. Grimes /* C C# D D# E F F# G G# A A# B*/ 1765b81b6b3SRodney W. Grimes /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, 1775b81b6b3SRodney W. Grimes /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 1785b81b6b3SRodney W. Grimes /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 1795b81b6b3SRodney W. Grimes /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1805b81b6b3SRodney W. Grimes /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, 1815b81b6b3SRodney W. Grimes /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 1825b81b6b3SRodney W. Grimes /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, 1835b81b6b3SRodney W. Grimes }; 1845b81b6b3SRodney W. Grimes 1855b81b6b3SRodney W. Grimes static void playinit() 1865b81b6b3SRodney W. Grimes { 1875b81b6b3SRodney W. Grimes octave = DFLT_OCTAVE; 1881e064bd4SAndrey A. Chernov whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; 1895b81b6b3SRodney W. Grimes fill = NORMAL; 1905b81b6b3SRodney W. Grimes value = DFLT_VALUE; 1915b81b6b3SRodney W. Grimes octtrack = FALSE; 1925b81b6b3SRodney W. Grimes octprefix = TRUE; /* act as though there was an initial O(n) */ 1935b81b6b3SRodney W. Grimes } 1945b81b6b3SRodney W. Grimes 1951e064bd4SAndrey A. Chernov static int playtone(pitch, value, sustain) 1965b81b6b3SRodney W. Grimes /* play tone of proper duration for current rhythm signature */ 1975b81b6b3SRodney W. Grimes int pitch, value, sustain; 1985b81b6b3SRodney W. Grimes { 1995b81b6b3SRodney W. Grimes register int sound, silence, snum = 1, sdenom = 1; 2001e064bd4SAndrey A. Chernov int error; 2015b81b6b3SRodney W. Grimes 2025b81b6b3SRodney W. Grimes /* this weirdness avoids floating-point arithmetic */ 2035b81b6b3SRodney W. Grimes for (; sustain; sustain--) 2045b81b6b3SRodney W. Grimes { 205e614aa8aSAndrey A. Chernov /* See the BUGS section in the man page for discussion */ 2065b81b6b3SRodney W. Grimes snum *= NUM_MULT; 2075b81b6b3SRodney W. Grimes sdenom *= DENOM_MULT; 2085b81b6b3SRodney W. Grimes } 2095b81b6b3SRodney W. Grimes 2105b81b6b3SRodney W. Grimes if (pitch == -1) 2111e064bd4SAndrey A. Chernov error = rest(whole * snum / (value * sdenom)); 2125b81b6b3SRodney W. Grimes else 2135b81b6b3SRodney W. Grimes { 2145b81b6b3SRodney W. Grimes sound = (whole * snum) / (value * sdenom) 2155b81b6b3SRodney W. Grimes - (whole * (FILLTIME - fill)) / (value * FILLTIME); 2165b81b6b3SRodney W. Grimes silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); 2175b81b6b3SRodney W. Grimes 2185b81b6b3SRodney W. Grimes #ifdef DEBUG 219e614aa8aSAndrey A. Chernov (void) printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", 2205b81b6b3SRodney W. Grimes pitch, sound, silence); 2215b81b6b3SRodney W. Grimes #endif /* DEBUG */ 2225b81b6b3SRodney W. Grimes 2231e064bd4SAndrey A. Chernov error = tone(pitchtab[pitch], sound); 2241e064bd4SAndrey A. Chernov if (error) return error; 2255b81b6b3SRodney W. Grimes if (fill != LEGATO) 2261e064bd4SAndrey A. Chernov error = rest(silence); 2275b81b6b3SRodney W. Grimes } 2281e064bd4SAndrey A. Chernov return error; 2295b81b6b3SRodney W. Grimes } 2305b81b6b3SRodney W. Grimes 2315b81b6b3SRodney W. Grimes static int abs(n) 2325b81b6b3SRodney W. Grimes int n; 2335b81b6b3SRodney W. Grimes { 2345b81b6b3SRodney W. Grimes if (n < 0) 2355b81b6b3SRodney W. Grimes return(-n); 2365b81b6b3SRodney W. Grimes else 2375b81b6b3SRodney W. Grimes return(n); 2385b81b6b3SRodney W. Grimes } 2395b81b6b3SRodney W. Grimes 2401e064bd4SAndrey A. Chernov static int playstring(cp, slen) 2415b81b6b3SRodney W. Grimes /* interpret and play an item from a notation string */ 2425b81b6b3SRodney W. Grimes char *cp; 2435b81b6b3SRodney W. Grimes size_t slen; 2445b81b6b3SRodney W. Grimes { 245e614aa8aSAndrey A. Chernov int pitch, oldfill, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; 2461e064bd4SAndrey A. Chernov int error; 2475b81b6b3SRodney W. Grimes 2485b81b6b3SRodney W. Grimes #define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ 2495b81b6b3SRodney W. Grimes {v = v * 10 + (*++cp - '0'); slen--;} 2505b81b6b3SRodney W. Grimes for (; slen--; cp++) 2515b81b6b3SRodney W. Grimes { 2525b81b6b3SRodney W. Grimes int sustain, timeval, tempo; 2535b81b6b3SRodney W. Grimes register char c = toupper(*cp); 2545b81b6b3SRodney W. Grimes 2555b81b6b3SRodney W. Grimes #ifdef DEBUG 256e614aa8aSAndrey A. Chernov (void) printf("playstring: %c (%x)\n", c, c); 2575b81b6b3SRodney W. Grimes #endif /* DEBUG */ 2585b81b6b3SRodney W. Grimes 2595b81b6b3SRodney W. Grimes switch (c) 2605b81b6b3SRodney W. Grimes { 2615b81b6b3SRodney W. Grimes case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 2625b81b6b3SRodney W. Grimes 2635b81b6b3SRodney W. Grimes /* compute pitch */ 2645b81b6b3SRodney W. Grimes pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; 2655b81b6b3SRodney W. Grimes 2665b81b6b3SRodney W. Grimes /* this may be followed by an accidental sign */ 2675b81b6b3SRodney W. Grimes if (cp[1] == '#' || cp[1] == '+') 2685b81b6b3SRodney W. Grimes { 2695b81b6b3SRodney W. Grimes ++pitch; 2705b81b6b3SRodney W. Grimes ++cp; 2715b81b6b3SRodney W. Grimes slen--; 2725b81b6b3SRodney W. Grimes } 2735b81b6b3SRodney W. Grimes else if (cp[1] == '-') 2745b81b6b3SRodney W. Grimes { 2755b81b6b3SRodney W. Grimes --pitch; 2765b81b6b3SRodney W. Grimes ++cp; 2775b81b6b3SRodney W. Grimes slen--; 2785b81b6b3SRodney W. Grimes } 2795b81b6b3SRodney W. Grimes 2805b81b6b3SRodney W. Grimes /* 2815b81b6b3SRodney W. Grimes * If octave-tracking mode is on, and there has been no octave- 2825b81b6b3SRodney W. Grimes * setting prefix, find the version of the current letter note 2835b81b6b3SRodney W. Grimes * closest to the last regardless of octave. 2845b81b6b3SRodney W. Grimes */ 2855b81b6b3SRodney W. Grimes if (octtrack && !octprefix) 2865b81b6b3SRodney W. Grimes { 2875b81b6b3SRodney W. Grimes if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) 2885b81b6b3SRodney W. Grimes { 2895b81b6b3SRodney W. Grimes ++octave; 2905b81b6b3SRodney W. Grimes pitch += OCTAVE_NOTES; 2915b81b6b3SRodney W. Grimes } 2925b81b6b3SRodney W. Grimes 2935b81b6b3SRodney W. Grimes if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) 2945b81b6b3SRodney W. Grimes { 2955b81b6b3SRodney W. Grimes --octave; 2965b81b6b3SRodney W. Grimes pitch -= OCTAVE_NOTES; 2975b81b6b3SRodney W. Grimes } 2985b81b6b3SRodney W. Grimes } 2995b81b6b3SRodney W. Grimes octprefix = FALSE; 3005b81b6b3SRodney W. Grimes lastpitch = pitch; 3015b81b6b3SRodney W. Grimes 3025b81b6b3SRodney W. Grimes /* ...which may in turn be followed by an override time value */ 3035b81b6b3SRodney W. Grimes GETNUM(cp, timeval); 3045b81b6b3SRodney W. Grimes if (timeval <= 0 || timeval > MIN_VALUE) 3055b81b6b3SRodney W. Grimes timeval = value; 3065b81b6b3SRodney W. Grimes 3075b81b6b3SRodney W. Grimes /* ...and/or sustain dots */ 3085b81b6b3SRodney W. Grimes for (sustain = 0; cp[1] == '.'; cp++) 3095b81b6b3SRodney W. Grimes { 3105b81b6b3SRodney W. Grimes slen--; 3115b81b6b3SRodney W. Grimes sustain++; 3125b81b6b3SRodney W. Grimes } 3135b81b6b3SRodney W. Grimes 314e614aa8aSAndrey A. Chernov /* ...and/or a slur mark */ 315e614aa8aSAndrey A. Chernov oldfill = fill; 316e614aa8aSAndrey A. Chernov if (cp[1] == '_') 317e614aa8aSAndrey A. Chernov { 318e614aa8aSAndrey A. Chernov fill = LEGATO; 319e614aa8aSAndrey A. Chernov ++cp; 320e614aa8aSAndrey A. Chernov slen--; 321e614aa8aSAndrey A. Chernov } 322e614aa8aSAndrey A. Chernov 3235b81b6b3SRodney W. Grimes /* time to emit the actual tone */ 3241e064bd4SAndrey A. Chernov error = playtone(pitch, timeval, sustain); 325e614aa8aSAndrey A. Chernov 326e614aa8aSAndrey A. Chernov fill = oldfill; 3271e064bd4SAndrey A. Chernov if (error) return error; 3285b81b6b3SRodney W. Grimes break; 3295b81b6b3SRodney W. Grimes 3305b81b6b3SRodney W. Grimes case 'O': 3315b81b6b3SRodney W. Grimes if (cp[1] == 'N' || cp[1] == 'n') 3325b81b6b3SRodney W. Grimes { 3335b81b6b3SRodney W. Grimes octprefix = octtrack = FALSE; 3345b81b6b3SRodney W. Grimes ++cp; 3355b81b6b3SRodney W. Grimes slen--; 3365b81b6b3SRodney W. Grimes } 3375b81b6b3SRodney W. Grimes else if (cp[1] == 'L' || cp[1] == 'l') 3385b81b6b3SRodney W. Grimes { 3395b81b6b3SRodney W. Grimes octtrack = TRUE; 3405b81b6b3SRodney W. Grimes ++cp; 3415b81b6b3SRodney W. Grimes slen--; 3425b81b6b3SRodney W. Grimes } 3435b81b6b3SRodney W. Grimes else 3445b81b6b3SRodney W. Grimes { 3455b81b6b3SRodney W. Grimes GETNUM(cp, octave); 3465b81b6b3SRodney W. Grimes if (octave >= sizeof(pitchtab) / OCTAVE_NOTES) 3475b81b6b3SRodney W. Grimes octave = DFLT_OCTAVE; 3485b81b6b3SRodney W. Grimes octprefix = TRUE; 3495b81b6b3SRodney W. Grimes } 3505b81b6b3SRodney W. Grimes break; 3515b81b6b3SRodney W. Grimes 3525b81b6b3SRodney W. Grimes case '>': 3535b81b6b3SRodney W. Grimes if (octave < sizeof(pitchtab) / OCTAVE_NOTES - 1) 3545b81b6b3SRodney W. Grimes octave++; 3555b81b6b3SRodney W. Grimes octprefix = TRUE; 3565b81b6b3SRodney W. Grimes break; 3575b81b6b3SRodney W. Grimes 3585b81b6b3SRodney W. Grimes case '<': 3595b81b6b3SRodney W. Grimes if (octave > 0) 3605b81b6b3SRodney W. Grimes octave--; 3615b81b6b3SRodney W. Grimes octprefix = TRUE; 3625b81b6b3SRodney W. Grimes break; 3635b81b6b3SRodney W. Grimes 3645b81b6b3SRodney W. Grimes case 'N': 3655b81b6b3SRodney W. Grimes GETNUM(cp, pitch); 3665b81b6b3SRodney W. Grimes for (sustain = 0; cp[1] == '.'; cp++) 3675b81b6b3SRodney W. Grimes { 3685b81b6b3SRodney W. Grimes slen--; 3695b81b6b3SRodney W. Grimes sustain++; 3705b81b6b3SRodney W. Grimes } 371e614aa8aSAndrey A. Chernov oldfill = fill; 372e614aa8aSAndrey A. Chernov if (cp[1] == '_') 373e614aa8aSAndrey A. Chernov { 374e614aa8aSAndrey A. Chernov fill = LEGATO; 375e614aa8aSAndrey A. Chernov ++cp; 376e614aa8aSAndrey A. Chernov slen--; 377e614aa8aSAndrey A. Chernov } 3781e064bd4SAndrey A. Chernov error = playtone(pitch - 1, value, sustain); 379e614aa8aSAndrey A. Chernov fill = oldfill; 3801e064bd4SAndrey A. Chernov if (error) return error; 3815b81b6b3SRodney W. Grimes break; 3825b81b6b3SRodney W. Grimes 3835b81b6b3SRodney W. Grimes case 'L': 3845b81b6b3SRodney W. Grimes GETNUM(cp, value); 3855b81b6b3SRodney W. Grimes if (value <= 0 || value > MIN_VALUE) 3865b81b6b3SRodney W. Grimes value = DFLT_VALUE; 3875b81b6b3SRodney W. Grimes break; 3885b81b6b3SRodney W. Grimes 3895b81b6b3SRodney W. Grimes case 'P': 3905b81b6b3SRodney W. Grimes case '~': 3915b81b6b3SRodney W. Grimes /* this may be followed by an override time value */ 3925b81b6b3SRodney W. Grimes GETNUM(cp, timeval); 3935b81b6b3SRodney W. Grimes if (timeval <= 0 || timeval > MIN_VALUE) 3945b81b6b3SRodney W. Grimes timeval = value; 3955b81b6b3SRodney W. Grimes for (sustain = 0; cp[1] == '.'; cp++) 3965b81b6b3SRodney W. Grimes { 3975b81b6b3SRodney W. Grimes slen--; 3985b81b6b3SRodney W. Grimes sustain++; 3995b81b6b3SRodney W. Grimes } 4001e064bd4SAndrey A. Chernov error = playtone(-1, timeval, sustain); 4011e064bd4SAndrey A. Chernov if (error) return error; 4025b81b6b3SRodney W. Grimes break; 4035b81b6b3SRodney W. Grimes 4045b81b6b3SRodney W. Grimes case 'T': 4055b81b6b3SRodney W. Grimes GETNUM(cp, tempo); 4065b81b6b3SRodney W. Grimes if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) 4075b81b6b3SRodney W. Grimes tempo = DFLT_TEMPO; 4081e064bd4SAndrey A. Chernov whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo; 4095b81b6b3SRodney W. Grimes break; 4105b81b6b3SRodney W. Grimes 4115b81b6b3SRodney W. Grimes case 'M': 4125b81b6b3SRodney W. Grimes if (cp[1] == 'N' || cp[1] == 'n') 4135b81b6b3SRodney W. Grimes { 4145b81b6b3SRodney W. Grimes fill = NORMAL; 4155b81b6b3SRodney W. Grimes ++cp; 4165b81b6b3SRodney W. Grimes slen--; 4175b81b6b3SRodney W. Grimes } 4185b81b6b3SRodney W. Grimes else if (cp[1] == 'L' || cp[1] == 'l') 4195b81b6b3SRodney W. Grimes { 4205b81b6b3SRodney W. Grimes fill = LEGATO; 4215b81b6b3SRodney W. Grimes ++cp; 4225b81b6b3SRodney W. Grimes slen--; 4235b81b6b3SRodney W. Grimes } 4245b81b6b3SRodney W. Grimes else if (cp[1] == 'S' || cp[1] == 's') 4255b81b6b3SRodney W. Grimes { 4265b81b6b3SRodney W. Grimes fill = STACCATO; 4275b81b6b3SRodney W. Grimes ++cp; 4285b81b6b3SRodney W. Grimes slen--; 4295b81b6b3SRodney W. Grimes } 4305b81b6b3SRodney W. Grimes break; 4315b81b6b3SRodney W. Grimes } 4325b81b6b3SRodney W. Grimes } 4331e064bd4SAndrey A. Chernov return 0; 4345b81b6b3SRodney W. Grimes } 4355b81b6b3SRodney W. Grimes 4365b81b6b3SRodney W. Grimes /******************* UNIX DRIVER HOOKS BEGIN HERE ************************** 4375b81b6b3SRodney W. Grimes * 4385b81b6b3SRodney W. Grimes * This section implements driver hooks to run playstring() and the tone(), 4395b81b6b3SRodney W. Grimes * endtone(), and rest() functions defined above. 4405b81b6b3SRodney W. Grimes */ 4415b81b6b3SRodney W. Grimes 442e614aa8aSAndrey A. Chernov static int spkr_active = FALSE; /* exclusion flag */ 4435b81b6b3SRodney W. Grimes static struct buf *spkr_inbuf; /* incoming buf */ 4445b81b6b3SRodney W. Grimes 4455b81b6b3SRodney W. Grimes int spkropen(dev) 4465b81b6b3SRodney W. Grimes dev_t dev; 4475b81b6b3SRodney W. Grimes { 4485b81b6b3SRodney W. Grimes #ifdef DEBUG 449e614aa8aSAndrey A. Chernov (void) printf("spkropen: entering with dev = %x\n", dev); 4505b81b6b3SRodney W. Grimes #endif /* DEBUG */ 4515b81b6b3SRodney W. Grimes 4525b81b6b3SRodney W. Grimes if (minor(dev) != 0) 4535b81b6b3SRodney W. Grimes return(ENXIO); 4545b81b6b3SRodney W. Grimes else if (spkr_active) 4555b81b6b3SRodney W. Grimes return(EBUSY); 4565b81b6b3SRodney W. Grimes else 4575b81b6b3SRodney W. Grimes { 458e614aa8aSAndrey A. Chernov #ifdef DEBUG 459e614aa8aSAndrey A. Chernov (void) printf("spkropen: about to perform play initialization\n"); 460e614aa8aSAndrey A. Chernov #endif /* DEBUG */ 4615b81b6b3SRodney W. Grimes playinit(); 4625b81b6b3SRodney W. Grimes spkr_inbuf = geteblk(DEV_BSIZE); 463e614aa8aSAndrey A. Chernov spkr_active = TRUE; 4645b81b6b3SRodney W. Grimes return(0); 4655b81b6b3SRodney W. Grimes } 466e614aa8aSAndrey A. Chernov } 4675b81b6b3SRodney W. Grimes 4685b81b6b3SRodney W. Grimes int spkrwrite(dev, uio) 4695b81b6b3SRodney W. Grimes dev_t dev; 4705b81b6b3SRodney W. Grimes struct uio *uio; 4715b81b6b3SRodney W. Grimes { 4725b81b6b3SRodney W. Grimes #ifdef DEBUG 4735b81b6b3SRodney W. Grimes printf("spkrwrite: entering with dev = %x, count = %d\n", 4745b81b6b3SRodney W. Grimes dev, uio->uio_resid); 4755b81b6b3SRodney W. Grimes #endif /* DEBUG */ 4765b81b6b3SRodney W. Grimes 4775b81b6b3SRodney W. Grimes if (minor(dev) != 0) 4785b81b6b3SRodney W. Grimes return(ENXIO); 479e614aa8aSAndrey A. Chernov else if (uio->uio_resid > DEV_BSIZE) /* prevent system crashes */ 480e614aa8aSAndrey A. Chernov return(E2BIG); 4815b81b6b3SRodney W. Grimes else 4825b81b6b3SRodney W. Grimes { 483e614aa8aSAndrey A. Chernov unsigned n; 484e614aa8aSAndrey A. Chernov char *cp; 485e614aa8aSAndrey A. Chernov int error; 486e614aa8aSAndrey A. Chernov 487e614aa8aSAndrey A. Chernov n = uio->uio_resid; 4885b81b6b3SRodney W. Grimes cp = spkr_inbuf->b_un.b_addr; 489e614aa8aSAndrey A. Chernov if (!(error = uiomove(cp, n, uio))) 4901e064bd4SAndrey A. Chernov error = playstring(cp, n); 4915b81b6b3SRodney W. Grimes return(error); 4925b81b6b3SRodney W. Grimes } 4935b81b6b3SRodney W. Grimes } 4945b81b6b3SRodney W. Grimes 4955b81b6b3SRodney W. Grimes int spkrclose(dev) 4965b81b6b3SRodney W. Grimes dev_t dev; 4975b81b6b3SRodney W. Grimes { 4985b81b6b3SRodney W. Grimes #ifdef DEBUG 499e614aa8aSAndrey A. Chernov (void) printf("spkrclose: entering with dev = %x\n", dev); 5005b81b6b3SRodney W. Grimes #endif /* DEBUG */ 5015b81b6b3SRodney W. Grimes 5025b81b6b3SRodney W. Grimes if (minor(dev) != 0) 5035b81b6b3SRodney W. Grimes return(ENXIO); 5045b81b6b3SRodney W. Grimes else 5055b81b6b3SRodney W. Grimes { 5061e064bd4SAndrey A. Chernov wakeup((caddr_t)&endtone); 5071e064bd4SAndrey A. Chernov wakeup((caddr_t)&endrest); 5085b81b6b3SRodney W. Grimes brelse(spkr_inbuf); 509e614aa8aSAndrey A. Chernov spkr_active = FALSE; 5105b81b6b3SRodney W. Grimes return(0); 5115b81b6b3SRodney W. Grimes } 512e614aa8aSAndrey A. Chernov } 5135b81b6b3SRodney W. Grimes 5145b81b6b3SRodney W. Grimes int spkrioctl(dev, cmd, cmdarg) 5155b81b6b3SRodney W. Grimes dev_t dev; 5165b81b6b3SRodney W. Grimes int cmd; 5175b81b6b3SRodney W. Grimes caddr_t cmdarg; 5185b81b6b3SRodney W. Grimes { 5195b81b6b3SRodney W. Grimes #ifdef DEBUG 520e614aa8aSAndrey A. Chernov (void) printf("spkrioctl: entering with dev = %x, cmd = %x\n"); 5215b81b6b3SRodney W. Grimes #endif /* DEBUG */ 5225b81b6b3SRodney W. Grimes 5235b81b6b3SRodney W. Grimes if (minor(dev) != 0) 5245b81b6b3SRodney W. Grimes return(ENXIO); 5255b81b6b3SRodney W. Grimes else if (cmd == SPKRTONE) 5265b81b6b3SRodney W. Grimes { 5275b81b6b3SRodney W. Grimes tone_t *tp = (tone_t *)cmdarg; 5285b81b6b3SRodney W. Grimes 5295b81b6b3SRodney W. Grimes if (tp->frequency == 0) 5301e064bd4SAndrey A. Chernov return rest(tp->duration); 5315b81b6b3SRodney W. Grimes else 5321e064bd4SAndrey A. Chernov return tone(tp->frequency, tp->duration); 5335b81b6b3SRodney W. Grimes } 5345b81b6b3SRodney W. Grimes else if (cmd == SPKRTUNE) 5355b81b6b3SRodney W. Grimes { 5365b81b6b3SRodney W. Grimes tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); 5375b81b6b3SRodney W. Grimes tone_t ttp; 5385b81b6b3SRodney W. Grimes int error; 5395b81b6b3SRodney W. Grimes 5405b81b6b3SRodney W. Grimes for (; ; tp++) { 5415b81b6b3SRodney W. Grimes error = copyin(tp, &ttp, sizeof(tone_t)); 5425b81b6b3SRodney W. Grimes if (error) 5435b81b6b3SRodney W. Grimes return(error); 5445b81b6b3SRodney W. Grimes if (ttp.duration == 0) 5455b81b6b3SRodney W. Grimes break; 5465b81b6b3SRodney W. Grimes if (ttp.frequency == 0) 5471e064bd4SAndrey A. Chernov error = rest(ttp.duration); 5485b81b6b3SRodney W. Grimes else 5491e064bd4SAndrey A. Chernov error = tone(ttp.frequency, ttp.duration); 5501e064bd4SAndrey A. Chernov if (error) 5511e064bd4SAndrey A. Chernov return(error); 5525b81b6b3SRodney W. Grimes } 5535b81b6b3SRodney W. Grimes return(0); 5545b81b6b3SRodney W. Grimes } 555e614aa8aSAndrey A. Chernov return(EINVAL); 556e614aa8aSAndrey A. Chernov } 5575b81b6b3SRodney W. Grimes 5585b81b6b3SRodney W. Grimes #endif /* NSPEAKER > 0 */ 5595b81b6b3SRodney W. Grimes /* spkr.c ends here */ 560