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