ref: 8e668058eaedc563b3d14ce1c364abaca3907c98
dir: /src/effects.c/
/*
* 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.
*/
/* SoX Effects chain (c) 2007 robs@users.sourceforge.net */
#include "sox_i.h"
struct sox_effect * effects[MAX_EFFECTS];
unsigned neffects;
void add_effect(struct sox_effect * e, sox_signalinfo_t * in, sox_signalinfo_t * out, int * effects_mask)
{
unsigned f, flows;
*effects_mask = sox_updateeffect(e, in, out, *effects_mask);
flows = (e->h->flags & SOX_EFF_MCHAN)? 1 : e->ininfo.channels;
effects[neffects] = xcalloc(flows, sizeof(effects[neffects][0]));
effects[neffects][0] = *e;
effects[neffects][0].flows = flows;
for (f = 1; f < flows; ++f)
effects[neffects][f] = effects[neffects][0];
++neffects;
}
static void stop_effect(unsigned e)
{
unsigned i;
sox_size_t clips = 0;
int (*stop)(eff_t effp) =
effects[e][0].h->stop? effects[e][0].h->stop : sox_effect_nothing;
for (i = 0; i < effects[e][0].flows; ++i) {
stop(&effects[e][i]);
clips += effects[e][i].clips;
}
if (clips != 0)
sox_warn("'%s' clipped %u samples; decrease volume?",effects[e][0].name,clips);
}
void stop_effects(void)
{
unsigned e;
for (e = 0; e < neffects; ++e)
stop_effect(e);
}
static void kill_effect(unsigned e)
{
int (*kill)(eff_t effp) =
effects[e][0].h->kill? effects[e][0].h->kill : sox_effect_nothing;
kill(&effects[e][0]); /* One kill for all flows */
}
void kill_effects(void)
{
unsigned e;
for (e = 0; e < neffects; ++e)
kill_effect(e);
}
int start_effects(void)
{
unsigned i, j;
int ret = SOX_SUCCESS;
for (i = 0; i < neffects; ++i) {
struct sox_effect * e = &effects[i][0];
sox_bool is_always_null = (e->h->flags & SOX_EFF_NULL) != 0;
int (*start)(eff_t effp) = e->h->start? e->h->start : sox_effect_nothing;
if (is_always_null)
sox_report("'%s' has no effect (is a proxy effect)", e->name);
else {
e->clips = 0;
ret = start(e);
if (ret == SOX_EFF_NULL)
sox_warn("'%s' has no effect in this configuration", e->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(effects[i]);
--neffects;
for (j = i--; j < neffects; ++j)
effects[j] = effects[j + 1];
}
else for (j = 1; j < effects[i][0].flows; ++j) {
effects[i][j].clips = 0;
if (start(&effects[i][j]) != SOX_SUCCESS)
return SOX_EOF;
}
}
for (i = 0; i < neffects; ++i) {
struct sox_effect * e = &effects[i][0];
sox_report("Effects chain: %-10s %uHz %u channels %s",
e->name, e->ininfo.rate, e->ininfo.channels,
(e->h->flags & SOX_EFF_MCHAN)? "(multi)" : "");
}
return SOX_SUCCESS;
}
static sox_ssample_t **ibufc, **obufc; /* Channel interleave buffers */
static int flow_effect(unsigned e)
{
sox_size_t i, f, idone, odone;
const sox_ssample_t *ibuf;
int effstatus = SOX_SUCCESS;
int (*flow)(eff_t, sox_ssample_t const*, sox_ssample_t*, sox_size_t*, sox_size_t*) =
effects[e][0].h->flow? effects[e][0].h->flow : sox_effect_nothing_flow;
idone = effects[e - 1][0].olen - effects[e - 1][0].odone;
odone = sox_bufsiz - effects[e][0].olen;
if (effects[e][0].flows == 1) /* Run effect on all channels at once */
effstatus = flow(&effects[e][0],
&effects[e - 1][0].obuf[effects[e - 1][0].odone],
&effects[e][0].obuf[effects[e][0].olen], &idone, &odone);
else { /* Run effect on each channel individually */
sox_ssample_t *obuf = &effects[e][0].obuf[effects[e][0].olen];
sox_size_t idone_last, odone_last;
ibuf = &effects[e - 1][0].obuf[effects[e - 1][0].odone];
for (i = 0; i < idone; i += effects[e][0].flows)
for (f = 0; f < effects[e][0].flows; ++f)
ibufc[f][i / effects[e][0].flows] = *ibuf++;
for (f = 0; f < effects[e][0].flows; ++f) {
sox_size_t idonec = idone / effects[e][0].flows;
sox_size_t odonec = odone / effects[e][0].flows;
int eff_status_c =
flow(&effects[e][f], ibufc[f], obufc[f], &idonec, &odonec);
if (f && (idonec != idone_last || odonec != odone_last)) {
sox_fail("'%s' flowed asymmetrically!", effects[e][0].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 < effects[e][0].flows; ++f)
*obuf++ = obufc[f][i];
idone = f * idone_last;
odone = f * odone_last;
}
effects[e - 1][0].odone += idone;
if (effects[e - 1][0].odone == effects[e - 1][0].olen) /* Can reuse this buffer? */
effects[e - 1][0].odone = effects[e - 1][0].olen = 0;
effects[e][0].olen += odone;
return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF;
}
static int drain_effect(unsigned e)
{
sox_size_t i, f, odone;
int effstatus = SOX_SUCCESS;
int (*drain)(eff_t, sox_ssample_t*, sox_size_t*) =
effects[e][0].h->drain? effects[e][0].h->drain : sox_effect_nothing_drain;
odone = sox_bufsiz - effects[e][0].olen;
if (effects[e][0].flows == 1) /* Run effect on all channels at once */
effstatus = drain(&effects[e][0],
&effects[e][0].obuf[effects[e][0].olen], &odone);
else { /* Run effect on each channel individually */
sox_ssample_t *obuf = &effects[e][0].obuf[effects[e][0].olen];
sox_size_t odone_last;
for (f = 0; f < effects[e][0].flows; ++f) {
sox_size_t odonec = odone / effects[e][0].flows;
int eff_status_c =
drain(&effects[e][f], obufc[f], &odonec);
if (f && (odonec != odone_last)) {
sox_fail("'%s' drained asymmetrically!", effects[e][0].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 < effects[e][0].flows; ++f)
*obuf++ = obufc[f][i];
odone = f * odone_last;
}
if (!odone)
effstatus = SOX_EOF;
effects[e][0].olen += odone;
return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF;
}
int 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)neffects; ++e) {
effects[e][0].obuf = xmalloc(sox_bufsiz * sizeof(effects[e][0].obuf[0]));
effects[e][0].odone = effects[e][0].olen = 0;
max_flows = max(max_flows, 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)neffects) {
if (e == source_e && (draining || effects[e - 1][0].odone == 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 (effects[e][0].odone < effects[e][0].olen)
++e;
else if (--e < source_e)
e = source_e;
update_status(*user_abort || source_e == (int)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)neffects; ++e)
free(effects[e][0].obuf);
return flow_status;
}
void delete_effects(void)
{
while (neffects)
free(effects[--neffects]);
}