/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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 (c) 1993-2001 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/param.h>
#include <convert.h>
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
#endif
const char *opt_string = "pf:o:i:FTD?";
char *Stdin;
char *Stdout;
char *Suffix = (char *)".AUDCVTMP";
char *progname; // program name
char *fake_argv[] = {(char *)"-", NULL}; // stdin with no args
extern char *optarg;
extern int optind;
int Statistics = 0;
int Debug = 0;
void init_header(AudioHdr&);
void usage();
main(
int argc,
char *argv[])
{
AudioUnixfile* ifp = NULL; // input & output audio objects
AudioUnixfile* ofp = NULL;
AudioHdr ihdr; // input/output headers
AudioHdr ohdr;
char *infile = NULL; // input/output file names
char *outfile = NULL;
char *realfile = NULL;
char *out_fmt = NULL; // output fmt string
AudioError err; // for error msgs
int c; // for getopt
int pflag = 0; // in place flag
int fflag = 0; // ignore header (force conversion)
int stdin_seen = 0; // already read stdin
int israw = 0; // once we've seen -i, it's raw data
format_type ofmt = F_SUN; // output format type
format_type ifmt = F_SUN; // expected input format type
format_type fmt = F_SUN; // actual input format type
off_t o_offset = 0; // output offset (ignored)
off_t i_offset = 0; // input offset
int i;
struct stat st;
setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
// basename of program
if (progname = strrchr(argv[0], '/')) {
progname++;
} else {
progname = argv[0];
}
Stdin = MGET("(stdin)");
Stdout = MGET("(stdout)");
// init the input & output headers
init_header(ihdr);
init_header(ohdr);
// some conversions depend on invocation name. we'll create
// default input/output formats based on argv[0] that
// can be overridden by -o or -i options.
if (strcmp(progname, "ulaw2pcm") == 0) {
(void) parse_format((char *)"ulaw", ihdr, ifmt, i_offset);
(void) parse_format((char *)"pcm", ohdr, ofmt, o_offset);
} else if (strcmp(progname, "pcm2ulaw") == 0) {
(void) parse_format((char *)"pcm", ihdr, ifmt, i_offset);
(void) parse_format((char *)"ulaw", ohdr, ofmt, o_offset);
} else if (strcmp(progname, "adpcm_enc") == 0) {
(void) parse_format((char *)"ulaw", ihdr, ifmt, i_offset);
(void) parse_format((char *)"g721", ohdr, ofmt, o_offset);
} else if (strcmp(progname, "adpcm_dec") == 0) {
(void) parse_format((char *)"g721", ihdr, ifmt, i_offset);
(void) parse_format((char *)"ulaw", ohdr, ofmt, o_offset);
} else if (strcmp(progname, "raw2audio") == 0) {
(void) parse_format((char *)"ulaw", ihdr, ifmt, i_offset);
(void) parse_format((char *)"ulaw", ohdr, ofmt, o_offset);
israw++;
pflag++;
} else if (argc <= 1) {
// audioconvert with no arguments
usage();
}
// now parse the rest of the arg's
while ((c = getopt(argc, argv, opt_string)) != -1) {
switch (c) {
#ifdef DEBUG
case 'D':
// enable debug messages
Debug++;
break;
#endif
case 'p':
// convert files in place
if (outfile != NULL) {
Err(MGET("can't use -p with -o\n"));
exit(1);
}
pflag++;
break;
case 'F':
// force treatment of audio files as raw files
// (ignore filehdr).
fflag++;
break;
case 'f':
// save format string to parse later, but verify now
out_fmt = optarg;
if (parse_format(out_fmt, ohdr, ofmt, o_offset) == -1)
exit(1);
if (o_offset != 0) {
Err(MGET("can't specify an offset with -f\n"));
exit(1);
}
break;
case 'o':
if (pflag) {
Err(MGET("can't use -o with -p\n"));
exit(1);
}
outfile = optarg;
break;
case 'i':
// if bogus input header, exit ...
if (parse_format(optarg, ihdr, ifmt, i_offset) == -1) {
exit(1);
}
israw++;
break;
default:
case '?':
usage();
}
}
// XXX - should check argument consistency here....
// If no args left, we're taking input from stdin.
// In this case, make argv point to a fake argv with "-" as a file
// name, and set optind and argc apropriately so we'll go through
// the loop below once.
if (optind >= argc) {
argv = fake_argv;
argc = 1;
optind = 0;
/*
* XXX - we turn off pflag if stdin is the only input file.
* this is kind of a hack. if invoked as raw2audio, pflag
* it turned on. if no files are given, we want to turn
* it off, otherwise we'll complain about using -p with
* stdin, which won't make sense if invoked as raw2audio.
* instead, just silently ignore. the message is still given
* and stdin is ignored if it's specified as one of several
* input files.
*/
pflag = 0;
}
// From this point on we're looking at file names or -i args
// for input format specs.
for (; optind < argc; optind++) {
// new input format spec.
if (strcmp(argv[optind], "-i") == 0) {
init_header(ihdr);
i_offset = 0;
ifmt = F_SUN;
// if bogus input header, exit ...
if (parse_format(argv[++optind], ihdr, ifmt, i_offset)
== -1) {
exit(1);
}
israw++;
} else if (strcmp(argv[optind], "-") == 0) {
// ignore stdin argument if in place
if (pflag) {
Err(MGET("can't use %s with -p flag\n"),
Stdin);
continue;
}
if (stdin_seen) {
Err(MGET("already used stdin for input\n"));
continue;
} else {
stdin_seen++;
}
infile = Stdin;
} else {
infile = argv[optind];
}
// if no audio object returned, just continue to the next
// file. if a fatal error occurs, open_input_file()
// will exit the program.
ifp =
open_input_file(infile, ihdr, israw, fflag, i_offset, fmt);
if (!ifp) {
continue;
}
if ((err = ifp->Open()) != AUDIO_SUCCESS) {
Err(MGET("open error on input file %s - %s\n"),
infile, err.msg());
exit(1);
}
ifp->Reference();
// create the output file if not created yet, or if
// converting in place. ofp will be NULL only the first
// time through. use the header of the first input file
// to base the output format on - then create the output
// header w/the output format spec.
if ((ofp == NULL) && !pflag) {
ohdr = ifp->GetHeader();
ohdr = ifp->GetHeader();
ofmt = ifmt;
// just use input hdr if no output hdr spec
if (out_fmt) {
if (parse_format(out_fmt, ohdr, ofmt, o_offset)
== -1) {
exit(1);
}
}
// need to check before output is opened ...
if (verify_conversion(ifp->GetHeader(), ohdr) == -1) {
// XXX - bomb out or skip?
exit(3);
}
// Create the file and set the info string.
char *infoString;
int infoStringLen;
infoString = ifp->GetInfostring(infoStringLen);
ofp = create_output_file(outfile, ohdr, ofmt,
infoString);
} else if (pflag) {
// create new output header based on each input file
ohdr = ifp->GetHeader();
ofmt = ifmt;
// just use input hdr if no output hdr spec
if (out_fmt) {
if (parse_format(out_fmt, ohdr, ofmt, o_offset)
== -1) {
exit(1);
}
}
// get the *real* path of the infile (follow sym-links),
// and the stat info.
realfile = infile;
get_realfile(realfile, &st);
// if the file is read-only, give up
if (access(realfile, W_OK)) {
// XXX - do we really want to exit?
perror(infile);
Err(MGET("cannot rewrite in place\n"));
exit(1);
}
// this is now the output file.
i = strlen(realfile) + strlen(Suffix) + 1;
outfile = (char *)malloc((unsigned)i);
if (outfile == NULL) {
Err(MGET("out of memory\n"));
exit(1);
}
(void) sprintf(outfile, "%s%s", realfile, Suffix);
// outfile will get re-assigned to a tmp file
if (verify_conversion(ifp->GetHeader(), ohdr) == -1) {
// XXX - bomb out or skip?
exit(3);
}
// If no conversion, just skip the file
if (noop_conversion(ifp->GetHeader(), ohdr,
fmt, ofmt, i_offset, o_offset)) {
if (Debug)
Err(MGET(
"%s: no-op conversion...skipping\n"),
infile);
continue;
}
// Get the input info string.
char *infoString;
int infoStringLen;
infoString = ifp->GetInfostring(infoStringLen);
ofp = create_output_file(outfile, ohdr, ofmt,
infoString);
}
// verify that it's a valid conversion by looking at the
// file headers. (this will be called twice for the first
// file if *not* converting in place. that's ok....
if (!pflag && (verify_conversion(ifp->GetHeader(), ohdr)
== -1)) {
// XXX - bomb out or skip file if invalid conversion?
exit(3);
}
// do the conversion, if error, bomb out
if (do_convert(ifp, ofp) == -1) {
exit(4);
}
ifp->Close();
ifp->Dereference();
// if in place, finish up by renaming the outfile to
// back to the infile.
if (pflag) {
delete(ofp); // will close and deref, etc.
if (rename(outfile, realfile) < 0) {
perror(outfile);
Err(MGET("error renaming %s to %s"),
outfile, realfile);
exit(1);
}
/* Set the permissions to match the original */
if (chmod(realfile, (int)st.st_mode) < 0) {
Err(MGET("WARNING: could not reset mode of"));
perror(realfile);
}
}
}
if (!pflag) {
delete(ofp); // close output file
}
exit(0); // outta here!
}
// initialize audio hdr to default val's
void
init_header(
AudioHdr& hdr)
{
hdr.encoding = NONE;
hdr.sample_rate = 0;
hdr.samples_per_unit = 0;
hdr.bytes_per_unit = 0;
hdr.channels = 0;
}
extern "C" { void _doprnt(char *, ...); }
// report a fatal error and exit
void
Err(char *format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "%s: ", progname);
_doprnt(format, ap, stderr);
fflush(stderr);
va_end(ap);
}
void
usage()
{
fprintf(stderr, MGET(
"Convert between audio file formats and data encodings -- usage:\n"
"\t%s [-pF] [-f outfmt] [-o outfile] [[-i infmt] [file ...]] ...\n"
"where:\n"
"\t-p\tConvert files in place\n"
"\t-F\tForce interpretation of -i (ignore existing file hdr)\n"
"\t-f\tOutput format description\n"
"\t-o\tOutput file (default: stdout)\n"
"\t-i\tInput format description\n"
"\tfile\tList of files to convert (default: stdin)\n\n"
"Format Description:\n"
"\tkeyword=value[,keyword=value...]\n"
"where:\n"
"\tKeywords:\tValues:\n"
"\trate\t\tSample Rate in samples/second\n"
"\tchannels\tNumber of interleaved channels\n"
"\tencoding\tAudio encoding. One of:\n"
"\t\t\t ulaw, alaw, g721, g723,\n"
"\t\t\t linear8, linear16, linear32\n"
"\t\t\t pcm (same as linear16)\n"
"\t\t\t voice (ulaw,mono,rate=8k)\n"
"\t\t\t cd (linear16,stereo,rate=44.1k)\n"
"\t\t\t dat (linear16,stereo,rate=48k)\n"
"\tformat\t\tFile format. One of:\n"
"\t\t\t sun, raw (no format)\n"
"\toffset\t\tByte offset (raw input only)\n"),
progname);
exit(1);
}