XEmacs 21.2-b1
[chise/xemacs-chise.git.1] / src / libsst.c
1 /* libsst.c - SPARC sound tools library
2 **
3 ** Copyright (C) 1989 by Jef Poskanzer.
4 **
5 ** Permission to use, copy, modify, and distribute this software and its
6 ** documentation for any purpose and without fee is hereby granted, provided
7 ** that the above copyright notice appear in all copies and that both that
8 ** copyright notice and this permission notice appear in supporting
9 ** documentation.  This software is provided "as is" without express or
10 ** implied warranty.
11
12 ** Hacked on by jwz for emacs.
13
14 */
15
16 /* Synched up with: Not in FSF. */
17
18 #ifdef emacs
19 #include <config.h>
20 #include "lisp.h"
21 #endif
22
23 #ifdef STDC_HEADERS
24 #include <stdlib.h>
25 #endif
26
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30
31 #include <stdio.h>
32 #include <fcntl.h>
33 #include "libsst.h"
34
35 #define AUDBUF 1024
36
37 extern void usleep();
38
39 int
40 sst_open(play_level, record_level)
41     int play_level, record_level;
42     {
43     int fd, i, gr, ger, gx;
44     struct audio_ioctl ai;
45     char *getenv(), *ep;
46
47     fd = open( "/dev/audio", O_RDWR );
48     if ( fd < 0 )
49         {
50         perror( "sst_open: open /dev/audio" );
51         return( fd );
52         }
53
54 #ifdef AUDIOSETQSIZE /* This no longer exists as of 4.1.2. */
55
56     /* Shrink audio device's queue size, to cut down time delay. */
57     i = AUDBUF;
58     if ( ioctl( fd, AUDIOSETQSIZE, &i ) < 0 )
59         {
60         perror( "sst_open: SETQSIZE" );
61         return( fd );
62         }
63 #endif /* AUDIOSETQSIZE */
64
65     /* Set gains.  -10 <= ger <= 18,  -18 <= gr <= 12,  -18 <= gx <= 12. */
66     if (!play_level) 
67     {
68         play_level = 75;
69         if ( (ep = getenv( "SST_PLAY" )) != NULL )
70         {
71             play_level = atoi( ep );
72             if ( play_level < 0 || play_level > 99 )
73             {
74                 warn( "sst_open: SST_PLAY must be between 0 and 99" );
75                 return( -1 );
76             }
77         }
78     }
79     if (!record_level) 
80     {
81         record_level = 75;
82         if ( (ep = getenv( "SST_RECORD" )) != NULL )
83         {
84             record_level = atoi( ep );
85             if ( record_level < 0 || record_level > 99 )
86             {
87                 warn( "sst_open: SST_RECORD must be between 0 and 99" );
88                 return( -1 );
89             }
90         }
91     }
92
93     play_level = play_level * 59 / 100 - 28;
94     ger = play_level / 2;
95     gr = play_level - ger;
96     if ( ger < -10 )
97         {
98         ger = -10;
99         gr = play_level - ger;
100         }
101     if ( gr > 12 )
102         {
103         gr = 12;
104         ger = play_level - gr;
105         }
106     gx = record_level * 31 / 100 - 18;
107     sst_set_gr( fd, gr );
108     sst_set_ger( fd, ger );
109     sst_set_gx( fd, gx );
110
111     /*  Initialize the MMR2 register to send the output to either
112     **  the speaker or the earphone jack, depending on SST_EARPHONES.
113     */
114     ai.control = AUDIO_MAP_MMR2;
115     if ( ioctl( fd, AUDIOGETREG, &ai ) < 0 )
116         {
117         perror( "sst_open: GETREG MMR2" );
118         return( -1 );
119         }
120     if ( (ep = getenv( "SST_EARPHONES" )) != NULL )
121         ai.data[0] &= ~AUDIO_MMR2_BITS_LS;
122     else
123         ai.data[0] |= AUDIO_MMR2_BITS_LS;
124     if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
125         {
126         perror( "sst_open: SETREG MMR2" );
127         return( fd );
128         }
129
130     return fd;
131     }
132
133 void
134 sst_close( fd )
135 int fd;
136     {
137     struct audio_ioctl ai;
138
139     ai.control = AUDIO_MAP_MMR1;
140     ai.data[0] = 0;
141     if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
142         {
143         perror( "sst_close: SETREG MMR1" );
144         }
145     ai.control = AUDIO_MAP_MMR2;
146     ai.data[0] = 0;
147     if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
148         {
149         perror( "sst_close: SETREG MMR2" );
150         }
151     close( fd );
152     }
153
154 /* These are tables of values to be loaded into various gain registers.
155 */
156
157 static unsigned char ger_table[][2] = {
158     0xaa,       0xaa,   /* -10db */
159     0x79,       0xac,
160     0x41,       0x99,
161     0x9c,       0xde,
162     0x74,       0x9c,   /* -6db */
163     0x6a,       0xae,
164     0xab,       0xdf,
165     0x64,       0xab,
166     0x2a,       0xbd,
167     0x5c,       0xce,
168     0x00,       0x99,   /* 0db */
169     0x43,       0xdd,
170     0x52,       0xef,
171     0x55,       0x42,
172     0x31,       0xdd,
173     0x43,       0x1f,
174     0x40,       0xdd,   /* 6db */
175     0x44,       0x0f,
176     0x31,       0x1f,
177     0x10,       0xdd,
178     0x41,       0x0f,
179     0x60,       0x0b,
180     0x42,       0x10,   /* 12db */
181     0x11,       0x0f,
182     0x72,       0x00,
183     0x21,       0x10,
184     0x22,       0x00,
185     0x00,       0x0b,
186     0x00,       0x0f,   /* 18db */
187     };
188
189
190 static unsigned char gr_gx_table[][2] = {
191     0x8b,       0x7c,   /* -18db */
192     0x8b,       0x35,
193     0x8b,       0x24,
194     0x91,       0x23,
195     0x91,       0x2a,
196     0x91,       0x3b,
197     0x91,       0xf9,   /* -12db */
198     0x91,       0xb6,
199     0x91,       0xa4,
200     0x92,       0x32,
201     0x92,       0xaa,
202     0x93,       0xb3,
203     0x9f,       0x91,   /* -6db */
204     0x9b,       0xf9,
205     0x9a,       0x4a,
206     0xa2,       0xa2,
207     0xaa,       0xa3,
208     0xbb,       0x52,
209     0x08,       0x08,   /* 0db */
210     0x3d,       0xac,
211     0x25,       0x33,
212     0x21,       0x22,
213     0x12,       0xa2,
214     0x11,       0x3b,
215     0x10,       0xf2,   /* 6db */
216     0x02,       0xca,
217     0x01,       0x5a,
218     0x01,       0x12,
219     0x00,       0x32,
220     0x00,       0x13,
221     0x00,       0x0e,   /* 12db */
222     };
223
224 void
225 sst_set_ger( fd, value )
226 int fd, value;
227     {
228     struct audio_ioctl ai;
229
230     if ( ( value < -10 ) || ( value > 18 ) )
231         {
232           char buf [255];
233           sprintf (buf, "sst_set_ger: GER %d out of range", value);
234           warn(buf);
235           return;
236         }
237
238     /*  Add 10 to the value to get the index into the table.  */
239     ai.control = AUDIO_MAP_GER;
240     ai.data[0] = ger_table[value + 10][1];
241     ai.data[1] = ger_table[value + 10][0];
242
243     if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
244         {
245         perror( "sst_set_ger: SETREG GER" );
246         }
247
248     ai.control = AUDIO_MAP_MMR1;
249     if ( ioctl( fd, AUDIOGETREG, &ai ) < 0 )
250         {
251         perror( "sst_set_ger: GETREG MMR1" );
252         }
253     ai.data[0] |= AUDIO_MMR1_BITS_LOAD_GER;
254     if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
255         {
256         perror( "sst_set_ger: SETREG MMR1" );
257         }
258     }
259
260 void
261 sst_set_gr( fd, value )
262 int fd, value;
263     {
264     struct audio_ioctl ai;
265
266     if ( ( value < -18 ) || ( value > 12 ) )
267         {
268           char buf [255];
269           sprintf (buf,  "sst_set_gr: GR %d out of range", value);
270           warn (buf);
271           return;
272         }
273
274     ai.control = AUDIO_MAP_GR;
275     ai.data[0] = gr_gx_table[value + 18][1];
276     ai.data[1] = gr_gx_table[value + 18][0];
277
278     if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
279         {
280         perror( "sst_set_gr: SETREG GR" );
281         }
282
283     ai.control = AUDIO_MAP_MMR1;
284     if ( ioctl( fd, AUDIOGETREG, &ai ) < 0 )
285         {
286         perror( "sst_set_gr: GETREG MMR1" );
287         }
288     ai.data[0] |= AUDIO_MMR1_BITS_LOAD_GR;
289     if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
290         {
291         perror( "sst_set_gr: SETREG MMR1" );
292         }
293     }
294
295 void
296 sst_set_gx( fd, value )
297 int fd, value;
298     {
299     struct audio_ioctl ai;
300     char buf [255];
301
302     if ( ( value < -18 ) || ( value > 12 ) )
303         {
304           sprintf (buf, "sst_set_gx: GX %d out of range", value);
305           warn (buf);
306           return;
307         }
308
309     /*  We add 18 to get the index into the table, since entry 0 represents
310     *  -18db.
311     */
312     ai.control = AUDIO_MAP_GX;
313     ai.data[0] = gr_gx_table[value + 18][1];
314     ai.data[1] = gr_gx_table[value + 18][0];
315
316     if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
317         {
318         perror( "sst_set_gx: SETREG GX" );
319         }
320
321     ai.control = AUDIO_MAP_MMR1;
322     if ( ioctl( fd, AUDIOGETREG, &ai ) < 0 )
323         {
324         perror( "sst_set_gx: GETREG MMR1" );
325         }
326     ai.data[0] |= AUDIO_MMR1_BITS_LOAD_GX;
327     if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
328         {
329         perror( "sst_set_gx: SETREG MMR1" );
330         }
331     }
332
333 void
334 sst_tones( fd, dhz1, dhz2, thz, rhz, usec )
335 int fd, dhz1, dhz2, thz, rhz, usec;
336     {
337     char buf [255];
338     struct audio_ioctl ai;
339     int dval1, dval2, tval, rval;
340     unsigned char oldmmr2, newmmr2;
341
342     if ( dhz1 == 0 )
343         dval1 = 0;
344     else
345         {
346         dval1 = ( dhz1 * 128 + 63 ) / 1000;
347         if ( ( dval1 < 1 ) || ( dval1 > 255 ) )
348             {
349               sprintf(buf, "sst_tones: dhz1 %d out of range", dhz1 );
350               warn (buf);
351               return;
352             }
353         }
354
355     if ( dhz2 == 0 )
356         dval2 = 0;
357     else
358         {
359         dval2 = ( dhz2 * 128 + 63 ) / 1000;
360         if ( ( dval2 < 1 ) || ( dval2 > 255 ) )
361             {
362               sprintf(buf, "sst_tones: dhz2 %d out of range", dhz2 );
363               warn (buf);
364               return;
365             }
366         }
367
368     if ( thz == 0 )
369         tval = 0;
370     else
371         {
372         tval = ( thz * 128 + 63 ) / 2000;
373         if ( ( tval < 1 ) || ( tval > 255 ) )
374             {
375               sprintf(buf, "sst_tones: thz %d out of range", thz );
376               warn (buf);
377               return;
378             }
379         }
380
381     if ( rhz == 0 )
382         rval = 0;
383     else
384         {
385         rval = ( rhz * 128 + 63 ) / 2000;
386         if ( ( rval < 1 ) || ( rval > 255 ) )
387             {
388               sprintf(buf, "sst_tones: rhz %d out of range", dhz2 );
389               warn (buf);
390               return;
391             }
392         }
393
394     if ( ( dval1 != 0 || dval2 != 0 ) && ( tval != 0 || rval != 0 ) )
395         {
396           sprintf(buf, "sst_tones: cannot use DTMF and TONE or RINGER at the same time", dhz2 );
397           warn (buf);
398           return;
399         }
400
401     if ( tval != 0 && rval != 0 )
402         {
403           sprintf(buf, "sst_tones: cannot use TONE and RINGER at the same time", dhz2 );
404           warn (buf);
405         return;
406         }
407
408     ai.control = AUDIO_MAP_MMR2;
409     if ( ioctl( fd, AUDIOGETREG, &ai ) < 0 )
410         {
411         perror( "sst_tones: GETREG MMR2" );
412         }
413     oldmmr2 = newmmr2 = ai.data[0];
414
415     if ( dval1 != 0 || dval2 != 0 )
416         {
417         newmmr2 |= AUDIO_MMR2_BITS_DTMF;
418         ai.control = AUDIO_MAP_FTGR;
419         ai.data[0] = dval1;
420         ai.data[1] = dval2;
421         if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
422             {
423             perror( "sst_tones: SETREG FTGR" );
424             }
425         }
426
427     if ( tval != 0 )
428         {
429         newmmr2 |= AUDIO_MMR2_BITS_TONE;
430         ai.control = AUDIO_MAP_FTGR;
431         ai.data[0] = tval;
432         ai.data[1] = 0;
433         if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
434             {
435             perror( "sst_tones: SETREG FTGR" );
436             }
437         }
438
439     if ( rval != 0 )
440         {
441         newmmr2 |= AUDIO_MMR2_BITS_RINGER;
442         ai.control = AUDIO_MAP_FTGR;
443         ai.data[0] = rval;
444         ai.data[1] = 0;
445         if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
446             {
447             perror( "sst_tones: SETREG FTGR" );
448             }
449         }
450
451     ai.control = AUDIO_MAP_MMR2;
452     ai.data[0] = newmmr2;
453     if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
454         {
455         perror( "sst_tones: SETREG MMR2" );
456         }
457
458     usleep( usec );
459
460     ai.data[0] = oldmmr2;
461     if ( ioctl( fd, AUDIOSETREG, &ai ) < 0 )
462         {
463         perror( "sst_tones: SETREG MMR2" );
464         }
465     }
466
467 void
468 sst_dtmf( fd, dial, usecper, usecpause )
469 int fd, usecper, usecpause;
470 char *dial;
471     {
472     char *cp;
473
474     for ( cp = dial; *cp != '\0'; cp++ )
475         {
476         switch ( *cp )
477             {
478             case '1': sst_tones( fd, 703, 1211, 0, 0, usecper ); break;
479             case '2': sst_tones( fd, 703, 1336, 0, 0, usecper ); break;
480             case '3': sst_tones( fd, 703, 1492, 0, 0, usecper ); break;
481             case 'A': sst_tones( fd, 703, 1648, 0, 0, usecper ); break;
482             case '4': sst_tones( fd, 773, 1211, 0, 0, usecper ); break;
483             case '5': sst_tones( fd, 773, 1336, 0, 0, usecper ); break;
484             case '6': sst_tones( fd, 773, 1492, 0, 0, usecper ); break;
485             case 'B': sst_tones( fd, 773, 1648, 0, 0, usecper ); break;
486             case '7': sst_tones( fd, 859, 1211, 0, 0, usecper ); break;
487             case '8': sst_tones( fd, 859, 1336, 0, 0, usecper ); break;
488             case '9': sst_tones( fd, 859, 1492, 0, 0, usecper ); break;
489             case 'C': sst_tones( fd, 859, 1648, 0, 0, usecper ); break;
490             case '*': sst_tones( fd, 945, 1211, 0, 0, usecper ); break;
491             case '0': sst_tones( fd, 945, 1336, 0, 0, usecper ); break;
492             case '#': sst_tones( fd, 945, 1492, 0, 0, usecper ); break;
493             case 'D': sst_tones( fd, 945, 1648, 0, 0, usecper ); break;
494
495             case ' ': case '-': case '(': case ')': case '+':
496             continue;   /* ignore */
497
498             case ',': usleep( usecper ); break; /* big pause */
499
500             default:
501               {
502                 char buf [255];
503                 sprintf( buf, "sst_dtmf: unknown dialing code '%c'", *cp );
504                 warn (buf);
505               }
506             }
507         usleep( usecpause );
508         }
509     }