bool audio_format_parse(struct audio_format *dest, const char *src, bool mask, GError **error_r) { uint32_t rate; enum sample_format sample_format; uint8_t channels; audio_format_clear(dest); /* parse sample rate */ #if GCC_CHECK_VERSION(4,7) /* workaround -Wmaybe-uninitialized false positive */ rate = 0; #endif if (!parse_sample_rate(src, mask, &rate, &src, error_r)) return false; if (*src++ != ':') { g_set_error(error_r, audio_parser_quark(), 0, "Sample format missing"); return false; } /* parse sample format */ #if GCC_CHECK_VERSION(4,7) /* workaround -Wmaybe-uninitialized false positive */ sample_format = SAMPLE_FORMAT_UNDEFINED; #endif if (!parse_sample_format(src, mask, &sample_format, &src, error_r)) return false; if (*src++ != ':') { g_set_error(error_r, audio_parser_quark(), 0, "Channel count missing"); return false; } /* parse channel count */ if (!parse_channel_count(src, mask, &channels, &src, error_r)) return false; if (*src != 0) { g_set_error(error_r, audio_parser_quark(), 0, "Extra data after channel count: %s", src); return false; } audio_format_init(dest, rate, sample_format, channels); assert(mask ? audio_format_mask_valid(dest) : audio_format_valid(dest)); return true; }
bool audio_format_parse(struct audio_format *dest, const char *src, bool mask, GError **error_r) { uint32_t rate; enum sample_format sample_format; uint8_t channels; audio_format_clear(dest); /* parse sample rate */ if (!parse_sample_rate(src, mask, &rate, &src, error_r)) return false; if (*src++ != ':') { g_set_error(error_r, audio_parser_quark(), 0, "Sample format missing"); return false; } /* parse sample format */ if (!parse_sample_format(src, mask, &sample_format, &src, error_r)) return false; if (*src++ != ':') { g_set_error(error_r, audio_parser_quark(), 0, "Channel count missing"); return false; } /* parse channel count */ if (!parse_channel_count(src, mask, &channels, &src, error_r)) return false; if (*src != 0) { g_set_error(error_r, audio_parser_quark(), 0, "Extra data after channel count: %s", src); return false; } audio_format_init(dest, rate, sample_format, channels); assert(mask ? audio_format_mask_valid(dest) : audio_format_valid(dest)); return true; }
/* * Record from the audio device to a file. */ int main(int argc, char **argv) { int i; int cnt; int err; int file_type; int ofd; int swapBytes = FALSE; double vol; struct stat st; struct pollfd pfd; char *cp; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); /* Get the program name */ prog = strrchr(argv[0], '/'); if (prog == NULL) prog = argv[0]; else prog++; Stdout = MGET("(stdout)"); /* first check AUDIODEV environment for audio device name */ if (cp = getenv("AUDIODEV")) { Audio_dev = cp; } /* Set the endian nature of the machine */ if ((ulong_t)1 != htonl((ulong_t)1)) { NetEndian = FALSE; } err = 0; while ((i = getopt(argc, argv, prog_opts)) != EOF) { switch (i) { case 'v': if (parse_unsigned(optarg, &Volume, "-v")) { err++; } else if (Volume > MAX_GAIN) { Error(stderr, MGET("%s: invalid value for " "-v\n"), prog); err++; } break; case 't': Time = audio_str_to_secs(optarg); if ((Time == HUGE_VAL) || (Time < 0.)) { Error(stderr, MGET("%s: invalid value for " "-t\n"), prog); err++; } break; case 'd': Audio_dev = optarg; break; case 'f': Force = TRUE; break; case 'a': Append = TRUE; break; case 'i': Info = optarg; /* set information string */ Ilen = strlen(Info); break; case 's': if (parse_sample_rate(optarg, &Sample_rate)) { err++; } break; case 'c': if (strncmp(optarg, "mono", strlen(optarg)) == 0) { Channels = 1; } else if (strncmp(optarg, "stereo", strlen(optarg)) == 0) { Channels = 2; } else if (parse_unsigned(optarg, &Channels, "-c")) { err++; } else if ((Channels != 1) && (Channels != 2)) { Error(stderr, "%s: invalid value for -c\n", prog); err++; } break; case 'e': if (strncmp(optarg, "ulinear", strlen(optarg)) == 0) { Encoding = AUDIO_ENCODING_LINEAR8; Precision = 8; } else if (strncmp(optarg, "linear8", strlen("linear8")) == 0) { Encoding = AUDIO_ENCODING_LINEAR; Precision = 8; } else if (strncmp(optarg, "ulaw", strlen(optarg)) == 0) { Encoding = AUDIO_ENCODING_ULAW; Precision = 8; } else if (strncmp(optarg, "alaw", strlen(optarg)) == 0) { Encoding = AUDIO_ENCODING_ALAW; Precision = 8; } else if ((strncmp(optarg, "linear", strlen(optarg)) == 0) || (strncmp(optarg, "pcm", strlen(optarg)) == 0)) { Encoding = AUDIO_ENCODING_LINEAR; Precision = 16; } else { Error(stderr, MGET("%s: invalid value for " "-e\n"), prog); err++; } break; case 'T': if (strncmp(optarg, "au", strlen(optarg)) == 0) { File_type = FILE_AU; } else if (strncmp(optarg, "wav", strlen(optarg)) == 0) { File_type = FILE_WAV; } else if (strncmp(optarg, "aif", strlen(optarg)) == 0) { File_type = FILE_AIFF; } else if (strncmp(optarg, "aiff", strlen(optarg)) == 0) { File_type = FILE_AIFF; } else { Error(stderr, MGET("%s: invalid value for " "-T\n"), prog); err++; } File_type_set = TRUE; break; case '?': usage(); /*NOTREACHED*/ } } if (Append && (Info != NULL)) { Error(stderr, MGET("%s: cannot specify -a and -i\n"), prog); err++; } if (err > 0) exit(1); argc -= optind; /* update arg pointers */ argv += optind; /* Open the output file */ if (argc <= 0) { Ofile = Stdout; } else { Ofile = *argv++; argc--; /* Interpret "-" filename to mean stdout */ if (strcmp(Ofile, "-") == 0) Ofile = Stdout; /* if -T not set then we use the file suffix */ if (File_type_set == FALSE) { char *file_name; char *start; /* get the file name without the path */ file_name = basename(Ofile); /* get the true suffix */ start = strrchr(file_name, '.'); /* if no '.' then there's no suffix */ if (start) { /* is this a .au file? */ if (strcasecmp(start, ".au") == 0) { File_type = FILE_AU; } else if (strcasecmp(start, ".wav") == 0) { File_type = FILE_WAV; } else if (strcasecmp(start, ".aif") == 0) { File_type = FILE_AIFF; } else if (strcasecmp(start, ".aiff") == 0) { File_type = FILE_AIFF; } else { /* the default is .au */ File_type = FILE_AU; } } else { /* no suffix, so default to .au */ File_type = FILE_AU; } } } if (Ofile == Stdout) { ofd = fileno(stdout); Append = FALSE; } else { ofd = open(Ofile, (O_RDWR | O_CREAT | (Append ? 0 : O_TRUNC)), 0666); if (ofd < 0) { Error(stderr, MGET("%s: cannot open "), prog); perror(Ofile); exit(1); } if (Append) { /* * Check to make sure we're appending to an audio file. * It must be a regular file (if zero-length, simply * write it from scratch). Also, its file header * must match the input device configuration. */ if ((fstat(ofd, &st) < 0) || (!S_ISREG(st.st_mode))) { Error(stderr, MGET("%s: %s is not a regular file\n"), prog, Ofile); exit(1); } if (st.st_size == 0) { Append = FALSE; goto openinput; } err = audio_read_filehdr(ofd, &File_hdr, &file_type, (char *)NULL, 0); if (err != AUDIO_SUCCESS) { Error(stderr, MGET("%s: %s is not a valid audio file\n"), prog, Ofile); exit(1); } /* we need to make sure file types match */ if (File_type_set == TRUE) { /* specified by the command line, must match */ if (File_type != file_type) { Error(stderr, MGET("%s: file types must match\n"), prog); exit(1); } } else { /* not specified, so force */ File_type = file_type; } /* * Set the format state to the format * in the file header. */ Sample_rate = File_hdr.sample_rate; Channels = File_hdr.channels; Encoding = File_hdr.encoding; Precision = File_hdr.bytes_per_unit * 8; /* make sure we support the encoding method */ switch (Encoding) { case AUDIO_ENCODING_LINEAR8: case AUDIO_ENCODING_ULAW: case AUDIO_ENCODING_ALAW: case AUDIO_ENCODING_LINEAR: break; default: { char msg[AUDIO_MAX_ENCODE_INFO]; (void) audio_enc_to_str(&File_hdr, msg); Error(stderr, MGET("%s: Append is not supported " "for "), prog); Error(stderr, MGET("this file encoding:\n\t" "[%s]\n"), msg); exit(1); } } /* Get the current size, if possible */ Oldsize = File_hdr.data_size; if ((Oldsize == AUDIO_UNKNOWN_SIZE) && ((err = (int)lseek(ofd, 0L, SEEK_CUR)) >= 0)) { if (err < 0) { Error(stderr, MGET("%s: %s is not a valid audio " "file\n"), prog, Ofile); exit(1); } Oldsize = st.st_size - err; } /* Seek to end to start append */ if ((int)lseek(ofd, st.st_size, SEEK_SET) < 0) { Error(stderr, MGET("%s: cannot find end of %s\n"), prog, Ofile); exit(1); } } } openinput: /* Validate and open the audio device */ err = stat(Audio_dev, &st); if (err < 0) { Error(stderr, MGET("%s: cannot open "), prog); perror(Audio_dev); exit(1); } if (!S_ISCHR(st.st_mode)) { Error(stderr, MGET("%s: %s is not an audio device\n"), prog, Audio_dev); exit(1); } /* * For the mixer environment we need to open the audio device before * the control device. If successful we pause right away to keep * from queueing up a bunch of useless data. */ Audio_fd = open(Audio_dev, O_RDONLY | O_NONBLOCK); if (Audio_fd < 0) { if (errno == EBUSY) { Error(stderr, MGET("%s: %s is busy\n"), prog, Audio_dev); } else { Error(stderr, MGET("%s: error opening "), prog); perror(Audio_dev); } exit(1); } if (audio_pause_record(Audio_fd) != AUDIO_SUCCESS) { Error(stderr, MGET("%s: not able to pause recording\n"), prog); exit(1); } /* get the current settings */ if (audio_get_record_config(Audio_fd, &Save_hdr) != AUDIO_SUCCESS) { (void) close(Audio_fd); Error(stderr, MGET("%s: %s is not an audio device\n"), prog, Audio_dev); exit(1); } /* make a copy into the working data structure */ bcopy(&Save_hdr, &Dev_hdr, sizeof (Save_hdr)); /* flush any queued audio data */ if (audio_flush_record(Audio_fd) != AUDIO_SUCCESS) { Error(stderr, MGET("%s: not able to flush recording\n"), prog); exit(1); } if (Sample_rate != 0) { Dev_hdr.sample_rate = Sample_rate; } if (Channels != 0) { Dev_hdr.channels = Channels; } if (Precision != 0) { Dev_hdr.bytes_per_unit = Precision / 8; } if (Encoding != 0) { Dev_hdr.encoding = Encoding; } /* * For .wav we always record 8-bit linear as unsigned. Thus we * force unsigned linear to make life a lot easier on the user. * * For .aiff we set the default to 8-bit signed linear, not * u-law, if Encoding isn't already set. */ if (File_type == FILE_WAV && Dev_hdr.encoding == AUDIO_ENCODING_LINEAR && Dev_hdr.bytes_per_unit == 1) { /* force to unsigned */ Dev_hdr.encoding = AUDIO_ENCODING_LINEAR8; } else if (File_type == FILE_AIFF && Encoding == 0) { Dev_hdr.encoding = AUDIO_ENCODING_LINEAR; if (Precision == 0) { Dev_hdr.bytes_per_unit = AUDIO_PRECISION_8 / 8; } } if (audio_set_record_config(Audio_fd, &Dev_hdr) != AUDIO_SUCCESS) { Error(stderr, MGET( "%s: Audio format not supported by the audio device\n"), prog); exit(1); } if (audio_resume_record(Audio_fd) != AUDIO_SUCCESS) { Error(stderr, MGET("%s: not able to resume recording\n"), prog); exit(1); } /* If appending to an existing file, check the configuration */ if (Append) { char msg[AUDIO_MAX_ENCODE_INFO]; switch (audio_cmp_hdr(&Dev_hdr, &File_hdr)) { case 0: /* configuration matches */ break; case 1: /* all but sample rate matches */ if (Force) { Error(stderr, MGET("%s: WARNING: appending " "%.3fkHz data to %s (%.3fkHz)\n"), prog, ((double)Dev_hdr.sample_rate / 1000.), Ofile, ((double)File_hdr.sample_rate / 1000.)); break; } /* if not -f, fall through */ default: /* encoding mismatch */ (void) audio_enc_to_str(&Dev_hdr, msg); Error(stderr, MGET("%s: device encoding [%s]\n"), prog, msg); (void) audio_enc_to_str(&File_hdr, msg); Error(stderr, MGET("\tdoes not match file encoding [%s]\n"), msg); exit(1); } } else if (!isatty(ofd)) { if (audio_write_filehdr(ofd, &Dev_hdr, File_type, Info, Ilen) != AUDIO_SUCCESS) { Error(stderr, MGET("%s: error writing header for %s\n"), prog, Ofile); exit(1); } } /* * 8-bit audio isn't a problem, however 16-bit audio is. If the file * is an endian that is different from the machine then the bytes * will need to be swapped. * * Note: The following if() could be simplified, but then it gets * to be very hard to read. So it's left as is. */ if (Dev_hdr.bytes_per_unit == 2 && ((!NetEndian && File_type == FILE_AIFF) || (!NetEndian && File_type == FILE_AU) || (NetEndian && File_type == FILE_WAV))) { swapBytes = TRUE; } /* If -v flag, set the record volume now */ if (Volume != INT_MAX) { vol = (double)Volume / (double)MAX_GAIN; (void) audio_get_record_gain(Audio_fd, &Savevol); err = audio_set_record_gain(Audio_fd, &vol); if (err != AUDIO_SUCCESS) { Error(stderr, MGET("%s: could not set record volume for %s\n"), prog, Audio_dev); exit(1); } } if (isatty(ofd)) { Error(stderr, MGET("%s: No files and stdout is a tty\n"), prog); exit(1); } /* Set up SIGINT handler so that final buffers may be flushed */ (void) signal(SIGINT, sigint); /* * At this point, we're (finally) ready to copy the data. * Init a poll() structure, to use when there's nothing to read. */ if (Time > 0) Limit = audio_secs_to_bytes(&Dev_hdr, Time); pfd.fd = Audio_fd; pfd.events = POLLIN; while ((Limit == AUDIO_UNKNOWN_SIZE) || (Limit != 0)) { /* Fill the buffer or read to the time limit */ cnt = read(Audio_fd, (char *)buf, ((Limit != AUDIO_UNKNOWN_SIZE) && (Limit < sizeof (buf)) ? (int)Limit : sizeof (buf))); if (cnt == 0) /* normally, eof can't happen */ break; /* If error, probably have to wait for input */ if (cnt < 0) { if (Cleanup) break; /* done if ^C seen */ switch (errno) { case EAGAIN: (void) poll(&pfd, 1L, -1); break; case EOVERFLOW: /* Possibly a Large File */ Error(stderr, MGET("%s: error reading"), prog); perror("Large File"); exit(1); default: Error(stderr, MGET("%s: error reading"), prog); perror(Audio_dev); exit(1); } continue; } /* Swab the output if required. */ if (swapBytes) { swab((char *)buf, swapBuf, cnt); err = write(ofd, swapBuf, cnt); } else { err = write(ofd, (char *)buf, cnt); } if (err < 0) { Error(stderr, MGET("%s: error writing "), prog); perror(Ofile); exit(1); } if (err != cnt) { Error(stderr, MGET("%s: error writing "), prog); perror(Ofile); break; } Size += cnt; if (Limit != AUDIO_UNKNOWN_SIZE) Limit -= cnt; } /* Attempt to rewrite the data_size field of the file header */ if (!Append || (Oldsize != AUDIO_UNKNOWN_SIZE)) { if (Append) Size += Oldsize; (void) audio_rewrite_filesize(ofd, File_type, Size, Dev_hdr.channels, Dev_hdr.bytes_per_unit); } (void) close(ofd); /* close input file */ /* Check for error during record */ if (audio_get_record_error(Audio_fd, (unsigned *)&err) != AUDIO_SUCCESS) Error(stderr, MGET("%s: error reading device status\n"), prog); else if (err) Error(stderr, MGET("%s: WARNING: Data overflow occurred\n"), prog); /* Reset record volume, encoding */ if (Volume != INT_MAX) (void) audio_set_record_gain(Audio_fd, &Savevol); if (audio_cmp_hdr(&Save_hdr, &Dev_hdr) != 0) { (void) audio_set_record_config(Audio_fd, &Save_hdr); } (void) close(Audio_fd); return (0); }