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