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