ref: b123511e766ad231f601a2909f3e1cbec82a6256
parent: 0e90c7f765e3c04f07196c08ed57bc9fa90f6fb6
author: robs <robs>
date: Sat Jun 9 08:23:39 EDT 2007
Effects chain can now handle multiple effects changing rate or chans, though current effects are not suited to this
--- a/src/effects.c
+++ b/src/effects.c
@@ -23,57 +23,45 @@
#include <strings.h>
#undef sox_fail
-#undef sox_warn
#undef sox_report
#define sox_fail sox_message_filename=effp->handler.name,sox_fail
-#define sox_warn sox_message_filename=effp->handler.name,sox_warn
#define sox_report sox_message_filename=effp->handler.name,sox_report
-/* dummy effect routine for do-nothing functions */
-static int effect_nothing(sox_effect_t * effp UNUSED)
+
+
+/* Default effect handler functions for do-nothing situations: */
+
+static int default_function(sox_effect_t * effp UNUSED)
{
return SOX_SUCCESS;
}
-static int effect_nothing_flow(sox_effect_t * effp UNUSED, const sox_ssample_t *ibuf UNUSED, sox_ssample_t *obuf UNUSED, sox_size_t *isamp, sox_size_t *osamp)
+/* Pass through samples verbatim */
+static int default_flow(sox_effect_t * effp UNUSED, const sox_ssample_t *ibuf UNUSED, sox_ssample_t *obuf UNUSED, sox_size_t *isamp, sox_size_t *osamp)
{
- /* Pass through samples verbatim */
*isamp = *osamp = min(*isamp, *osamp);
memcpy(obuf, ibuf, *isamp * sizeof(*obuf));
return SOX_SUCCESS;
}
-static int effect_nothing_drain(sox_effect_t * effp UNUSED, sox_ssample_t *obuf UNUSED, sox_size_t *osamp)
+/* Inform no more samples to drain */
+static int default_drain(sox_effect_t * effp UNUSED, sox_ssample_t *obuf UNUSED, sox_size_t *osamp)
{
- /* Inform no more samples to drain */
*osamp = 0;
return SOX_EOF;
}
-static int effect_nothing_getopts(sox_effect_t * effp, int argc, char **argv UNUSED)
+/* Check that no parameters have been given */
+static int default_getopts(sox_effect_t * effp, int argc, char **argv UNUSED)
{
if (argc) {
- sox_fail(effp->handler.usage);
+ sox_fail("takes no parameters");
return SOX_EOF;
}
return SOX_SUCCESS;
}
-
-/* Effect chain routines */
-
-sox_effect_handler_t const * sox_find_effect(char const * name)
-{
- int e;
-
- for (e = 0; sox_effect_fns[e]; ++e) {
- const sox_effect_handler_t *effp = sox_effect_fns[e] ();
- if (effp && effp->name && strcasecmp(effp->name, name) == 0)
- return effp; /* Found it. */
- }
- return NULL;
-}
-
+/* Partially initialise the effect structure; signal info will come later */
void sox_create_effect(sox_effect_t * effp, sox_effect_handler_t const * eh)
{
assert(eh);
@@ -80,70 +68,22 @@
memset(effp, 0, sizeof(*effp));
effp->global_info = &effects_global_info;
effp->handler = *eh;
- if (!effp->handler.getopts) effp->handler.getopts = effect_nothing_getopts;
- if (!effp->handler.start ) effp->handler.start = effect_nothing;
- if (!effp->handler.flow ) effp->handler.flow = effect_nothing_flow;
- if (!effp->handler.drain ) effp->handler.drain = effect_nothing_drain;
- if (!effp->handler.stop ) effp->handler.stop = effect_nothing;
- if (!effp->handler.kill ) effp->handler.kill = effect_nothing;
+ if (!effp->handler.getopts) effp->handler.getopts = default_getopts;
+ if (!effp->handler.start ) effp->handler.start = default_function;
+ if (!effp->handler.flow ) effp->handler.flow = default_flow;
+ if (!effp->handler.drain ) effp->handler.drain = default_drain;
+ if (!effp->handler.stop ) effp->handler.stop = default_function;
+ if (!effp->handler.kill ) effp->handler.kill = default_function;
}
-/*
- * Copy input and output signal info into effect structures.
- * Must pass in a bitmask containing info on whether SOX_EFF_CHAN
- * or SOX_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 SOX_EFF_CHAN or
- * SOX_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 sox_update_effect(sox_effect_t * effp, const sox_signalinfo_t * in,
- const sox_signalinfo_t * out, int effect_mask)
-{
- effp->ininfo = *in;
- effp->outinfo = *out;
- if (in->channels != out->channels) {
- /* Only effects with SOX_EFF_CHAN flag can actually handle
- * outputing a different number of channels then the input.
- */
- if (!(effp->handler.flags & SOX_EFF_CHAN)) {
- /* If this effect is being run before a SOX_EFF_CHAN effect
- * then its output is the same as the input file; otherwise,
- * its input contains the same number of channels as the
- * output file. */
- if (effect_mask & SOX_EFF_CHAN)
- effp->ininfo.channels = out->channels;
- else
- effp->outinfo.channels = in->channels;
- }
- }
+/* Effects chain: */
- if (in->rate != out->rate) {
- /* Only SOX_EFF_RATE effects can handle an input that
- * has a different sample rate from the output. */
- if (!(effp->handler.flags & SOX_EFF_RATE)) {
- if (effect_mask & SOX_EFF_RATE)
- effp->ininfo.rate = out->rate;
- else
- effp->outinfo.rate = in->rate;
- }
- }
-
- if (effp->handler.flags & SOX_EFF_CHAN)
- effect_mask |= SOX_EFF_CHAN;
- if (effp->handler.flags & SOX_EFF_RATE)
- effect_mask |= SOX_EFF_RATE;
-
- return effect_mask;
-}
-
sox_effect_t * sox_effects[SOX_MAX_EFFECTS];
unsigned sox_neffects;
+/* Effect can call in start() or flow() to set minimum input size to flow() */
int sox_effect_set_imin(sox_effect_t * effp, sox_size_t imin)
{
if (imin > sox_bufsiz / effp->flows) {
@@ -155,94 +95,57 @@
return SOX_SUCCESS;
}
-int sox_add_effect(sox_effect_t * effp, sox_signalinfo_t * in, sox_signalinfo_t * out, int * effects_mask)
-{
- unsigned f, flows;
+/* Add an effect to the chain. *in is the input signal for this effect. *out is
+ * a suggestion as to what the output signal should be, but depending on its
+ * given options and *in, the effect can choose to do differently. Whatever
+ * output rate and channels the effect does produce are written back to *in,
+ * ready for the next effect in the chain.
+ */
+int sox_add_effect(sox_effect_t * effp, sox_signalinfo_t * in, sox_signalinfo_t
+ const * out) { int ret, (*start)(sox_effect_t * effp) =
+ effp->handler.start; unsigned f;
- if (sox_neffects == SOX_MAX_EFFECTS)
+ if (effp->handler.flags & SOX_EFF_NULL) {
+ sox_report("has no effect (is a proxy effect)");
+ return SOX_EFF_NULL;
+ }
+ effp->outinfo = effp->ininfo = *in;
+ if (effp->handler.flags & SOX_EFF_CHAN)
+ effp->outinfo.channels = out->channels;
+ if (effp->handler.flags & SOX_EFF_RATE)
+ effp->outinfo.rate = out->rate;
+ effp->flows =
+ (effp->handler.flags & SOX_EFF_MCHAN)? 1 : effp->ininfo.channels;
+ effp->clips = 0;
+ effp->imin = 0;
+ ret = start(effp);
+ if (ret == SOX_EFF_NULL) {
+ sox_report("has no effect in this configuration");
+ return SOX_EFF_NULL;
+ }
+ if (ret != SOX_SUCCESS)
return SOX_EOF;
+ *in = effp->outinfo;
- *effects_mask = sox_update_effect(effp, in, out, *effects_mask);
-
- flows = (effp->handler.flags & SOX_EFF_MCHAN)? 1 : effp->ininfo.channels;
-
- sox_effects[sox_neffects] = xcalloc(flows, sizeof(sox_effects[sox_neffects][0]));
+ if (sox_neffects == SOX_MAX_EFFECTS) {
+ sox_fail("Too many effects!");
+ return SOX_EOF;
+ }
+ sox_effects[sox_neffects] =
+ xcalloc(effp->flows, sizeof(sox_effects[sox_neffects][0]));
sox_effects[sox_neffects][0] = *effp;
- sox_effects[sox_neffects][0].flows = flows;
- for (f = 1; f < flows; ++f)
- sox_effects[sox_neffects][f] = sox_effects[sox_neffects][0];
+ for (f = 1; f < effp->flows; ++f) {
+ sox_effects[sox_neffects][f] = *effp;
+ sox_effects[sox_neffects][f].flow = f;
+ if (start(&sox_effects[sox_neffects][f]) != SOX_SUCCESS)
+ return SOX_EOF;
+ }
++sox_neffects;
return SOX_SUCCESS;
}
-static void stop_effect(unsigned e)
-{
- sox_effect_t * effp = &sox_effects[e][0];
- unsigned f;
-
- sox_size_t clips = 0;
-
- for (f = 0; f < effp->flows; ++f) {
- effp->handler.stop(&sox_effects[e][f]);
- clips += sox_effects[e][f].clips;
- }
- if (clips != 0)
- sox_warn("clipped %u samples; decrease volume?", clips);
-}
-
-void sox_stop_effects(void)
-{
- unsigned e;
- for (e = 0; e < sox_neffects; ++e)
- stop_effect(e);
-}
-
-int sox_start_effects(void)
-{
- unsigned e, f, i;
- int ret = SOX_SUCCESS;
-
- for (e = 0; e < sox_neffects; ++e) {
- sox_effect_t * effp = &sox_effects[e][0];
- sox_bool is_always_null = (effp->handler.flags & SOX_EFF_NULL) != 0;
- int (*start)(sox_effect_t * effp) = effp->handler.start;
-
- if (is_always_null)
- sox_report("has no effect (is a proxy effect)");
- else {
- effp->clips = 0;
- effp->imin = 0;
- ret = start(effp);
- if (ret == SOX_EFF_NULL)
- sox_report("has no effect in this configuration");
- else if (ret != SOX_SUCCESS)
- return SOX_EOF;
- }
- if (is_always_null || ret == SOX_EFF_NULL) { /* remove from the chain */
- free(sox_effects[e]);
- --sox_neffects;
- for (i = e--; i < sox_neffects; ++i)
- sox_effects[i] = sox_effects[i + 1];
- }
- else for (f = 1; f < sox_effects[e][0].flows; ++f) {
- sox_effects[e][f].clips = 0;
- if (start(&sox_effects[e][f]) != SOX_SUCCESS)
- return SOX_EOF;
- }
- }
- for (e = 0; e < sox_neffects; ++e) {
- sox_effect_t * effp = &sox_effects[e][0];
- #undef sox_report
- #define sox_report sox_message_filename="effects chain",sox_report
- sox_report("%-10s %uHz %u channels %s",
- effp->handler.name, effp->ininfo.rate, effp->ininfo.channels,
- (effp->handler.flags & SOX_EFF_MCHAN)? "(multi)" : "");
- }
- return SOX_SUCCESS;
-}
-
static sox_ssample_t **ibufc, **obufc; /* Channel interleave buffers */
static int flow_effect(unsigned n)
@@ -344,6 +247,7 @@
return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF;
}
+/* Flow data through the effects chain until an effect or callback gives EOF */
int sox_flow_effects(int (* callback)(sox_bool all_done))
{
int flow_status = SOX_SUCCESS;
@@ -403,6 +307,7 @@
return flow_status;
}
+/* Remove all effects from the chain */
void sox_delete_effects(void)
{
while (sox_neffects)
@@ -410,8 +315,9 @@
}
-/* Effects handlers. */
+/* Effects library: */
+
sox_effect_fn_t sox_effect_fns[] = {
#define EFFECT(f) sox_##f##_effect_fn,
#include "effects.h"
@@ -418,3 +324,16 @@
#undef EFFECT
NULL
};
+
+/* Find a named effect in the effects library */
+sox_effect_handler_t const * sox_find_effect(char const * name)
+{
+ int e;
+
+ for (e = 0; sox_effect_fns[e]; ++e) {
+ const sox_effect_handler_t *eh = sox_effect_fns[e] ();
+ if (eh && eh->name && strcasecmp(eh->name, name) == 0)
+ return eh; /* Found it. */
+ }
+ return NULL;
+}
--- a/src/sox.c
+++ b/src/sox.c
@@ -1216,79 +1216,68 @@
return &handler;
}
-static void add_default_effect(char const *name, int *effects_mask)
+static void add_auto_effect(char const * name, sox_signalinfo_t * signal)
{
- sox_effect_t e;
+ sox_effect_t eff;
- /* Default name should always be correct! */
- sox_create_effect(&e, sox_find_effect(name));
- if (e.handler.getopts(&e, 0, NULL) == SOX_EOF) /* Set up with default opts */
+ /* Auto effect should always succeed here */
+ sox_create_effect(&eff, sox_find_effect(name));
+ eff.handler.getopts(&eff, 0, NULL); /* Set up with default opts */
+
+ /* But could fail here */
+ if (sox_add_effect(&eff, signal, &ofile->ft->signal) != SOX_SUCCESS)
exit(2);
- sox_add_effect(&e, &combiner, &ofile->ft->signal, effects_mask);
}
/* If needed effects are not given, auto-add at (performance) optimal point. */
static void add_effects(void)
{
- unsigned i;
- int effects_mask = 0;
- sox_bool need_rate = combiner.rate != ofile->ft->signal.rate;
- sox_bool need_chan = combiner.channels != ofile->ft->signal.channels;
- int user_mchan = -1;
+ sox_signalinfo_t signal = combiner;
+ unsigned i, min_chan = 0, min_rate = 0;
sox_effect_t eff;
- { /* Check if we have to add effects to change rate/chans or if the
- user has specified effects to do this, in which case, check if
- too many rate/channel-changing effects have been specified: */
- int user_chan_effects = 0, user_rate_effects = 0;
-
- for (i = 0; i < nuser_effects; i++) {
- if (user_efftab[i].handler.flags & SOX_EFF_CHAN) {
- need_chan = sox_false;
- ++user_chan_effects;
- }
- if (user_efftab[i].handler.flags & SOX_EFF_RATE) {
- need_rate = sox_false;
- ++user_rate_effects;
- }
- if (user_efftab[i].handler.flags & SOX_EFF_MCHAN)
- user_mchan = i;
- }
- if (user_chan_effects > 1) {
- sox_fail("Cannot specify multiple effects that change number of channels");
- exit(2);
- }
- if (user_rate_effects > 1) {
- sox_fail("Cannot specify multiple effects that change sample rate");
- exit(2);
- }
+ /* Find points after which we might add effects to change rate/chans */
+ for (i = 0; i < nuser_effects; i++) {
+ if (user_efftab[i].handler.flags & (SOX_EFF_CHAN|SOX_EFF_MCHAN))
+ min_chan = i + 1;
+ if (user_efftab[i].handler.flags & SOX_EFF_RATE)
+ min_rate = i + 1;
}
-
+ /* 1st `effect' in the chain is the input combiner */
sox_create_effect(&eff, input_combiner_effect_fn());
- sox_add_effect(&eff, &combiner, &ofile->ft->signal, &effects_mask);
+ sox_add_effect(&eff, &signal, &ofile->ft->signal);
- /* Copy user specified effects into the real effects */
+ /* Add auto effects if appropriate; add user specified effects */
for (i = 0; i <= nuser_effects; i++) {
/* If reducing channels, it's faster to do so before all other effects: */
- if ((int)i > user_mchan && need_chan && combiner.channels > ofile->ft->signal.channels) {
- add_default_effect("mixer", &effects_mask);
- need_chan = sox_false;
- }
+ if (signal.channels > ofile->ft->signal.channels && i >= min_chan)
+ add_auto_effect("mixer", &signal);
+
/* If reducing rate, it's faster to do so before all other effects
* (except reducing channels): */
- if (need_rate)
- if (i == nuser_effects || combiner.rate > ofile->ft->signal.rate) {
- add_default_effect("resample", &effects_mask);
- need_rate = sox_false;
- }
+ if (signal.rate > ofile->ft->signal.rate && i >= min_rate)
+ add_auto_effect("resample", &signal);
+
if (i < nuser_effects)
- sox_add_effect(&user_efftab[i], &combiner, &ofile->ft->signal, &effects_mask);
+ sox_add_effect(&user_efftab[i], &signal, &ofile->ft->signal);
}
- if (need_chan)
- add_default_effect("mixer", &effects_mask);
+ /* Add auto effects if still needed at this point */
+ if (signal.rate != ofile->ft->signal.rate)
+ add_auto_effect("resample", &signal); /* Must be up-sampling */
+ if (signal.channels != ofile->ft->signal.channels)
+ add_auto_effect("mixer", &signal); /* Must be increasing channels */
+ /* Last `effect' in the chain is the output file */
sox_create_effect(&eff, output_effect_fn());
- sox_add_effect(&eff, &combiner, &ofile->ft->signal, &effects_mask);
+ if (sox_add_effect(&eff, &signal, &ofile->ft->signal) != SOX_SUCCESS)
+ exit(2);
+
+ for (i = 0; i < sox_neffects; ++i) {
+ sox_effect_t * effp = &sox_effects[i][0];
+ sox_report("effects chain: %-10s %uHz %u channels %s",
+ effp->handler.name, effp->ininfo.rate, effp->ininfo.channels,
+ (effp->handler.flags & SOX_EFF_MCHAN)? "(multi)" : "");
+ }
}
static void open_output_file(sox_size_t olen)
@@ -1347,6 +1336,22 @@
return user_abort? SOX_EOF : SOX_SUCCESS;
}
+static void sox_stop_effects(void)
+{
+ unsigned e, f;
+ for (e = 0; e < sox_neffects; ++e) {
+ sox_effect_t * effp = &sox_effects[e][0];
+ sox_size_t clips = 0;
+
+ for (f = 0; f < effp->flows; ++f) {
+ effp->handler.stop(&sox_effects[e][f]);
+ clips += sox_effects[e][f].clips;
+ }
+ if (clips != 0)
+ sox_warn("clipped %u samples; decrease volume?", clips);
+ }
+}
+
/*
* Process: Input(s) -> Balancing -> Combiner -> Effects -> Output
*/
@@ -1418,8 +1423,6 @@
open_output_file(olen);
add_effects();
- if (sox_start_effects() != SOX_SUCCESS)
- exit(2); /* The failing effect should have displayed an error message */
optimize_trim();
--- a/src/sox.h
+++ b/src/sox.h
@@ -399,8 +399,8 @@
#define SOX_MAX_EFFECT_PRIVSIZE SOX_MAX_FILE_PRIVSIZE
-#define SOX_EFF_CHAN 1 /* Effect can mix channels up/down */
-#define SOX_EFF_RATE 2 /* Effect can alter data rate */
+#define SOX_EFF_CHAN 1 /* Effect can alter # of channels */
+#define SOX_EFF_RATE 2 /* Effect can alter sample rate */
#define SOX_EFF_LENGTH 4 /* Effect can alter audio length */
#define SOX_EFF_MCHAN 8 /* Effect can handle multi-channel */
#define SOX_EFF_NULL 16 /* Effect does nothing */
@@ -435,12 +435,12 @@
sox_size_t odone, olen; /* consumed, total length */
sox_size_t imin; /* minimum input buffer size */
sox_size_t clips; /* increment if clipping occurs */
- sox_size_t flows;
+ sox_size_t flows; /* 1 if MCHAN, # chans otherwise */
+ sox_size_t flow; /* flow # */
};
sox_effect_handler_t const *sox_find_effect(char const * name);
void sox_create_effect(sox_effect_t * effp, sox_effect_handler_t const *e);
-int sox_update_effect(sox_effect_t * effp, const sox_signalinfo_t *in, const sox_signalinfo_t *out, int effect_mask);
/* Effects chain */
#define SOX_MAX_EFFECTS 20
@@ -447,10 +447,8 @@
extern sox_effect_t * sox_effects[SOX_MAX_EFFECTS];
extern unsigned sox_neffects;
int sox_effect_set_imin(sox_effect_t * effp, sox_size_t imin);
-int sox_add_effect(sox_effect_t * e, sox_signalinfo_t * in, sox_signalinfo_t * out, int * effects_mask);
-int sox_start_effects(void);
+int sox_add_effect(sox_effect_t * effp, sox_signalinfo_t * in, sox_signalinfo_t const * out);
int sox_flow_effects(int (* callback)(sox_bool all_done));
-void sox_stop_effects(void);
void sox_delete_effects(void);
char const * sox_parsesamples(sox_rate_t rate, const char *str, sox_size_t *samples, int def);