ref: f9dc9dd5e2523833fc771b8b6f748fc4d95c2f06
parent: 9d11b22fadb9698e745947151f175c472a62aac4
author: robs <robs>
date: Sat Oct 18 08:39:22 EDT 2008
reprieve for stretch
--- a/ChangeLog
+++ b/ChangeLog
@@ -4,7 +4,7 @@
This file contains a list of all changes starting after the release of
sox-11gamma.
-sox-14.1.1 2008-TBD
+sox-14.2.0 2008-TBD
----------
Previously deprecated features that have been removed in this release:
@@ -12,7 +12,6 @@
Deprec- Feature [O(ption)]
ated in [F(ormat)] [E(ffect)] Replacement
------- ---------------------- ----------------------
- 14.0.0 E stretch ~= tempo
14.0.0 E pitch new pitch = old key
Previously deprecated features (to be removed in future):
@@ -20,10 +19,11 @@
Deprec- Feature [O(ption)] Removal
ated in [F(ormat)] [E(ffect)] Replacement due after
------- ---------------------- ---------------------- -------
- 14.1.0 F flac: libFLAC 1.1.1 libFLAC > 1.1.1 2009-01-29
+ 14.0.0 E stretch ~= tempo 14.2.0 + 6 months
14.1.0 E resample ~= rate 2009-01-29
14.1.0 E polyphase ~= rate 2009-01-29
14.1.0 E rabbit ~= rate 2009-01-29
+ 14.1.0 F flac: libFLAC 1.1.1 libFLAC > 1.1.1 2009-01-29
14.1.0 F wve (native) wve (libsndfile) 2009-07-29
14.1.0 Behaviour whereby soxi 2009-07-29
sox -V file(s) -n
@@ -34,8 +34,8 @@
Deprec- Feature [O(ption)] Removal
ated in [F(ormat)] [E(ffect)] Replacement due after
------- ---------------------- ---------------------- -------
- 14.1.1 E key renamed to pitch 14.1.1 + 6 months
- 14.1.1 E pan ~= mixer/remix 14.1.1 + 6 months
+ 14.2.0 E key renamed to pitch 14.2.0 + 6 months
+ 14.2.0 E pan ~= mixer/remix 14.2.0 + 6 months
File formats:
@@ -55,6 +55,7 @@
o New `bend' effect; pitch bending. (robs)
o New -b option for the norm effect; can be used to fix stereo
imbalance. (robs)
+ o Wider tempo range for `tempo' effect. (robs)
o New --effects-file option to read effects and arguments from
a file instead of command line. (cbagwell)
o `filter' effect now supports --plot. (robs)
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -13,7 +13,7 @@
else(${ver} LESS 403)
add_definitions(-Wtraditional-conversion)
endif(${ver} LESS 403)
- add_definitions(-Werror)
+ #add_definitions(-Werror)
endif(CMAKE_COMPILER_IS_GNUCC)
if (NOT EXTERNAL_GSM)
@@ -36,7 +36,7 @@
compandt output reverb tremolo
contrast filter pad reverse trim
dcshift flanger pan silence vol
- delay input phaser skeleff
+ delay input phaser skeleff stretch
dither loudness pitch speed
)
set(formats_srcs
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -262,7 +262,7 @@
pan.c phaser.c pitch.c polyphas.c rabbit.c rate.c \
rate_filters.h rate_half_fir.h rate_poly_fir0.h rate_poly_fir.h \
remix.c repeat.c resample.c reverb.c reverse.c silence.c skeleff.c \
- speed.c splice.c stat.c swap.c synth.c tempo.c tremolo.c \
+ speed.c splice.c stat.c swap.c stretch.c synth.c tempo.c tremolo.c \
trim.c vol.c
if HAVE_PNG
libsfx_la_SOURCES += spectrogram.c
--- a/src/effects.h
+++ b/src/effects.h
@@ -73,6 +73,7 @@
EFFECT(speed)
EFFECT(splice)
EFFECT(stat)
+ EFFECT(stretch)
EFFECT(swap)
EFFECT(synth)
EFFECT(tempo)
--- /dev/null
+++ b/src/stretch.c
@@ -1,0 +1,324 @@
+/* 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 n, char **argv)
+{
+ priv_t * p = (priv_t *) effp->priv;
+
+ /* default options */
+ p->factor = 1.0; /* default is no change */
+ p->window = DEFAULT_STRETCH_WINDOW;
+
+ if (n > 0 && !sscanf(argv[0], "%lf", &p->factor)) {
+ sox_fail("error while parsing factor");
+ return lsx_usage(effp);
+ }
+
+ if (n > 1 && !sscanf(argv[1], "%lf", &p->window)) {
+ sox_fail("error while parsing window size");
+ return lsx_usage(effp);
+ }
+
+ if (n > 2) {
+ switch (argv[2][0]) {
+ case 'l':
+ case 'L':
+ break;
+ default:
+ sox_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 (n > 3 && !sscanf(argv[3], "%lf", &p->shift)) {
+ sox_fail("error while parsing shift ratio");
+ return lsx_usage(effp);
+ }
+
+ if (p->shift > 1.0 || p->shift <= 0.0) {
+ sox_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 (n > 4 && !sscanf(argv[4], "%lf", &p->fading)) {
+ sox_fail("error while parsing fading ratio");
+ return lsx_usage(effp);
+ }
+
+ if (p->fading > 0.5 || p->fading < 0.0) {
+ sox_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;
+
+ sox_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 *sox_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 | SOX_EFF_DEPRECATED,
+ getopts, start, flow, drain, stop, NULL, sizeof(priv_t)
+ };
+ return &handler;
+}