1 /*
2 ** Vsound - a virtual audio loopback cable used for recording /dev/dsp streams
3 ** Copyright (C) 2004 Nathan Chantrell <nsc@zorg.org>
4 ** Copyright (C) 2003 Richard Taylor <r.taylor@bcs.org.uk>
5 ** Copyright (C) 2000,2001 Erik de Castro Lopo <erikd@zip.com.au>
6 ** Copyright (C) 1999 James Henstridge <james@daa.com.au>
7 **
8 **   Based on the esddsp utility that is part of esound.  Esddsp's copyright:
9 **   Copyright (C) 1998, 1999 Manish Singh <yosh@gimp.org>
10 **
11 ** This library is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU Library General Public
13 ** License as published by the Free Software Foundation; either
14 ** version 2 of the License, or (at your option) any later version.
15 **
16 ** This library is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 ** Library General Public License for more details.
20 **
21 ** You should have received a copy of the GNU Library General Public
22 ** License along with this library; if not, write to the
23 ** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ** Boston, MA 02111-1307, USA.
25 */
26 
27 #include <stdarg.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <sys/types.h>
33 #include <sys/time.h>
34 #include <sys/stat.h>
35 #include <stdio.h>
36 #include <signal.h>
37 
38 #ifdef ENABLE_DEBUG
39 	#include <errno.h>
40 	#define DPRINTF(format, args...)	fprintf(stderr, format, ## args)
41 #else
42 	#define DPRINTF(format, args...)
43 #endif
44 
45 #ifdef HAVE_CONFIG_H
46 #include "config.h"
47 #endif
48 
49 #ifdef HAVE_MACHINE_SOUNDCARD_H
50 	#include <machine/soundcard.h>
51 #else
52 	#ifdef HAVE_SOUNDCARD_H
53 		#include <soundcard.h>
54 	#else
55 		#include <sys/soundcard.h>
56 	#endif
57 #endif
58 
59 /* It seems that Debian Woody (and possibly others) do not define RTLD_NEXT. */
60 #include <dlfcn.h>
61 #ifndef RTLD_NEXT
62 	#define RTLD_NEXT  ((void *) -1l)
63 #endif
64 
65 #define REAL_LIBC RTLD_NEXT
66 
67 #ifdef __FreeBSD__
68 typedef unsigned long request_t;
69 #else
70 typedef int request_t;
71 #endif
72 
73 /*------------------------------------------------------------------------------
74 ** Macros to handle big/little endian issues.
75 */
76 #ifdef HAVE_ENDIAN_H
77 	/* This is the best way to do it. Unfortunately Sparc Solaris (and
78 	** possibly others) don't have <endian.h>
79 	*/
80 	#include	<sys/endian.h>
81 	#if (__BYTE_ORDER == __LITTLE_ENDIAN)
82 		#define	CPU_IS_LITTLE_ENDIAN		1
83 		#define	CPU_IS_BIG_ENDIAN			0
84 	#elif (__BYTE_ORDER == __BIG_ENDIAN)
85 		#define	CPU_IS_LITTLE_ENDIAN		0
86 		#define	CPU_IS_BIG_ENDIAN			1
87 	#else
88 		#error "A bit confused about endian-ness! Have <endian.h> but not __BYTEORDER."
89 	#endif
90 #else
91 	/* If we don't have <endian.h> use the guess based on target processor
92 	** from the autoconf process.
93 	*/
94 	#if GUESS_LITTLE_ENDIAN
95 		#define	CPU_IS_LITTLE_ENDIAN		1
96 		#define	CPU_IS_BIG_ENDIAN			0
97 	#elif GUESS_BIG_ENDIAN
98 		#define	CPU_IS_LITTLE_ENDIAN		0
99 		#define	CPU_IS_BIG_ENDIAN			1
100 	#else
101 		#error "Endian guess seems incorrect."
102 	#endif
103 #endif
104 
105 #define	ENDSWAP_SHORT(x)	((((x)>>8)&0xFF)|(((x)&0xFF)<<8))
106 #define	ENDSWAP_INT(x)		((((x)>>24)&0xFF)|(((x)>>8)&0xFF00)|(((x)&0xFF00)<<8)|(((x)&0xFF)<<24))
107 
108 #if (CPU_IS_LITTLE_ENDIAN == 1)
109 	#define	MAKE_MARKER(a,b,c,d)		((a)|((b)<<8)|((c)<<16)|((d)<<24))
110 
111 	#define	H2LE_SHORT(x)				(x)
112 	#define	H2LE_INT(x)					(x)
113 	#define	H2BE_SHORT(x)				ENDSWAP_SHORT(x)
114 	#define	H2BE_INT(x)					ENDSWAP_INT(x)
115 
116 #elif (CPU_IS_BIG_ENDIAN == 1)
117 	#define	MAKE_MARKER(a,b,c,d)		(((a)<<24)|((b)<<16)|((c)<<8)|(d))
118 
119 	#define	H2LE_SHORT(x)				ENDSWAP_SHORT(x)
120 	#define	H2LE_INT(x)					ENDSWAP_INT(x)
121 	#define	H2BE_SHORT(x)				(x)
122 	#define	H2BE_INT(x)					(x)
123 
124 #else
125 	#error "Cannot determine endian-ness of processor."
126 #endif
127 
128 #define DOTSND_MARKER	(MAKE_MARKER ('.', 's', 'n', 'd'))
129 #define DNSDOT_MARKER	(MAKE_MARKER ('d', 'n', 's', '.'))
130 
131 
132 
133 
134 /*----------------------------------------------------------------------------
135 ** Typedefs and enums.
136 */
137 
138 typedef struct
139 {	int		magic ;
140 	int		dataoffset ;
141 	int		datasize ;
142 	int		encoding ;
143 	int		samplerate ;
144 	int		channels ;
145 } AU_HEADER ;
146 
147 enum
148 {	AU_ENCODING_ULAW_8	= 1,    /* 8-bit u-law samples */
149 	AU_ENCODING_PCM_8	= 2,    /* 8-bit linear samples */
150 	AU_ENCODING_PCM_16	= 3,    /* 16-bit linear samples */
151 	AU_ENCODING_ALAW_8	= 27,   /* 8-bit A-law samples */
152 } ;
153 
154 /*----------------------------------------------------------------------------
155 ** Function Prototypes.
156 */
157 
158 static void dsp_init (void) ;
159 static int  dspctl (request_t request, void *argp) ;
160 static void fix_header (AU_HEADER *au_header) ;
161 static void endswap_short_array	(short* buffer, int count) ;
162 static int  au_bytes_per_sample (AU_HEADER *au_header) ;
163 static u_long usec_diff_timeval (struct timeval *start, struct timeval *end) ;
164 static void start_autostop_timer(void);
165 static void stop_autostop_timer(void);
166 
167 /*----------------------------------------------------------------------------
168 ** Static Data.
169 */
170 
171 static int filefd = -1, dspfd = -1 ;
172 static int enable_dspout = 0, enable_stdout = 0, enable_timing = 0 ;
173 static int devdsp_format = 0, done_header = 0 ;
174 static int stopdelay = 0, ignore_autostop = 1;
175 
176 static char *datafile = NULL ;
177 
178 static AU_HEADER au_header =
179 {	DNSDOT_MARKER,			/* magic */
180 	sizeof (AU_HEADER),		/* dataoffset */
181 	0xFFFFFFFF,				/* datasize - unknown length. */
182 	AU_ENCODING_PCM_16,		/* encoding */
183     44100,			 		/* samplerate */
184 	2						/* channels */
185 } ;
186 
187 static struct {
188 	u_long 			max_samples ;
189 	struct timeval	start_time ;
190 	struct timeval	current_time ;
191 	u_long			bytes_per_sample ;
192 	u_long			samples_written ;
193 	u_long			sample_rate ;
194 } virtual_device ;
195 
196 /*============================================================================
197 ** Public Functions.
198 */
199 
open(const char * pathname,int flags,...)200 int open (const char *pathname, int flags, ...)
201 {	static int (*func_open) (const char *, int, mode_t) = NULL;
202 	va_list args;
203 	mode_t mode;
204 	int fd ;
205 
206 
207 	if (!func_open)
208 		func_open = (int (*) (const char *, int, mode_t)) dlsym (REAL_LIBC, "open") ;
209 
210 	dsp_init () ;
211 
212 	va_start (args, flags) ;
213 	mode = va_arg (args, int) ;
214 	va_end (args) ;
215 
216 	if (strcmp (pathname, "/dev/dsp"))
217 	{	fd = func_open (pathname, flags, mode) ;
218 	        /*DPRINTF ("Not /dev/dsp. open (%s) = %d\n", pathname, fd) ;*/
219 		return fd ;
220 		} ;
221 
222 
223 	stop_autostop_timer();
224 
225 	/* Opening /dev/dsp device so reset done_header. */
226 	done_header = 0 ;
227 	au_header.datasize = 0xFFFFFFFF ;
228 	/* Set time to zero. */
229 	memset (&virtual_device, 0, sizeof (virtual_device)) ;
230 
231 	if (enable_dspout && enable_stdout)
232 	{	filefd = 1 ;
233 		dspfd  = func_open (pathname, flags, mode) ;
234 		}
235 	else if (enable_dspout && ! enable_stdout)
236 	{	filefd = func_open (datafile, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR) ;
237 		dspfd  = func_open (pathname, flags, mode) ;
238 		}
239 	else if (! enable_stdout)
240 		filefd = dspfd  = func_open (datafile, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR) ;
241 	else
242 		filefd = dspfd = 1 ;
243 
244 	DPRINTF ("open (%s) = (filefd: %d, dspfd = %d)\n", pathname, filefd, dspfd) ;
245 
246 	return dspfd ;
247 } /* open */
248 
ioctl(int fd,request_t request,...)249 int	ioctl (int fd, request_t request, ...)
250 {	static int (*func_ioctl) (int, request_t, void *) = NULL;
251 	va_list args;
252 	void *argp;
253 
254 	if (! func_ioctl)
255 		func_ioctl = (int (*) (int, request_t, void *)) dlsym (REAL_LIBC, "ioctl") ;
256 	va_start (args, request) ;
257 	argp = va_arg (args, void *) ;
258 	va_end (args) ;
259 
260 	if (fd != dspfd)
261 		return func_ioctl (fd, request, argp) ;
262 
263 	/* Capture the ioctl() calls to /dev/dsp. */
264 	dspctl (request, argp) ;
265 
266 	if (enable_dspout)	/* Call the real ioctl() on the/dev/dsp descriptor. */
267 		return func_ioctl (dspfd, request, argp) ;
268 
269 	/* The RealPlayer calls this ioctl() and expects sensible results
270 	** back. So, feed it some bullshit it will believe :-).
271 	*/
272 	if (request == SNDCTL_DSP_GETOSPACE)
273 	{	audio_buf_info *info ;
274 
275 		info = (audio_buf_info*) argp ;
276 		info->fragments  = 32 ;
277 		info->fragstotal = 32 ;
278 		info->fragsize   = 4096 ;
279 		info->bytes      = 131072 ;
280 		} ;
281 
282 	return 0 ;
283 }	/* ioctl */
284 
close(int fd)285 int close (int fd)
286 {	static int (*func_close) (int) = NULL;
287 	int retval = 0 ;
288 
289 
290 	DPRINTF ("close (%d)\n", fd) ;
291 
292 	if (! func_close)
293 		func_close = (int (*) (int)) dlsym (REAL_LIBC, "close") ;
294 
295 	/* If data is going to stdout and the header hasn't been
296 	** written yet, don't close the descriptor.
297 	*/
298 	if (fd == 1 && filefd == 1 && ! done_header)
299 		return 0 ;
300 
301 	retval = func_close (fd) ;
302 
303 	if (fd == dspfd)
304 	{	dspfd = -1;
305 		filefd = -1;
306 		/* Closing /dev/dsp so reset done_header. */
307 		done_header = 0 ;
308 		au_header.datasize = 0xFFFFFFFF ;
309 		start_autostop_timer();
310 		} ;
311 
312 
313 	return retval ;
314 }	/* close */
315 
write(int fd,const void * buf,size_t count)316 ssize_t write (int fd, const void *buf, size_t count)
317 {	static ssize_t (*func_write) (int, const void*, size_t) = NULL ;
318 	ssize_t  retval = 0 ;
319 
320 
321 	if (! func_write)
322 		func_write = (ssize_t (*) (int, const void*, size_t)) dlsym (REAL_LIBC, "write") ;
323 
324 	if (fd != dspfd)
325 		return func_write (fd, buf, count) ;
326 
327 	if (! done_header)
328 	{	fix_header (&au_header) ; /* Byte swap header if required. */
329 		if (enable_dspout)
330 		{	DPRINTF ("Writing AU header to fd = %d.\n", filefd) ;
331 			func_write (filefd, &au_header, sizeof (au_header)) ;
332 			}
333 		else
334 		{	DPRINTF ("Writing AU header to fd = %d.\n", dspfd) ;
335 			func_write (dspfd, &au_header, sizeof (au_header)) ;
336 			} ;
337 		fix_header (&au_header) ; /* Reverse effects of earlier byte swap. */
338 		done_header = 1 ;
339 		}
340 
341 	if (! virtual_device.start_time.tv_sec)
342 	{	gettimeofday (& (virtual_device.start_time), NULL) ;
343 		virtual_device.bytes_per_sample = au_bytes_per_sample (&au_header) ;
344 		virtual_device.sample_rate = au_header.samplerate ;
345 
346 		/* Number of bytes was stored previously in an ioctl () call. */
347 		virtual_device.max_samples /= virtual_device.bytes_per_sample ;
348 		} ;
349 
350 	if (enable_dspout)
351 		func_write (dspfd, buf, count) ;
352 
353 	if (CPU_IS_LITTLE_ENDIAN)
354 		endswap_short_array ((short*) buf, count / sizeof (short)) ;
355 
356 	retval = func_write (filefd, buf, count) ;
357 
358 	if (retval != count)
359 		DPRINTF ("write (%d) returns %d\n", filefd, retval) ;
360 
361 	/* Count bytes written to the file so that timing can be calculated. */
362 	virtual_device.samples_written += retval / virtual_device.bytes_per_sample ;
363 
364 	gettimeofday (& (virtual_device.current_time), NULL) ;
365 
366 	if (enable_timing)
367 	{	u_long diff_time = usec_diff_timeval (&virtual_device.start_time, &virtual_device.current_time) ;
368 		u_long usec_sleep = (1000000.0 * virtual_device.samples_written) / virtual_device.sample_rate - diff_time ;
369 
370 
371 
372 		DPRINTF ("time = %lu\n", diff_time / 1000000) ;
373 
374 		if (usec_sleep > 0 && usec_sleep < 1000000)
375 			usleep (usec_sleep) ;
376 		} ;
377 
378 
379 	return retval ;
380 } 	/* write */
381 
382 /*------------------------------------------------------------------------------
383 ** Static functions.
384 */
385 
386 static
fix_header(AU_HEADER * pheader)387 void fix_header (AU_HEADER *pheader)
388 {	if (CPU_IS_LITTLE_ENDIAN)
389 	{	pheader->magic      = DOTSND_MARKER ;
390 		pheader->dataoffset	= H2BE_INT (sizeof (AU_HEADER)) ;
391 		pheader->datasize	= 0xFFFFFFFF ; /* Unknown length. */
392 		pheader->encoding	= H2BE_INT (pheader->encoding) ;
393 		pheader->samplerate	= H2BE_INT (pheader->samplerate) ;
394 		pheader->channels	= H2BE_INT (pheader->channels) ;
395 		} ;
396 	return ;
397 } /* fix_header */
398 
399 static
endswap_short_array(short * buffer,int count)400 void endswap_short_array	(short* buffer, int count)
401 {	int k ;
402 
403 	for (k = 0 ; k < count ; k++)
404 		buffer [k] = ENDSWAP_SHORT (buffer [k]) ;
405 
406 } /* endswap_short_array */
407 
408 static
dsp_init(void)409 void dsp_init (void)
410 {	static int inited = 0;
411 	char	*cptr ;
412 
413 	if (inited) return;
414 	inited = 1;
415 
416 	datafile = getenv ("VSOUND_DATA") ;
417 	if (! datafile)
418 		datafile = "./vsound.data";
419 
420 	cptr = getenv ("VSOUND_DSPOUT") ;
421 	if (cptr)
422 	{	DPRINTF ("Enabling /dev/dsp output.\n") ;
423 		enable_dspout = 1 ;
424 		}
425 	else
426 		DPRINTF ("No /dev/dsp output.\n") ;
427 
428 	cptr = getenv ("VSOUND_TIMING") ;
429 	if (cptr && ! enable_dspout)
430 	{	DPRINTF ("Enabling timing delays for streaming data.\n") ;
431 		enable_timing = 1 ;
432 		}
433 	else
434 		DPRINTF ("No timing delays.\n") ;
435 
436 	cptr = getenv ("VSOUND_STDOUT") ;
437 	if (cptr)
438 	{	DPRINTF ("Enabling output to stdout.\n") ;
439 		enable_stdout = 1 ;
440 		}
441 	else
442 		DPRINTF ("Output goes to file.\n") ;
443 
444 	cptr = getenv("VSOUND_STOPDELAY");
445 	if (cptr)
446 	  stopdelay = atoi(cptr);
447 	  DPRINTF("Autostoping after %d seconds of inactivity\n", stopdelay);
448 
449 
450 
451 } /* dsp_init */
452 
453 static
dspctl(request_t request,void * argp)454 int dspctl (request_t request, void *argp)
455 {	int *arg = (int *) argp;
456 
457 	switch (request)
458 	{	case SNDCTL_DSP_RESET:
459 				DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_RESET, %p)\n",  argp) ;
460 				break ;
461 
462 		case SNDCTL_DSP_POST:
463 				DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_POST, %p)\n",  argp) ;
464 				break ;
465 
466 		case SNDCTL_DSP_SETFMT:
467 				DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_SETFMT, 0x%08X)\n", *((int*) argp)) ;
468 
469 				au_header.magic = CPU_IS_BIG_ENDIAN ? DOTSND_MARKER : DNSDOT_MARKER ;
470 
471 				switch (*arg)
472 				{	case AFMT_QUERY:
473 							if (CPU_IS_BIG_ENDIAN)
474 								*arg = AFMT_S16_BE;  /* this is arbitrary */
475 							else
476 								*arg = AFMT_S16_LE;  /* this is arbitrary */
477 
478 							DPRINTF ("AFMT_QUERY\n") ;
479 							au_header.encoding = AU_ENCODING_PCM_16 ;
480 							break ;
481 
482 					case AFMT_MU_LAW:
483 							DPRINTF ("AFMT_MU_LAW\n") ;
484 							au_header.encoding = AU_ENCODING_ULAW_8 ;
485 							break ;
486 
487 					case AFMT_A_LAW:
488 							DPRINTF ("AFMT_A_LAW\n") ;
489 							au_header.encoding = AU_ENCODING_ALAW_8 ;
490 							break ;
491 
492 					case AFMT_S8:
493 							DPRINTF ("AFMT_S8\n") ;
494 							au_header.encoding = AU_ENCODING_PCM_8 ;
495 							break ;
496 
497 					case AFMT_U8:
498 							DPRINTF ("AFMT_U8\n") ;
499 							au_header.encoding = 0 ;
500 							break ;
501 
502 					case AFMT_S16_LE:
503 							DPRINTF ("AFMT_S16_LE\n") ;
504 							au_header.magic = DNSDOT_MARKER ;
505 							au_header.encoding = AU_ENCODING_PCM_16 ;
506 							break ;
507 
508 					case AFMT_S16_BE:
509 							DPRINTF ("AFMT_S16_BE\n") ;
510 							au_header.magic = DOTSND_MARKER ;
511 							au_header.encoding = AU_ENCODING_PCM_16 ;
512 							break ;
513 
514 					case AFMT_U16_LE:
515 							DPRINTF ("AFMT_U16_LE\n") ;
516 							au_header.magic = DNSDOT_MARKER ;
517 							au_header.encoding = 0 ;
518 							break ;
519 
520 					case AFMT_U16_BE:
521 							DPRINTF ("AFMT_U16_BE\n") ;
522 							au_header.magic = DOTSND_MARKER ;
523 							au_header.encoding = 0 ;
524 							break ;
525 					default:
526 						DPRINTF ("Unknown format : %08X\n", *((int*) arg)) ;
527 						break ;
528 
529       				}
530 				devdsp_format = *((int*) arg) ;
531 				break ;
532 
533 		case SNDCTL_DSP_SPEED:
534 				DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_SPEED, %d)\n",  *((int*) argp)) ;
535 				au_header.samplerate = *((int*) argp) ;
536 				break ;
537 
538 		case SNDCTL_DSP_STEREO:
539 				DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_STEREO, %p)\n",  argp) ;
540 				au_header.channels = (*arg) ? 2 : 1 ;
541 				break ;
542 
543 		case SNDCTL_DSP_CHANNELS:
544 				DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_CHANNELS, %d)\n",  *((int*) argp)) ;
545 				au_header.channels = *((int *) arg) ;
546 				break ;
547 
548 		case SNDCTL_DSP_GETBLKSIZE:
549 				DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_GETBLKSIZE, %p)\n",  argp) ;
550 				/*
551 				** DPRINTF ("dsp_getblksize (returning 4k)\n") ;
552 				** *arg = 4 * 1024;
553 				*/
554 				break ;
555 
556 		case SNDCTL_DSP_GETFMTS:
557 				DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_GETFMTS, %p)\n",  argp) ;
558 				/*
559 				** DPRINTF ("dsp_getfmts (returning 0x38)\n") ;
560 				** *arg = AFMT_MU_LAW|AFMT_A_LAW|AFMT_U8|AFMT_S16_LE|AFMT_S16_BE|
561 				** AFMT_S8|AFMT_U16_LE|AFMT_U16_BE;
562 				*/
563 
564 				*arg = AFMT_S16_LE | AFMT_S16_BE ;
565 				DPRINTF ("dsp_getfmts (returning %d)\n", *((int*) arg)) ;
566 				break ;
567 
568 		case SNDCTL_DSP_GETCAPS:
569 				DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_GETCAPS, %p)\n",  argp) ;
570 				/*
571 				** *arg = 0;
572 				*/
573 				break ;
574 
575 		case SNDCTL_DSP_GETOSPACE:
576 				{	int	value ;
577 
578 					/*
579 					**	audio_buf_info *info ;
580 					**	info = (audio_buf_info*) argp ;
581 					**
582 					**	DPRINTF ("    fragments  : %d\n", info->fragments) ;
583 					**	DPRINTF ("    fragstotal : %d\n", info->fragstotal) ;
584 					**	DPRINTF ("    fragsize   : %d\n", info->fragsize) ;
585 					**	DPRINTF ("    bytes      : %d\n", info->bytes) ;
586 					**
587 					** audio_buf_info *bufinfo = (audio_buf_info *) argp;
588 					** bufinfo->bytes = 4 * 1024;
589 					**
590 					*/
591 
592 					value = *((int*) argp) ;
593 					value = value < 0 ? -value : value ;
594 					DPRINTF ("ioctl (/dev/dsp, SNDCTL_DSP_GETOSPACE, %p) = %d (%08X)\n",  argp, value, value) ;
595 					virtual_device.max_samples = value ;
596 					}
597 				break ;
598 
599 
600 		default:
601 				/* DPRINTF ("Unknown ioctl() : %08X\n", request) ; */
602 				break ;
603 		} ;
604 
605 	return 0;
606 }	/* dspctl */
607 
608 static
au_bytes_per_sample(AU_HEADER * au_header)609 int  au_bytes_per_sample (AU_HEADER *au_header)
610 {	int	bytes = au_header->channels ;
611 
612 	if (au_header->encoding == AU_ENCODING_PCM_16)
613 		bytes *= 2 ;
614 
615 	DPRINTF ("au_bytes_per_sample : %d\n", bytes) ;
616 
617 	return bytes ;
618 } /* au_bytes_per_sample */
619 
620 static
usec_diff_timeval(struct timeval * start,struct timeval * end)621 u_long usec_diff_timeval (struct timeval *start, struct timeval *end)
622 {	u_long diff ;
623 
624 	if (end->tv_sec < start->tv_sec)
625 	{	DPRINTF ("warning : end->tv_sec < start->tv_sec. Possible wrap around???\n") ;
626 		return 0 ; /* Just to be safe. */
627 		} ;
628 
629 	diff = (end->tv_sec - start->tv_sec) * 1000000 ;
630 	diff += end->tv_usec - start->tv_usec ;
631 
632 	return diff ;
633 } /* usec_diff_timeval */
634 
635 
636 /* Signal handler for SIGQUIT. */
SIGALRM_handler(int signum)637 void SIGALRM_handler (int signum)
638 {
639   DPRINTF ("Autostop alarm\n") ;
640   if (!ignore_autostop){
641     DPRINTF ("Sending sigint\n") ;
642     exit(1);
643   }
644 }
645 
646 static
start_autostop_timer(void)647 void start_autostop_timer(void)
648 {
649   static int firsttime = 1;
650   static struct itimerval timerval;
651   struct itimerval otimerval;
652   struct sigaction sa;
653 
654   if (firsttime && stopdelay) {
655     sigemptyset (&sa.sa_mask);
656     sa.sa_flags = 0;
657 
658     /* Register the handler for SIGALARM. */
659     sa.sa_handler = SIGALRM_handler;
660     sigaction (SIGALRM, &sa, 0);
661 
662     timerval.it_interval.tv_sec = (long)stopdelay;
663     timerval.it_interval.tv_usec = 0;
664     timerval.it_value.tv_sec = (long)stopdelay;
665     timerval.it_value.tv_usec = 0;
666   }
667 
668   if (stopdelay)
669     {     ignore_autostop = 0;
670           setitimer (ITIMER_REAL, &timerval, &otimerval);
671           DPRINTF ("Watchdog started from -> %ld \n", otimerval.it_value.tv_sec) ;
672     }
673 
674 } /* start_autostop_timer */
675 
676 static
stop_autostop_timer(void)677 void stop_autostop_timer(void)
678 {
679   static int firsttime = 1;
680   static struct itimerval timerval;
681   struct itimerval otimerval;
682 
683 
684   if (firsttime && stopdelay) {
685     timerval.it_interval.tv_sec = (long)0;
686     timerval.it_interval.tv_usec = 0;
687     timerval.it_value.tv_sec = (long)0;
688     timerval.it_value.tv_usec = 0;
689   }
690 
691   if (stopdelay) {
692     ignore_autostop = 1;
693     setitimer (ITIMER_REAL, &timerval, &otimerval);
694     DPRINTF ("Watchdog stopped at -> %ld \n", otimerval.it_value.tv_sec) ;
695   }
696 
697 } /* stop_autostop_timer */
698