Category : C Source Code
Archive   : SOX.ZIP
Filename : SOX.C

 
Output of file : SOX.C contained in archive : SOX.ZIP
/*
* July 5, 1991
* Copyright 1991 Lance Norskog And Sundry Contributors
* This source code is freely redistributable and may be used for
* any purpose. This copyright notice must be maintained.
* Lance Norskog And Sundry Contributors are not responsible for
* the consequences of using this software.
*/

#include "st.h"
#include
#include
#include
#include
#include

/*
* SOX main program.
*
* Rewrite for new nicer option syntax. July 13, 1991.
* Rewrite for separate effects library. Sep. 15, 1991.
*/

float volume = 1.0; /* expansion coefficient */
int dovolume = 0;
int clipped = 0; /* Volume change clipping errors */

float amplitude = 1.0; /* Largest sample so far in intermediate buffer */

int writing = 0; /* are we writing to a file? */

/* export flags */
int verbose = 0; /* be noisy on stderr */
int summary = 0; /* just print summary of information */

long ibuf[BUFSIZ]; /* Intermediate processing buffer */
long obuf[BUFSIZ]; /* Intermediate processing buffer */


long volumechange();

#ifdef DOS
char writebuf[BUFSIZ]; /* output write buffer */
#endif

void gettype(), report(), geteffect(), checkeffect();

struct soundstream informat, outformat;

char *myname;
extern errno;
extern char *sys_errlist[];

ft_t ft;
struct effect eff;
char *ifile, *ofile, *itype, *otype;
extern char *optarg;
extern int optind;

main(n, args)
int n;
char **args;
{
myname = args[0];
init();

ifile = ofile = NULL;

/* Get input format options */
ft = &informat;
doopts(n, args);
/* Get input file */
if (optind >= n)
usage("No input file?");
ifile = args[optind];
if (! strcmp(ifile, "-"))
ft->fp = stdin;
else if ((ft->fp = fopen(ifile, READBINARY)) == NULL)
fail("Can't open input file '%s': %s",
ifile, sys_errlist[errno]);
ft->filename = ifile;
optind++;

/* Let -e allow no output file, just do an effect */
if (optind < n) {
if (strcmp(args[optind], "-e")) {
/* Get output format options */
ft = &outformat;
doopts(n, args);
/* Get output file */
if (optind >= n)
usage("No output file?");
ofile = args[optind];
ft->filename = ofile;
/*
* There are two choices here:
* 1) stomp the old file - normal shell "> file" behavior
* 2) fail if the old file already exists - csh mode
*/
if (! strcmp(ofile, "-"))
ft->fp = stdout;
else {
#ifdef unix
/*
* Remove old file if it's a text file, but
* preserve Unix /dev/sound files. I'm not sure
* this needs to be here, but it's not hurting
* anything.
*/
if ((ft->fp = fopen(ofile, WRITEBINARY)) &&
(filetype(fileno(ft->fp)) == S_IFREG)) {
fclose(ft->fp);
unlink(ofile);
creat(ofile, 0666);
ft->fp = fopen(ofile, WRITEBINARY);
}
#else
ft->fp = fopen(ofile, WRITEBINARY);
#endif
if (ft->fp == NULL)
fail("Can't open output file '%s': %s",
ofile, sys_errlist[errno]);
#ifdef DOS
if (setvbuf (ft->fp,writebuf,_IOFBF,sizeof(writebuf)))
fail("Can't set write buffer");
#endif
}
writing = 1;
}
optind++;
}

/* ??? */
/*
if ((optind < n) && !writing && !eff.name)
fail("Can't do an effect without an output file!");
*/

/* Get effect name */
if (optind < n) {
eff.name = args[optind];
optind++;
geteffect(&eff);
(* eff.h->getopts)(&eff, n - optind, &args[optind]);
}

/*
* If we haven't specifically set an output file
* don't write a file; we could be doing a summary
* or a format check.
*/
/*
if (! ofile)
usage("Must be given an output file name");
*/
if (! ofile)
writing = 0;
/* Check global arguments */
if (volume <= 0.0)
fail("Volume must be greater than 0.0");
informat.seekable = (filetype(fileno(informat.fp)) == S_IFREG);
outformat.seekable = (filetype(fileno(outformat.fp)) == S_IFREG);

/* If file types have not been set with -t, set from file names. */
if (! informat.filetype) {
if (informat.filetype = strrchr(ifile, '/'))
informat.filetype++;
else
informat.filetype = ifile;
if (informat.filetype = strrchr(informat.filetype, '.'))
informat.filetype++;
}
if (writing && ! outformat.filetype) {
if (outformat.filetype = strrchr(ofile, '/'))
outformat.filetype++;
else
outformat.filetype = ofile;
if (outformat.filetype = strrchr(outformat.filetype, '.'))
outformat.filetype++;
}

process();
statistics();
exit(0);
}

doopts(n, args)
int n;
char **args;
{
int c;
char *str;

while ((c = getopt(n, args, "r:v:t:c:suUAbwlfdDxSV")) != -1) {
switch(c) {
case 't':
if (! ft) usage("-t");
ft->filetype = optarg;
if (ft->filetype[0] == '.')
ft->filetype++;
break;

case 'r':
if (! ft) usage("-r");
str = optarg;
if ((! sscanf(str, "%d", &ft->info.rate)) ||
(ft->info.rate <= 0))
fail("-r must be given a positive integer");
break;
case 'v':
if (! ft) usage("-v");
str = optarg;
if ((! sscanf(str, "%e", &volume)) ||
(volume <= 0))
fail("Volume value '%s' is not a number",
optarg);
dovolume = 1;
break;

case 'c':
if (! ft) usage("-c");
str = optarg;
if (! sscanf(str, "%d", &ft->info.channels))
fail("-c must be given a number");
break;
case 'b':
if (! ft) usage("-b");
ft->info.size = BYTE;
break;
case 'w':
if (! ft) usage("-w");
ft->info.size = WORD;
break;
case 'l':
if (! ft) usage("-l");
ft->info.size = LONG;
break;
case 'f':
if (! ft) usage("-f");
ft->info.size = FLOAT;
break;
case 'd':
if (! ft) usage("-d");
ft->info.size = DOUBLE;
break;
case 'D':
if (! ft) usage("-D");
ft->info.size = IEEE;
break;

case 's':
if (! ft) usage("-s");
ft->info.style = SIGN2;
break;
case 'u':
if (! ft) usage("-u");
ft->info.style = UNSIGNED;
break;
case 'U':
if (! ft) usage("-U");
ft->info.style = ULAW;
break;
case 'A':
if (! ft) usage("-A");
ft->info.style = ALAW;
break;

case 'x':
if (! ft) usage("-x");
ft->swap = 1;
break;

/* stat effect does this ?
case 'S':
summary = 1;
break;
*/
case 'V':
verbose = 1;
break;
}
}
}

init() {

/* init files */
informat.info.rate = outformat.info.rate = 0.0;
informat.info.size = outformat.info.size = -1;
informat.info.style = outformat.info.style = -1;
informat.info.channels = outformat.info.channels = -1;
informat.swap = 0;
informat.filetype = outformat.filetype = (char *) 0;
informat.fp = stdin;
outformat.fp = stdout;
informat.filename = "input";
outformat.filename = "output";
}

/*
* Process input file -> effect -> output file
* one buffer at a time
*/

process() {
long isamp, osamp, istart;
long i, idone, odone;

gettype(&informat);
if (writing)
gettype(&outformat);
/* Read and write starters can change their formats. */
(* informat.h->startread)(&informat);
checkformat(&informat);
report("Input file: using sample rate %d\n\tsize %s, style %s, %d %s",
informat.info.rate, sizes[informat.info.size],
styles[informat.info.style], informat.info.channels,
(informat.info.channels > 1) ? "channels" : "channel");
if (writing) {
copyformat(&informat, &outformat);
(* outformat.h->startwrite)(&outformat);
checkformat(&outformat);
cmpformats(&informat, &outformat);
report("Output file: using sample rate %d\n\tsize %s, style %s, %d %s",
outformat.info.rate, sizes[outformat.info.size],
styles[outformat.info.style], outformat.info.channels,
(outformat.info.channels > 1) ? "channels" : "channel");
}
/* Very Important:
* Effect fabrication and start is called AFTER files open.
* Effect may write out data beforehand, and
* some formats don't know their sample rate until now.
*/
checkeffect();
/* inform effect about signal information */
eff.ininfo = informat.info;
eff.outinfo = outformat.info;
(* eff.h->start)(&eff);
istart = 0;
while((isamp = (*informat.h->read)(&informat,&ibuf[istart],
(long) BUFSIZ-istart))>0) {
long *ib = ibuf;

isamp += istart;
/* Do volume before effect or after? idunno */
if (dovolume) for (i = 0; i < isamp; i++)
ibuf[i] = volumechange(ibuf[i]);
osamp = sizeof(obuf) / sizeof(long);
/* Effect (i.e. rate change) may do different sizes I and O */
while (isamp) {
idone = isamp;
odone = osamp;
(* eff.h->flow)(&eff, ib, obuf, &idone, &odone);
/*
* kludge!
* Effect is stuck. Start over with new buffer.
* This can drop samples at end of file.
* No effects currently do this, but it could happen.
*/
if (idone == 0) {
int i;
for(i = isamp - 1; i; i--)
ibuf[i] = ib[i];
istart = isamp;
isamp = 0;
break;
}
if (writing && (odone > 0))
(* outformat.h->write)(&outformat, obuf, (long) odone);
isamp -= idone;
ib += idone;
}
}
/* Drain effect out */
if (writing) {
odone = sizeof(obuf) / sizeof(long);
(* eff.h->drain)(&eff, obuf, &odone);
if (odone > 0)
(* outformat.h->write)(&outformat, obuf, (long) odone);
/* keep calling it until it returns a partial buffer */
while (odone == (sizeof(obuf) / sizeof(long))) {
(* eff.h->drain)(&eff, obuf, &odone);
if (odone)
(* outformat.h->write)(&outformat, obuf, (long) odone);
}
}
/* Very Important:
* Effect stop is called BEFORE files close.
* Effect may write out more data after.
*/
(* eff.h->stop)(&eff);
(* informat.h->stopread)(&informat);
fclose(informat.fp);
if (writing)
(* outformat.h->stopwrite)(&outformat);
if (writing)
fclose(outformat.fp);
}

/*
* Check that we have a known format suffix string.
*/
void
gettype(formp)
ft_t formp;
{
char **list;
int i;
extern format_t formats[];

if (! formp->filetype)
fail("Must give file type for %s file, either as suffix or with -t option",
formp->filename);
for(i = 0; formats[i].names; i++) {
for(list = formats[i].names; *list; list++) {
char *s1 = *list, *s2 = formp->filetype;
if (! strcmpcase(s1, s2))
break; /* not a match */
}
if (! *list)
continue;
/* Found it! */
formp->h = &formats[i];
return;
}
if (! strcmpcase(formp->filetype, "snd")) {
verbose = 1;
report("File type '%s' is used to name several different formats.", formp->filetype);
report("If the file came from a Macintosh, it is probably");
report("a .ub file with a sample rate of 11025 (or possibly 5012 or 22050).");
report("Use the sequence '-t .ub -r 11025 file.snd'");
report("If it came from a PC, it's probably a Soundtool file.");
report("Use the sequence '-t .sndt file.snd'");
report("If it came from a NeXT, it's probably a .au file.");
fail("Use the sequence '-t .au file.snd'\n");
}
fail("File type '%s' of %s file is not known!",
formp->filetype, formp->filename);
}

copyformat(ft, ft2)
ft_t ft, ft2;
{
int noise = 0;
if (ft2->info.rate == 0.0) {
ft2->info.rate = ft->info.rate;
noise = 1;
}
if (outformat.info.size == -1) {
ft2->info.size = ft->info.size;
noise = 1;
}
if (outformat.info.style == -1) {
ft2->info.style = ft->info.style;
noise = 1;
}
if (outformat.info.channels == -1) {
ft2->info.channels = ft->info.channels;
noise = 1;
}
return noise;
}

cmpformats(ft, ft2)
ft_t ft, ft2;
{
int noise = 0;
float abs;

}

/* check that all settings have been given */
checkformat(ft)
ft_t ft;
{
if (ft->info.rate == 0.0)
fail("Sampling rate for %s file was not given\n", ft->filename);
if ((ft->info.rate < 100) || (ft->info.rate > 50000))
fail("Sampling rate %d for %s file is bogus\n",
ft->info.rate, ft->filename);
if (ft->info.size == -1)
fail("Data size was not given for %s file\nUse one of -b/-w/-l/-f/-d/-D", ft->filename);
if (ft->info.style == -1)
fail("Data style was not given for %s file\nUse one of -s/-u/-U/-A", ft->filename);
/* it's so common, might as well default */
if (ft->info.channels == -1)
ft->info.channels = 1;
/* fail("Number of output channels was not given for %s file",
ft->filename); */
}

/*
* If no effect given, decide what it should be.
*/
void
checkeffect(effp)
eff_t effp;
{
int already = (eff.name != (char *) 0);
char *rate = 0, *chan = 0;
int i;

for (i = 0; effects[i].name; i++) {
if (!chan && (effects[i].flags & EFF_CHAN))
chan = effects[i].name;
if (! rate && (effects[i].flags & EFF_RATE))
rate = effects[i].name;
}

if (eff.name && ! writing)
return;

/*
* Require mixdown for channel mismatch.
* XXX Doesn't handle channel expansion. Need an effect for this.
* Require one of the rate-changers on a rate change.
* Override a rate change by explicitly giving 'copy' command.
*/
if (informat.info.channels != outformat.info.channels) {
if (eff.name && !(eff.h->flags & EFF_CHAN))
fail("Need to do change number of channels first. Try the '%s' effect.", chan);
if (! eff.name) {
eff.name = chan;
report(
"Changing %d input channels to %d output channels with '%s' effect\n",
informat.info.channels, outformat.info.channels, chan);
geteffect(&eff);
}
}
/*
* Be liberal on rate difference errors.
* Note that the SPARC 8000-8192 problem
* comes in just under the wire. XXX
*
* Bogus. Should just do a percentage.
*/
if (abs(informat.info.rate - outformat.info.rate) > 200) {
if (eff.name && !(eff.h->flags & EFF_RATE))
fail("Need to do rate change first. Try the '%s' effect.",
rate);
if (! eff.name) {
eff.name = rate;
report(
"Changing sample rate %d to rate %d via noisy 'rate' effect\n",
informat.info.rate, outformat.info.rate);
geteffect(&eff);
}
}
/* don't need to change anything */
if (! eff.name)
eff.name = "copy";
if (! already) {
geteffect(&eff);
/* give default opts for manufactured effect */
(eff.h->getopts)(&eff, 0, (char *) 0);
}
}

/*
* Check that we have a known effect name.
*/
void
geteffect(effp)
eff_t effp;
{
int i;

for(i = 0; effects[i].name; i++) {
char *s1 = effects[i].name, *s2 = effp->name;
while(*s1 && *s2 && (tolower(*s1) == tolower(*s2)))
s1++, s2++;
if (*s1 || *s2)
continue; /* not a match */
/* Found it! */
effp->h = &effects[i];
return;
}
/* Guido Van Rossum fix */
fprintf(stderr, "Known effects:");
for (i = 0; effects[i].name; i++)
fprintf(stderr, "\t%s\n", effects[i].name);
fail("\nEffect '%s' is not known!", effp->name);
}

/* Guido Van Rossum fix */
statistics() {
if (dovolume && clipped > 0)
report("Volume change clipped %d samples", clipped);
}

long volumechange(y)
long y;
{
double y1;

y1 = y * volume;
if (y1 < -2147483647.0) {
y1 = -2147483647.0;
clipped++;
}
else if (y1 > 2147483647.0) {
y1 = 2147483647.0;
clipped++;
}

return y1;
}

filetype(fd)
int fd;
{
struct stat st;

fstat(fd, &st);

return st.st_mode & S_IFMT;
}

char *usagestr =
"[ -V -S ] [ fopts ] ifile [ fopts ] ofile [ effect [ effopts ] ]\nfopts: -r rate -v volume -c channels -s/-u/-U/-A -b/-w/-l/-f/-d/-D -x\neffects and effopts: various";

usage(opt)
char *opt;
{
#ifndef DOS
/* single-threaded machines don't really need this */
fprintf(stderr, "%s: ", myname);
#endif
fprintf(stderr, "Usage: %s", usagestr);
if (opt)
fprintf(stderr, "\nFailed at: %s\n", opt);
exit(1);
}

void
report(va_alist)
va_dcl
{
va_list args;
char *fmt;

if (! verbose)
return;
#ifndef DOS
/* single-threaded machines don't really need this */
fprintf(stderr, "%s: ", myname);
#endif
va_start(args);
fmt = va_arg(args, char *);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
}

fail(va_alist)
va_dcl
{
va_list args;
char *fmt;

#ifndef DOS
/* single-threaded machines don't really need this */
fprintf(stderr, "%s: ", myname);
#endif
va_start(args);
fmt = va_arg(args, char *);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
exit(2);
}


strcmpcase(s1, s2)
char *s1, *s2;
{
while(*s1 && *s2 && (tolower(*s1) == tolower(*s2)))
s1++, s2++;
return *s1 - *s2;
}


  3 Responses to “Category : C Source Code
Archive   : SOX.ZIP
Filename : SOX.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/