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