audiorecord.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/* Command-line audio record utility */
#include <stdio.h>
#include <libgen.h>
#include <errno.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <locale.h>
#include <fcntl.h>
#include <signal.h>
#include <limits.h> /* All occurances of INT_MAX used to be ~0 (by MCA) */
#include <stropts.h>
#include <poll.h>
#include <libaudio.h>
#include <audio_device.h>
#define irint(d) ((int)d)
/* localization stuff */
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#endif
/* Defined until I get a copy of the apropriate audioio.h file. */
#ifndef AUDIO_CD_IN
#endif
/* Local variables */
static char *prog;
static char *Stdout;
/* XXX - the input buffer size should depend on sample_rate */
static unsigned char buf[AUDIO_BUFSIZ];
#define MID_BAL (0)
#define RIGHT_BAL (100)
static unsigned Ilen = 0; /* length of info data */
static double Savevol; /* saved volume */
static double Savemonvol; /* saved monitor volume */
static unsigned int Savebal; /* saved input balance */
static unsigned Saveport = 0; /* restore value of input port */
static unsigned Sample_rate = 0;
static unsigned Channels = 0;
static unsigned Precision = 0; /* based on encoding */
static unsigned Encoding = 0;
static int Audio_fd = -1;
/* file descriptor for audio device */
static char *Ofile; /* current filename */
static unsigned Size = 0; /* Size of output file */
static unsigned Oldsize = 0;
/* Size of input file, if append */
/* Global variables */
extern int getopt();
extern int optind;
extern char *optarg;
/* Local Functions */
static void usage(void);
static int scale_balance(int g);
static int parse_sample_rate(char *s, unsigned *rate);
static void
usage(void)
{
"\t%s [-af] [-v vol] [-b bal] [-m monvol]\n"
"\t%.*s [-p mic|line|cd|aux1|aux2|spdif]\n"
"\t%.*s [-c channels] [-s rate] [-e encoding]\n"
"\t%.*s [-t time] [-i info] [-d dev] [-T au|wav|aif[f]] [file]\n"
"where:\n"
"\t-a\tAppend to output file\n"
"\t-f\tIgnore sample rate differences on append\n"
"\t-v\tSet record volume (0 - %d)\n"
"\t-b\tSet record balance (%d=left, %d=center, %d=right)\n"
"\t-m\tSet monitor volume (0 - %d)\n"
"\t-p\tSpecify input port\n"
"\t-c\tSpecify number of channels to record\n"
"\t-s\tSpecify rate in samples per second\n"
"\t-e\tSpecify encoding (ulaw | alaw | [u]linear | linear8 )\n"
"\t-t\tSpecify record time (hh:mm:ss.dd)\n"
"\t-i\tSpecify a file header information string\n"
"\t-T\tSpecify the audio file type (default: au)\n"
"\tfile\tRecord to named file\n"
"\t\tIf no file specified, write to stdout\n"
"\t\tDefault audio encoding is ulaw, 8khz, mono\n"
"\t\tIf -t is not specified, record until ^C\n"),
prog,
exit(1);
}
static void
{
/* If this is the first ^C, set a flag for the main loop */
/* flush input queues before exiting */
return;
}
/* If double ^C, really quit */
if (Audio_fd >= 0) {
}
}
exit(1);
}
/*
* Record from the audio device to a file.
*/
int
{
int i;
int cnt;
int err;
int file_type;
int ofd;
double vol;
int bal;
char *cp;
char ctldev[MAXPATHLEN];
(void) textdomain(TEXT_DOMAIN);
/* Get the program name */
else
prog++;
/* first check AUDIODEV environment for audio device name */
}
/* Set the endian nature of the machine */
}
err = 0;
switch (i) {
case 'v':
err++;
"-v\n"), prog);
err++;
}
break;
case 'b':
"-b\n"), prog);
err++;
} else {
}
break;
case 'm':
err++;
"-m\n"), prog);
err++;
}
break;
case 't':
"-t\n"), prog);
err++;
}
break;
case 'd':
break;
case 'p':
/* a partial match is OK */
Port = AUDIO_CD_IN;
} else {
"-p\n"), prog);
err++;
}
break;
case 'f':
break;
case 'a':
break;
case 'i':
break;
case 's':
err++;
}
break;
case 'c':
Channels = 1;
Channels = 2;
err++;
prog);
err++;
}
break;
case 'e':
Precision = 8;
strlen("linear8")) == 0) {
Precision = 8;
Precision = 8;
Precision = 8;
Precision = 16;
} else {
"-e\n"), prog);
err++;
}
break;
case 'T':
} else {
"-T\n"), prog);
err++;
}
break;
case '?':
usage();
/*NOTREACHED*/
}
}
err++;
}
if (err > 0)
exit(1);
/* Open the output file */
if (argc <= 0) {
} else {
argc--;
/* Interpret "-" filename to mean 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 */
/* get the true suffix */
/* if no '.' then there's no suffix */
if (start) {
/* is this a .au file? */
} else {
/* the default is .au */
}
} else {
/* no suffix, so default to .au */
}
}
}
} else {
if (ofd < 0) {
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.
*/
MGET("%s: %s is not a regular file\n"),
exit(1);
}
goto openinput;
}
(char *)NULL, 0);
if (err != AUDIO_SUCCESS) {
MGET("%s: %s is not a valid audio file\n"),
exit(1);
}
/* we need to make sure file types match */
if (File_type_set == TRUE) {
/* specified by the command line, must match */
MGET("%s: file types must match\n"),
prog);
exit(1);
}
} else {
/* not specified, so force */
}
/*
* Set the format state to the format
* in the file header.
*/
/* 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];
MGET("%s: Append is not supported "
"for "), prog);
MGET("this file encoding:\n\t"
"[%s]\n"), msg);
exit(1);
}
}
/* Get the current size, if possible */
if ((Oldsize == AUDIO_UNKNOWN_SIZE) &&
if (err < 0) {
MGET("%s: %s is not a valid audio "
exit(1);
}
}
/* Seek to end to start append */
MGET("%s: cannot find end of %s\n"),
exit(1);
}
}
}
/* Validate and open the audio device */
if (err < 0) {
exit(1);
}
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.
*/
if (Audio_fd < 0) {
} else {
}
exit(1);
}
exit(1);
}
/* get the current settings */
exit(1);
}
/* make a copy into the working data structure */
/* flush any queued audio data */
exit(1);
}
if (Sample_rate != 0) {
}
if (Channels != 0) {
}
if (Precision != 0) {
}
if (Encoding != 0) {
}
/*
* 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.
*/
/* force to unsigned */
if (Precision == 0) {
}
}
"%s: Audio format not supported by the audio device\n"),
prog);
exit(1);
}
exit(1);
}
/* If appending to an existing file, check the configuration */
if (Append) {
char msg[AUDIO_MAX_ENCODE_INFO];
case 0: /* configuration matches */
break;
case 1: /* all but sample rate matches */
if (Force) {
"%.3fkHz data to %s (%.3fkHz)\n"), prog,
break;
} /* if not -f, fall through */
default: /* encoding mismatch */
exit(1);
}
Ilen) != AUDIO_SUCCESS) {
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 -v flag, set the record volume now */
if (err != AUDIO_SUCCESS) {
MGET("%s: could not set record volume for %s\n"),
exit(1);
}
}
if (err != AUDIO_SUCCESS) {
MGET("%s: could not set record balance for %s\n"),
exit(1);
}
}
/* If -m flag, set monitor volume now */
if (err != AUDIO_SUCCESS) {
MGET("%s: could not set monitor volume for %s\n"),
exit(1);
}
}
/* If -p flag, set the input port */
if (err != AUDIO_SUCCESS) {
MGET("%s: could not set input port %s\n"),
exit(1);
}
}
exit(0);
}
/* Set up SIGINT handler so that final buffers may be flushed */
/*
* 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)
/* Fill the buffer or read to the time limit */
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:
break;
case EOVERFLOW: /* Possibly a Large File */
perror("Large File");
exit(1);
default:
exit(1);
}
continue;
}
/* Swab the output if required. */
if (swapBytes) {
} else {
}
if (err < 0) {
exit(1);
}
break;
}
if (Limit != AUDIO_UNKNOWN_SIZE)
}
/* Attempt to rewrite the data_size field of the file header */
if (Append)
}
/* Check for error during record */
else if (err)
prog);
/* Reset record volume, balance, monitor volume, port, encoding */
}
return (0);
}
/* Parse an unsigned integer */
static int
{
char x;
return (1);
}
return (0);
}
/*
* set the sample rate. assume anything is ok. check later on to make sure
* the sample rate is valid.
*/
static int
parse_sample_rate(char *s, unsigned *rate)
{
char *cp;
double drate;
/*
* check if it's "cd" or "dat" or "voice". these also set
* the precision and encoding, etc.
*/
if (strcasecmp(s, "dat") == 0) {
drate = 48000.0;
} else if (strcasecmp(s, "cd") == 0) {
drate = 44100.0;
} else if (strcasecmp(s, "voice") == 0) {
drate = 8000.0;
} else {
/* just do an atof */
/*
* if the first non-digit is a "k" multiply by 1000,
* if it's an "h", leave it alone. anything else,
* return an error.
*/
/*
* XXX bug alert: could have multiple "." in string
* and mess things up.
*/
drate *= 1000.0;
/* bogus! */
MGET("invalid sample rate: %s\n"), s);
return (1);
}
}
}
return (0);
}
/* Convert local balance into device parameters */
static int
scale_balance(int g)
{
(double)AUDIO_RIGHT_BALANCE);
}