1 /* $NetBSD: record.c,v 1.33 2003/06/23 12:15:04 agc Exp $ */ 2 3 /* 4 * Copyright (c) 1999, 2002 Matthew R. Green 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * SunOS compatible audiorecord(1) 33 */ 34 #include <sys/cdefs.h> 35 36 #ifndef lint 37 __RCSID("$NetBSD: record.c,v 1.33 2003/06/23 12:15:04 agc Exp $"); 38 #endif 39 40 41 #include <sys/types.h> 42 #include <sys/audioio.h> 43 #include <sys/ioctl.h> 44 #include <sys/time.h> 45 #include <sys/uio.h> 46 47 #include <err.h> 48 #include <fcntl.h> 49 #include <paths.h> 50 #include <signal.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 56 #include "libaudio.h" 57 #include "auconv.h" 58 59 audio_info_t info, oinfo; 60 ssize_t total_size = -1; 61 const char *device; 62 int format = AUDIO_FORMAT_DEFAULT; 63 char *header_info; 64 char default_info[8] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; 65 int audiofd, outfd; 66 int qflag, aflag, fflag; 67 int verbose; 68 int monitor_gain, omonitor_gain; 69 int gain; 70 int balance; 71 int port; 72 int encoding; 73 char *encoding_str; 74 int precision; 75 int sample_rate; 76 int channels; 77 struct timeval record_time; 78 struct timeval start_time; 79 80 void (*conv_func) (u_char *, size_t); 81 82 void usage (void); 83 int main (int, char *[]); 84 int timeleft (struct timeval *, struct timeval *); 85 void cleanup (int) __attribute__((__noreturn__)); 86 int write_header_sun (void **, size_t *, int *); 87 int write_header_wav (void **, size_t *, int *); 88 void write_header (void); 89 void rewrite_header (void); 90 91 int 92 main(argc, argv) 93 int argc; 94 char *argv[]; 95 { 96 char *buffer; 97 size_t len, bufsize; 98 int ch, no_time_limit = 1; 99 const char *defdevice = _PATH_SOUND; 100 101 while ((ch = getopt(argc, argv, "ab:C:F:c:d:e:fhi:m:P:p:qt:s:Vv:")) != -1) { 102 switch (ch) { 103 case 'a': 104 aflag++; 105 break; 106 case 'b': 107 decode_int(optarg, &balance); 108 if (balance < 0 || balance > 63) 109 errx(1, "balance must be between 0 and 63"); 110 break; 111 case 'C': 112 /* Ignore, compatibility */ 113 break; 114 case 'F': 115 format = audio_format_from_str(optarg); 116 if (format < 0) 117 errx(1, "Unknown audio format; supported " 118 "formats: \"sun\", \"wav\", and \"none\""); 119 break; 120 case 'c': 121 decode_int(optarg, &channels); 122 if (channels < 0 || channels > 16) 123 errx(1, "channels must be between 0 and 16"); 124 break; 125 case 'd': 126 device = optarg; 127 break; 128 case 'e': 129 encoding_str = optarg; 130 break; 131 case 'f': 132 fflag++; 133 break; 134 case 'i': 135 header_info = optarg; 136 break; 137 case 'm': 138 decode_int(optarg, &monitor_gain); 139 if (monitor_gain < 0 || monitor_gain > 255) 140 errx(1, "monitor volume must be between 0 and 255"); 141 break; 142 case 'P': 143 decode_int(optarg, &precision); 144 if (precision != 4 && precision != 8 && 145 precision != 16 && precision != 24 && 146 precision != 32) 147 errx(1, "precision must be between 4, 8, 16, 24 or 32"); 148 break; 149 case 'p': 150 len = strlen(optarg); 151 152 if (strncmp(optarg, "mic", len) == 0) 153 port |= AUDIO_MICROPHONE; 154 else if (strncmp(optarg, "cd", len) == 0 || 155 strncmp(optarg, "internal-cd", len) == 0) 156 port |= AUDIO_CD; 157 else if (strncmp(optarg, "line", len) == 0) 158 port |= AUDIO_LINE_IN; 159 else 160 errx(1, 161 "port must be `cd', `internal-cd', `mic', or `line'"); 162 break; 163 case 'q': 164 qflag++; 165 break; 166 case 's': 167 decode_int(optarg, &sample_rate); 168 if (sample_rate < 0 || sample_rate > 48000 * 2) /* XXX */ 169 errx(1, "sample rate must be between 0 and 96000"); 170 break; 171 case 't': 172 no_time_limit = 0; 173 decode_time(optarg, &record_time); 174 break; 175 case 'V': 176 verbose++; 177 break; 178 case 'v': 179 decode_int(optarg, &gain); 180 if (gain < 0 || gain > 255) 181 errx(1, "volume must be between 0 and 255"); 182 break; 183 /* case 'h': */ 184 default: 185 usage(); 186 /* NOTREACHED */ 187 } 188 } 189 argc -= optind; 190 argv += optind; 191 192 /* 193 * open the audio device, and control device 194 */ 195 if (device == NULL && (device = getenv("AUDIODEVICE")) == NULL && 196 (device = getenv("AUDIODEV")) == NULL) /* Sun compatibility */ 197 device = defdevice; 198 199 audiofd = open(device, O_RDONLY); 200 if (audiofd < 0 && device == defdevice) { 201 device = _PATH_SOUND0; 202 audiofd = open(device, O_RDONLY); 203 } 204 if (audiofd < 0) 205 err(1, "failed to open %s", device); 206 207 /* 208 * work out the buffer size to use, and allocate it. also work out 209 * what the old monitor gain value is, so that we can reset it later. 210 */ 211 if (ioctl(audiofd, AUDIO_GETINFO, &oinfo) < 0) 212 err(1, "failed to get audio info"); 213 bufsize = oinfo.record.buffer_size; 214 if (bufsize < 32 * 1024) 215 bufsize = 32 * 1024; 216 omonitor_gain = oinfo.monitor_gain; 217 218 buffer = malloc(bufsize); 219 if (buffer == NULL) 220 err(1, "couldn't malloc buffer of %d size", (int)bufsize); 221 222 /* 223 * open the output file 224 */ 225 if (argc != 1) 226 usage(); 227 if (argv[0][0] != '-' && argv[0][1] != '\0') { 228 /* intuit the file type from the name */ 229 if (format == AUDIO_FORMAT_DEFAULT) 230 { 231 size_t flen = strlen(*argv); 232 const char *arg = *argv; 233 234 if (strcasecmp(arg + flen - 3, ".au") == 0) 235 format = AUDIO_FORMAT_SUN; 236 else if (strcasecmp(arg + flen - 4, ".wav") == 0) 237 format = AUDIO_FORMAT_WAV; 238 } 239 outfd = open(*argv, O_CREAT|(aflag ? O_APPEND : O_TRUNC)|O_WRONLY, 0666); 240 if (outfd < 0) 241 err(1, "could not open %s", *argv); 242 } else 243 outfd = STDOUT_FILENO; 244 245 /* 246 * convert the encoding string into a value. 247 */ 248 if (encoding_str) { 249 encoding = audio_enc_to_val(encoding_str); 250 if (encoding == -1) 251 errx(1, "unknown encoding, bailing..."); 252 } 253 else 254 encoding = AUDIO_ENCODING_ULAW; 255 256 /* 257 * set up audio device for recording with the speified parameters 258 */ 259 AUDIO_INITINFO(&info); 260 261 /* 262 * for these, get the current values for stuffing into the header 263 */ 264 #define SETINFO(x) if (x) info.record.x = x; else x = oinfo.record.x 265 SETINFO (sample_rate); 266 SETINFO (channels); 267 SETINFO (precision); 268 SETINFO (encoding); 269 SETINFO (gain); 270 SETINFO (port); 271 SETINFO (balance); 272 #undef SETINFO 273 274 if (monitor_gain) 275 info.monitor_gain = monitor_gain; 276 else 277 monitor_gain = oinfo.monitor_gain; 278 279 info.mode = AUMODE_RECORD; 280 if (ioctl(audiofd, AUDIO_SETINFO, &info) < 0) 281 err(1, "failed to set audio info"); 282 283 signal(SIGINT, cleanup); 284 write_header(); 285 total_size = 0; 286 287 if (verbose && conv_func) { 288 const char *s = NULL; 289 290 if (conv_func == swap_bytes) 291 s = "swap bytes (16 bit)"; 292 else if (conv_func == swap_bytes32) 293 s = "swap bytes (32 bit)"; 294 else if (conv_func == change_sign16_be) 295 s = "change sign (big-endian, 16 bit)"; 296 else if (conv_func == change_sign16_le) 297 s = "change sign (little-endian, 16 bit)"; 298 else if (conv_func == change_sign32_be) 299 s = "change sign (big-endian, 32 bit)"; 300 else if (conv_func == change_sign32_le) 301 s = "change sign (little-endian, 32 bit)"; 302 else if (conv_func == change_sign16_swap_bytes_be) 303 s = "change sign & swap bytes (big-endian, 16 bit)"; 304 else if (conv_func == change_sign16_swap_bytes_le) 305 s = "change sign & swap bytes (little-endian, 16 bit)"; 306 else if (conv_func == change_sign32_swap_bytes_be) 307 s = "change sign (big-endian, 32 bit)"; 308 else if (conv_func == change_sign32_swap_bytes_le) 309 s = "change sign & swap bytes (little-endian, 32 bit)"; 310 311 if (s) 312 fprintf(stderr, "%s: converting, using function: %s\n", 313 getprogname(), s); 314 else 315 fprintf(stderr, "%s: using unnamed conversion " 316 "function\n", getprogname()); 317 } 318 319 if (verbose) 320 fprintf(stderr, 321 "sample_rate=%d channels=%d precision=%d encoding=%s\n", 322 info.record.sample_rate, info.record.channels, 323 info.record.precision, 324 audio_enc_from_val(info.record.encoding)); 325 326 if (!no_time_limit && verbose) 327 fprintf(stderr, "recording for %lu seconds, %lu microseconds\n", 328 (u_long)record_time.tv_sec, (u_long)record_time.tv_usec); 329 330 (void)gettimeofday(&start_time, NULL); 331 while (no_time_limit || timeleft(&start_time, &record_time)) { 332 if (read(audiofd, buffer, bufsize) != bufsize) 333 err(1, "read failed"); 334 if (conv_func) 335 (*conv_func)(buffer, bufsize); 336 if (write(outfd, buffer, bufsize) != bufsize) 337 err(1, "write failed"); 338 total_size += bufsize; 339 } 340 cleanup(0); 341 } 342 343 int 344 timeleft(start_tvp, record_tvp) 345 struct timeval *start_tvp; 346 struct timeval *record_tvp; 347 { 348 struct timeval now, diff; 349 350 (void)gettimeofday(&now, NULL); 351 timersub(&now, start_tvp, &diff); 352 timersub(record_tvp, &diff, &now); 353 354 return (now.tv_sec > 0 || (now.tv_sec == 0 && now.tv_usec > 0)); 355 } 356 357 void 358 cleanup(signo) 359 int signo; 360 { 361 362 rewrite_header(); 363 close(outfd); 364 if (omonitor_gain) { 365 AUDIO_INITINFO(&info); 366 info.monitor_gain = omonitor_gain; 367 if (ioctl(audiofd, AUDIO_SETINFO, &info) < 0) 368 err(1, "failed to reset audio info"); 369 } 370 close(audiofd); 371 exit(0); 372 } 373 374 int 375 write_header_sun(hdrp, lenp, leftp) 376 void **hdrp; 377 size_t *lenp; 378 int *leftp; 379 { 380 static int warned = 0; 381 static sun_audioheader auh; 382 int sunenc, oencoding = encoding; 383 384 /* only perform conversions if we don't specify the encoding */ 385 switch (encoding) { 386 case AUDIO_ENCODING_ULINEAR_LE: 387 #if BYTE_ORDER == LITTLE_ENDIAN 388 case AUDIO_ENCODING_ULINEAR: 389 #endif 390 if (precision == 16) 391 conv_func = change_sign16_swap_bytes_le; 392 else if (precision == 32) 393 conv_func = change_sign32_swap_bytes_le; 394 if (conv_func) 395 encoding = AUDIO_ENCODING_SLINEAR_BE; 396 break; 397 398 case AUDIO_ENCODING_ULINEAR_BE: 399 #if BYTE_ORDER == BIG_ENDIAN 400 case AUDIO_ENCODING_ULINEAR: 401 #endif 402 if (precision == 16) 403 conv_func = change_sign16_be; 404 else if (precision == 32) 405 conv_func = change_sign32_be; 406 if (conv_func) 407 encoding = AUDIO_ENCODING_SLINEAR_BE; 408 break; 409 410 case AUDIO_ENCODING_SLINEAR_LE: 411 #if BYTE_ORDER == LITTLE_ENDIAN 412 case AUDIO_ENCODING_SLINEAR: 413 #endif 414 if (precision == 16) 415 conv_func = swap_bytes; 416 else if (precision == 32) 417 conv_func = swap_bytes32; 418 if (conv_func) 419 encoding = AUDIO_ENCODING_SLINEAR_BE; 420 break; 421 422 #if BYTE_ORDER == BIG_ENDIAN 423 case AUDIO_ENCODING_SLINEAR: 424 encoding = AUDIO_ENCODING_SLINEAR_BE; 425 break; 426 #endif 427 } 428 429 /* if we can't express this as a Sun header, don't write any */ 430 if (audio_encoding_to_sun(encoding, precision, &sunenc) != 0) { 431 if (!qflag && !warned) { 432 const char *s = audio_enc_from_val(oencoding); 433 434 if (s == NULL) 435 s = "(unknown)"; 436 warnx("failed to convert to sun encoding from %s " 437 "(precision %d);\nSun audio header not written", 438 s, precision); 439 } 440 format = AUDIO_FORMAT_NONE; 441 conv_func = 0; 442 warned = 1; 443 return -1; 444 } 445 446 auh.magic = htonl(AUDIO_FILE_MAGIC); 447 if (outfd == STDOUT_FILENO) 448 auh.data_size = htonl(AUDIO_UNKNOWN_SIZE); 449 else 450 auh.data_size = htonl(total_size); 451 auh.encoding = htonl(sunenc); 452 auh.sample_rate = htonl(sample_rate); 453 auh.channels = htonl(channels); 454 if (header_info) { 455 int len, infolen; 456 457 infolen = ((len = strlen(header_info)) + 7) & 0xfffffff8; 458 *leftp = infolen - len; 459 auh.hdr_size = htonl(sizeof(auh) + infolen); 460 } else { 461 *leftp = sizeof(default_info); 462 auh.hdr_size = htonl(sizeof(auh) + *leftp); 463 } 464 *(sun_audioheader **)hdrp = &auh; 465 *lenp = sizeof auh; 466 return 0; 467 } 468 469 int 470 write_header_wav(hdrp, lenp, leftp) 471 void **hdrp; 472 size_t *lenp; 473 int *leftp; 474 { 475 /* 476 * WAV header we write looks like this: 477 * 478 * bytes purpose 479 * 0-3 "RIFF" 480 * 4-7 file length (minus 8) 481 * 8-15 "WAVEfmt " 482 * 16-19 format size 483 * 20-21 format tag 484 * 22-23 number of channels 485 * 24-27 sample rate 486 * 28-31 average bytes per second 487 * 32-33 block alignment 488 * 34-35 bits per sample 489 * 490 * then for ULAW and ALAW outputs, we have an extended chunk size 491 * and a WAV "fact" to add: 492 * 493 * 36-37 length of extension (== 0) 494 * 38-41 "fact" 495 * 42-45 fact size 496 * 46-49 number of samples written 497 * 50-53 "data" 498 * 54-57 data length 499 * 58- raw audio data 500 * 501 * for PCM outputs we have just the data remaining: 502 * 503 * 36-39 "data" 504 * 40-43 data length 505 * 44- raw audio data 506 * 507 * RIFF\^@^C^@WAVEfmt ^P^@^@^@^A^@^B^@D<AC>^@^@^P<B1>^B^@^D^@^P^@data^@^@^C^@^@^@^@^@^@^@^@^@^@ 508 */ 509 char wavheaderbuf[64], *p = wavheaderbuf; 510 const char *riff = "RIFF", 511 *wavefmt = "WAVEfmt ", 512 *fact = "fact", 513 *data = "data"; 514 u_int32_t filelen, fmtsz, sps, abps, factsz = 4, nsample, datalen; 515 u_int16_t fmttag, nchan, align, bps, extln = 0; 516 517 if (header_info) 518 warnx("header information not supported for WAV"); 519 *leftp = NULL; 520 521 switch (precision) { 522 case 8: 523 bps = 8; 524 break; 525 case 16: 526 bps = 16; 527 break; 528 case 32: 529 bps = 32; 530 break; 531 default: 532 { 533 static int warned = 0; 534 535 if (warned == 0) { 536 warnx("can not support precision of %d", precision); 537 warned = 1; 538 } 539 } 540 return (-1); 541 } 542 543 switch (encoding) { 544 case AUDIO_ENCODING_ULAW: 545 fmttag = WAVE_FORMAT_MULAW; 546 fmtsz = 18; 547 align = channels; 548 break; 549 550 case AUDIO_ENCODING_ALAW: 551 fmttag = WAVE_FORMAT_ALAW; 552 fmtsz = 18; 553 align = channels; 554 break; 555 556 /* 557 * we could try to support RIFX but it seems to be more portable 558 * to output little-endian data for WAV files. 559 */ 560 case AUDIO_ENCODING_ULINEAR_BE: 561 #if BYTE_ORDER == BIG_ENDIAN 562 case AUDIO_ENCODING_ULINEAR: 563 #endif 564 if (bps == 16) 565 conv_func = change_sign16_swap_bytes_be; 566 else if (bps == 32) 567 conv_func = change_sign32_swap_bytes_be; 568 goto fmt_pcm; 569 570 case AUDIO_ENCODING_SLINEAR_BE: 571 #if BYTE_ORDER == BIG_ENDIAN 572 case AUDIO_ENCODING_SLINEAR: 573 #endif 574 if (bps == 16) 575 conv_func = swap_bytes; 576 else if (bps == 32) 577 conv_func = swap_bytes32; 578 goto fmt_pcm; 579 580 case AUDIO_ENCODING_ULINEAR_LE: 581 #if BYTE_ORDER == LITTLE_ENDIAN 582 case AUDIO_ENCODING_ULINEAR: 583 #endif 584 if (bps == 16) 585 conv_func = change_sign16_le; 586 else if (bps == 32) 587 conv_func = change_sign32_le; 588 /* FALLTHROUGH */ 589 590 case AUDIO_ENCODING_SLINEAR_LE: 591 case AUDIO_ENCODING_PCM16: 592 #if BYTE_ORDER == LITTLE_ENDIAN 593 case AUDIO_ENCODING_SLINEAR: 594 #endif 595 fmt_pcm: 596 fmttag = WAVE_FORMAT_PCM; 597 fmtsz = 16; 598 align = channels * (bps / 8); 599 break; 600 601 default: 602 { 603 static int warned = 0; 604 605 if (warned == 0) { 606 const char *s = wav_enc_from_val(encoding); 607 608 if (s == NULL) 609 warnx("can not support encoding of %s", s); 610 else 611 warnx("can not support encoding of %d", encoding); 612 warned = 1; 613 } 614 } 615 format = AUDIO_FORMAT_NONE; 616 return (-1); 617 } 618 619 nchan = channels; 620 sps = sample_rate; 621 622 /* data length */ 623 if (outfd == STDOUT_FILENO) 624 datalen = 0; 625 else 626 datalen = total_size; 627 628 /* file length */ 629 filelen = 4 + (8 + fmtsz) + (8 + datalen); 630 if (fmttag != WAVE_FORMAT_PCM) 631 filelen += 8 + factsz; 632 633 abps = (double)align*sample_rate / (double)1 + 0.5; 634 635 nsample = (datalen / bps) / sample_rate; 636 637 /* 638 * now we've calculated the info, write it out! 639 */ 640 #define put32(x) do { \ 641 u_int32_t _f; \ 642 putle32(_f, (x)); \ 643 memcpy(p, &_f, 4); \ 644 } while (0) 645 #define put16(x) do { \ 646 u_int16_t _f; \ 647 putle16(_f, (x)); \ 648 memcpy(p, &_f, 2); \ 649 } while (0) 650 memcpy(p, riff, 4); 651 p += 4; /* 4 */ 652 put32(filelen); 653 p += 4; /* 8 */ 654 memcpy(p, wavefmt, 8); 655 p += 8; /* 16 */ 656 put32(fmtsz); 657 p += 4; /* 20 */ 658 put16(fmttag); 659 p += 2; /* 22 */ 660 put16(nchan); 661 p += 2; /* 24 */ 662 put32(sps); 663 p += 4; /* 28 */ 664 put32(abps); 665 p += 4; /* 32 */ 666 put16(align); 667 p += 2; /* 34 */ 668 put16(bps); 669 p += 2; /* 36 */ 670 /* NON PCM formats have an extended chunk; write it */ 671 if (fmttag != WAVE_FORMAT_PCM) { 672 put16(extln); 673 p += 2; /* 38 */ 674 memcpy(p, fact, 4); 675 p += 4; /* 42 */ 676 put32(factsz); 677 p += 4; /* 46 */ 678 put32(nsample); 679 p += 4; /* 50 */ 680 } 681 memcpy(p, data, 4); 682 p += 4; /* 40/54 */ 683 put32(datalen); 684 p += 4; /* 44/58 */ 685 #undef put32 686 #undef put16 687 688 *hdrp = wavheaderbuf; 689 *lenp = (p - wavheaderbuf); 690 691 return 0; 692 } 693 694 void 695 write_header() 696 { 697 struct iovec iv[3]; 698 int veclen, left, tlen; 699 void *hdr; 700 size_t hdrlen; 701 702 switch (format) { 703 case AUDIO_FORMAT_DEFAULT: 704 case AUDIO_FORMAT_SUN: 705 if (write_header_sun(&hdr, &hdrlen, &left) != 0) 706 return; 707 break; 708 case AUDIO_FORMAT_WAV: 709 if (write_header_wav(&hdr, &hdrlen, &left) != 0) 710 return; 711 break; 712 case AUDIO_FORMAT_NONE: 713 return; 714 default: 715 errx(1, "unknown audio format"); 716 } 717 718 veclen = 0; 719 tlen = 0; 720 721 if (hdrlen != 0) { 722 iv[veclen].iov_base = hdr; 723 iv[veclen].iov_len = hdrlen; 724 tlen += iv[veclen++].iov_len; 725 } 726 if (header_info) { 727 iv[veclen].iov_base = header_info; 728 iv[veclen].iov_len = (int)strlen(header_info) + 1; 729 tlen += iv[veclen++].iov_len; 730 } 731 if (left) { 732 iv[veclen].iov_base = default_info; 733 iv[veclen].iov_len = left; 734 tlen += iv[veclen++].iov_len; 735 } 736 737 if (tlen == 0) 738 return; 739 740 if (writev(outfd, iv, veclen) != tlen) 741 err(1, "could not write audio header"); 742 } 743 744 void 745 rewrite_header() 746 { 747 748 /* can't do this here! */ 749 if (outfd == STDOUT_FILENO) 750 return; 751 752 if (lseek(outfd, SEEK_SET, 0) < 0) 753 err(1, "could not seek to start of file for header rewrite"); 754 write_header(); 755 } 756 757 void 758 usage() 759 { 760 761 fprintf(stderr, "Usage: %s [-afhqV] [options] {files ...|-}\n", 762 getprogname()); 763 fprintf(stderr, "Options:\n\t" 764 "-F format\n\t" 765 "-b balance (0-63)\n\t" 766 "-c channels\n\t" 767 "-d audio device\n\t" 768 "-e encoding\n\t" 769 "-i header information\n\t" 770 "-m monitor volume\n\t" 771 "-P precision bits (4, 8, 16, 24 or 32)\n\t" 772 "-p input port\n\t" 773 "-s sample rate\n\t" 774 "-t recording time\n\t" 775 "-v volume\n"); 776 exit(EXIT_FAILURE); 777 } 778