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