ref: d3f0b58897fb47913449b7489c2cef7ab9d0f510
dir: /src/stretch.c/
/* * (c) march/april 2000 Fabien COELHO <fabien@coelho.net> for sox. * * Basic time stretcher. * cross fade samples so as to go slower of faster. * * The automaton 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 * - type of cross fading * * I decided of the default values of these parameters based * on some small non extensive tests. maybe better defaults * can be suggested. * * It cannot handle different number of channels. * It cannot handle rate change. */ #include "st_i.h" #include <stdlib.h> /* malloc and free */ #include <string.h> /* memcpy() */ static st_effect_t st_stretch_effect; #ifndef MIN #define MIN(s1,s2) ((s1)<(s2)?(s1):(s2)) #endif #ifndef STRETCH_FLOAT #define STRETCH_FLOAT float #define STRETCH_FLOAT_SCAN "%f" #endif /* ok, it looks stupid to have such constant. this is because of the cast, if floats are switched to doubles. */ #define ZERO ((STRETCH_FLOAT)(0.0e0)) #define HALF ((STRETCH_FLOAT)(0.5e0)) #define ONE ((STRETCH_FLOAT)(1.0e0)) #define MONE ((STRETCH_FLOAT)(-1.0e0)) #define ONETHOUSANDS ((STRETCH_FLOAT)(0.001e0)) #define DEFAULT_SLOW_SHIFT_RATIO ((STRETCH_FLOAT)(0.8e0)) #define DEFAULT_FAST_SHIFT_RATIO ONE #define DEFAULT_STRETCH_WINDOW ((STRETCH_FLOAT)(20.0e0)) /* ms */ /* I'm planing to put some common fading stuff outside. It's also used in pitch.c */ typedef enum { st_linear_fading } st_fading_t; #define DEFAULT_FADING st_linear_fading typedef enum { input_state, output_state } stretch_status_t; typedef struct { /* options * Q: maybe shift could be allowed > 1.0 with factor < 1.0 ??? */ STRETCH_FLOAT factor; /* strech factor. 1.0 means copy. */ STRETCH_FLOAT window; /* window in ms */ st_fading_t fade; /* type of fading */ STRETCH_FLOAT shift; /* shift ratio wrt window. <1.0 */ STRETCH_FLOAT fading; /* fading ratio wrt window. <0.5 */ /* internal stuff */ stretch_status_t state; /* automaton status */ int clipped; /* number of clipped values. */ int size; /* buffer size */ int index; /* next available element */ st_sample_t *ibuf; /* input buffer */ int ishift; /* input shift */ int oindex; /* next evailable element */ STRETCH_FLOAT * obuf; /* output buffer */ int oshift; /* output shift */ int fsize; /* fading size */ STRETCH_FLOAT * fbuf; /* fading, 1.0 -> 0.0 */ } * stretch_t; /* static void debug(stretch_t s, char * where) { fprintf(stderr, "%s: (f=%.2f w=%.2f r=%.2f f=%.2f)" " st=%d s=%d ii=%d is=%d oi=%d os=%d fs=%d\n", where, s->factor, s->window, s->shift, s->fading, s->state, s->size, s->index, s->ishift, s->oindex, s->oshift, s->fsize); } */ /* * Process options */ int st_stretch_getopts(eff_t effp, int n, char **argv) { char usage[1024]; stretch_t stretch = (stretch_t) effp->priv; /* default options */ stretch->factor = ONE; /* default is no change */ stretch->window = DEFAULT_STRETCH_WINDOW; stretch->fade = st_linear_fading; if (n>0 && !sscanf(argv[0], STRETCH_FLOAT_SCAN, &stretch->factor)) { sprintf (usage, "%s\n\terror while parsing factor", st_stretch_effect.usage); st_fail(usage); return ST_EOF; } if (n>1 && !sscanf(argv[1], STRETCH_FLOAT_SCAN, &stretch->window)) { sprintf (usage, "%s\n\terror while parsing window size", st_stretch_effect.usage); st_fail(usage); return ST_EOF; } if (n>2) { switch (argv[2][0]) { case 'l': case 'L': stretch->fade = st_linear_fading; break; default: sprintf (usage, "%s\n\terror while parsing fade type", st_stretch_effect.usage); st_fail(usage); return ST_EOF; } } /* default shift depends whether we go slower or faster */ stretch->shift = (stretch->factor <= ONE) ? DEFAULT_FAST_SHIFT_RATIO: DEFAULT_SLOW_SHIFT_RATIO; if (n>3 && !sscanf(argv[3], STRETCH_FLOAT_SCAN, &stretch->shift)) { sprintf (usage, "%s\n\terror while parsing shift ratio", st_stretch_effect.usage); st_fail(usage); return ST_EOF; } if (stretch->shift > ONE || stretch->shift <= ZERO) { sprintf (usage, "%s\n\terror with shift ratio value", st_stretch_effect.usage); st_fail(usage); return ST_EOF; } /* default fading stuff... it makes sense for factor >= 0.5 */ if (stretch->factor<ONE) stretch->fading = ONE - (stretch->factor*stretch->shift); else stretch->fading = ONE - stretch->shift; if (stretch->fading > HALF) stretch->fading = HALF; if (n>4 && !sscanf(argv[4], STRETCH_FLOAT_SCAN, &stretch->fading)) { sprintf (usage, "%s\n\terror while parsing fading ratio", st_stretch_effect.usage); st_fail(usage); return ST_EOF; } if (stretch->fading > HALF || stretch->fading < ZERO) { sprintf (usage, "%s\n\terror with fading ratio value", st_stretch_effect.usage); st_fail(usage); return ST_EOF; } return ST_SUCCESS; } /* * Start processing */ int st_stretch_start(eff_t effp) { stretch_t stretch = (stretch_t) effp->priv; register int i; /* not necessary. taken care by effect processing? */ if (effp->outinfo.channels != effp->ininfo.channels) { st_fail("STRETCH cannot handle different channels (in=%d, out=%d)" " use avg or pan", effp->ininfo.channels, effp->outinfo.channels); return ST_EOF; } if (effp->outinfo.rate != effp->ininfo.rate) { st_fail("STRETCH cannot handle different rates (in=%ld, out=%ld)" " use resample or rate", effp->ininfo.rate, effp->outinfo.rate); return ST_EOF; } stretch->state = input_state; stretch->clipped = 0; stretch->size = (int)(effp->outinfo.rate * ONETHOUSANDS * stretch->window); /* start in the middle of an input to avoid initial fading... */ stretch->index = stretch->size/2; stretch->ibuf = (st_sample_t *) malloc(stretch->size * sizeof(st_sample_t)); /* the shift ratio deal with the longest of ishift/oshift hence ishift<=size and oshift<=size. should be asserted. */ if (stretch->factor < ONE) { stretch->ishift = (int) (stretch->shift * stretch->size); stretch->oshift = (int) (stretch->factor * stretch->ishift); } else { stretch->oshift = (int) (stretch->shift * stretch->size); stretch->ishift = (int) (stretch->oshift / stretch->factor); } stretch->oindex = stretch->index; /* start as synchronized */ stretch->obuf = (STRETCH_FLOAT *) malloc(stretch->size * sizeof(STRETCH_FLOAT)); stretch->fsize = (int) (stretch->fading * stretch->size); stretch->fbuf = (STRETCH_FLOAT *) malloc(stretch->fsize * sizeof(STRETCH_FLOAT)); if (!stretch->ibuf || !stretch->obuf || !stretch->fbuf) { st_fail("some malloc failed"); return ST_EOF; } /* initialize buffers */ for (i=0; i<stretch->size; i++) stretch->ibuf[i] = 0; for (i=0; i<stretch->size; i++) stretch->obuf[i] = ZERO; if (stretch->fsize>1) { register STRETCH_FLOAT slope = ONE / (stretch->fsize - 1); stretch->fbuf[0] = ONE; for (i=1; i<stretch->fsize-1; i++) stretch->fbuf[i] = slope * (stretch->fsize-i-1); stretch->fbuf[stretch->fsize-1] = ZERO; } else if (stretch->fsize==1) stretch->fbuf[0] = ONE; /* debug(stretch, "start"); */ return ST_SUCCESS; } /* accumulates input ibuf to output obuf with fading fbuf */ static void combine(stretch_t stretch) { register int i, size, fsize; size = stretch->size; fsize = stretch->fsize; /* fade in */ for (i=0; i<fsize; i++) stretch->obuf[i] += stretch->fbuf[fsize-i-1]*stretch->ibuf[i]; /* steady state */ for (; i<size-fsize; i++) stretch->obuf[i] += stretch->ibuf[i]; /* fade out */ for (; i<size; i++) stretch->obuf[i] += stretch->fbuf[i-size+fsize]*stretch->ibuf[i]; } /* * Processes flow. */ int st_stretch_flow(eff_t effp, st_sample_t *ibuf, st_sample_t *obuf, st_size_t *isamp, st_size_t *osamp) { stretch_t stretch = (stretch_t) effp->priv; st_size_t iindex, oindex; int i; iindex = 0; oindex = 0; while (iindex<*isamp && oindex<*osamp) { if (stretch->state == input_state) { st_size_t tocopy = MIN(*isamp-iindex, stretch->size-stretch->index); memcpy(stretch->ibuf+stretch->index, ibuf+iindex, tocopy*sizeof(st_sample_t)); iindex += tocopy; stretch->index += tocopy; if (stretch->index == stretch->size) { /* compute */ combine(stretch); /* shift input */ for (i=0; i+stretch->ishift<stretch->size; i++) stretch->ibuf[i] = stretch->ibuf[i+stretch->ishift]; stretch->index -= stretch->ishift; /* switch to output state */ stretch->state = output_state; } } if (stretch->state == output_state) { while (stretch->oindex<stretch->oshift && oindex<*osamp) { float f; f = stretch->obuf[stretch->oindex++]; ST_SAMPLE_CLIP_COUNT(f, stretch->clipped); obuf[oindex++] = f; } if (stretch->oindex >= stretch->oshift && oindex<*osamp) { stretch->oindex -= stretch->oshift; /* shift internal output buffer */ for (i=0; i+stretch->oshift<stretch->size; i++) stretch->obuf[i] = stretch->obuf[i+stretch->oshift]; /* pad with 0 */ for (; i<stretch->size; i++) stretch->obuf[i] = ZERO; stretch->state = input_state; } } } *isamp = iindex; *osamp = oindex; return ST_SUCCESS; } /* * Drain buffer at the end * maybe not correct ? end might be artificially faded? */ int st_stretch_drain(eff_t effp, st_sample_t *obuf, st_size_t *osamp) { stretch_t stretch = (stretch_t) effp->priv; register int i; st_size_t oindex; oindex = 0; if (stretch->state == input_state) { for (i=stretch->index; i<stretch->size; i++) stretch->ibuf[i] = 0; combine(stretch); stretch->state = output_state; } for (; oindex<*osamp && stretch->oindex<stretch->index;) { float f; f = stretch->obuf[stretch->oindex++]; ST_SAMPLE_CLIP_COUNT(f, stretch->clipped); obuf[oindex++] = f; } *osamp = oindex; if (stretch->oindex == stretch->index) return ST_EOF; else return ST_SUCCESS; } /* * Do anything required when you stop reading samples. * Don't close input file! */ int st_stretch_stop(eff_t effp) { stretch_t stretch = (stretch_t) effp->priv; free(stretch->ibuf); free(stretch->obuf); free(stretch->fbuf); if (stretch->clipped) st_warn("STRETCH clipped %d values...", stretch->clipped); return ST_SUCCESS; } static st_effect_t st_stretch_effect = { "stretch", "Usage: stretch factor [window fade shift fading]\n" " (expansion, frame in ms, lin/..., unit<1.0, unit<0.5)\n" " (defaults: 1.0 20 lin ...)", 0, st_stretch_getopts, st_stretch_start, st_stretch_flow, st_stretch_drain, st_stretch_stop }; const st_effect_t *st_stretch_effect_fn(void) { return &st_stretch_effect; }