1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /* Command-line audio record utility */
30 
31 #include <stdio.h>
32 #include <libgen.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <math.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <strings.h>
40 #include <locale.h>
41 #include <fcntl.h>
42 #include <signal.h>
43 #include <limits.h>	/* All occurances of INT_MAX used to be ~0  (by MCA) */
44 #include <sys/types.h>
45 #include <sys/file.h>
46 #include <sys/stat.h>
47 #include <sys/param.h>
48 #include <stropts.h>
49 #include <poll.h>
50 #include <sys/ioctl.h>
51 #include <netinet/in.h>
52 
53 #include <libaudio.h>
54 #include <audio_device.h>
55 
56 #define	irint(d)	((int)d)
57 
58 /* localization stuff */
59 #define	MGET(s)		(char *)gettext(s)
60 
61 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
62 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
63 #endif
64 
65 /* Defined until I get a copy of the apropriate audioio.h file. */
66 #ifndef AUDIO_CD_IN
67 #define	AUDIO_CD_IN 0x04	/* input from the internal CD player */
68 #endif
69 
70 #define	Error		(void) fprintf
71 
72 /* Local variables */
73 static char	*prog;
74 static char	prog_opts[] = "aft:v:b:m:d:p:i:e:s:c:T:?"; /* getopt() flags */
75 static char	*Stdout;
76 
77 /* XXX - the input buffer size should depend on sample_rate */
78 #define	AUDIO_BUFSIZ (1024 * 64)
79 static unsigned char	buf[AUDIO_BUFSIZ];
80 static char 		swapBuf[AUDIO_BUFSIZ];	/* for byte swapping */
81 
82 
83 #define	MAX_GAIN		(100)	/* maximum gain */
84 
85 #define	LEFT_BAL		(-100)	/* min/max balance */
86 #define	MID_BAL			(0)
87 #define	RIGHT_BAL		(100)
88 
89 static char	*Info = NULL;		/* pointer to info data */
90 static unsigned	Ilen = 0;		/* length of info data */
91 static unsigned	Volume = INT_MAX;	/* record volume */
92 static double	Savevol;		/* saved  volume */
93 static unsigned	Monvol = INT_MAX;	/* monitor volume */
94 static double	Savemonvol;		/* saved monitor volume */
95 static unsigned int	Balance = INT_MAX;	/* input balance */
96 static unsigned int	Savebal;		/* saved input balance */
97 static unsigned	Port = INT_MAX;		/* Input port (line, mic) */
98 static unsigned	Saveport = 0;		/* restore value of input port */
99 static unsigned	Sample_rate = 0;
100 static unsigned	Channels = 0;
101 static unsigned	Precision = 0;		/* based on encoding */
102 static unsigned	Encoding = 0;
103 
104 static int	NetEndian = TRUE;	/* endian nature of the machines */
105 
106 static int	Append = FALSE;		/* append to output file */
107 static int	Force = FALSE;		/* ignore rate differences on append */
108 static double	Time = -1.;		/* recording time */
109 static unsigned	Limit = AUDIO_UNKNOWN_SIZE;	/* recording limit */
110 static char	*Audio_dev = "/dev/audio";
111 
112 static int		Audio_fd = -1;
113 			/* file descriptor for audio device */
114 static Audio_hdr	Dev_hdr;		/* audio header for device */
115 static Audio_hdr	Save_hdr;		/* saved audio device header */
116 static char		*Ofile;			/* current filename */
117 static int		File_type = FILE_AU;	/* audio file type */
118 static int		File_type_set = FALSE;	/* file type specified as arg */
119 static Audio_hdr	File_hdr;		/* audio header for file */
120 static int		Cleanup = FALSE;	/* SIGINT sets this flag */
121 static unsigned		Size = 0;		/* Size of output file */
122 static unsigned		Oldsize = 0;
123 			/* Size of input file, if append */
124 
125 /* Global variables */
126 extern int getopt();
127 extern int optind;
128 extern char *optarg;
129 
130 /* Local Functions */
131 static void usage(void);
132 static void sigint(int sig);
133 static int scale_balance(int g);
134 static int parse_unsigned(char *str, unsigned *dst, char *flag);
135 static int parse_sample_rate(char *s, unsigned *rate);
136 
137 
138 static void
139 usage(void)
140 {
141 	Error(stderr, MGET("Record an audio file -- usage:\n"
142 	    "\t%s [-af] [-v vol] [-b bal] [-m monvol]\n"
143 	    "\t%.*s [-p mic|line|cd|aux1|aux2|spdif]\n"
144 	    "\t%.*s [-c channels] [-s rate] [-e encoding]\n"
145 	    "\t%.*s [-t time] [-i info] [-d dev] [-T au|wav|aif[f]] [file]\n"
146 	    "where:\n"
147 	    "\t-a\tAppend to output file\n"
148 	    "\t-f\tIgnore sample rate differences on append\n"
149 	    "\t-v\tSet record volume (0 - %d)\n"
150 	    "\t-b\tSet record balance (%d=left, %d=center, %d=right)\n"
151 	    "\t-m\tSet monitor volume (0 - %d)\n"
152 	    "\t-p\tSpecify input port\n"
153 	    "\t-c\tSpecify number of channels to record\n"
154 	    "\t-s\tSpecify rate in samples per second\n"
155 	    "\t-e\tSpecify encoding (ulaw | alaw | [u]linear | linear8 )\n"
156 	    "\t-t\tSpecify record time (hh:mm:ss.dd)\n"
157 	    "\t-i\tSpecify a file header information string\n"
158 	    "\t-d\tSpecify audio device (default: /dev/audio)\n"
159 	    "\t-T\tSpecify the audio file type (default: au)\n"
160 	    "\tfile\tRecord to named file\n"
161 	    "\t\tIf no file specified, write to stdout\n"
162 	    "\t\tDefault audio encoding is ulaw, 8khz, mono\n"
163 	    "\t\tIf -t is not specified, record until ^C\n"),
164 	    prog,
165 	    strlen(prog), "                    ",
166 	    strlen(prog), "                    ",
167 	    strlen(prog), "                    ",
168 	    MAX_GAIN, LEFT_BAL, MID_BAL, RIGHT_BAL, MAX_GAIN);
169 	exit(1);
170 }
171 
172 static void
173 sigint(int sig)
174 {
175 	/* If this is the first ^C, set a flag for the main loop */
176 	if (!Cleanup && (Audio_fd >= 0)) {
177 		/* flush input queues before exiting */
178 		Cleanup = TRUE;
179 		if (audio_pause_record(Audio_fd) == AUDIO_SUCCESS)
180 			return;
181 		Error(stderr, MGET("%s: could not flush input buffer\n"), prog);
182 	}
183 
184 	/* If double ^C, really quit */
185 	if (Audio_fd >= 0) {
186 		if (Volume != INT_MAX)
187 			(void) audio_set_record_gain(Audio_fd, &Savevol);
188 		if (Balance != INT_MAX)
189 			(void) audio_set_record_balance(Audio_fd, &Savebal);
190 		if (Monvol != INT_MAX)
191 			(void) audio_set_monitor_gain(Audio_fd, &Savemonvol);
192 		if (Port != INT_MAX)
193 			(void) audio_set_record_port(Audio_fd, &Saveport);
194 		if (audio_cmp_hdr(&Save_hdr, &Dev_hdr) != 0) {
195 			(void) audio_set_record_config(Audio_fd, &Save_hdr);
196 		}
197 	}
198 	exit(1);
199 }
200 
201 /*
202  * Record from the audio device to a file.
203  */
204 int
205 main(int argc, char **argv)
206 {
207 	int		i;
208 	int		cnt;
209 	int		err;
210 	int		file_type;
211 	int		ofd;
212 	int 		swapBytes = FALSE;
213 	double		vol;
214 	int		bal;
215 	struct stat	st;
216 	struct pollfd	pfd;
217 	char		*cp;
218 	char		ctldev[MAXPATHLEN];
219 
220 	(void) setlocale(LC_ALL, "");
221 	(void) textdomain(TEXT_DOMAIN);
222 
223 	/* Get the program name */
224 	prog = strrchr(argv[0], '/');
225 	if (prog == NULL)
226 		prog = argv[0];
227 	else
228 		prog++;
229 	Stdout = MGET("(stdout)");
230 
231 	/* first check AUDIODEV environment for audio device name */
232 	if (cp = getenv("AUDIODEV")) {
233 		Audio_dev = cp;
234 	}
235 
236 	/* Set the endian nature of the machine */
237 	if ((ulong_t)1 != htonl((ulong_t)1)) {
238 		NetEndian = FALSE;
239 	}
240 
241 	err = 0;
242 	while ((i = getopt(argc, argv, prog_opts)) != EOF) {
243 		switch (i) {
244 		case 'v':
245 			if (parse_unsigned(optarg, &Volume, "-v")) {
246 				err++;
247 			} else if (Volume > MAX_GAIN) {
248 				Error(stderr, MGET("%s: invalid value for "
249 				"-v\n"), prog);
250 				err++;
251 			}
252 			break;
253 		case 'b':
254 			bal = atoi(optarg);
255 			if ((bal > RIGHT_BAL) || (bal < LEFT_BAL)) {
256 				Error(stderr, MGET("%s: invalid value for "
257 				"-b\n"), prog);
258 				err++;
259 			} else {
260 				Balance = (unsigned)scale_balance(bal);
261 			}
262 			break;
263 		case 'm':
264 			if (parse_unsigned(optarg, &Monvol, "-m")) {
265 				err++;
266 			} else if (Monvol > MAX_GAIN) {
267 				Error(stderr, MGET("%s: invalid value for "
268 				"-m\n"), prog);
269 				err++;
270 			}
271 			break;
272 		case 't':
273 			Time = audio_str_to_secs(optarg);
274 			if ((Time == HUGE_VAL) || (Time < 0.)) {
275 				Error(stderr, MGET("%s: invalid value for "
276 				"-t\n"), prog);
277 				err++;
278 			}
279 			break;
280 		case 'd':
281 			Audio_dev = optarg;
282 			break;
283 		case 'p':
284 			/* a partial match is OK */
285 			if (strncmp(optarg, "microphone",
286 			    strlen(optarg)) == 0) {
287 				Port = AUDIO_MICROPHONE;
288 			} else if (strncmp(optarg, "line",
289 			    strlen(optarg)) == 0) {
290 				Port = AUDIO_LINE_IN;
291 			} else if ((strncmp(optarg, "cd",
292 			    strlen(optarg)) == 0) || (strncmp(optarg,
293 			    "internal-cd", strlen(optarg)) == 0)) {
294 				Port = AUDIO_CD_IN;
295 			} else if (strncmp(optarg, "aux1",
296 			    strlen(optarg)) == 0) {
297 				Port = AUDIO_AUX1_IN;
298 			} else if (strncmp(optarg, "aux2",
299 			    strlen(optarg)) == 0) {
300 				Port = AUDIO_AUX2_IN;
301 			} else if (strncmp(optarg, "spdif",
302 			    strlen(optarg)) == 0) {
303 				Port = AUDIO_SPDIF_IN;
304 			} else {
305 				Error(stderr, MGET("%s: invalid value for "
306 				"-p\n"), prog);
307 				err++;
308 			}
309 			break;
310 		case 'f':
311 			Force = TRUE;
312 			break;
313 		case 'a':
314 			Append = TRUE;
315 			break;
316 		case 'i':
317 			Info = optarg;		/* set information string */
318 			Ilen = strlen(Info);
319 			break;
320 		case 's':
321 			if (parse_sample_rate(optarg, &Sample_rate)) {
322 				err++;
323 			}
324 			break;
325 		case 'c':
326 			if (strncmp(optarg, "mono", strlen(optarg)) == 0) {
327 				Channels = 1;
328 			} else if (strncmp(optarg, "stereo",
329 			    strlen(optarg)) == 0) {
330 				Channels = 2;
331 			} else if (parse_unsigned(optarg, &Channels, "-c")) {
332 				err++;
333 			} else if ((Channels != 1) && (Channels != 2)) {
334 				Error(stderr, "%s: invalid value for -c\n",
335 				    prog);
336 				err++;
337 			}
338 			break;
339 		case 'e':
340 			if (strncmp(optarg, "ulinear", strlen(optarg)) == 0) {
341 				Encoding = AUDIO_ENCODING_LINEAR8;
342 				Precision = 8;
343 			} else if (strncmp(optarg, "linear8",
344 			    strlen("linear8")) == 0) {
345 				Encoding = AUDIO_ENCODING_LINEAR;
346 				Precision = 8;
347 			} else if (strncmp(optarg, "ulaw",
348 			    strlen(optarg)) == 0) {
349 				Encoding = AUDIO_ENCODING_ULAW;
350 				Precision = 8;
351 			} else if (strncmp(optarg, "alaw",
352 			    strlen(optarg)) == 0) {
353 				Encoding = AUDIO_ENCODING_ALAW;
354 				Precision = 8;
355 			} else if ((strncmp(optarg, "linear",
356 			    strlen(optarg)) == 0) || (strncmp(optarg, "pcm",
357 			    strlen(optarg)) == 0)) {
358 				Encoding = AUDIO_ENCODING_LINEAR;
359 				Precision = 16;
360 			} else {
361 				Error(stderr, MGET("%s: invalid value for "
362 				    "-e\n"), prog);
363 				err++;
364 			}
365 			break;
366 		case 'T':
367 			if (strncmp(optarg, "au", strlen(optarg)) == 0) {
368 				File_type = FILE_AU;
369 			} else if (strncmp(optarg, "wav",
370 			    strlen(optarg)) == 0) {
371 				File_type = FILE_WAV;
372 			} else if (strncmp(optarg, "aif",
373 			    strlen(optarg)) == 0) {
374 				File_type = FILE_AIFF;
375 			} else if (strncmp(optarg, "aiff",
376 			    strlen(optarg)) == 0) {
377 				File_type = FILE_AIFF;
378 			} else {
379 				Error(stderr, MGET("%s: invalid value for "
380 				    "-T\n"), prog);
381 				err++;
382 			}
383 			File_type_set = TRUE;
384 			break;
385 		case '?':
386 			usage();
387 	/*NOTREACHED*/
388 		}
389 	}
390 	if (Append && (Info != NULL)) {
391 		Error(stderr, MGET("%s: cannot specify -a and -i\n"), prog);
392 		err++;
393 	}
394 	if (err > 0)
395 		exit(1);
396 
397 	argc -= optind;		/* update arg pointers */
398 	argv += optind;
399 
400 	/* Open the output file */
401 	if (argc <= 0) {
402 		Ofile = Stdout;
403 	} else {
404 		Ofile = *argv++;
405 		argc--;
406 
407 		/* Interpret "-" filename to mean stdout */
408 		if (strcmp(Ofile, "-") == 0)
409 			Ofile = Stdout;
410 
411 		/* if -T not set then we use the file suffix */
412 		if (File_type_set == FALSE) {
413 			char	*file_name;
414 			char	*start;
415 
416 			/* get the file name without the path */
417 			file_name = basename(Ofile);
418 
419 			/* get the true suffix */
420 			start = strrchr(file_name, '.');
421 
422 			/* if no '.' then there's no suffix */
423 			if (start) {
424 				/* is this a .au file? */
425 				if (strcasecmp(start, ".au") == 0) {
426 					File_type = FILE_AU;
427 				} else if (strcasecmp(start, ".wav") == 0) {
428 					File_type = FILE_WAV;
429 				} else if (strcasecmp(start, ".aif") == 0) {
430 					File_type = FILE_AIFF;
431 				} else if (strcasecmp(start, ".aiff") == 0) {
432 					File_type = FILE_AIFF;
433 				} else {
434 					/* the default is .au */
435 					File_type = FILE_AU;
436 				}
437 			} else {
438 				/* no suffix, so default to .au */
439 				File_type = FILE_AU;
440 			}
441 		}
442 	}
443 
444 	if (Ofile == Stdout) {
445 		ofd = fileno(stdout);
446 		Append = FALSE;
447 	} else {
448 		ofd = open(Ofile,
449 		    (O_RDWR | O_CREAT | (Append ? 0 : O_TRUNC)), 0666);
450 		if (ofd < 0) {
451 			Error(stderr, MGET("%s: cannot open "), prog);
452 			perror(Ofile);
453 			exit(1);
454 		}
455 		if (Append) {
456 			/*
457 			 * Check to make sure we're appending to an audio file.
458 			 * It must be a regular file (if zero-length, simply
459 			 * write it from scratch).  Also, its file header
460 			 * must match the input device configuration.
461 			 */
462 			if ((fstat(ofd, &st) < 0) || (!S_ISREG(st.st_mode))) {
463 				Error(stderr,
464 				    MGET("%s: %s is not a regular file\n"),
465 				    prog, Ofile);
466 				exit(1);
467 			}
468 			if (st.st_size == 0) {
469 				Append = FALSE;
470 				goto openinput;
471 			}
472 
473 			err = audio_read_filehdr(ofd, &File_hdr, &file_type,
474 			    (char *)NULL, 0);
475 
476 			if (err != AUDIO_SUCCESS) {
477 				Error(stderr,
478 				    MGET("%s: %s is not a valid audio file\n"),
479 				    prog, Ofile);
480 				exit(1);
481 			}
482 
483 			/* we need to make sure file types match */
484 			if (File_type_set == TRUE) {
485 				/* specified by the command line, must match */
486 				if (File_type != file_type) {
487 					Error(stderr,
488 					    MGET("%s: file types must match\n"),
489 					    prog);
490 					exit(1);
491 				}
492 			} else {
493 				/* not specified, so force */
494 				File_type = file_type;
495 			}
496 
497 			/*
498 			 * Set the format state to the format
499 			 * in the file header.
500 			 */
501 			Sample_rate = File_hdr.sample_rate;
502 			Channels = File_hdr.channels;
503 			Encoding = File_hdr.encoding;
504 			Precision = File_hdr.bytes_per_unit * 8;
505 
506 			/* make sure we support the encoding method */
507 			switch (Encoding) {
508 				case AUDIO_ENCODING_LINEAR8:
509 				case AUDIO_ENCODING_ULAW:
510 				case AUDIO_ENCODING_ALAW:
511 				case AUDIO_ENCODING_LINEAR:
512 					break;
513 				default: {
514 					char	msg[AUDIO_MAX_ENCODE_INFO];
515 					(void) audio_enc_to_str(&File_hdr, msg);
516 					Error(stderr,
517 					    MGET("%s: Append is not supported "
518 					    "for "), prog);
519 					Error(stderr,
520 					    MGET("this file encoding:\n\t"
521 					    "[%s]\n"), msg);
522 					exit(1);
523 					}
524 			}
525 
526 			/* Get the current size, if possible */
527 			Oldsize = File_hdr.data_size;
528 			if ((Oldsize == AUDIO_UNKNOWN_SIZE) &&
529 			    ((err = (int)lseek(ofd, 0L, SEEK_CUR)) >= 0)) {
530 				if (err < 0) {
531 					Error(stderr,
532 					    MGET("%s: %s is not a valid audio "
533 					    "file\n"), prog, Ofile);
534 					exit(1);
535 				}
536 				Oldsize = st.st_size - err;
537 			}
538 			/* Seek to end to start append */
539 			if ((int)lseek(ofd, st.st_size, SEEK_SET) < 0) {
540 				Error(stderr,
541 				    MGET("%s: cannot find end of %s\n"),
542 				    prog, Ofile);
543 				exit(1);
544 			}
545 		}
546 	}
547 openinput:
548 	/* Validate and open the audio device */
549 	err = stat(Audio_dev, &st);
550 	if (err < 0) {
551 		Error(stderr, MGET("%s: cannot open "), prog);
552 		perror(Audio_dev);
553 		exit(1);
554 	}
555 	if (!S_ISCHR(st.st_mode)) {
556 		Error(stderr, MGET("%s: %s is not an audio device\n"), prog,
557 		    Audio_dev);
558 		exit(1);
559 	}
560 
561 	/*
562 	 * For the mixer environment we need to open the audio device before
563 	 * the control device. If successful we pause right away to keep
564 	 * from queueing up a bunch of useless data.
565 	 */
566 	Audio_fd = open(Audio_dev, O_RDONLY | O_NONBLOCK);
567 	if (Audio_fd < 0) {
568 		if (errno == EBUSY) {
569 			Error(stderr, MGET("%s: %s is busy\n"),
570 			    prog, Audio_dev);
571 		} else {
572 			Error(stderr, MGET("%s: error opening "), prog);
573 			perror(Audio_dev);
574 		}
575 		exit(1);
576 	}
577 	if (audio_pause_record(Audio_fd) != AUDIO_SUCCESS) {
578 		Error(stderr, MGET("%s: not able to pause recording\n"), prog);
579 		exit(1);
580 	}
581 
582 	/* get the current settings */
583 	if (audio_get_record_config(Audio_fd, &Save_hdr) != AUDIO_SUCCESS) {
584 		(void) close(Audio_fd);
585 		Error(stderr, MGET("%s: %s is not an audio device\n"),
586 		    prog, Audio_dev);
587 		exit(1);
588 	}
589 	/* make a copy into the working data structure */
590 	bcopy(&Save_hdr, &Dev_hdr, sizeof (Save_hdr));
591 
592 	/* flush any queued audio data */
593 	if (audio_flush_record(Audio_fd) != AUDIO_SUCCESS) {
594 		Error(stderr, MGET("%s: not able to flush recording\n"), prog);
595 		exit(1);
596 	}
597 
598 	if (Sample_rate != 0) {
599 		Dev_hdr.sample_rate = Sample_rate;
600 	}
601 	if (Channels != 0) {
602 		Dev_hdr.channels = Channels;
603 	}
604 	if (Precision != 0) {
605 		Dev_hdr.bytes_per_unit = Precision / 8;
606 	}
607 	if (Encoding != 0) {
608 		Dev_hdr.encoding = Encoding;
609 	}
610 
611 	/*
612 	 * For .wav we always record 8-bit linear as unsigned. Thus we
613 	 * force unsigned linear to make life a lot easier on the user.
614 	 *
615 	 * For .aiff we set the default to 8-bit signed linear, not
616 	 * u-law, if Encoding isn't already set.
617 	 */
618 	if (File_type == FILE_WAV &&
619 	    Dev_hdr.encoding == AUDIO_ENCODING_LINEAR &&
620 	    Dev_hdr.bytes_per_unit == 1) {
621 		/* force to unsigned */
622 		Dev_hdr.encoding = AUDIO_ENCODING_LINEAR8;
623 	} else if (File_type == FILE_AIFF && Encoding == 0) {
624 		Dev_hdr.encoding = AUDIO_ENCODING_LINEAR;
625 		if (Precision == 0) {
626 			Dev_hdr.bytes_per_unit = AUDIO_PRECISION_8 / 8;
627 		}
628 	}
629 
630 	if (audio_set_record_config(Audio_fd, &Dev_hdr) != AUDIO_SUCCESS) {
631 		Error(stderr, MGET(
632 		    "%s: Audio format not supported by the audio device\n"),
633 		    prog);
634 		exit(1);
635 	}
636 
637 	if (audio_resume_record(Audio_fd) != AUDIO_SUCCESS) {
638 		Error(stderr, MGET("%s: not able to resume recording\n"), prog);
639 		exit(1);
640 	}
641 
642 	/* If appending to an existing file, check the configuration */
643 	if (Append) {
644 		char	msg[AUDIO_MAX_ENCODE_INFO];
645 
646 		switch (audio_cmp_hdr(&Dev_hdr, &File_hdr)) {
647 		case 0:			/* configuration matches */
648 			break;
649 		case 1:			/* all but sample rate matches */
650 			if (Force) {
651 				Error(stderr, MGET("%s: WARNING: appending "
652 				    "%.3fkHz data to %s (%.3fkHz)\n"), prog,
653 				    ((double)Dev_hdr.sample_rate / 1000.),
654 				    Ofile,
655 				    ((double)File_hdr.sample_rate / 1000.));
656 				break;
657 			}		/* if not -f, fall through */
658 
659 		default:		/* encoding mismatch */
660 			(void) audio_enc_to_str(&Dev_hdr, msg);
661 			Error(stderr,
662 			    MGET("%s: device encoding [%s]\n"), prog, msg);
663 			(void) audio_enc_to_str(&File_hdr, msg);
664 			Error(stderr,
665 			    MGET("\tdoes not match file encoding [%s]\n"), msg);
666 			exit(1);
667 		}
668 	} else if (!isatty(ofd)) {
669 		if (audio_write_filehdr(ofd, &Dev_hdr, File_type, Info,
670 		    Ilen) != AUDIO_SUCCESS) {
671 			Error(stderr,
672 			    MGET("%s: error writing header for %s\n"), prog,
673 			    Ofile);
674 			exit(1);
675 		}
676 	}
677 
678 	/*
679 	 * 8-bit audio isn't a problem, however 16-bit audio is. If the file
680 	 * is an endian that is different from the machine then the bytes
681 	 * will need to be swapped.
682 	 *
683 	 * Note: The following if() could be simplified, but then it gets
684 	 * to be very hard to read. So it's left as is.
685 	 */
686 	if (Dev_hdr.bytes_per_unit == 2 &&
687 	    ((!NetEndian && File_type == FILE_AIFF) ||
688 	    (!NetEndian && File_type == FILE_AU) ||
689 	    (NetEndian && File_type == FILE_WAV))) {
690 		swapBytes = TRUE;
691 	}
692 
693 	/* If -v flag, set the record volume now */
694 	if (Volume != INT_MAX) {
695 		vol = (double)Volume / (double)MAX_GAIN;
696 		(void) audio_get_record_gain(Audio_fd, &Savevol);
697 		err = audio_set_record_gain(Audio_fd, &vol);
698 		if (err != AUDIO_SUCCESS) {
699 			Error(stderr,
700 			    MGET("%s: could not set record volume for %s\n"),
701 			    prog, Audio_dev);
702 			exit(1);
703 		}
704 	}
705 
706 	if (Balance != INT_MAX) {
707 		(void) audio_get_record_balance(Audio_fd, &Savebal);
708 		err = audio_set_record_balance(Audio_fd, &Balance);
709 		if (err != AUDIO_SUCCESS) {
710 			Error(stderr,
711 			    MGET("%s: could not set record balance for %s\n"),
712 			    prog, Audio_dev);
713 			exit(1);
714 		}
715 	}
716 
717 	/* If -m flag, set monitor volume now */
718 	if (Monvol != INT_MAX) {
719 		vol = (double)Monvol / (double)MAX_GAIN;
720 		(void) audio_get_monitor_gain(Audio_fd, &Savemonvol);
721 		err = audio_set_monitor_gain(Audio_fd, &vol);
722 		if (err != AUDIO_SUCCESS) {
723 			Error(stderr,
724 			    MGET("%s: could not set monitor volume for %s\n"),
725 			    prog, Audio_dev);
726 			exit(1);
727 		}
728 	}
729 
730 	/* If -p flag, set the input port */
731 	if (Port != INT_MAX) {
732 		(void) audio_get_record_port(Audio_fd, &Saveport);
733 		err = audio_set_record_port(Audio_fd, &Port);
734 		if (err != AUDIO_SUCCESS) {
735 			Error(stderr,
736 			    MGET("%s: could not set input port %s\n"),
737 			    prog, Audio_dev);
738 			exit(1);
739 		}
740 	}
741 
742 	if (isatty(ofd)) {
743 		exit(0);
744 	}
745 
746 	/* Set up SIGINT handler so that final buffers may be flushed */
747 	(void) signal(SIGINT, sigint);
748 
749 	/*
750 	 * At this point, we're (finally) ready to copy the data.
751 	 * Init a poll() structure, to use when there's nothing to read.
752 	 */
753 	if (Time > 0)
754 		Limit = audio_secs_to_bytes(&Dev_hdr, Time);
755 	pfd.fd = Audio_fd;
756 	pfd.events = POLLIN;
757 	while ((Limit == AUDIO_UNKNOWN_SIZE) || (Limit != 0)) {
758 		/* Fill the buffer or read to the time limit */
759 		cnt = read(Audio_fd, (char *)buf,
760 		    ((Limit != AUDIO_UNKNOWN_SIZE) && (Limit < sizeof (buf)) ?
761 		    (int)Limit : sizeof (buf)));
762 
763 		if (cnt == 0)		/* normally, eof can't happen */
764 			break;
765 
766 		/* If error, probably have to wait for input */
767 		if (cnt < 0) {
768 			if (Cleanup)
769 				break;		/* done if ^C seen */
770 			switch (errno) {
771 			case EAGAIN:
772 				(void) poll(&pfd, 1L, -1);
773 				break;
774 			case EOVERFLOW:  /* Possibly a Large File */
775 				Error(stderr, MGET("%s: error reading"), prog);
776 				perror("Large File");
777 				exit(1);
778 			default:
779 				Error(stderr, MGET("%s: error reading"), prog);
780 				perror(Audio_dev);
781 				exit(1);
782 			}
783 			continue;
784 		}
785 
786 		/* Swab the output if required. */
787 		if (swapBytes) {
788 			swab((char *)buf, swapBuf, cnt);
789 			err = write(ofd, swapBuf, cnt);
790 		} else {
791 			err = write(ofd, (char *)buf, cnt);
792 		}
793 		if (err < 0) {
794 			Error(stderr, MGET("%s: error writing "), prog);
795 			perror(Ofile);
796 			exit(1);
797 		}
798 		if (err != cnt) {
799 			Error(stderr, MGET("%s: error writing "), prog);
800 			perror(Ofile);
801 			break;
802 		}
803 		Size += cnt;
804 		if (Limit != AUDIO_UNKNOWN_SIZE)
805 			Limit -= cnt;
806 	}
807 
808 	/* Attempt to rewrite the data_size field of the file header */
809 	if (!Append || (Oldsize != AUDIO_UNKNOWN_SIZE)) {
810 		if (Append)
811 			Size += Oldsize;
812 		(void) audio_rewrite_filesize(ofd, File_type, Size,
813 		    Dev_hdr.channels, Dev_hdr.bytes_per_unit);
814 	}
815 
816 	(void) close(ofd);			/* close input file */
817 
818 
819 	/* Check for error during record */
820 	if (audio_get_record_error(Audio_fd, (unsigned *)&err) != AUDIO_SUCCESS)
821 		Error(stderr, MGET("%s: error reading device status\n"), prog);
822 	else if (err)
823 		Error(stderr, MGET("%s: WARNING: Data overflow occurred\n"),
824 		    prog);
825 
826 	/* Reset record volume, balance, monitor volume, port, encoding */
827 	if (Volume != INT_MAX)
828 		(void) audio_set_record_gain(Audio_fd, &Savevol);
829 	if (Balance != INT_MAX)
830 		(void) audio_set_record_balance(Audio_fd, &Savebal);
831 	if (Monvol != INT_MAX)
832 		(void) audio_set_monitor_gain(Audio_fd, &Savemonvol);
833 	if (Port != INT_MAX)
834 		(void) audio_set_record_port(Audio_fd, &Saveport);
835 	if (audio_cmp_hdr(&Save_hdr, &Dev_hdr) != 0) {
836 		(void) audio_set_record_config(Audio_fd, &Save_hdr);
837 	}
838 	(void) close(Audio_fd);
839 	return (0);
840 }
841 
842 /* Parse an unsigned integer */
843 static int
844 parse_unsigned(char *str, unsigned *dst, char *flag)
845 {
846 	char		x;
847 
848 	if (sscanf(str, "%u%c", dst, &x) != 1) {
849 		Error(stderr, MGET("%s: invalid value for %s\n"), prog, flag);
850 		return (1);
851 	}
852 	return (0);
853 }
854 
855 /*
856  * set the sample rate. assume anything is ok. check later on to make sure
857  * the sample rate is valid.
858  */
859 static int
860 parse_sample_rate(char *s, unsigned *rate)
861 {
862 	char		*cp;
863 	double		drate;
864 
865 	/*
866 	 * check if it's "cd" or "dat" or "voice". these also set
867 	 * the precision and encoding, etc.
868 	 */
869 	if (strcasecmp(s, "dat") == 0) {
870 		drate = 48000.0;
871 	} else if (strcasecmp(s, "cd") == 0) {
872 		drate = 44100.0;
873 	} else if (strcasecmp(s, "voice") == 0) {
874 		drate = 8000.0;
875 	} else {
876 		/* just do an atof */
877 		drate = atof(s);
878 
879 		/*
880 		 * if the first non-digit is a "k" multiply by 1000,
881 		 * if it's an "h", leave it alone. anything else,
882 		 * return an error.
883 		 */
884 
885 		/*
886 		 * XXX bug alert: could have multiple "." in string
887 		 * and mess things up.
888 		 */
889 		for (cp = s; *cp && (isdigit(*cp) || (*cp == '.')); cp++);
890 		if (*cp != NULL) {
891 			if ((*cp == 'k') || (*cp == 'K')) {
892 				drate *= 1000.0;
893 			} else if ((*cp != 'h') || (*cp != 'H')) {
894 				/* bogus! */
895 				Error(stderr,
896 				    MGET("invalid sample rate: %s\n"), s);
897 				return (1);
898 			}
899 		}
900 
901 	}
902 
903 	*rate = irint(drate);
904 	return (0);
905 }
906 
907 /* Convert local balance into device parameters */
908 static int
909 scale_balance(int g)
910 {
911 	return (int)(((g + RIGHT_BAL) / (double)(RIGHT_BAL - LEFT_BAL)) *
912 	    (double)AUDIO_RIGHT_BALANCE);
913 }
914