wmshutdown: Add icon for freedesktop.org icon themes.
[dockapps.git] / ascd / libworkman / plat_sun_audio.c
blob075edf8dc2fc4201d314d8b320a601f8c43f272e
1 /*
2 * $Id: plat_sun_audio.c,v 1.3 1999/03/07 08:36:41 dirk Exp $
4 * This file is part of WorkMan, the civilized CD player library
5 * (c) 1991-1997 by Steven Grimm (original author)
6 * (c) by Dirk Försterling (current 'author' = maintainer)
7 * The maintainer can be contacted by his e-mail address:
8 * milliByte@DeathsDoor.com
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the Free
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Sun (really Solaris) digital audio functions.
28 #include "include/wm_config.h"
29 #if defined(sun) && defined(SYSV) && defined(BUILD_CDDA)
31 static char plat_sun_audio_id[] = "$Id: plat_sun_audio.c,v 1.3 1999/03/07 08:36:41 dirk Exp $";
33 #include "include/wm_cdda.h"
35 /* types.h included by wmcdda.h */
37 #include <stdio.h>
38 #include <malloc.h>
39 #include <sys/ioctl.h>
40 #include <sys/audioio.h>
41 #include <sys/stropts.h>
42 #include <sys/time.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <signal.h>
47 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
50 * Since there's a lag time between writing audio to the audio device and
51 * hearing it, we need to make sure the status indicators correlate to what's
52 * playing out the speaker. Luckily, Solaris gives us some audio
53 * synchronization facilities that make this pretty easy.
55 * We maintain a circular queue of status information. When we write some
56 * sound to the audio device, we put its status info into the queue. We write
57 * a marker into the audio stream; when the audio device driver encounters the
58 * marker, it increments a field in a status structure. When we see that
59 * field go up, we grab the next status structure from the queue and send it
60 * to the parent process.
62 * The minimum size of the queue depends on the latency of the audio stream.
64 #define QSIZE 500
66 struct cdda_block queue[QSIZE];
67 int qtail;
68 int qstart;
71 * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed
72 * to be playing; this is used to keep track.
74 extern int playing;
76 static int aufd, aucfd;
77 static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */
80 * For fast linear-to-ulaw mapping, we use a lookup table that's generated
81 * at startup.
83 unsigned char *ulawmap, linear_to_ulaw();
85 char *getenv();
88 * Dummy signal handler so writes to /dev/audio will interrupt.
90 static void
91 dummy( void )
93 signal(SIGALRM, dummy);
97 * Initialize the audio device.
99 void
100 wmaudio_init( void )
102 audio_info_t info;
103 char *audiodev, *acdev;
104 int linval;
106 audiodev = getenv("AUDIODEV");
107 if (audiodev == NULL)
108 audiodev = "/dev/audio";
110 acdev = malloc(strlen(audiodev) + 4);
111 if (acdev == NULL)
113 perror("Can't allocate audio control filename");
114 exit(1);
116 strcpy(acdev, audiodev);
117 strcat(acdev, "ctl");
119 aucfd = open(acdev, O_WRONLY, 0);
120 if (aucfd < 0)
122 perror(acdev);
123 exit(1);
125 free(acdev);
127 aufd = open(audiodev, O_WRONLY, 0);
128 if (aufd < 0)
130 perror(audiodev);
131 exit(1);
134 signal(SIGALRM, dummy);
137 * Try to set the device to CD-style audio; we can process it
138 * with the least CPU overhead.
140 AUDIO_INITINFO(&info);
141 info.play.sample_rate = 44100;
142 info.play.channels = 2;
143 info.play.precision = 16;
144 info.play.encoding = AUDIO_ENCODING_LINEAR;
145 info.play.pause = 0;
146 info.record.pause = 0;
147 info.monitor_gain = 0;
149 if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
150 if (errno == EINVAL)
153 * Oh well, so much for that idea.
155 AUDIO_INITINFO(&info);
156 info.play.sample_rate = 8000;
157 info.play.channels = 1;
158 info.play.precision = 8;
159 info.play.encoding = AUDIO_ENCODING_ULAW;
160 info.play.pause = 0;
161 info.record.pause = 0;
162 info.monitor_gain = 0;
163 if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
165 perror("Can't set up audio device");
166 exit(1);
170 * Initialize the linear-to-ulaw mapping table.
172 if (ulawmap == NULL)
173 ulawmap = malloc(65536);
174 if (ulawmap == NULL)
176 perror("malloc");
177 exit(1);
179 for (linval = 0; linval < 65536; linval++)
180 ulawmap[linval] = linear_to_ulaw(linval-32768);
181 ulawmap += 32768;
182 raw_audio = 0;
184 else
186 perror(audiodev);
187 exit(1);
192 * Get ready to play some sound.
194 void
195 wmaudio_ready( void )
197 audio_info_t info;
200 * Start at the correct queue position.
202 if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
203 qtail = info.play.eof % QSIZE;
204 qstart = qtail;
206 queue[qtail].status = WMCDDA_OK;
210 * Stop the audio immediately.
212 void
213 wmaudio_stop( void )
215 if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
216 perror("flush");
220 * Close the audio device.
222 void
223 wmaudio_close( void )
225 wmaudio_stop();
226 close(aufd);
227 close(aucfd);
231 * Set the volume level.
233 void
234 wmaudio_volume(int level)
236 audio_info_t info;
238 AUDIO_INITINFO(&info);
239 if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
240 info.play.gain = level;
241 if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
245 * Set the balance level.
247 void
248 wmaudio_balance(int level)
250 audio_info_t info;
252 AUDIO_INITINFO(&info);
253 if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
254 level *= AUDIO_RIGHT_BALANCE;
255 info.play.balance = level / 255;
256 if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
260 * Mark the most recent audio block on the queue as the last one.
262 void
263 wmaudio_mark_last( void )
265 queue[qtail].status = WMCDDA_DONE;
269 * Figure out the most recent status information and send it upstream.
272 wmaudio_send_status( void )
274 audio_info_t info;
275 int qhead;
278 * Now send the most current status information to our parent.
280 if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
281 perror("AUDIO_GETINFO");
282 qhead = info.play.eof % QSIZE;
284 if (qhead != qstart && playing)
286 int balance;
288 if (queue[qhead].status != WMCDDA_DONE)
289 queue[qhead].status = WMCDDA_PLAYED;
290 queue[qhead].volume = info.play.gain;
291 queue[qhead].balance = (info.play.balance * 255) /
292 AUDIO_RIGHT_BALANCE;
294 send_status(queue + qhead);
295 qstart = -1;
298 return (queue[qhead].status == WMCDDA_DONE);
302 * Play some audio and pass a status message upstream, if applicable.
303 * Returns 0 on success.
306 wmaudio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
308 int i;
309 short *buf16;
310 int alarmcount = 0;
311 struct itimerval it;
313 alarm(1);
315 while (write(aufd, rawbuf, buflen) <= 0)
316 if (errno == EINTR)
318 if (! raw_audio && alarmcount++ < 5)
321 * 8KHz /dev/audio blocks for several seconds
322 * waiting for its queue to drop below a low
323 * water mark.
325 wmaudio_send_status();
326 timerclear(&it.it_interval);
327 timerclear(&it.it_value);
328 it.it_value.tv_usec = 500000;
329 setitimer(ITIMER_REAL, &it, NULL);
330 continue;
333 /* close(aufd);
334 close(aucfd);
335 wmaudio_init();
336 */ wmaudio_stop();
337 alarm(2);
338 continue;
340 else
342 blk->status = WMCDDA_ERROR;
343 return (-1);
345 alarm(0);
348 * Mark this spot in the audio stream.
350 * Marks don't always succeed (if the audio buffer is empty
351 * this call will block forever) so do it asynchronously.
353 fcntl(aufd, F_SETFL, O_NONBLOCK);
354 if (write(aufd, rawbuf, 0) < 0)
356 if (errno != EAGAIN)
357 perror("audio mark");
359 else
360 qtail = (qtail + 1) % QSIZE;
362 fcntl(aufd, F_SETFL, 0);
364 queue[qtail] = *blk;
366 if (wmaudio_send_status() < 0)
367 return (-1);
368 else
369 return (0);
373 * Get the current audio state.
375 void
376 wmaudio_state(struct cdda_block *blk)
378 audio_info_t info;
379 int balance;
381 if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
382 perror("AUDIO_GETINFO");
383 blk->volume = info.play.gain;
384 blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
388 ** This routine converts from linear to ulaw.
390 ** Craig Reese: IDA/Supercomputing Research Center
391 ** Joe Campbell: Department of Defense
392 ** 29 September 1989
394 ** References:
395 ** 1) CCITT Recommendation G.711 (very difficult to follow)
396 ** 2) "A New Digital Technique for Implementation of Any
397 ** Continuous PCM Companding Law," Villeret, Michel,
398 ** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
399 ** 1973, pg. 11.12-11.17
400 ** 3) MIL-STD-188-113,"Interoperability and Performance Standards
401 ** for Analog-to_Digital Conversion Techniques,"
402 ** 17 February 1987
404 ** Input: Signed 16 bit linear sample
405 ** Output: 8 bit ulaw sample
407 #define ZEROTRAP /* turn on the trap as per the MIL-STD */
408 #define BIAS 0x84 /* define the add-in bias for 16 bit samples */
409 #define CLIP 32635
411 unsigned char
412 linear_to_ulaw( sample )
413 int sample;
415 static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
416 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
417 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
418 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
419 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
420 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
421 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
422 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
423 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
424 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
425 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
426 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
427 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
428 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
429 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
430 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
431 int sign, exponent, mantissa;
432 unsigned char ulawbyte;
434 /* Get the sample into sign-magnitude. */
435 sign = (sample >> 8) & 0x80; /* set aside the sign */
436 if ( sign != 0 ) sample = -sample; /* get magnitude */
437 if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */
439 /* Convert from 16 bit linear to ulaw. */
440 sample = sample + BIAS;
441 exponent = exp_lut[( sample >> 7 ) & 0xFF];
442 mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
443 ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
444 #ifdef ZEROTRAP
445 if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */
446 #endif
448 return ulawbyte;
452 * Downsample a block of CDDA data, if necessary, for playing out an old-style
453 * audio device.
455 long
456 wmaudio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
458 short *buf16 = (short *)rawbuf;
459 int i, j, samples;
460 int mono_value;
461 unsigned char *rbend = rawbuf + buflen;
463 /* Don't do anything if the audio device can take the raw values. */
464 if (raw_audio)
465 return (buflen);
467 for (i = 0; buf16 < (short *)(rbend); i++)
469 /* Downsampling to 8KHz is a little irregular. */
470 samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
472 /* And unfortunately, we don't always end on a nice boundary. */
473 if (buf16 + samples > (short *)(rbend))
474 samples = ((short *)rbend) - buf16;
477 * No need to average all the values; taking the first one
478 * is sufficient and less CPU-intensive. But we do need to
479 * do both channels.
481 mono_value = (buf16[0] + buf16[1]) / 2;
482 buf16 += samples;
483 rawbuf[i] = ulawmap[mono_value];
486 return (i);
489 #endif /* ... && BUILD_CDDA */