ref: e38ffc2c9716d8764aef7e88ad40d0ebbb572ff2
dir: /src/effects.c/
/* SoX Effects chain (c) 2007 robs@users.sourceforge.net
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define LSX_EFF_ALIAS
#include "sox_i.h"
#include "sgetopt.h"
#include <assert.h>
#include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#define DEBUG_EFFECTS_CHAIN 0
/* FIXME: Not thread safe using globals */
sox_effects_globals_t sox_effects_globals =
{sox_plot_off, &sox_globals};
/* Default effect handler functions for do-nothing situations: */
static int default_function(sox_effect_t * effp UNUSED)
{
return SOX_SUCCESS;
}
/* Pass through samples verbatim */
int lsx_flow_copy(sox_effect_t * effp, const sox_sample_t * ibuf,
sox_sample_t * obuf, size_t * isamp, size_t * osamp)
{
*isamp = *osamp = min(*isamp, *osamp);
memcpy(obuf, ibuf, *isamp * sizeof(*obuf));
(void)effp;
return SOX_SUCCESS;
}
/* Inform no more samples to drain */
static int default_drain(sox_effect_t * effp UNUSED, sox_sample_t *obuf UNUSED, size_t *osamp)
{
*osamp = 0;
return SOX_EOF;
}
/* Check that no parameters have been given */
static int default_getopts(sox_effect_t * effp, int argc, char **argv UNUSED)
{
return --argc? lsx_usage(effp) : SOX_SUCCESS;
}
/* Partially initialise the effect structure; signal info will come later */
sox_effect_t * sox_create_effect(sox_effect_handler_t const * eh)
{
sox_effect_t * effp = lsx_calloc(1, sizeof(*effp));
effp->global_info = &sox_effects_globals;
effp->handler = *eh;
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 = lsx_flow_copy;
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;
effp->priv = lsx_calloc(1, effp->handler.priv_size);
return effp;
} /* sox_create_effect */
int sox_effect_options(sox_effect_t *effp, int argc, char * const argv[])
{
int result, callers_optind = lsx_optind, callers_opterr = opterr;
char * * argv2 = lsx_malloc((argc + 1) * sizeof(*argv2));
argv2[0] = (char *)effp->handler.name;
memcpy(argv2 + 1, argv, argc * sizeof(*argv2));
lsx_optind = 1, opterr = 0;
result = effp->handler.getopts(effp, argc + 1, argv2);
lsx_optind = callers_optind, opterr = callers_opterr;
free(argv2);
return result;
} /* sox_effect_options */
/* Effects chain: */
sox_effects_chain_t * sox_create_effects_chain(
sox_encodinginfo_t const * in_enc, sox_encodinginfo_t const * out_enc)
{
sox_effects_chain_t * result = lsx_calloc(1, sizeof(sox_effects_chain_t));
result->global_info = sox_effects_globals;
result->in_enc = in_enc;
result->out_enc = out_enc;
return result;
} /* sox_create_effects_chain */
void sox_delete_effects_chain(sox_effects_chain_t *ecp)
{
if (ecp && ecp->length)
sox_delete_effects(ecp);
free(ecp);
} /* sox_delete_effects_chain */
/* Effect can call in start() or flow() to set minimum input size to flow() */
int lsx_effect_set_imin(sox_effect_t * effp, size_t imin)
{
if (imin > sox_globals.bufsiz / effp->flows) {
lsx_fail("sox_bufsiz not big enough");
return SOX_EOF;
}
effp->imin = imin;
return SOX_SUCCESS;
}
/* 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_effects_chain_t * chain, 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;
sox_effect_t eff0; /* Copy of effect for flow 0 before calling start */
effp->global_info = &chain->global_info;
effp->in_signal = *in;
effp->out_signal = *out;
effp->in_encoding = chain->in_enc;
effp->out_encoding = chain->out_enc;
if (!(effp->handler.flags & SOX_EFF_CHAN))
effp->out_signal.channels = in->channels;
if (!(effp->handler.flags & SOX_EFF_RATE))
effp->out_signal.rate = in->rate;
if (!(effp->handler.flags & SOX_EFF_PREC))
effp->out_signal.precision = (effp->handler.flags & SOX_EFF_MODIFY)?
in->precision : SOX_SAMPLE_PRECISION;
if (!(effp->handler.flags & SOX_EFF_GAIN))
effp->out_signal.mult = in->mult;
effp->flows =
(effp->handler.flags & SOX_EFF_MCHAN)? 1 : effp->in_signal.channels;
effp->clips = 0;
effp->imin = 0;
eff0 = *effp, eff0.priv = lsx_memdup(eff0.priv, eff0.handler.priv_size);
eff0.in_signal.mult = NULL; /* Only used in channel 0 */
ret = start(effp);
if (ret == SOX_EFF_NULL) {
lsx_report("has no effect in this configuration");
free(eff0.priv);
return SOX_SUCCESS;
}
if (ret != SOX_SUCCESS) {
free(eff0.priv);
return SOX_EOF;
}
if (in->mult)
lsx_debug("mult=%g", *in->mult);
*in = effp->out_signal;
if (chain->length == SOX_MAX_EFFECTS) {
lsx_fail("Too many effects!");
free(eff0.priv);
return SOX_EOF;
}
chain->effects[chain->length] =
lsx_calloc(effp->flows, sizeof(chain->effects[chain->length][0]));
chain->effects[chain->length][0] = *effp;
for (f = 1; f < effp->flows; ++f) {
chain->effects[chain->length][f] = eff0;
chain->effects[chain->length][f].flow = f;
chain->effects[chain->length][f].priv = lsx_memdup(eff0.priv, eff0.handler.priv_size);
if (start(&chain->effects[chain->length][f]) != SOX_SUCCESS) {
free(eff0.priv);
return SOX_EOF;
}
}
++chain->length;
free(eff0.priv);
return SOX_SUCCESS;
}
static int flow_effect(sox_effects_chain_t * chain, size_t n)
{
sox_effect_t * effp1 = &chain->effects[n - 1][0];
sox_effect_t * effp = &chain->effects[n][0];
int effstatus = SOX_SUCCESS, f = 0;
size_t i;
const sox_sample_t *ibuf;
size_t idone = effp1->oend - effp1->obeg;
size_t obeg = sox_globals.bufsiz - effp->oend;
#if DEBUG_EFFECTS_CHAIN
size_t pre_idone = idone;
size_t pre_odone = obeg;
#endif
if (effp->flows == 1) /* Run effect on all channels at once */
effstatus = effp->handler.flow(effp, &effp1->obuf[effp1->obeg],
&effp->obuf[effp->oend], &idone, &obeg);
else { /* Run effect on each channel individually */
sox_sample_t *obuf = &effp->obuf[effp->oend];
size_t idone_last = 0, odone_last = 0; /* Initialised to prevent warning */
ibuf = &effp1->obuf[effp1->obeg];
for (i = 0; i < idone; i += effp->flows)
for (f = 0; f < (int)effp->flows; ++f)
chain->ibufc[f][i / effp->flows] = *ibuf++;
#ifdef HAVE_OPENMP
#pragma omp parallel for
#endif
for (f = 0; f < (int)effp->flows; ++f) {
size_t idonec = idone / effp->flows;
size_t odonec = obeg / effp->flows;
int eff_status_c = effp->handler.flow(&chain->effects[n][f],
chain->ibufc[f], chain->obufc[f], &idonec, &odonec);
#ifndef HAVE_OPENMP
if (f && (idonec != idone_last || odonec != odone_last)) {
lsx_fail("flowed asymmetrically!");
effstatus = SOX_EOF;
}
idone_last = idonec;
odone_last = odonec;
#else
if (!f) {
idone_last = idonec;
odone_last = odonec;
}
#endif
if (eff_status_c != SOX_SUCCESS)
effstatus = SOX_EOF;
}
for (i = 0; i < odone_last; ++i)
for (f = 0; f < (int)effp->flows; ++f)
*obuf++ = chain->obufc[f][i];
idone = effp->flows * idone_last;
obeg = effp->flows * odone_last;
}
#if DEBUG_EFFECTS_CHAIN
lsx_report("flow: %5u%5u%5u%5u", pre_idone, pre_odone, idone, obeg);
#endif
effp1->obeg += idone;
if (effp1->obeg == effp1->oend)
effp1->obeg = effp1->oend = 0;
else if (effp1->oend - effp1->obeg < effp->imin ) { /* Need to refill? */
memmove(effp1->obuf, &effp1->obuf[effp1->obeg], (effp1->oend - effp1->obeg) * sizeof(*effp1->obuf));
effp1->oend -= effp1->obeg;
effp1->obeg = 0;
}
effp->oend += obeg;
return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF;
}
/* The same as flow_effect but with no input */
static int drain_effect(sox_effects_chain_t * chain, size_t n)
{
sox_effect_t * effp = &chain->effects[n][0];
int effstatus = SOX_SUCCESS;
size_t i, f;
size_t obeg = sox_globals.bufsiz - effp->oend;
#if DEBUG_EFFECTS_CHAIN
size_t pre_odone = obeg;
#endif
if (effp->flows == 1) /* Run effect on all channels at once */
effstatus = effp->handler.drain(effp, &effp->obuf[effp->oend], &obeg);
else { /* Run effect on each channel individually */
sox_sample_t *obuf = &effp->obuf[effp->oend];
size_t odone_last = 0; /* Initialised to prevent warning */
for (f = 0; f < effp->flows; ++f) {
size_t odonec = obeg / effp->flows;
int eff_status_c = effp->handler.drain(&chain->effects[n][f], chain->obufc[f], &odonec);
if (f && (odonec != odone_last)) {
lsx_fail("drained asymmetrically!");
effstatus = SOX_EOF;
}
odone_last = odonec;
if (eff_status_c != SOX_SUCCESS)
effstatus = SOX_EOF;
}
for (i = 0; i < odone_last; ++i)
for (f = 0; f < effp->flows; ++f)
*obuf++ = chain->obufc[f][i];
obeg = f * odone_last;
}
#if DEBUG_EFFECTS_CHAIN
lsx_report("drain: %5u%5u%5u%5u", 0, pre_odone, 0, obeg);
#endif
if (!obeg) /* This is the only thing that drain has and flow hasn't */
effstatus = SOX_EOF;
effp->oend += obeg;
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(sox_effects_chain_t * chain, int (* callback)(sox_bool all_done, void * client_data), void * client_data)
{
int flow_status = SOX_SUCCESS;
size_t e, source_e = 0; /* effect indices */
size_t f, max_flows = 0;
sox_bool draining = sox_true;
for (e = 0; e < chain->length; ++e) {
chain->effects[e][0].obuf = lsx_malloc(sox_globals.bufsiz * sizeof(chain->effects[e][0].obuf[0]));
chain->effects[e][0].obeg = chain->effects[e][0].oend = 0;
max_flows = max(max_flows, chain->effects[e][0].flows);
}
chain->ibufc = lsx_calloc(max_flows, sizeof(*chain->ibufc));
chain->obufc = lsx_calloc(max_flows, sizeof(*chain->obufc));
for (f = 0; f < max_flows; ++f) {
chain->ibufc[f] = lsx_calloc(sox_globals.bufsiz / 2, sizeof(chain->ibufc[f][0]));
chain->obufc[f] = lsx_calloc(sox_globals.bufsiz / 2, sizeof(chain->obufc[f][0]));
}
e = chain->length - 1;
while (source_e < chain->length) {
#define have_imin (e > 0 && e < chain->length && chain->effects[e - 1][0].oend - chain->effects[e - 1][0].obeg >= chain->effects[e][0].imin)
size_t osize = chain->effects[e][0].oend - chain->effects[e][0].obeg;
if (e == source_e && (draining || !have_imin)) {
if (drain_effect(chain, e) == SOX_EOF) {
++source_e;
draining = sox_false;
}
} else if (have_imin && flow_effect(chain, e) == SOX_EOF) {
flow_status = SOX_EOF;
if (e == chain->length - 1)
break;
source_e = e;
draining = sox_true;
}
if (e < chain->length && chain->effects[e][0].oend - chain->effects[e][0].obeg > osize) /* False for output */
++e;
else if (e == source_e)
draining = sox_true;
else if ((int)--e < (int)source_e)
e = source_e;
if (callback && callback(source_e == chain->length, client_data) != SOX_SUCCESS) {
flow_status = SOX_EOF; /* Client has requested to stop the flow. */
break;
}
}
for (f = 0; f < max_flows; ++f) {
free(chain->ibufc[f]);
free(chain->obufc[f]);
}
free(chain->obufc);
free(chain->ibufc);
for (e = 0; e < chain->length; ++e)
free(chain->effects[e][0].obuf);
return flow_status;
}
size_t sox_effects_clips(sox_effects_chain_t * chain)
{
unsigned i, f;
size_t clips = 0;
for (i = 1; i < chain->length - 1; ++i)
for (f = 0; f < chain->effects[i][0].flows; ++f)
clips += chain->effects[i][f].clips;
return clips;
}
size_t sox_stop_effect(sox_effect_t *effp)
{
unsigned f;
size_t clips = 0;
for (f = 0; f < effp->flows; ++f) {
effp[f].handler.stop(&effp[f]);
clips += effp[f].clips;
}
return clips;
}
void sox_push_effect_last(sox_effects_chain_t *chain, sox_effect_t *effp)
{
chain->effects[chain->length++] = effp;;
} /* sox_push_effect_last */
sox_effect_t *sox_pop_effect_last(sox_effects_chain_t *chain)
{
if (chain->length > 0)
{
sox_effect_t *effp;
chain->length--;
effp = chain->effects[chain->length];
chain->effects[chain->length] = NULL;
return effp;
}
else
return NULL;
} /* sox_pop_effect_last */
/* Free resources related to effect.
* Note: This currently closes down the effect which might
* not be obvious from name.
*/
void sox_delete_effect(sox_effect_t *effp)
{
size_t clips;
unsigned f;
if ((clips = sox_stop_effect(effp)) != 0)
lsx_warn("%s clipped %lu samples; decrease volume?",
effp->handler.name, (unsigned long)clips);
effp->handler.kill(effp); /* N.B. only one kill; not one per flow */
for (f = 0; f < effp->flows; ++f)
free(effp[f].priv);
free(effp);
}
void sox_delete_effect_last(sox_effects_chain_t *chain)
{
if (chain->length > 0)
{
chain->length--;
sox_delete_effect(chain->effects[chain->length]);
chain->effects[chain->length] = NULL;
}
} /* sox_delete_effect_last */
/* Remove all effects from the chain.
* Note: This currently closes down the effect which might
* not be obvious from name.
*/
void sox_delete_effects(sox_effects_chain_t * chain)
{
size_t e;
for (e = 0; e < chain->length; ++e) {
sox_delete_effect(chain->effects[e]);
chain->effects[e] = NULL;
}
chain->length = 0;
}
/*----------------------------- Effects library ------------------------------*/
sox_effect_fn_t sox_effect_fns[] = {
#define EFFECT(f) lsx_##f##_effect_fn,
#include "effects.h"
#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;
}