|
|
1.1 root 1: /*
2: * spkr.c -- device driver for console speaker on 80386
3: *
4: * v1.1 by Eric S. Raymond ([email protected]) Feb 1990
5: * modified for 386bsd by Andrew A. Chernov <[email protected]>
6: * 386bsd only clean version, all SYSV stuff removed
7: * use hz value from param.c
8: *
9: * spkr.c,v 1.5 1993/06/16 19:41:54 brezak Exp
10: */
11:
12: #include "speaker.h"
13:
14: #if NSPEAKER > 0
15:
16: #include "param.h"
17: #include "kernel.h"
18: #include "errno.h"
19: #include "buf.h"
20: #include "uio.h"
21: #include "i386/include/spkr.h"
22: #include "isa.h"
23: #include "timerreg.h"
24: #include "spkr_reg.h"
25:
26: /**************** MACHINE DEPENDENT PART STARTS HERE *************************
27: *
28: * This section defines a function tone() which causes a tone of given
29: * frequency and duration from the 80x86's console speaker.
30: * Another function endtone() is defined to force sound off, and there is
31: * also a rest() entry point to do pauses.
32: *
33: * Audible sound is generated using the Programmable Interval Timer (PIT) and
34: * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The
35: * PPI controls whether sound is passed through at all; the PIT's channel 2 is
36: * used to generate clicks (a square wave) of whatever frequency is desired.
37: */
38:
39: /*
40: * Magic numbers for timer control.
41: */
42: #define PIT_MODE (TIMER_SEL2|TIMER_MSB|TIMER_SQWAVE)
43:
44: static int endtone()
45: /* turn off the speaker, ending current tone */
46: {
47: wakeup((caddr_t)endtone);
48: outb(PITAUX_PORT, inb(PITAUX_PORT) & ~PIT_SPKR);
49: }
50:
51: static void tone(hz, ticks)
52: /* emit tone of frequency hz for given number of ticks */
53: unsigned int hz, ticks;
54: {
55: unsigned int divisor = TIMER_DIV(hz);
56: int sps;
57:
58: #ifdef DEBUG
59: printf("tone: hz=%d ticks=%d\n", hz, ticks);
60: #endif /* DEBUG */
61:
62: /* set timer to generate clicks at given frequency in Hertz */
63: sps = spltty();
64: outb(TIMER_MODE, PIT_MODE); /* prepare timer */
65: outb(TIMER_CNTR2, (unsigned char) divisor); /* send lo byte */
66: outb(TIMER_CNTR2, (divisor >> 8)); /* send hi byte */
67: splx(sps);
68:
69: /* turn the speaker on */
70: outb(PITAUX_PORT, inb(PITAUX_PORT) | PIT_SPKR);
71:
72: /*
73: * Set timeout to endtone function, then give up the timeslice.
74: * This is so other processes can execute while the tone is being
75: * emitted.
76: */
77: timeout((caddr_t)endtone, (caddr_t)NULL, ticks);
78: sleep((caddr_t)endtone, PZERO - 1);
79: }
80:
81: static int endrest()
82: /* end a rest */
83: {
84: wakeup((caddr_t)endrest);
85: }
86:
87: static void rest(ticks)
88: /* rest for given number of ticks */
89: int ticks;
90: {
91: /*
92: * Set timeout to endrest function, then give up the timeslice.
93: * This is so other processes can execute while the rest is being
94: * waited out.
95: */
96: #ifdef DEBUG
97: printf("rest: %d\n", ticks);
98: #endif /* DEBUG */
99: timeout((caddr_t)endrest, (caddr_t)NULL, ticks);
100: sleep((caddr_t)endrest, PZERO - 1);
101: }
102:
103: /**************** PLAY STRING INTERPRETER BEGINS HERE **********************
104: *
105: * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement;
106: * M[LNS] are missing and the ~ synonym and octave-tracking facility is added.
107: * Requires tone(), rest(), and endtone(). String play is not interruptible
108: * except possibly at physical block boundaries.
109: */
110:
111: typedef int bool;
112: #define TRUE 1
113: #define FALSE 0
114:
115: #define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z')))
116: #define isdigit(c) (((c) >= '0') && ((c) <= '9'))
117: #define dtoi(c) ((c) - '0')
118:
119: static int octave; /* currently selected octave */
120: static int whole; /* whole-note time at current tempo, in ticks */
121: static int value; /* whole divisor for note time, quarter note = 1 */
122: static int fill; /* controls spacing of notes */
123: static bool octtrack; /* octave-tracking on? */
124: static bool octprefix; /* override current octave-tracking state? */
125:
126: /*
127: * Magic number avoidance...
128: */
129: #define SECS_PER_MIN 60 /* seconds per minute */
130: #define WHOLE_NOTE 4 /* quarter notes per whole note */
131: #define MIN_VALUE 64 /* the most we can divide a note by */
132: #define DFLT_VALUE 4 /* default value (quarter-note) */
133: #define FILLTIME 8 /* for articulation, break note in parts */
134: #define STACCATO 6 /* 6/8 = 3/4 of note is filled */
135: #define NORMAL 7 /* 7/8ths of note interval is filled */
136: #define LEGATO 8 /* all of note interval is filled */
137: #define DFLT_OCTAVE 4 /* default octave */
138: #define MIN_TEMPO 32 /* minimum tempo */
139: #define DFLT_TEMPO 120 /* default tempo */
140: #define MAX_TEMPO 255 /* max tempo */
141: #define NUM_MULT 3 /* numerator of dot multiplier */
142: #define DENOM_MULT 2 /* denominator of dot multiplier */
143:
144: /* letter to half-tone: A B C D E F G */
145: static int notetab[8] = {9, 11, 0, 2, 4, 5, 7};
146:
147: /*
148: * This is the American Standard A440 Equal-Tempered scale with frequencies
149: * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook...
150: * our octave 0 is standard octave 2.
151: */
152: #define OCTAVE_NOTES 12 /* semitones per octave */
153: static int pitchtab[] =
154: {
155: /* C C# D D# E F F# G G# A A# B*/
156: /* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123,
157: /* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247,
158: /* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,
159: /* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988,
160: /* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975,
161: /* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
162: /* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902,
163: };
164:
165: static void playinit()
166: {
167: octave = DFLT_OCTAVE;
168: whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO;
169: fill = NORMAL;
170: value = DFLT_VALUE;
171: octtrack = FALSE;
172: octprefix = TRUE; /* act as though there was an initial O(n) */
173: }
174:
175: static void playtone(pitch, value, sustain)
176: /* play tone of proper duration for current rhythm signature */
177: int pitch, value, sustain;
178: {
179: register int sound, silence, snum = 1, sdenom = 1;
180:
181: /* this weirdness avoids floating-point arithmetic */
182: for (; sustain; sustain--)
183: {
184: snum *= NUM_MULT;
185: sdenom *= DENOM_MULT;
186: }
187:
188: if (pitch == -1)
189: rest(whole * snum / (value * sdenom));
190: else
191: {
192: sound = (whole * snum) / (value * sdenom)
193: - (whole * (FILLTIME - fill)) / (value * FILLTIME);
194: silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom);
195:
196: #ifdef DEBUG
197: printf("playtone: pitch %d for %d ticks, rest for %d ticks\n",
198: pitch, sound, silence);
199: #endif /* DEBUG */
200:
201: tone(pitchtab[pitch], sound);
202: if (fill != LEGATO)
203: rest(silence);
204: }
205: }
206:
207: static int abs(n)
208: int n;
209: {
210: if (n < 0)
211: return(-n);
212: else
213: return(n);
214: }
215:
216: static void playstring(cp, slen)
217: /* interpret and play an item from a notation string */
218: char *cp;
219: size_t slen;
220: {
221: int pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE;
222:
223: #define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \
224: {v = v * 10 + (*++cp - '0'); slen--;}
225: for (; slen--; cp++)
226: {
227: int sustain, timeval, tempo;
228: register char c = toupper(*cp);
229:
230: #ifdef DEBUG
231: printf("playstring: %c (%x)\n", c, c);
232: #endif /* DEBUG */
233:
234: switch (c)
235: {
236: case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
237:
238: /* compute pitch */
239: pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES;
240:
241: /* this may be followed by an accidental sign */
242: if (cp[1] == '#' || cp[1] == '+')
243: {
244: ++pitch;
245: ++cp;
246: slen--;
247: }
248: else if (cp[1] == '-')
249: {
250: --pitch;
251: ++cp;
252: slen--;
253: }
254:
255: /*
256: * If octave-tracking mode is on, and there has been no octave-
257: * setting prefix, find the version of the current letter note
258: * closest to the last regardless of octave.
259: */
260: if (octtrack && !octprefix)
261: {
262: if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch))
263: {
264: ++octave;
265: pitch += OCTAVE_NOTES;
266: }
267:
268: if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch))
269: {
270: --octave;
271: pitch -= OCTAVE_NOTES;
272: }
273: }
274: octprefix = FALSE;
275: lastpitch = pitch;
276:
277: /* ...which may in turn be followed by an override time value */
278: GETNUM(cp, timeval);
279: if (timeval <= 0 || timeval > MIN_VALUE)
280: timeval = value;
281:
282: /* ...and/or sustain dots */
283: for (sustain = 0; cp[1] == '.'; cp++)
284: {
285: slen--;
286: sustain++;
287: }
288:
289: /* time to emit the actual tone */
290: playtone(pitch, timeval, sustain);
291: break;
292:
293: case 'O':
294: if (cp[1] == 'N' || cp[1] == 'n')
295: {
296: octprefix = octtrack = FALSE;
297: ++cp;
298: slen--;
299: }
300: else if (cp[1] == 'L' || cp[1] == 'l')
301: {
302: octtrack = TRUE;
303: ++cp;
304: slen--;
305: }
306: else
307: {
308: GETNUM(cp, octave);
309: if (octave >= sizeof(pitchtab) / OCTAVE_NOTES)
310: octave = DFLT_OCTAVE;
311: octprefix = TRUE;
312: }
313: break;
314:
315: case '>':
316: if (octave < sizeof(pitchtab) / OCTAVE_NOTES - 1)
317: octave++;
318: octprefix = TRUE;
319: break;
320:
321: case '<':
322: if (octave > 0)
323: octave--;
324: octprefix = TRUE;
325: break;
326:
327: case 'N':
328: GETNUM(cp, pitch);
329: for (sustain = 0; cp[1] == '.'; cp++)
330: {
331: slen--;
332: sustain++;
333: }
334: playtone(pitch - 1, value, sustain);
335: break;
336:
337: case 'L':
338: GETNUM(cp, value);
339: if (value <= 0 || value > MIN_VALUE)
340: value = DFLT_VALUE;
341: break;
342:
343: case 'P':
344: case '~':
345: /* this may be followed by an override time value */
346: GETNUM(cp, timeval);
347: if (timeval <= 0 || timeval > MIN_VALUE)
348: timeval = value;
349: for (sustain = 0; cp[1] == '.'; cp++)
350: {
351: slen--;
352: sustain++;
353: }
354: playtone(-1, timeval, sustain);
355: break;
356:
357: case 'T':
358: GETNUM(cp, tempo);
359: if (tempo < MIN_TEMPO || tempo > MAX_TEMPO)
360: tempo = DFLT_TEMPO;
361: whole = (hz * SECS_PER_MIN * WHOLE_NOTE) / tempo;
362: break;
363:
364: case 'M':
365: if (cp[1] == 'N' || cp[1] == 'n')
366: {
367: fill = NORMAL;
368: ++cp;
369: slen--;
370: }
371: else if (cp[1] == 'L' || cp[1] == 'l')
372: {
373: fill = LEGATO;
374: ++cp;
375: slen--;
376: }
377: else if (cp[1] == 'S' || cp[1] == 's')
378: {
379: fill = STACCATO;
380: ++cp;
381: slen--;
382: }
383: break;
384: }
385: }
386: }
387:
388: /******************* UNIX DRIVER HOOKS BEGIN HERE **************************
389: *
390: * This section implements driver hooks to run playstring() and the tone(),
391: * endtone(), and rest() functions defined above.
392: */
393:
394: static int spkr_active; /* exclusion flag */
395: static struct buf *spkr_inbuf; /* incoming buf */
396:
397: int spkropen(dev)
398: dev_t dev;
399: {
400: #ifdef DEBUG
401: printf("spkropen: entering with dev = %x\n", dev);
402: #endif /* DEBUG */
403:
404: if (minor(dev) != 0)
405: return(ENXIO);
406: else if (spkr_active)
407: return(EBUSY);
408: else
409: {
410: playinit();
411: spkr_inbuf = geteblk(DEV_BSIZE);
412: spkr_active = 1;
413: }
414: return(0);
415: }
416:
417: int spkrwrite(dev, uio)
418: dev_t dev;
419: struct uio *uio;
420: {
421: register unsigned n;
422: char *cp;
423: int error;
424: #ifdef DEBUG
425: printf("spkrwrite: entering with dev = %x, count = %d\n",
426: dev, uio->uio_resid);
427: #endif /* DEBUG */
428:
429: if (minor(dev) != 0)
430: return(ENXIO);
431: else
432: {
433: n = MIN(DEV_BSIZE, uio->uio_resid);
434: cp = spkr_inbuf->b_un.b_addr;
435: error = uiomove(cp, n, uio);
436: if (!error)
437: playstring(cp, n);
438: return(error);
439: }
440: }
441:
442: int spkrclose(dev)
443: dev_t dev;
444: {
445: #ifdef DEBUG
446: printf("spkrclose: entering with dev = %x\n", dev);
447: #endif /* DEBUG */
448:
449: if (minor(dev) != 0)
450: return(ENXIO);
451: else
452: {
453: endtone();
454: brelse(spkr_inbuf);
455: spkr_active = 0;
456: }
457: return(0);
458: }
459:
460: int spkrioctl(dev, cmd, cmdarg)
461: dev_t dev;
462: int cmd;
463: caddr_t cmdarg;
464: {
465: #ifdef DEBUG
466: printf("spkrioctl: entering with dev = %x, cmd = %x\n", dev, cmd);
467: #endif /* DEBUG */
468:
469: if (minor(dev) != 0)
470: return(ENXIO);
471: else if (cmd == SPKRTONE)
472: {
473: tone_t *tp = (tone_t *)cmdarg;
474:
475: if (tp->frequency == 0)
476: rest(tp->duration);
477: else
478: tone(tp->frequency, tp->duration);
479: }
480: else if (cmd == SPKRTUNE)
481: {
482: tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg);
483: tone_t ttp;
484: int error;
485:
486: for (; ; tp++) {
487: error = copyin(tp, &ttp, sizeof(tone_t));
488: if (error)
489: return(error);
490: if (ttp.duration == 0)
491: break;
492: if (ttp.frequency == 0)
493: rest(ttp.duration);
494: else
495: tone(ttp.frequency, ttp.duration);
496: }
497: }
498: else
499: return(EINVAL);
500: return(0);
501: }
502:
503: #endif /* NSPEAKER > 0 */
504: /* spkr.c ends here */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.