ref: 29c4dba087e245d7ff9dc33d276e660c1a94dff7
parent: fa9a674a9f5313609e755da2653ee43dbb5dcd09
author: cbagwell <cbagwell>
date: Thu Aug 3 15:30:28 EDT 2000
Added support for handling multiple effects on the command line.
--- a/Changelog
+++ b/Changelog
@@ -77,6 +77,8 @@
o Changed the default block alignement for IMA ADPCM WAV files to use
256 which is what windows programs use. Badly written readers expect
256.
+ o Matthias Nutt helped add support for specifying multiple effects
+ to SoX on the command line.
sox-12.16
---------
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -730,7 +730,7 @@
{"split", ST_EFF_CHAN | ST_EFF_MCHAN,
st_split_getopts, st_split_start, st_split_flow,
st_null_drain, st_split_stop},
- {"stat", ST_EFF_MCHAN | ST_EFF_REPORT | ST_EFF_RATE | ST_EFF_CHAN,
+ {"stat", ST_EFF_MCHAN | ST_EFF_REPORT,
st_stat_getopts, st_stat_start, st_stat_flow,
st_stat_drain, st_stat_stop},
{"stretch", 0,
--- a/src/sox.c
+++ b/src/sox.c
@@ -91,7 +91,7 @@
static void process(P0);
static void statistics(P0);
static LONG volumechange(P3(LONG *buf, LONG ct, double vol));
-static void checkeffect(P1(eff_t));
+static void checkeffect(P0);
static int flow_effect(P1(int));
static int drain_effect(P1(int));
@@ -99,12 +99,33 @@
static ft_t ft;
-#define MAXEFF 4
-static struct st_effect eff;
-static struct st_effect efftab[MAXEFF]; /* table of left/mono channel effects */
-static struct st_effect efftabR[MAXEFF]; /* table of right channel effects */
- /* efftab[0] is the input stream */
-static int neffects; /* # of effects */
+/* We parse effects into a temporary effects table and then place into
+ * the real effects table. This makes it easier to reorder some effects
+ * as needed. For instance, we can run a resampling effect before
+ * converting a mono file to stereo. This allows the resample to work
+ * on half the data.
+ *
+ * Real effects table only needs to be 2 entries bigger then the user
+ * specified table. This is because at most we will need to add
+ * a resample effect and an channel averaging effect.
+ */
+#define MAX_EFF 16
+#define MAX_USER_EFF 14
+
+/*
+ * In efftab's, location 0 is always the input stream.
+ *
+ * If one was to support effects for quad-channel files, there would
+ * need to be an effect tabel for each channel.
+ */
+
+static struct st_effect efftab[MAX_EFF]; /* left/mono channel effects */
+static struct st_effect efftabR[MAX_EFF];/* right channel effects */
+static int neffects; /* # of effects to run on data */
+
+static struct st_effect user_efftab[MAX_USER_EFF];
+static int nuser_effects;
+
static char *ifile, *ofile;
int main(argc, argv)
@@ -111,7 +132,11 @@
int argc;
char **argv;
{
+
+ int argc_effect;
+
myname = argv[0];
+
init();
ifile = ofile = NULL;
@@ -174,21 +199,51 @@
*/
}
- /* Get effect name */
- if (optind < argc) {
- eff.name = argv[optind];
- optind++;
- st_geteffect(&eff);
- (* eff.h->getopts)(&eff, argc - optind, &argv[optind]);
- } else {
- eff.name = "null";
- st_geteffect(&eff);
+
+ /* Loop through the reset of the arguments looking for effects */
+ nuser_effects = 0;
+
+ while (optind < argc)
+ {
+ if (nuser_effects >= MAX_USER_EFF)
+ {
+ st_fail("Sorry, too many effects specified.\n");
+ }
+
+ argc_effect = st_geteffect_opt(&user_efftab[nuser_effects],
+ argc - optind, &argv[optind]);
+
+ if (argc_effect == ST_EOF)
+ {
+ int i1;
+ fprintf(stderr, "%s: Known effects: ",myname);
+ for (i1 = 1; st_effects[i1].name; i1++)
+ fprintf(stderr, "%s ", st_effects[i1].name);
+ fprintf(stderr, "\n");
+ st_fail("Effect '%s' is not known!", argv[optind]);
+ }
+
+
+ /* Skip past effect name */
+ optind++;
+
+ (*user_efftab[nuser_effects].h->getopts)(&user_efftab[nuser_effects],
+ argc_effect,
+ &argv[optind]);
+
+ /* Skip past the effect arguments */
+ optind += argc_effect;
+ nuser_effects++;
}
/* Check global arguments */
if (informat.info.dovol && informat.info.vol == 0.0)
- st_fail("Volume must be non-zero"); /* negative volume is phase-reversal */
-
+ st_fail("Volume must be non-zero");
+
+ /* negative volume is phase-reversal */
+ if (informat.info.vol < 0.0)
+ st_report("Volume adjustment is negative. This will result in a phase change\n");
+
/* If file types have not been set with -t, set from file names. */
if (! informat.filetype) {
if ((informat.filetype = strrchr(ifile, LASTCHAR)) != NULL)
@@ -397,7 +452,6 @@
*/
static void process(P0) {
- LONG i;
int e, f, havedata;
st_gettype(&informat);
@@ -423,7 +477,6 @@
if (informat.comment)
st_report("Input file: comment \"%s\"\n", informat.comment);
- /* need to check EFF_REPORT */
if (writing) {
/*
* There are two choices here:
@@ -478,22 +531,8 @@
st_report("Output file: comment \"%s\"\n", outformat.comment);
}
- /* 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.
- */
-
- /* inform effect about signal information */
- eff.ininfo = informat.info;
- eff.outinfo = outformat.info;
- for(i = 0; i < 8; i++) {
- memcpy(&eff.loops[i], &informat.loops[i], sizeof(struct st_loopinfo));
- }
- eff.instr = informat.instr;
-
/* build efftab */
- checkeffect(&eff);
+ checkeffect();
/* Start all effects */
for(e = 1; e < neffects; e++) {
@@ -503,10 +542,21 @@
}
/* Reserve an output buffer for all effects */
- for(e = 0; e < neffects; e++) {
+ for(e = 0; e < neffects; e++)
+ {
efftab[e].obuf = (LONG *) malloc(BUFSIZ * sizeof(LONG));
+ if (efftab[e].obuf == NULL)
+ {
+ st_fail("could not allocate memory");
+ }
if (efftabR[e].name)
+ {
efftabR[e].obuf = (LONG *) malloc(BUFSIZ * sizeof(LONG));
+ if (efftabR[e].obuf == NULL)
+ {
+ st_fail("could not allocate memory");
+ }
+ }
}
@@ -711,20 +761,6 @@
return(efftab[e].olen);
}
-#define setin(eff, effname) \
- {eff.name = effname; \
- eff.ininfo.rate = informat.info.rate; \
- eff.ininfo.channels = informat.info.channels; \
- eff.outinfo.rate = informat.info.rate; \
- eff.outinfo.channels = informat.info.channels;}
-
-#define setout(eff, effname) \
- {eff.name = effname; \
- eff.ininfo.rate = outformat.info.rate; \
- eff.ininfo.channels = outformat.info.channels; \
- eff.outinfo.rate = outformat.info.rate; \
- eff.outinfo.channels = outformat.info.channels;}
-
/*
* If no effect given, decide what it should be.
* Smart ruleset for multiple effects in sequence.
@@ -731,183 +767,175 @@
* Puts user-specified effect in right place.
*/
static void
-checkeffect(effp)
-eff_t effp;
+checkeffect()
{
- int i, j;
- int needchan = 0, needrate = 0;
+ int i;
+ int needchan = 0, needrate = 0, haschan = 0, hasrate = 0;
+ int effects_mask = 0;
- /* if given effect does these, we don't need to add them */
- needrate = (informat.info.rate != outformat.info.rate) &&
- ! (effp->h->flags & ST_EFF_RATE);
- needchan = (informat.info.channels != outformat.info.channels) &&
- ! (effp->h->flags & ST_EFF_MCHAN);
+ needrate = (informat.info.rate != outformat.info.rate);
+ needchan = (informat.info.channels != outformat.info.channels);
- neffects = 1;
- /* effect #0 is the input stream */
- /* inform all effects about all relevant changes */
- for(i = 0; i < MAXEFF; i++) {
- efftab[i].name = efftabR[i].name = (char *) 0;
- /* inform effect about signal information */
- efftab[i].ininfo = informat.info;
- efftabR[i].ininfo = informat.info;
- efftab[i].outinfo = outformat.info;
- efftabR[i].outinfo = outformat.info;
- for(j = 0; j < 8; j++) {
- memcpy(&efftab[i].loops[j],
- &informat.loops[j], sizeof(struct st_loopinfo));
- memcpy(&efftabR[i].loops[j],
- &informat.loops[j], sizeof(struct st_loopinfo));
- }
- efftab[i].instr = informat.instr;
- efftabR[i].instr = informat.instr;
+ for (i = 0; i < nuser_effects; i++)
+ {
+ if (user_efftab[i].h->flags & ST_EFF_CHAN)
+ {
+ haschan++;
+ }
+ if (user_efftab[i].h->flags & ST_EFF_RATE)
+ {
+ hasrate++;
+ }
}
- /* If not writing output, then just add the user specified effect.
- * This is to avoid channel and rate averaging since you don't have
- * a real output format.
+ if (haschan > 1)
+ st_fail("Can not specify multiple effects that modify channel #");
+ if (hasrate > 1)
+ st_fail("Can not specify multiple effects that change sampel rate");
+ if (haschan && !needchan)
+ st_fail("Can not specify channel effects when input and output channel # are equal");
+ if (hasrate && !needrate)
+ st_fail("Can not specify sample rate effects when input and output rate are equal");
+
+ /* If not writing output then do not worry about adding
+ * channel and rate effects. This is just to speed things
+ * up.
*/
- if (! writing) {
- neffects = 2;
- efftab[1].name = effp->name;
- if ((informat.info.channels == 2) &&
- (! (effp->h->flags & ST_EFF_MCHAN)))
- efftabR[1].name = effp->name;
+ if (!writing)
+ {
+ needchan = 0;
+ needrate = 0;
}
- else if (soxpreview) {
- /* to go faster, i suppose rate could come first if downsampling */
- if (needchan && (informat.info.channels > outformat.info.channels))
- {
- if (needrate) {
- neffects = 4;
- efftab[1].name = "avg";
- efftab[2].name = "rate";
- setout(efftab[3], effp->name);
- } else {
- neffects = 3;
- efftab[1].name = "avg";
- setout(efftab[2], effp->name);
- }
- } else if (needchan &&
- (informat.info.channels < outformat.info.channels)) {
- if (needrate) {
- neffects = 4;
- efftab[1].name = effp->name;
- efftab[1].outinfo.rate = informat.info.rate;
- efftab[1].outinfo.channels = informat.info.channels;
- efftab[2].name = "rate";
- efftab[3].name = "avg";
- } else {
- neffects = 3;
- efftab[1].name = effp->name;
- efftab[1].outinfo.channels = informat.info.channels;
- efftab[2].name = "avg";
- }
- } else {
- if (needrate) {
- neffects = 3;
- efftab[1].name = effp->name;
- efftab[1].outinfo.rate = informat.info.rate;
- efftab[2].name = "rate";
- if (informat.info.channels == 2)
- efftabR[2].name = "rate";
- } else {
- neffects = 2;
- efftab[1].name = effp->name;
- }
- if ((informat.info.channels == 2) &&
- (! (effp->h->flags & ST_EFF_MCHAN)))
- efftabR[1].name = effp->name;
+
+ /* --------- add the effects ------------------------ */
+
+ /* efftab[0] is always the input stream and always exists */
+ neffects = 1;
+
+ /* If reducing channels then its faster to run all effects
+ * after the avg effect.
+ */
+ if (needchan && !(haschan) &&
+ (informat.info.channels > outformat.info.channels))
+ {
+ /* Find effect and update initial pointers */
+ st_geteffect(&efftab[neffects], "avg");
+
+ /* give default opts for added effects */
+ (* efftab[neffects].h->getopts)(&efftab[neffects],(int)0,
+ (char **)0);
+
+ /* Copy format info to effect table */
+ effects_mask = st_updateeffect(&efftab[neffects], &informat,
+ &outformat, effects_mask);
+
+ neffects++;
+ }
+
+ /* If reducing the number of samples, its faster to run all effects
+ * after the resample effect.
+ */
+ if (needrate && !(hasrate) &&
+ (informat.info.rate > outformat.info.rate))
+ {
+ if (soxpreview)
+ st_geteffect(&efftab[neffects], "rate");
+ else
+ st_geteffect(&efftab[neffects], "resample");
+
+ /* set up & give default opts for added effects */
+ (* efftab[neffects].h->getopts)(&efftab[neffects],(int)0,
+ (char **)0);
+
+ /* Copy format info to effect table */
+ effects_mask = st_updateeffect(&efftab[neffects], &informat,
+ &outformat, effects_mask);
+
+ /* Rate can't handle multiple channels so be sure and
+ * account for that.
+ */
+ if (efftab[neffects].ininfo.channels > 1)
+ {
+ memcpy(&efftabR[neffects], &efftab[neffects],
+ sizeof(struct st_effect));
}
- } else { /* not preview mode */
- /* [ sum to mono,] [ then rate,] then effect */
- /* not the purest, but much faster */
- if (needchan &&
- (informat.info.channels > outformat.info.channels)) {
- if (needrate && (informat.info.rate != outformat.info.rate)) {
- neffects = 4;
- efftab[1].name = "avg";
- efftab[2].name = effp->name;
- efftab[2].outinfo.rate = informat.info.rate;
- efftab[2].outinfo.channels = informat.info.channels;
- efftab[3].name = "rate";
- } else {
- neffects = 3;
- efftab[1].name = "avg";
- efftab[2].name = effp->name;
- efftab[2].outinfo.rate = informat.info.rate;
- efftab[2].outinfo.channels = informat.info.channels;
- }
- } else if (needchan &&
- (informat.info.channels < outformat.info.channels)) {
- if (needrate) {
- neffects = 4;
- efftab[1].name = effp->name;
- if (! (effp->h->flags & ST_EFF_MCHAN))
- efftabR[1].name = effp->name;
- efftab[1].outinfo.rate = informat.info.rate;
- efftab[1].outinfo.channels = informat.info.channels;
- efftab[2].name = "resample";
- efftab[3].name = "avg";
- } else {
- neffects = 3;
- efftab[1].name = effp->name;
- if (! (effp->h->flags & ST_EFF_MCHAN))
- efftabR[1].name = effp->name;
- efftab[1].outinfo.channels = informat.info.channels;
- efftab[2].name = "avg";
- }
- } else {
- if (needrate) {
- neffects = 3;
- efftab[1].name = effp->name;
- efftab[1].outinfo.rate = informat.info.rate;
- efftab[2].name = "resample";
- if (informat.info.channels == 2)
- efftabR[2].name = "rate";
- } else {
- neffects = 2;
- efftab[1].name = effp->name;
- }
- if ((informat.info.channels == 2) &&
- (! (effp->h->flags & ST_EFF_MCHAN)))
- efftabR[1].name = effp->name;
+
+ neffects++;
+ }
+
+ /* Copy over user specified effects into real efftab */
+ for(i = 0; i < nuser_effects; i++)
+ {
+ memcpy(&efftab[neffects], &user_efftab[i],
+ sizeof(struct st_effect));
+
+ /* Copy format info to effect table */
+ effects_mask = st_updateeffect(&efftab[neffects], &informat,
+ &outformat, effects_mask);
+
+ /* If this effect can't handle multiple channels then
+ * account for this.
+ */
+ if ((efftab[neffects].ininfo.channels > 1) &&
+ !(efftab[neffects].h->flags & ST_EFF_MCHAN))
+ {
+ memcpy(&efftabR[neffects], &efftab[neffects],
+ sizeof(struct st_effect));
}
- }
- for(i = 1; i < neffects; i++) {
- /* pointer comparison OK here */
- /* shallow copy of initialized effect data */
- /* XXX this assumes that effect_getopt() doesn't malloc() */
- if (efftab[i].name == effp->name) {
- memcpy(&efftab[i], &eff, sizeof(struct st_effect));
- if (efftabR[i].name)
- memcpy(&efftabR[i], &eff, sizeof(struct st_effect));
- } else {
- /* set up & give default opts for added effects */
- st_geteffect(&efftab[i]);
- (* efftab[i].h->getopts)(&efftab[i],(int)0,(char **)0);
- if (efftabR[i].name)
- memcpy(&efftabR[i], &efftab[i],
- sizeof(struct st_effect));
- }
+ neffects++;
}
-
- /* If a user doesn't specify an effect then a null entry could
- * have been placed in the middle of the list above. Remove
- * those entries here.
- */
- for(i = 1; i < neffects; i++)
- if (! strcmp(efftab[i].name, "null")) {
- for(; i < neffects; i++) {
- efftab[i] = efftab[i+1];
- efftabR[i] = efftabR[i+1];
- }
- neffects--;
+
+ /* If rate effect hasn't been added by now then add it here.
+ * Check adding rate before avg because its faster to run
+ * rate on less channels then more.
+ */
+ if (needrate && !(effects_mask & ST_EFF_RATE))
+ {
+ if (soxpreview)
+ st_geteffect(&efftab[neffects], "rate");
+ else
+ st_geteffect(&efftab[neffects], "resample");
+
+ /* set up & give default opts for added effects */
+ (* efftab[neffects].h->getopts)(&efftab[neffects],(int)0,
+ (char **)0);
+
+ /* Copy format info to effect table */
+ effects_mask = st_updateeffect(&efftab[neffects], &informat,
+ &outformat, effects_mask);
+
+ /* Rate can't handle multiple channels so be sure and
+ * account for that.
+ */
+ if (efftab[neffects].ininfo.channels > 1)
+ {
+ memcpy(&efftabR[neffects], &efftab[neffects],
+ sizeof(struct st_effect));
}
+
+ neffects++;
+ }
+
+ /* If code up until know still hasn't added avg effect then
+ * do it now.
+ */
+ if (needchan && !(effects_mask & ST_EFF_CHAN))
+ {
+ st_geteffect(&efftab[neffects], "avg");
+
+ /* set up & give default opts for added effects */
+ (* efftab[neffects].h->getopts)(&efftab[neffects],(int)0,
+ (char **)0);
+
+ /* Copy format info to effect table */
+ effects_mask = st_updateeffect(&efftab[neffects], &informat,
+ &outformat, effects_mask);
+
+ neffects++;
+ }
}
-/* Guido Van Rossum fix */
static void statistics(P0) {
if (informat.info.dovol && clipped > 0)
st_report("Volume change clipped %d samples", clipped);
--- a/src/st.h
+++ b/src/st.h
@@ -335,7 +335,9 @@
void st_fail(P2(const char *, ...))NORET;
void st_fail_errno(P4(ft_t, int, const char *, ...));
-void st_geteffect(P1(eff_t));
+int st_geteffect_opt(P3(eff_t, int, char **));
+int st_geteffect(P2(eff_t, char *));
+int st_updateeffect(P4(eff_t, ft_t, ft_t, int));
void st_gettype(P1(ft_t));
void st_checkformat(P1(ft_t));
void st_copyformat(P2(ft_t, ft_t));
--- a/src/util.c
+++ b/src/util.c
@@ -147,30 +147,153 @@
}
/*
- * Check that we have a known effect name.
+ * Check that we have a known effect name. If found, copy name of
+ * effect into structure and place a pointer to internal data.
+ * Returns -1 on error else it turns the total number of arguments
+ * that should be passed to this effects getopt() function.
*/
-void
-st_geteffect(effp)
-eff_t effp;
+int st_geteffect_opt(eff_t effp, int argc, char **argv)
{
+ int i, optind;
+
+ for(i = 0; st_effects[i].name; i++)
+ {
+ char *s1 = st_effects[i].name, *s2 = argv[0];
+
+ while(*s1 && *s2 && (tolower(*s1) == tolower(*s2)))
+ s1++, s2++;
+ if (*s1 || *s2)
+ continue; /* not a match */
+
+ /* Found it! */
+ effp->name = st_effects[i].name;
+ effp->h = &st_effects[i];
+
+ optind = 1;
+
+ while (optind < argc)
+ {
+ for (i = 0; st_effects[i].name; i++)
+ {
+ char *s1 = st_effects[i].name, *s2 = argv[optind];
+ while (*s1 && *s2 && (tolower(*s1) == tolower(*s2)))
+ s1++, s2++;
+ if (*s1 || *s2)
+ continue;
+
+ /* Found it! */
+ return (optind - 1);
+ }
+ /* Didn't find a match, try the next argument. */
+ optind++;
+ }
+ /*
+ * No matches found, all the following arguments are
+ * for this effect passed in.
+ */
+ return (optind - 1);
+ }
+
+ return (ST_EOF);
+}
+
+/*
+ * Check that we have a known effect name. If found, copy name of
+ * effect into structure and place a pointer to internal data.
+ * Returns -1 on on failure.
+ */
+
+int st_geteffect(eff_t effp, char *effect_name)
+{
int i;
for(i = 0; st_effects[i].name; i++) {
- char *s1 = st_effects[i].name, *s2 = effp->name;
+ char *s1 = st_effects[i].name, *s2 = effect_name;
+
while(*s1 && *s2 && (tolower(*s1) == tolower(*s2)))
s1++, s2++;
if (*s1 || *s2)
continue; /* not a match */
+
/* Found it! */
+ effp->name = st_effects[i].name;
effp->h = &st_effects[i];
- return;
+
+ return ST_SUCCESS;
}
- /* Guido Van Rossum fix */
- fprintf(stderr, "%s: Known effects: ",myname);
- for (i = 1; st_effects[i].name; i++)
- fprintf(stderr, "%s ", st_effects[i].name);
- fprintf(stderr, "\n");
- st_fail("Effect '%s' is not known!", effp->name);
+
+ return (ST_EOF);
+}
+
+/*
+ * Copy input and output signal info into effect structures.
+ * Must pass in a bitmask containing info of wheither ST_EFF_CHAN
+ * or ST_EFF_RATE has been used previously on this effect stream.
+ * If not running multiple effects then just pass in a value of 0.
+ *
+ * Return value is the same mask plus addition of ST_EFF_CHAN or
+ * ST_EFF_RATE if it was used in this effect. That make this
+ * return value can be passed back into this function in future
+ * calls.
+ */
+
+int st_updateeffect(eff_t effp, ft_t in, ft_t out, int effect_mask)
+{
+ int i;
+
+ effp->ininfo = in->info;
+ effp->ininfo = in->info;
+
+ effp->outinfo = out->info;
+ effp->outinfo = out->info;
+
+ for(i = 0; i < 8; i++) {
+ memcpy(&effp->loops[i], &in->loops[i], sizeof(struct st_loopinfo));
+ memcpy(&effp->loops[i], &in->loops[i], sizeof(struct st_loopinfo));
+ }
+ effp->instr = in->instr;
+ effp->instr = in->instr;
+
+ if (in->info.channels != out->info.channels)
+ {
+ /* Only effects with ST_EFF_CHAN flag can actually handle
+ * outputing a different number of channels then the input.
+ */
+ if (!(effp->h->flags & ST_EFF_CHAN))
+ {
+ /* If this effect is being ran before a ST_EFF_CHAN effect
+ * then effect's output is the same as the input file. Else its
+ * input contains same number of channels as the output
+ * file.
+ */
+ if (effect_mask & ST_EFF_CHAN)
+ effp->ininfo.channels = out->info.channels;
+ else
+ effp->outinfo.channels = in->info.channels;
+
+ }
+ }
+
+ if (in->info.rate != out->info.rate)
+ {
+ /* Only the ST_EFF_RATE effect can handle an input that
+ * is a different sample rate then the output.
+ */
+ if (!(effp->h->flags & ST_EFF_RATE))
+ {
+ if (effect_mask & ST_EFF_RATE)
+ effp->ininfo.rate = out->info.rate;
+ else
+ effp->outinfo.rate = in->info.rate;
+ }
+ }
+
+ if (effp->h->flags & ST_EFF_CHAN)
+ effect_mask |= ST_EFF_CHAN;
+ if (effp->h->flags & ST_EFF_RATE)
+ effect_mask |= ST_EFF_RATE;
+
+ return effect_mask;
}
/*