ref: 8568f0f0450a1a8e5e595f618ea5bd257b15edc4
dir: /src/stretch.c/
/* libSoX Basic time stretcher.
* (c) march/april 2000 Fabien COELHO <fabien@coelho.net> for sox.
*
* cross fade samples so as to go slower or faster.
*
* The filter is based on 6 parameters:
* - stretch factor f
* - window size w
* - input step i
* output step o=f*i
* - steady state of window s, ss = s*w
*
* I decided of the default values of these parameters based
* on some small non extensive tests. maybe better defaults
* can be suggested.
*/
#include "sox_i.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define DEFAULT_SLOW_SHIFT_RATIO 0.8
#define DEFAULT_FAST_SHIFT_RATIO 1.0
#define DEFAULT_STRETCH_WINDOW 20.0 /* ms */
typedef enum { input_state, output_state } stretch_status_t;
typedef struct {
/* options
* FIXME: maybe shift could be allowed > 1.0 with factor < 1.0 ???
*/
double factor; /* strech factor. 1.0 means copy. */
double window; /* window in ms */
double shift; /* shift ratio wrt window. <1.0 */
double fading; /* fading ratio wrt window. <0.5 */
/* internal stuff */
stretch_status_t state; /* automaton status */
size_t segment; /* buffer size */
size_t index; /* next available element */
sox_sample_t *ibuf; /* input buffer */
size_t ishift; /* input shift */
size_t oindex; /* next evailable element */
double * obuf; /* output buffer */
size_t oshift; /* output shift */
size_t overlap; /* fading size */
double * fade_coefs; /* fading, 1.0 -> 0.0 */
} priv_t;
/*
* Process options
*/
static int getopts(sox_effect_t * effp, int argc, char **argv)
{
priv_t * p = (priv_t *) effp->priv;
--argc, ++argv;
/* default options */
p->factor = 1.0; /* default is no change */
p->window = DEFAULT_STRETCH_WINDOW;
if (argc > 0 && !sscanf(argv[0], "%lf", &p->factor)) {
lsx_fail("error while parsing factor");
return lsx_usage(effp);
}
if (argc > 1 && !sscanf(argv[1], "%lf", &p->window)) {
lsx_fail("error while parsing window size");
return lsx_usage(effp);
}
if (argc > 2) {
switch (argv[2][0]) {
case 'l':
case 'L':
break;
default:
lsx_fail("error while parsing fade type");
return lsx_usage(effp);
}
}
/* default shift depends whether we go slower or faster */
p->shift = (p->factor <= 1.0) ?
DEFAULT_FAST_SHIFT_RATIO: DEFAULT_SLOW_SHIFT_RATIO;
if (argc > 3 && !sscanf(argv[3], "%lf", &p->shift)) {
lsx_fail("error while parsing shift ratio");
return lsx_usage(effp);
}
if (p->shift > 1.0 || p->shift <= 0.0) {
lsx_fail("error with shift ratio value");
return lsx_usage(effp);
}
/* default fading stuff...
it makes sense for factor >= 0.5 */
if (p->factor < 1.0)
p->fading = 1.0 - (p->factor * p->shift);
else
p->fading = 1.0 - p->shift;
if (p->fading > 0.5)
p->fading = 0.5;
if (argc > 4 && !sscanf(argv[4], "%lf", &p->fading)) {
lsx_fail("error while parsing fading ratio");
return lsx_usage(effp);
}
if (p->fading > 0.5 || p->fading < 0.0) {
lsx_fail("error with fading ratio value");
return lsx_usage(effp);
}
return SOX_SUCCESS;
}
/*
* Start processing
*/
static int start(sox_effect_t * effp)
{
priv_t * p = (priv_t *)effp->priv;
size_t i;
if (p->factor == 1)
return SOX_EFF_NULL;
p->state = input_state;
p->segment = (int)(effp->out_signal.rate * 0.001 * p->window);
/* start in the middle of an input to avoid initial fading... */
p->index = p->segment / 2;
p->ibuf = lsx_malloc(p->segment * sizeof(sox_sample_t));
/* the shift ratio deal with the longest of ishift/oshift
hence ishift<=segment and oshift<=segment. */
if (p->factor < 1.0) {
p->ishift = p->shift * p->segment;
p->oshift = p->factor * p->ishift;
} else {
p->oshift = p->shift * p->segment;
p->ishift = p->oshift / p->factor;
}
assert(p->ishift <= p->segment);
assert(p->oshift <= p->segment);
p->oindex = p->index; /* start as synchronized */
p->obuf = lsx_malloc(p->segment * sizeof(double));
p->overlap = (int)(p->fading * p->segment);
p->fade_coefs = lsx_malloc(p->overlap * sizeof(double));
/* initialize buffers */
for (i = 0; i<p->segment; i++)
p->ibuf[i] = 0;
for (i = 0; i<p->segment; i++)
p->obuf[i] = 0.0;
if (p->overlap>1) {
double slope = 1.0 / (p->overlap - 1);
p->fade_coefs[0] = 1.0;
for (i = 1; i < p->overlap - 1; i++)
p->fade_coefs[i] = slope * (p->overlap - i - 1);
p->fade_coefs[p->overlap - 1] = 0.0;
} else if (p->overlap == 1)
p->fade_coefs[0] = 1.0;
lsx_debug("start: (factor=%g segment=%g shift=%g overlap=%g)\nstate=%d\n"
"segment=%lu\nindex=%lu\nishift=%lu\noindex=%lu\noshift=%lu\noverlap=%lu",
p->factor, p->window, p->shift, p->fading, p->state,
(unsigned long)p->segment, (unsigned long)p->index,
(unsigned long)p->ishift, (unsigned long)p->oindex,
(unsigned long)p->oshift, (unsigned long)p->overlap);
return SOX_SUCCESS;
}
/* accumulates input ibuf to output obuf with fading fade_coefs */
static void combine(priv_t * p)
{
size_t i;
/* fade in */
for (i = 0; i < p->overlap; i++)
p->obuf[i] += p->fade_coefs[p->overlap - 1 - i] * p->ibuf[i];
/* steady state */
for (; i < p->segment - p->overlap; i++)
p->obuf[i] += p->ibuf[i];
/* fade out */
for (; i<p->segment; i++)
p->obuf[i] += p->fade_coefs[i - p->segment + p->overlap] * p->ibuf[i];
}
/*
* Processes flow.
*/
static int flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
size_t *isamp, size_t *osamp)
{
priv_t * p = (priv_t *) effp->priv;
size_t iindex = 0, oindex = 0;
size_t i;
while (iindex<*isamp && oindex<*osamp) {
if (p->state == input_state) {
size_t tocopy = min(*isamp-iindex,
p->segment-p->index);
memcpy(p->ibuf + p->index, ibuf + iindex, tocopy * sizeof(sox_sample_t));
iindex += tocopy;
p->index += tocopy;
if (p->index == p->segment) {
/* compute */
combine(p);
/* shift input */
for (i = 0; i + p->ishift < p->segment; i++)
p->ibuf[i] = p->ibuf[i+p->ishift];
p->index -= p->ishift;
/* switch to output state */
p->state = output_state;
}
}
if (p->state == output_state) {
while (p->oindex < p->oshift && oindex < *osamp) {
float f;
f = p->obuf[p->oindex++];
SOX_SAMPLE_CLIP_COUNT(f, effp->clips);
obuf[oindex++] = f;
}
if (p->oindex >= p->oshift && oindex<*osamp) {
p->oindex -= p->oshift;
/* shift internal output buffer */
for (i = 0; i + p->oshift < p->segment; i++)
p->obuf[i] = p->obuf[i + p->oshift];
/* pad with 0 */
for (; i < p->segment; i++)
p->obuf[i] = 0.0;
p->state = input_state;
}
}
}
*isamp = iindex;
*osamp = oindex;
return SOX_SUCCESS;
}
/*
* Drain buffer at the end
* maybe not correct ? end might be artificially faded?
*/
static int drain(sox_effect_t * effp, sox_sample_t *obuf, size_t *osamp)
{
priv_t * p = (priv_t *) effp->priv;
size_t i;
size_t oindex = 0;
if (p->state == input_state) {
for (i=p->index; i<p->segment; i++)
p->ibuf[i] = 0;
combine(p);
p->state = output_state;
}
while (oindex<*osamp && p->oindex<p->index) {
float f = p->obuf[p->oindex++];
SOX_SAMPLE_CLIP_COUNT(f, effp->clips);
obuf[oindex++] = f;
}
*osamp = oindex;
if (p->oindex == p->index)
return SOX_EOF;
else
return SOX_SUCCESS;
}
static int stop(sox_effect_t * effp)
{
priv_t * p = (priv_t *) effp->priv;
free(p->ibuf);
free(p->obuf);
free(p->fade_coefs);
return SOX_SUCCESS;
}
const sox_effect_handler_t *lsx_stretch_effect_fn(void)
{
static const sox_effect_handler_t handler = {
"stretch",
"factor [window fade shift fading]\n"
" (expansion, frame in ms, lin/..., unit<1.0, unit<0.5)\n"
" (defaults: 1.0 20 lin ...)",
SOX_EFF_LENGTH,
getopts, start, flow, drain, stop, NULL, sizeof(priv_t)
};
return &handler;
}