ref: 0adab62cc4a342a0f0b30d2934e33abcb6586cd4
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 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,
* Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
*/
#include "sox_i.h"
#include <assert.h>
#include <string.h>
#include <strings.h>
/* dummy effect routine for do-nothing functions */
static int effect_nothing(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 */
*isamp = *osamp = min(*isamp, *osamp);
memcpy(obuf, ibuf, *isamp * sizeof(sox_ssample_t));
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 */
*osamp = 0;
return SOX_EOF;
}
static int effect_nothing_getopts(sox_effect_t * effp, int n, char **argv UNUSED)
{
#undef sox_fail
#define sox_fail sox_message_filename=effp->handler.name,sox_fail
if (n) {
sox_fail(effp->handler.usage);
return (SOX_EOF);
}
return (SOX_SUCCESS);
#undef sox_fail
}
/* Effect chain routines */
sox_effect_handler_t const * sox_find_effect(char const * name)
{
int i;
for (i = 0; sox_effect_fns[i]; ++i) {
const sox_effect_handler_t *e = sox_effect_fns[i] ();
if (e && e->name && strcasecmp(e->name, name) == 0)
return e; /* Found it. */
}
return NULL;
}
void sox_create_effect(sox_effect_t * effp, sox_effect_handler_t const * e)
{
assert(e);
memset(effp, 0, sizeof(*effp));
effp->global_info = &effects_global_info;
effp->handler = *e;
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;
}
/*
* 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;
}
}
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;
int sox_add_effect(sox_effect_t * e, sox_signalinfo_t * in, sox_signalinfo_t * out, int * effects_mask)
{
unsigned f, flows;
if (sox_neffects == SOX_MAX_EFFECTS)
return SOX_EOF;
*effects_mask = sox_update_effect(e, in, out, *effects_mask);
flows = (e->handler.flags & SOX_EFF_MCHAN)? 1 : e->ininfo.channels;
sox_effects[sox_neffects] = xcalloc(flows, sizeof(sox_effects[sox_neffects][0]));
sox_effects[sox_neffects][0] = *e;
sox_effects[sox_neffects][0].flows = flows;
for (f = 1; f < flows; ++f)
sox_effects[sox_neffects][f] = sox_effects[sox_neffects][0];
++sox_neffects;
return SOX_SUCCESS;
}
static void stop_effect(unsigned e)
{
unsigned i;
sox_size_t clips = 0;
for (i = 0; i < sox_effects[e][0].flows; ++i) {
sox_effects[e][0].handler.stop(&sox_effects[e][i]);
clips += sox_effects[e][i].clips;
}
if (clips != 0)
sox_warn("'%s' clipped %u samples; decrease volume?",sox_effects[e][0].handler.name,clips);
}
void sox_stop_effects(void)
{
unsigned e;
for (e = 0; e < sox_neffects; ++e)
stop_effect(e);
}
static void kill_effect(unsigned e)
{
sox_effects[e][0].handler.kill(&sox_effects[e][0]);/* One kill for all flows */
}
void sox_kill_effects(void)
{
unsigned e;
for (e = 0; e < sox_neffects; ++e)
kill_effect(e);
}
int sox_start_effects(void)
{
unsigned i, j;
int ret = SOX_SUCCESS;
for (i = 0; i < sox_neffects; ++i) {
sox_effect_t * e = &sox_effects[i][0];
sox_bool is_always_null = (e->handler.flags & SOX_EFF_NULL) != 0;
int (*start)(sox_effect_t * effp) = e->handler.start;
if (is_always_null)
sox_report("'%s' has no effect (is a proxy effect)", e->handler.name);
else {
e->clips = 0;
ret = start(e);
if (ret == SOX_EFF_NULL)
sox_warn("'%s' has no effect in this configuration", e->handler.name);
else if (ret != SOX_SUCCESS)
return SOX_EOF;
}
if (is_always_null || ret == SOX_EFF_NULL) { /* remove from the chain */
kill_effect(i);
free(sox_effects[i]);
--sox_neffects;
for (j = i--; j < sox_neffects; ++j)
sox_effects[j] = sox_effects[j + 1];
}
else for (j = 1; j < sox_effects[i][0].flows; ++j) {
sox_effects[i][j].clips = 0;
if (start(&sox_effects[i][j]) != SOX_SUCCESS)
return SOX_EOF;
}
}
for (i = 0; i < sox_neffects; ++i) {
sox_effect_t * e = &sox_effects[i][0];
sox_report("Effects chain: %-10s %uHz %u channels %s",
e->handler.name, e->ininfo.rate, e->ininfo.channels,
(e->handler.flags & SOX_EFF_MCHAN)? "(multi)" : "");
}
return SOX_SUCCESS;
}
static sox_ssample_t **ibufc, **obufc; /* Channel interleave buffers */
static int flow_effect(unsigned e)
{
int effstatus = SOX_SUCCESS;
sox_size_t i, f;
const sox_ssample_t *ibuf;
sox_size_t idone = sox_effects[e - 1][0].olen - sox_effects[e - 1][0].odone;
sox_size_t odone = sox_bufsiz - sox_effects[e][0].olen;
if (sox_effects[e][0].flows == 1) /* Run effect on all channels at once */
effstatus = sox_effects[e][0].handler.flow(&sox_effects[e][0],
&sox_effects[e - 1][0].obuf[sox_effects[e - 1][0].odone],
&sox_effects[e][0].obuf[sox_effects[e][0].olen], &idone, &odone);
else { /* Run effect on each channel individually */
sox_ssample_t *obuf = &sox_effects[e][0].obuf[sox_effects[e][0].olen];
sox_size_t idone_last, odone_last;
ibuf = &sox_effects[e - 1][0].obuf[sox_effects[e - 1][0].odone];
for (i = 0; i < idone; i += sox_effects[e][0].flows)
for (f = 0; f < sox_effects[e][0].flows; ++f)
ibufc[f][i / sox_effects[e][0].flows] = *ibuf++;
for (f = 0; f < sox_effects[e][0].flows; ++f) {
sox_size_t idonec = idone / sox_effects[e][0].flows;
sox_size_t odonec = odone / sox_effects[e][0].flows;
int eff_status_c = sox_effects[e][0].handler.flow(&sox_effects[e][f],
ibufc[f], obufc[f], &idonec, &odonec);
if (f && (idonec != idone_last || odonec != odone_last)) {
sox_fail("'%s' flowed asymmetrically!", sox_effects[e][0].handler.name);
effstatus = SOX_EOF;
}
idone_last = idonec;
odone_last = odonec;
if (eff_status_c != SOX_SUCCESS)
effstatus = SOX_EOF;
}
for (i = 0; i < odone_last; ++i)
for (f = 0; f < sox_effects[e][0].flows; ++f)
*obuf++ = obufc[f][i];
idone = f * idone_last;
odone = f * odone_last;
}
sox_effects[e - 1][0].odone += idone;
if (sox_effects[e - 1][0].odone == sox_effects[e - 1][0].olen) /* Can reuse this buffer? */
sox_effects[e - 1][0].odone = sox_effects[e - 1][0].olen = 0;
sox_effects[e][0].olen += odone;
return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF;
}
/* The same as flow_effect but with no input */
static int drain_effect(unsigned e)
{
int effstatus = SOX_SUCCESS;
sox_size_t i, f;
sox_size_t odone = sox_bufsiz - sox_effects[e][0].olen;
if (sox_effects[e][0].flows == 1) /* Run effect on all channels at once */
effstatus = sox_effects[e][0].handler.drain(&sox_effects[e][0],
&sox_effects[e][0].obuf[sox_effects[e][0].olen], &odone);
else { /* Run effect on each channel individually */
sox_ssample_t *obuf = &sox_effects[e][0].obuf[sox_effects[e][0].olen];
sox_size_t odone_last;
for (f = 0; f < sox_effects[e][0].flows; ++f) {
sox_size_t odonec = odone / sox_effects[e][0].flows;
int eff_status_c =
sox_effects[e][0].handler.drain(&sox_effects[e][f], obufc[f], &odonec);
if (f && (odonec != odone_last)) {
sox_fail("'%s' drained asymmetrically!", sox_effects[e][0].handler.name);
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 < sox_effects[e][0].flows; ++f)
*obuf++ = obufc[f][i];
odone = f * odone_last;
}
if (!odone) /* This is the only thing that drain has and flow hasn't */
effstatus = SOX_EOF;
sox_effects[e][0].olen += odone;
return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF;
}
int sox_flow_effects(void (* update_status)(sox_bool), sox_bool * user_abort)
{
int e, source_e = 0; /* effect indices */
int flow_status = SOX_SUCCESS;
sox_bool draining = sox_true;
sox_size_t f, max_flows = 0;
for (e = 0; e < (int)sox_neffects; ++e) {
sox_effects[e][0].obuf = xmalloc(sox_bufsiz * sizeof(sox_effects[e][0].obuf[0]));
sox_effects[e][0].odone = sox_effects[e][0].olen = 0;
max_flows = max(max_flows, sox_effects[e][0].flows);
}
ibufc = xcalloc(max_flows, sizeof(*ibufc));
obufc = xcalloc(max_flows, sizeof(*obufc));
for (f = 0; f < max_flows; ++f) {
ibufc[f] = xcalloc(sox_bufsiz / 2, sizeof(ibufc[f][0]));
obufc[f] = xcalloc(sox_bufsiz / 2, sizeof(obufc[f][0]));
}
--e;
while (source_e < (int)sox_neffects) {
if (e == source_e && (draining || sox_effects[e - 1][0].odone == sox_effects[e - 1][0].olen)) {
if (drain_effect(e) == SOX_EOF) {
++source_e;
draining = sox_false;
}
} else if (flow_effect(e) == SOX_EOF) {
flow_status = SOX_EOF;
source_e = e;
draining = sox_true;
}
if (sox_effects[e][0].odone < sox_effects[e][0].olen)
++e;
else if (--e < source_e)
e = source_e;
update_status(*user_abort || source_e == (int)sox_neffects);
if (*user_abort) /* Don't get stuck in this loop. */
return SOX_EOF;
}
for (f = 0; f < max_flows; ++f) {
free(ibufc[f]);
free(obufc[f]);
}
free(obufc);
free(ibufc);
for (e = 0; e < (int)sox_neffects; ++e)
free(sox_effects[e][0].obuf);
return flow_status;
}
void sox_delete_effects(void)
{
while (sox_neffects)
free(sox_effects[--sox_neffects]);
}
/* Effects handlers. */
/* FIXME: Generate this list automatically */
sox_effect_fn_t sox_effect_fns[] = {
sox_allpass_effect_fn,
sox_avg_effect_fn,
sox_band_effect_fn,
sox_bandpass_effect_fn,
sox_bandreject_effect_fn,
sox_bass_effect_fn,
sox_chorus_effect_fn,
sox_compand_effect_fn,
sox_dcshift_effect_fn,
sox_deemph_effect_fn,
sox_dither_effect_fn,
sox_earwax_effect_fn,
sox_echo_effect_fn,
sox_echos_effect_fn,
sox_equalizer_effect_fn,
sox_fade_effect_fn,
sox_filter_effect_fn,
sox_flanger_effect_fn,
sox_highpass_effect_fn,
sox_highp_effect_fn,
#ifdef HAVE_LADSPA_H
sox_ladspa_effect_fn,
#endif
sox_lowpass_effect_fn,
sox_lowp_effect_fn,
sox_mask_effect_fn,
sox_mcompand_effect_fn,
sox_mixer_effect_fn,
sox_noiseprof_effect_fn,
sox_noisered_effect_fn,
sox_pad_effect_fn,
sox_pan_effect_fn,
sox_phaser_effect_fn,
sox_pick_effect_fn,
sox_pitch_effect_fn,
sox_polyphase_effect_fn,
#ifdef HAVE_SAMPLERATE_H
sox_rabbit_effect_fn,
#endif
sox_rate_effect_fn,
sox_repeat_effect_fn,
sox_resample_effect_fn,
sox_reverb_effect_fn,
sox_reverse_effect_fn,
sox_silence_effect_fn,
sox_speed_effect_fn,
sox_stat_effect_fn,
sox_stretch_effect_fn,
sox_swap_effect_fn,
sox_synth_effect_fn,
sox_treble_effect_fn,
sox_tremolo_effect_fn,
sox_trim_effect_fn,
sox_vibro_effect_fn,
sox_vol_effect_fn,
NULL
};