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 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