2 * $Id: plat_linux_audio.c,v 1.3 1999/03/07 08:36:40 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 * Linux digital audio functions.
28 #include "include/wm_config.h"
30 #if defined(linux) && defined(BUILD_CDDA) /* { */
32 static char plat_linux_audio_id
[] = "$Id: plat_linux_audio.c,v 1.3 1999/03/07 08:36:40 dirk Exp $";
34 #include "include/wm_cdda.h"
36 /* types.h included by wm_cdda.h */
40 #include <sys/ioctl.h>
41 #include <sys/audioio.h>
42 #include <sys/stropts.h>
48 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
51 * Since there's a lag time between writing audio to the audio device and
52 * hearing it, we need to make sure the status indicators correlate to what's
53 * playing out the speaker. Luckily, Solaris gives us some audio
54 * synchronization facilities that make this pretty easy.
56 * We maintain a circular queue of status information. When we write some
57 * sound to the audio device, we put its status info into the queue. We write
58 * a marker into the audio stream; when the audio device driver encounters the
59 * marker, it increments a field in a status structure. When we see that
60 * field go up, we grab the next status structure from the queue and send it
61 * to the parent process.
63 * The minimum size of the queue depends on the latency of the audio stream.
67 struct cdda_block queue
[QSIZE
];
72 * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed
73 * to be playing; this is used to keep track.
77 static int aufd
, aucfd
;
78 static int raw_audio
= 1; /* Can /dev/audio take 44.1KHz stereo? */
81 * For fast linear-to-ulaw mapping, we use a lookup table that's generated
84 unsigned char *ulawmap
, linear_to_ulaw();
89 * Dummy signal handler so writes to /dev/audio will interrupt.
94 signal(SIGALRM
, dummy
);
98 * Initialize the audio device.
104 char *audiodev
, *acdev
;
107 audiodev
= getenv("AUDIODEV");
108 if (audiodev
== NULL
)
109 audiodev
= "/dev/audio";
111 acdev
= malloc(strlen(audiodev
) + 4);
114 perror("Can't allocate audio control filename");
117 strcpy(acdev
, audiodev
);
118 strcat(acdev
, "ctl");
120 aucfd
= open(acdev
, O_WRONLY
, 0);
128 aufd
= open(audiodev
, O_WRONLY
, 0);
135 signal(SIGALRM
, dummy
);
138 * Try to set the device to CD-style audio; we can process it
139 * with the least CPU overhead.
141 AUDIO_INITINFO(&info
);
142 info
.play
.sample_rate
= 44100;
143 info
.play
.channels
= 2;
144 info
.play
.precision
= 16;
145 info
.play
.encoding
= AUDIO_ENCODING_LINEAR
;
147 info
.record
.pause
= 0;
148 info
.monitor_gain
= 0;
150 if (ioctl(aufd
, AUDIO_SETINFO
, &info
) < 0)
154 * Oh well, so much for that idea.
156 AUDIO_INITINFO(&info
);
157 info
.play
.sample_rate
= 8000;
158 info
.play
.channels
= 1;
159 info
.play
.precision
= 8;
160 info
.play
.encoding
= AUDIO_ENCODING_ULAW
;
162 info
.record
.pause
= 0;
163 info
.monitor_gain
= 0;
164 if (ioctl(aufd
, AUDIO_SETINFO
, &info
) < 0)
166 perror("Can't set up audio device");
171 * Initialize the linear-to-ulaw mapping table.
174 ulawmap
= malloc(65536);
180 for (linval
= 0; linval
< 65536; linval
++)
181 ulawmap
[linval
] = linear_to_ulaw(linval
-32768);
193 * Get ready to play some sound.
196 wmaudio_ready( void )
201 * Start at the correct queue position.
203 if (ioctl(aucfd
, AUDIO_GETINFO
, &info
) < 0) perror("AUDIO_GETINFO");
204 qtail
= info
.play
.eof
% QSIZE
;
207 queue
[qtail
].status
= WMCDDA_OK
;
211 * Stop the audio immediately.
216 if (ioctl(aufd
, I_FLUSH
, FLUSHRW
) < 0)
221 * Close the audio device.
224 wmaudio_close( void )
232 * Set the volume level.
235 wmaudio_volume(int level
)
239 AUDIO_INITINFO(&info
);
240 if (ioctl(aucfd
, AUDIO_GETINFO
, &info
) < 0) perror("AUDIO_GETINFO");
241 info
.play
.gain
= level
;
242 if (ioctl(aucfd
, AUDIO_SETINFO
, &info
) < 0) perror("AUDIO_SETINFO");
246 * Set the balance level.
249 wmaudio_balance(int level
)
253 AUDIO_INITINFO(&info
);
254 if (ioctl(aucfd
, AUDIO_GETINFO
, &info
) < 0) perror("AUDIO_GETINFO");
255 level
*= AUDIO_RIGHT_BALANCE
;
256 info
.play
.balance
= level
/ 255;
257 if (ioctl(aucfd
, AUDIO_SETINFO
, &info
) < 0) perror("AUDIO_SETINFO");
261 * Mark the most recent audio block on the queue as the last one.
264 wmaudio_mark_last( void )
266 queue
[qtail
].status
= WMCDDA_DONE
;
270 * Figure out the most recent status information and send it upstream.
273 wmaudio_send_status( void )
279 * Now send the most current status information to our parent.
281 if (ioctl(aucfd
, AUDIO_GETINFO
, &info
) < 0)
282 perror("AUDIO_GETINFO");
283 qhead
= info
.play
.eof
% QSIZE
;
285 if (qhead
!= qstart
&& playing
)
289 if (queue
[qhead
].status
!= WMCDDA_DONE
)
290 queue
[qhead
].status
= WMCDDA_PLAYED
;
291 queue
[qhead
].volume
= info
.play
.gain
;
292 queue
[qhead
].balance
= (info
.play
.balance
* 255) /
295 send_status(queue
+ qhead
);
299 return (queue
[qhead
].status
== WMCDDA_DONE
);
303 * Play some audio and pass a status message upstream, if applicable.
304 * Returns 0 on success.
307 wmaudio_play(unsigned char *rawbuf
, long buflen
, struct cdda_block
*blk
)
316 while (write(aufd
, rawbuf
, buflen
) <= 0)
319 if (! raw_audio
&& alarmcount
++ < 5)
322 * 8KHz /dev/audio blocks for several seconds
323 * waiting for its queue to drop below a low
326 wmaudio_send_status();
327 timerclear(&it
.it_interval
);
328 timerclear(&it
.it_value
);
329 it
.it_value
.tv_usec
= 500000;
330 setitimer(ITIMER_REAL
, &it
, NULL
);
337 */ wmaudio_stop( void );
343 blk
->status
= WMCDDA_ERROR
;
349 * Mark this spot in the audio stream.
351 * Marks don't always succeed (if the audio buffer is empty
352 * this call will block forever) so do it asynchronously.
354 fcntl(aufd
, F_SETFL
, O_NONBLOCK
);
355 if (write(aufd
, rawbuf
, 0) < 0)
358 perror("audio mark");
361 qtail
= (qtail
+ 1) % QSIZE
;
363 fcntl(aufd
, F_SETFL
, 0);
367 if (wmaudio_send_status() < 0)
374 * Get the current audio state.
377 wmaudio_state(struct cdda_block
*blk
)
382 if (ioctl(aucfd
, AUDIO_GETINFO
, &info
) < 0)
383 perror("AUDIO_GETINFO");
384 blk
->volume
= info
.play
.gain
;
385 blk
->balance
= (info
.play
.balance
* 255) / AUDIO_RIGHT_BALANCE
;
389 ** This routine converts from linear to ulaw.
391 ** Craig Reese: IDA/Supercomputing Research Center
392 ** Joe Campbell: Department of Defense
396 ** 1) CCITT Recommendation G.711 (very difficult to follow)
397 ** 2) "A New Digital Technique for Implementation of Any
398 ** Continuous PCM Companding Law," Villeret, Michel,
399 ** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
400 ** 1973, pg. 11.12-11.17
401 ** 3) MIL-STD-188-113,"Interoperability and Performance Standards
402 ** for Analog-to_Digital Conversion Techniques,"
405 ** Input: Signed 16 bit linear sample
406 ** Output: 8 bit ulaw sample
408 #define ZEROTRAP /* turn on the trap as per the MIL-STD */
409 #define BIAS 0x84 /* define the add-in bias for 16 bit samples */
413 linear_to_ulaw( sample
)
416 static int exp_lut
[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
417 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
418 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
419 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
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 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
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 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
432 int sign
, exponent
, mantissa
;
433 unsigned char ulawbyte
;
435 /* Get the sample into sign-magnitude. */
436 sign
= (sample
>> 8) & 0x80; /* set aside the sign */
437 if ( sign
!= 0 ) sample
= -sample
; /* get magnitude */
438 if ( sample
> CLIP
) sample
= CLIP
; /* clip the magnitude */
440 /* Convert from 16 bit linear to ulaw. */
441 sample
= sample
+ BIAS
;
442 exponent
= exp_lut
[( sample
>> 7 ) & 0xFF];
443 mantissa
= ( sample
>> ( exponent
+ 3 ) ) & 0x0F;
444 ulawbyte
= ~ ( sign
| ( exponent
<< 4 ) | mantissa
);
446 if ( ulawbyte
== 0 ) ulawbyte
= 0x02; /* optional CCITT trap */
453 * Downsample a block of CDDA data, if necessary, for playing out an old-style
457 wmaudio_convert(unsigned char *rawbuf
, long buflen
, struct cdda_block
*blk
)
459 short *buf16
= (short *)rawbuf
;
462 unsigned char *rbend
= rawbuf
+ buflen
;
464 /* Don't do anything if the audio device can take the raw values. */
468 for (i
= 0; buf16
< (short *)(rbend
); i
++)
470 /* Downsampling to 8KHz is a little irregular. */
471 samples
= (i
& 1) ? ((i
% 20) ? 10 : 12) : 12;
473 /* And unfortunately, we don't always end on a nice boundary. */
474 if (buf16
+ samples
> (short *)(rbend
))
475 samples
= ((short *)rbend
) - buf16
;
478 * No need to average all the values; taking the first one
479 * is sufficient and less CPU-intensive. But we do need to
482 mono_value
= (buf16
[0] + buf16
[1]) / 2;
484 rawbuf
[i
] = ulawmap
[mono_value
];
490 #endif /* ... && BUILD_CDDA */