1 /* This file is part of the KDE project
2
3 Copyright (C) 1991-1997 by Steven Grimm <koreth@midwinter.com>
4 Copyright (C) by Dirk Försterling <milliByte@DeathsDoor.com>
5 Copyright (C) 2006 Alexander Kern <alex.kern@gmx.de>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License version 2 as published by the Free Software Foundation.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20
21 Sun (really Solaris) digital audio functions.
22 */
23
24 #if defined(sun) || defined(__sun__)
25
26 #include "audio.h"
27
28 #include <stdio.h>
29 #include <malloc.h>
30 #include <sys/ioctl.h>
31 #include <sys/audioio.h>
32 #include <sys/stropts.h>
33 #include <sys/time.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <signal.h>
37
38 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
39
40 /*
41 * Since there's a lag time between writing audio to the audio device and
42 * hearing it, we need to make sure the status indicators correlate to what's
43 * playing out the speaker. Luckily, Solaris gives us some audio
44 * synchronization facilities that make this pretty easy.
45 *
46 * We maintain a circular queue of status information. When we write some
47 * sound to the audio device, we put its status info into the queue. We write
48 * a marker into the audio stream; when the audio device driver encounters the
49 * marker, it increments a field in a status structure. When we see that
50 * field go up, we grab the next status structure from the queue and send it
51 * to the parent process.
52 *
53 * The minimum size of the queue depends on the latency of the audio stream.
54 */
55 #define QSIZE 500
56
57 struct cdda_block queue[QSIZE];
58 int qtail;
59 int qstart;
60
61 /*
62 * We only send WM_CDM_PLAYING status messages upstream when the CD is supposed
63 * to be playing; this is used to keep track.
64 */
65 extern int playing;
66
67 static int aufd, aucfd;
68 static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */
69
70 /*
71 * For fast linear-to-ulaw mapping, we use a lookup table that's generated
72 * at startup.
73 */
74 unsigned char *ulawmap, linear_to_ulaw();
75
76 char *getenv();
77
78 /*
79 * Dummy signal handler so writes to /dev/audio will interrupt.
80 */
81 static void
dummy(void)82 dummy( void )
83 {
84 signal(SIGALRM, dummy);
85 }
86
87 /*
88 * Initialize the audio device.
89 */
90 void
sun_audio_init(void)91 sun_audio_init( void )
92 {
93 audio_info_t info;
94 char *audiodev, *acdev;
95 int linval;
96
97 audiodev = getenv("AUDIODEV");
98 if (audiodev == NULL ||
99 strncmp("/dev/", audiodev, 5) ||
100 strstr(audiodev, "/../") )
101 audiodev = "/dev/audio";
102
103 acdev = malloc(strlen(audiodev) + 4);
104 if (acdev == NULL)
105 {
106 perror("Cannot allocate audio control filename");
107 exit(1);
108 }
109 strcpy(acdev, audiodev);
110 strcat(acdev, "ctl");
111
112 aucfd = open(acdev, O_WRONLY, 0);
113 if (aucfd < 0)
114 {
115 perror(acdev);
116 exit(1);
117 }
118 free(acdev);
119
120 aufd = open(audiodev, O_WRONLY, 0);
121 if (aufd < 0)
122 {
123 perror(audiodev);
124 exit(1);
125 }
126
127 signal(SIGALRM, dummy);
128
129 /*
130 * Try to set the device to CD-style audio; we can process it
131 * with the least CPU overhead.
132 */
133 AUDIO_INITINFO(&info);
134 info.play.sample_rate = 44100;
135 info.play.channels = 2;
136 info.play.precision = 16;
137 info.play.encoding = AUDIO_ENCODING_LINEAR;
138 info.play.pause = 0;
139 info.record.pause = 0;
140 info.monitor_gain = 0;
141
142 if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
143 if (errno == EINVAL)
144 {
145 /*
146 * Oh well, so much for that idea.
147 */
148 AUDIO_INITINFO(&info);
149 info.play.sample_rate = 8000;
150 info.play.channels = 1;
151 info.play.precision = 8;
152 info.play.encoding = AUDIO_ENCODING_ULAW;
153 info.play.pause = 0;
154 info.record.pause = 0;
155 info.monitor_gain = 0;
156 if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
157 {
158 perror("Can't set up audio device");
159 exit(1);
160 }
161
162 /*
163 * Initialize the linear-to-ulaw mapping table.
164 */
165 if (ulawmap == NULL)
166 ulawmap = malloc(65536);
167 if (ulawmap == NULL)
168 {
169 perror("malloc");
170 exit(1);
171 }
172 for (linval = 0; linval < 65536; linval++)
173 ulawmap[linval] = linear_to_ulaw(linval-32768);
174 ulawmap += 32768;
175 raw_audio = 0;
176 }
177 else
178 {
179 perror(audiodev);
180 exit(1);
181 }
182 }
183
184 /*
185 * Get ready to play some sound.
186 */
187 void
sun_audio_ready(void)188 sun_audio_ready( void )
189 {
190 audio_info_t info;
191
192 /*
193 * Start at the correct queue position.
194 */
195 if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
196 qtail = info.play.eof % QSIZE;
197 qstart = qtail;
198
199 queue[qtail].status = WM_CDM_PLAYING;
200 }
201
202 /*
203 * Stop the audio immediately.
204 */
205 int
sun_audio_stop(void)206 sun_audio_stop( void )
207 {
208 if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
209 perror("flush");
210 return 0;
211 }
212
213 /*
214 * Close the audio device.
215 */
216 int
sun_audio_close(void)217 sun_audio_close( void )
218 {
219 wmaudio_stop();
220 close(aufd);
221 close(aucfd);
222 return 0;
223 }
224
225 /*
226 * Set/get the balance and volume level.
227 */
228 int
sun_audio_balvol(int setget,unsigned char * balance,unsigned char * volume)229 sun_audio_balvol(int setget, unsigned char *balance, unsigned char *volume)
230 {
231 audio_info_t info;
232 AUDIO_INITINFO(&info);
233 if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) {
234 perror("AUDIO_GETINFO");
235 return -1;
236 }
237
238 if(setget) {
239 balance *= AUDIO_RIGHT_BALANCE;
240 info.play.balance = *balance / 255;
241 info.play.gain = *volume;
242 if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) {
243 perror("AUDIO_SETINFO");
244 return -1;
245 }
246 } else {
247 *volume = info.play.gain;
248 *balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
249 }
250 return 0;
251 }
252
253 /*
254 * Mark the most recent audio block on the queue as the last one.
255 */
256 void
sun_audio_mark_last(void)257 sun_audio_mark_last( void )
258 {
259 queue[qtail].status = WM_CDM_TRACK_DONE;
260 }
261
262 /*
263 * Figure out the most recent status information and send it upstream.
264 */
265 int
sun_audio_send_status(void)266 sun_audio_send_status( void )
267 {
268 audio_info_t info;
269 int qhead;
270
271 /*
272 * Now send the most current status information to our parent.
273 */
274 if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
275 perror("AUDIO_GETINFO");
276 qhead = info.play.eof % QSIZE;
277
278 if (qhead != qstart && playing)
279 {
280 int balance;
281
282 if (queue[qhead].status != WM_CDM_TRACK_DONE)
283 queue[qhead].status = WM_CDM_PLAYING;
284 queue[qhead].volume = info.play.gain;
285 queue[qhead].balance = (info.play.balance * 255) /
286 AUDIO_RIGHT_BALANCE;
287
288 send_status(queue + qhead);
289 qstart = -1;
290 }
291
292 return (queue[qhead].status == WM_CDM_TRACK_DONE);
293 }
294
295 /*
296 * Play some audio and pass a status message upstream, if applicable.
297 * Returns 0 on success.
298 */
299 int
sun_audio_play(unsigned char * rawbuf,long buflen,struct cdda_block * blk)300 sun_audio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
301 {
302 int i;
303 short *buf16;
304 int alarmcount = 0;
305 struct itimerval it;
306 long playablelen;
307
308 alarm(1);
309 playablelen = dev_audio_convert(rawbuf, buflen, blk);
310 while (write(aufd, rawbuf, playablelen) <= 0)
311 if (errno == EINTR)
312 {
313 if (! raw_audio && alarmcount++ < 5)
314 {
315 /*
316 * 8KHz /dev/audio blocks for several seconds
317 * waiting for its queue to drop below a low
318 * water mark.
319 */
320 wmaudio_send_status();
321 timerclear(&it.it_interval);
322 timerclear(&it.it_value);
323 it.it_value.tv_usec = 500000;
324 setitimer(ITIMER_REAL, &it, NULL);
325 continue;
326 }
327
328 /* close(aufd);
329 close(aucfd);
330 wmaudio_init();
331 */ sun_audio_stop();
332 alarm(2);
333 continue;
334 }
335 else
336 {
337 blk->status = WM_CDM_CDDAERROR;
338 return (-1);
339 }
340 alarm(0);
341
342 /*
343 * Mark this spot in the audio stream.
344 *
345 * Marks don't always succeed (if the audio buffer is empty
346 * this call will block forever) so do it asynchronously.
347 */
348 fcntl(aufd, F_SETFL, O_NONBLOCK);
349 if (write(aufd, rawbuf, 0) < 0)
350 {
351 if (errno != EAGAIN)
352 perror("audio mark");
353 }
354 else
355 qtail = (qtail + 1) % QSIZE;
356
357 fcntl(aufd, F_SETFL, 0);
358
359 queue[qtail] = *blk;
360
361 if (wmaudio_send_status() < 0)
362 return (-1);
363 else
364 return (0);
365 }
366
367 /*
368 ** This routine converts from linear to ulaw.
369 **
370 ** Craig Reese: IDA/Supercomputing Research Center
371 ** Joe Campbell: Department of Defense
372 ** 29 September 1989
373 **
374 ** References:
375 ** 1) CCITT Recommendation G.711 (very difficult to follow)
376 ** 2) "A New Digital Technique for Implementation of Any
377 ** Continuous PCM Companding Law," Villeret, Michel,
378 ** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
379 ** 1973, pg. 11.12-11.17
380 ** 3) MIL-STD-188-113,"Interoperability and Performance Standards
381 ** for Analog-to_Digital Conversion Techniques,"
382 ** 17 February 1987
383 **
384 ** Input: Signed 16 bit linear sample
385 ** Output: 8 bit ulaw sample
386 */
387 #define ZEROTRAP /* turn on the trap as per the MIL-STD */
388 #define BIAS 0x84 /* define the add-in bias for 16 bit samples */
389 #define CLIP 32635
390
391 unsigned char
linear_to_ulaw(sample)392 linear_to_ulaw( sample )
393 int sample;
394 {
395 static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
396 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
397 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
398 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
399 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
400 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
401 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
402 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
403 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
404 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
405 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
406 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
407 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
408 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
409 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
410 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
411 int sign, exponent, mantissa;
412 unsigned char ulawbyte;
413
414 /* Get the sample into sign-magnitude. */
415 sign = (sample >> 8) & 0x80; /* set aside the sign */
416 if ( sign != 0 ) sample = -sample; /* get magnitude */
417 if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
418
419 /* Convert from 16 bit linear to ulaw. */
420 sample = sample + BIAS;
421 exponent = exp_lut[( sample >> 7 ) & 0xFF];
422 mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
423 ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
424 #ifdef ZEROTRAP
425 if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
426 #endif
427
428 return ulawbyte;
429 }
430
431 /*
432 * Downsample a block of CDDA data, if necessary, for playing out an old-style
433 * audio device.
434 */
435 long
dev_audio_convert(unsigned char * rawbuf,long buflen,struct cdda_block * blk)436 dev_audio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
437 {
438 short *buf16 = (short *)rawbuf;
439 int i, j, samples;
440 int mono_value;
441 unsigned char *rbend = rawbuf + buflen;
442
443 /* Don't do anything if the audio device can take the raw values. */
444 if (raw_audio)
445 return (buflen);
446
447 for (i = 0; buf16 < (short *)(rbend); i++)
448 {
449 /* Downsampling to 8KHz is a little irregular. */
450 samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
451
452 /* And unfortunately, we don't always end on a nice boundary. */
453 if (buf16 + samples > (short *)(rbend))
454 samples = ((short *)rbend) - buf16;
455
456 /*
457 * No need to average all the values; taking the first one
458 * is sufficient and less CPU-intensive. But we do need to
459 * do both channels.
460 */
461 mono_value = (buf16[0] + buf16[1]) / 2;
462 buf16 += samples;
463 rawbuf[i] = ulawmap[mono_value];
464 }
465
466 return (i);
467 }
468
469 static struct audio_oops sun_audio_oops = {
470 .wmaudio_open = sun_audio_open,
471 .wmaudio_close = sun_audio_close,
472 .wmaudio_play = sun_audio_play,
473 .wmaudio_stop = sun_audio_stop,
474 .wmaudio_state = NULL,
475 .wmaudio_balvol = sun_audio_balvol
476 };
477
478 struct audio_oops*
setup_sun_audio(const char * dev,const char * ctl)479 setup_sun_audio(const char *dev, const char *ctl)
480 {
481 int err;
482
483 if((err = sun_audio_init())) {
484 ERRORLOG("cannot initialize SUN /dev/audio subsystem \n");
485 return NULL;
486 }
487
488 sun_audio_open();
489
490 return &sun_audio_oops;
491 }
492
493 #endif
494