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 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 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 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 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 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 != NULL) { 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