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