405 lines
9.7 KiB
C
405 lines
9.7 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)record.c 1.1 94/10/31 Copyr 1989 Sun Micro";
|
|
#endif
|
|
/* Copyright (c) 1989 by Sun Microsystems, Inc. */
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/file.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/param.h>
|
|
#include <sys/signal.h>
|
|
|
|
#include <stropts.h>
|
|
#include <poll.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <multimedia/libaudio.h>
|
|
#include <multimedia/audio_device.h>
|
|
|
|
|
|
#define Error (void) fprintf
|
|
|
|
|
|
/* Local variables */
|
|
char *prog;
|
|
char prog_desc[] = "Record an audio file";
|
|
char prog_opts[] = "aft:v:d:i:?"; /* getopt() flags */
|
|
|
|
char *Stdout = "stdout";
|
|
|
|
/* XXX - the input buffer size should depend on sample_rate */
|
|
unsigned char buf[1024 * 64];
|
|
|
|
|
|
#define MAX_GAIN (100) /* maximum gain */
|
|
|
|
|
|
char *Info = NULL; /* pointer to info data */
|
|
unsigned Ilen = 0; /* length of info data */
|
|
unsigned Volume = ~0; /* record volume */
|
|
double Savevol; /* saved record volume */
|
|
int Append = FALSE; /* append to output file */
|
|
int Force = FALSE; /* ignore rate differences on append */
|
|
double Time = -1.; /* recording time */
|
|
unsigned Limit = AUDIO_UNKNOWN_SIZE; /* recording limit */
|
|
char *Audio_dev = "/dev/audio";
|
|
|
|
int Audio_fd = -1; /* file descriptor for audio device */
|
|
Audio_hdr Dev_hdr; /* audio header for device */
|
|
char *Ofile; /* current filename */
|
|
Audio_hdr File_hdr; /* audio header for file */
|
|
int Cleanup = FALSE; /* SIGINT sets this flag */
|
|
unsigned Size = 0; /* Size of output file */
|
|
unsigned Oldsize = 0; /* Size of input file, if append */
|
|
|
|
|
|
/* Global variables */
|
|
extern int getopt();
|
|
extern int optind;
|
|
extern char *optarg;
|
|
|
|
|
|
usage()
|
|
{
|
|
Error(stderr, "%s -- usage:\n\t%s ", prog_desc, prog);
|
|
Error(stderr, "\t[-a] [-v #] [-t #] [-i msg] [-d dev] [file]\n");
|
|
Error(stderr, "where:\n\t-a\tAppend to output file\n");
|
|
Error(stderr, "\t-f\tIgnore sample rate differences on append\n");
|
|
Error(stderr, "\t-v #\tSet record volume (0 - %d)\n", MAX_GAIN);
|
|
Error(stderr, "\t-t #\tSpecify record time (hh:mm:ss.dd)\n");
|
|
Error(stderr, "\t-i msg\tSpecify a file header information string\n");
|
|
Error(stderr, "\t-d dev\tSpecify audio device (default: /dev/audio)\n");
|
|
Error(stderr, "\tfile\tRecord to named file\n");
|
|
Error(stderr, "\t\tIf no file specified, write to stdout\n");
|
|
Error(stderr, "\t\tIf no -t specified, recording continues until ^C\n");
|
|
exit(1);
|
|
}
|
|
|
|
void
|
|
sigint()
|
|
{
|
|
/* If this is the first ^C, set a flag for the main loop */
|
|
if (!Cleanup && (Audio_fd >= 0)) {
|
|
/* flush input queues before exiting */
|
|
Cleanup = TRUE;
|
|
if (audio_pause_record(Audio_fd) == AUDIO_SUCCESS)
|
|
return;
|
|
Error(stderr, "%s: could not flush input buffer\n", prog);
|
|
}
|
|
|
|
/* If double ^C, really quit */
|
|
if ((Volume != ~0) && (Audio_fd >= 0)) {
|
|
(void) audio_set_record_gain(Audio_fd, &Savevol);
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Record from the audio device to a file.
|
|
*/
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
int i;
|
|
int cnt;
|
|
int err;
|
|
int ofd;
|
|
double vol;
|
|
struct stat st;
|
|
struct sigvec vec;
|
|
struct pollfd pfd;
|
|
|
|
|
|
prog = argv[0]; /* save program initiation name */
|
|
|
|
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, "%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, "%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 '?':
|
|
usage();
|
|
/*NOTREACHED*/
|
|
}
|
|
if (Append && (Info != NULL)) {
|
|
Error(stderr, "%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 (Ofile == Stdout) {
|
|
ofd = fileno(stdout);
|
|
Append = FALSE;
|
|
if (isatty(ofd)) {
|
|
Error(stderr,
|
|
"%s: Specify a filename or redirect stdout to a file\n", prog);
|
|
exit(1);
|
|
}
|
|
} else {
|
|
ofd = open(Ofile,
|
|
(O_RDWR | O_CREAT | (Append ? 0 : O_TRUNC)), 0666);
|
|
if (ofd < 0) {
|
|
Error(stderr, "%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, "%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,
|
|
(char *)NULL, 0);
|
|
|
|
if (err != AUDIO_SUCCESS) {
|
|
Error(stderr,
|
|
"%s: %s is not a valid audio file\n",
|
|
prog, Ofile);
|
|
exit(1);
|
|
}
|
|
/* Get the current size, if possible */
|
|
Oldsize = File_hdr.data_size;
|
|
if ((Oldsize == AUDIO_UNKNOWN_SIZE) &&
|
|
((err = (int)lseek(ofd, 0L, L_INCR)) >= 0))
|
|
Oldsize = st.st_size - err;
|
|
|
|
/* Seek to end to start append */
|
|
if ((int)lseek(ofd, st.st_size, L_SET) < 0) {
|
|
Error(stderr,
|
|
"%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, "%s: cannot open ", prog);
|
|
perror(Audio_dev);
|
|
exit(1);
|
|
}
|
|
if (!S_ISCHR(st.st_mode)) {
|
|
Error(stderr, "%s: %s is not an audio device\n", prog,
|
|
Audio_dev);
|
|
exit(1);
|
|
}
|
|
Audio_fd = open(Audio_dev, O_RDONLY | O_NDELAY);
|
|
if (Audio_fd < 0) {
|
|
if (errno == EBUSY) {
|
|
Error(stderr, "%s: %s is busy\n", prog, Audio_dev);
|
|
} else {
|
|
Error(stderr, "%s: error opening ", prog);
|
|
perror(Audio_dev);
|
|
}
|
|
exit(1);
|
|
}
|
|
if (audio_get_record_config(Audio_fd, &Dev_hdr) != AUDIO_SUCCESS) {
|
|
Error(stderr, "%s: %s is not an audio device\n",
|
|
prog, Audio_dev);
|
|
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,
|
|
"%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,
|
|
"%s: device encoding [%s]\n", prog, msg);
|
|
(void) audio_enc_to_str(&File_hdr, msg);
|
|
Error(stderr,
|
|
"\tdoes not match file encoding [%s]\n", msg);
|
|
exit(1);
|
|
}
|
|
} else {
|
|
if (audio_write_filehdr(ofd, &Dev_hdr, Info, Ilen) !=
|
|
AUDIO_SUCCESS) {
|
|
Error(stderr, "%s: error writing header for \n", prog);
|
|
perror(Ofile);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* If -v flag, set the record volume now */
|
|
if (Volume != ~0) {
|
|
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,
|
|
"%s: could not set record volume for %s\n",
|
|
prog, Audio_dev);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/* Set the device up for non-blocking reads */
|
|
(void) fcntl(Audio_fd, F_SETFL, fcntl(Audio_fd, F_GETFL, 0) | O_NDELAY);
|
|
|
|
/* Set up SIGINT handler so that final buffers may be flushed */
|
|
vec.sv_handler = sigint;
|
|
vec.sv_mask = 0;
|
|
vec.sv_flags = 0;
|
|
(void) sigvec(SIGINT, &vec, (struct sigvec *)NULL);
|
|
|
|
|
|
/*
|
|
* 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:
|
|
case EWOULDBLOCK:
|
|
(void) poll(&pfd, 1L, -1);
|
|
break;
|
|
default:
|
|
Error(stderr, "%s: error reading ", prog);
|
|
perror(Audio_dev);
|
|
exit(1);
|
|
}
|
|
continue;
|
|
}
|
|
err = write(ofd, (char *)buf, cnt);
|
|
if (err != cnt) {
|
|
Error(stderr, "%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, Size);
|
|
}
|
|
|
|
(void) close(ofd); /* close input file */
|
|
|
|
|
|
/* Check for error during record */
|
|
if (audio_get_record_error(Audio_fd, (unsigned *)&err) != AUDIO_SUCCESS)
|
|
Error(stderr, "%s: error reading device status\n", prog);
|
|
else if (err)
|
|
Error(stderr, "%s: WARNING: Data overflow occurred\n", prog);
|
|
|
|
/* Reset record volume */
|
|
if (Volume != ~0)
|
|
(void) audio_set_record_gain(Audio_fd, &Savevol);
|
|
(void) close(Audio_fd);
|
|
|
|
exit(0);
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/* Parse an unsigned integer */
|
|
parse_unsigned(str, dst, flag)
|
|
char *str;
|
|
unsigned *dst;
|
|
char *flag;
|
|
{
|
|
char x;
|
|
|
|
if (sscanf(str, "%u%c", dst, &x) != 1) {
|
|
Error(stderr, "%s: invalid value for %s\n", prog, flag);
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|