shithub: sox

Download patch

ref: 89867eadb031ee884e230549c6fbdd08b3a29fdb
parent: 1097cf3938f1b42c149bbfc9f3801c565d741ccb
author: cbagwell <cbagwell>
date: Tue Apr 25 18:18:02 EDT 2000

Adding pitch and stretch effects.  Removed support for OS9.

--- a/Makefile.dos
+++ b/Makefile.dos
@@ -12,15 +12,16 @@
 
 FOBJ	= 8svx.obj adpcm.obj aiff.obj alsa.obj au.obj auto.obj avr.obj cdr.obj \
 	  cvsd.obj dat.obj g721.obj g723_24.obj g723_40.obj g72x.obj gsm.obj \
-	  hcom.obj ima_rw.o maud.obj oss.obj raw.obj sf.obj smp.obj \
+	  hcom.obj ima_rw.obj maud.obj oss.obj raw.obj sf.obj smp.obj \
 	  sndrtool.obj sunaudio.obj tx16w.obj voc.obj wav.obj wve.obj
 
 EOBJ	= avg.obj band.obj bandpass.obj breject.obj btrworth.obj chorus.obj \
 	  compand.obj copy.obj cut.obj deemphas.obj echo.obj \
 	  echos.obj filter.obj flanger.obj highp.obj highpass.obj lowp.obj \
-          lowpass.obj map.obj mask.obj phaser.obj pick.obj pan.o \
+          lowpass.obj map.obj mask.obj phaser.obj pick.obj pitch.obj pan.obj \
 	  polyphase.obj rate.obj resample.obj reverb.obj \
-	  reverse.obj speed.obj split.obj stat.obj swap.obj vibro.obj vol.obj
+	  reverse.obj speed.obj split.obj stat.obj stretch.obj \
+	  swap.obj vibro.obj vol.obj
 
 LIBOBJS   = $(FOBJ) $(EOBJ) handlers.obj libst.obj misc.obj getopt.obj util.obj
 
--- a/Makefile.gcc
+++ b/Makefile.gcc
@@ -36,8 +36,8 @@
 EOBJ    = avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
           copy.o cut.o deemphas.o echo.o echos.o filter.o flanger.o \
           highp.o highpass.o lowp.o lowpass.o map.o mask.o pan.o \
-          phaser.o pick.o polyphas.o rate.o resample.o reverb.o \
-          reverse.o speed.o split.o stat.o swap.o vibro.o vol.o
+          phaser.o pick.o pitch.o polyphas.o rate.o resample.o reverb.o \
+          reverse.o speed.o split.o stat.o stretch.o swap.o vibro.o vol.o
 
 SOUNDLIB = libst.a
 LIBOBJS = $(FOBJ) $(EOBJ) handlers.o libst.o misc.o util.o getopt.o
--- a/Makefile.os9
+++ /dev/null
@@ -1,61 +1,0 @@
-
-# Sound Tools Makefile
-# 	builds libst.a and sox
-# This makefile assumes Microware Ultra C
-#
-# NOTE! You have to rename 8svx.c to svx8.c
-#
-# Boisy G. Pitre (boisy@microware.com)
-
-RDIR	=	RELS
-CFLAGS	=	-ai -DOS9 -DHAVE_GETOPT	# use strict ANSI mode, shared libraries
-LFLAGS	=	$(CFLAGS) -l=/dd/lib/sys_clib.l
-CC		=	cc
-
-FOBJ	= $(RDIR)/8svx.r $(RDIR)/adpcm.r $(RDIR)/aiff.r $(RDIR)/alsa.r \
-	  $(RDIR)/au.r $(RDIR)/auto.r $(RDIR)/avr.r \
-	  $(RDIR)/cdr.r $(RDIR)/cvsd.r $(RDIR)/dat.r \
-	  $(RDIR)/g721.r $(RDIR)/g723_24.r $(RDIR)/g723_40.r \
-	  $(RDIR)/g72x.r $(RDIR)/gsm.r $(RDIR)/hcom.r $(RDIR)/ima_rw.r \
-	  $(RDIR)/maud.r \
-	  $(RDIR)/oss.r $(RDIR)/raw.r $(RDIR)/sf.r \
-	  $(RDIR)/smp.r $(RDIR)/sndrtool.r $(RDIR)/sunaudio.r \
-	  $(RDIR)/tx16w.r $(RDIR)/voc.r $(RDIR)/wav.r $(RDIR)/wve.r
-
-EOBJ	= $(RDIR)/avg.r $(RDIR)/band.r $(RDIR)/chorus.r \
-	  $(RDIR)/copy.r $(RDIR)/comand.r $(RDIR)/cut.r $(RDIR)/deemphas.r \
-	  $(RDIR)/dyn.r $(RDIR)/echo.r $(RDIR)/echos.r $(RDIR)/filter.r \
-	  $(RDIR)/flanger.r \
-	  $(RDIR)/highp.r $(RDIR)/lowp.r $(RDIR)/map.r $(RDIR)/mask.r \
-	  $(RDIR)/pan.r $(RDIR)/phaser.r $(RDIR)/pick.r \
-	  $(RDIR)/polyphas.r $(RDIR)/rate.r \
-	  $(RDIR)/resample.r $(RDIR)/reverb.r $(RDIR)/reverse.r \
-	  $(RDIR)/speed.r $(RDIR)/split.r $(RDIR)/stat.r $(RDIR)/swap.r \
-	  $(RDIR)/vibro.r $(RDIR)/vol.r
-
-
-LIBOBJS	=	$(FOBJ) $(EOBJ) $(RDIR)/handlers.r $(RDIR)/libst.r \
-			$(RDIR)/misc.r $(RDIR)/getopt.r $(RDIR)/util.r
-
-all:	sox
-	@echo Done
-
-sox:	$(RDIR)/sox.r $(LIBOBJS)
-	$(CC) -f=$@ $(RDIR)/sox.r $(LIBOBJS) $(LFLAGS)
-
-sox.r:		sox.c st.h
-
-$(LIBOBJS):	st.h version.h patchlvl.h
-
-# OS-9 systems need the appropriate programs
-# to make use of this section.
-man: sox.1 libst.3
-	del sox.txt
-	del libst.txt
-	nroff -man sox.1 ! col -b > sox.txt
-	nroff -man libst.3 ! col -b > libst.txt
-
-# Just guessing here
-svx8.c: 8svx.c
-	@echo Hey! You need to copy 8svx.c to svx8.c
-	# what's the cp command?
--- a/sox.1
+++ b/sox.1
@@ -95,6 +95,8 @@
 .br
 	pick
 .br
+	pitch \fIshift [ width interpole fade ]\fB
+.br
 	polyphase [ \fI-w \fR< \fInut\fR / \fIham\fR > ] 
                [ \fI -width \fR< \fI long \fR / \fIshort \fR / \fI# \fR> ] 
                [ \fI-cutoff # \fR ]
@@ -107,12 +109,14 @@
 .br
 	reverse
 .br
-	speed \ffactor\f
+	speed \fIfactor\fB
 .br
 	split
 .br
 	stat [ \fIdebug\fB | \fI-v\fB ]
 .br
+	stretch [ \fIfactor [ window fade shift fading ]\fB
+.br
 	swap [ \fI1 2 3 4\fB ]
 .br
 	vibro \fIspeed \fB[ \fIdepth\fB ]
@@ -705,6 +709,21 @@
 pick
 Select the left or right channel of a stereo sample,
 or one of four channels in a quadrophonic sample.
+.TP 10
+pitch \fIshift [ width interpole fade ]\fB
+Change the pitch of file without affecting its duration by cross-fading
+shifted samples.
+.I shift
+is given in cents. Use a positive value to shift to treble, negative value to shift to bass.
+Default shift is 0.
+.I width
+of window is in ms. Default width is 20ms. Try 30ms to lower pitch,
+and 10ms to raise pitch.
+.I interpole
+option, can be "cubic" or "linear". Default is "cubic".  The
+.I fade
+option, can be "cos", "hamming", "linear" or "trapezoid".
+Default is "cos".
 .TP
 polyphase [ \fI-w \fR< \fInut\fR / \fIham\fR > ] 
 .TP
@@ -850,7 +869,7 @@
 Reverse the sound sample completely.
 Included for finding Satanic subliminals.
 .TP 10
-speed \ffactor\f
+speed \fIfactor\fB
 Speed up or down the sound, as a magnetic tape with a speed control. 
 It affects both pitch and time. A factor of 1.0 means no change, 
 and is the default. 
@@ -883,6 +902,21 @@
 sound file from the internal buffer that is in 32-bit signed PCM data.
 This is mainly only of use in tracking down endian problems that
 creep in to sox on cross-platform versions.
+.TP 10
+stretch \fIfactor [window fade shift fading]\fB
+Time stretch file by a given factor. Change duration without affecting the pitch. 
+.I factor
+of stretching: >1.0 lengthen, <1.0 shorten duration.
+.I window
+size is in ms. Default is 20ms. The
+.I fade
+option, can be "lin".
+.I shift
+ratio, in [0.0 1.0]. Default depends on stretch factor. 1.0
+to shorten, 0.8 to lengthen.  The
+.I fading
+ratio, in [0.0 0.5]. The amount of a fade's default depends on factor
+and shift.
 .TP 10
 swap [ \fI1 2 3 4\fB ]
 Swap channels in multi-channel sound files.  In files with more than 2
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -48,8 +48,8 @@
 EOBJ	= avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
 	  copy.o cut.o deemphas.o echo.o echos.o filter.o flanger.o \
 	  highp.o highpass.o lowp.o lowpass.o map.o mask.o pan.o \
-	  phaser.o pick.o polyphas.o rate.o resample.o reverb.o \
-	  reverse.o speed.o split.o stat.o swap.o vibro.o vol.o
+	  phaser.o pick.o pitch.o polyphas.o rate.o resample.o reverb.o \
+	  reverse.o speed.o split.o stat.o stretch.o swap.o vibro.o vol.o
 
 OSSOBJ_0    =
 OSSOBJ_1    = oss.o
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -552,6 +552,12 @@
 extern int st_pick_flow();
 extern int st_pick_stop();
 
+extern int st_pitch_getopts();
+extern int st_pitch_start();
+extern int st_pitch_flow();
+extern int st_pitch_drain();
+extern int st_pitch_stop();
+
 extern int st_poly_getopts();
 extern int st_poly_start();
 extern int st_poly_flow();
@@ -597,6 +603,12 @@
 extern int st_stat_flow();
 extern int st_stat_stop();
 
+extern int st_stretch_getopts();
+extern int st_stretch_start();
+extern int st_stretch_flow();
+extern int st_stretch_drain();
+extern int st_stretch_stop();
+
 extern int st_swap_getopts();
 extern int st_swap_start();
 extern int st_swap_flow();
@@ -693,6 +705,9 @@
 	{"pick", ST_EFF_CHAN | ST_EFF_MCHAN, 
 		st_pick_getopts, st_pick_start, st_pick_flow, 
 		st_null_drain, st_pick_stop},
+	{"pitch", 0,
+	        st_pitch_getopts, st_pitch_start, st_pitch_flow,
+	        st_pitch_drain, st_pitch_stop},
 	{"polyphase", ST_EFF_RATE,
 	        st_poly_getopts, st_poly_start, st_poly_flow,
 	        st_poly_drain, st_poly_stop},
@@ -717,6 +732,9 @@
 	{"stat", ST_EFF_MCHAN | ST_EFF_REPORT | ST_EFF_RATE | ST_EFF_CHAN,
 		st_stat_getopts, st_stat_start, st_stat_flow, 
 		st_null_drain, st_stat_stop},
+	{"stretch", 0,
+	        st_stretch_getopts, st_stretch_start, st_stretch_flow,
+	        st_stretch_drain, st_stretch_stop},
 	{"swap", ST_EFF_MCHAN,
 		st_swap_getopts, st_swap_start, st_swap_flow, 
 		st_swap_drain, st_swap_stop},
--- /dev/null
+++ b/src/pitch.c
@@ -1,0 +1,626 @@
+/*
+ * (c) Fabien Coelho <fabien@coelho.net> 03/2000 for sox. see sox copyright.
+ *
+ * pitch shifting.
+ *
+ * I found a code on the Computer Music Journal web site
+ * <http://mitpress.mit.edu/e-journals/Computer_Music_Journal/>
+ * for pitch shifting the AD 1848 PC soundcards, with
+ * a lot of (unclear) pointer and integer arithmetics, and 
+ * combine effects (feedback, delay, mixing).
+ *
+ * I tried to understand the code, dropped the other effects,
+ * translated the stuff in float so it's easier to understand, 
+ * drop one of the lookup tables (I know that sin(pi/2-x) = cos(x)), 
+ * and added interpolation and fade options of my own.
+ * cross fading is always symetric.
+ *
+ * Basically, the algorithm performs a resampling at the desire rate
+ * to achieve the shift (interpolation function) on small overlapping windows,
+ * and successive windows are faded in/out one into the other to
+ * rebuild the final signal.
+ *
+ * I'm quite disappointed. At first thought, I looked for an FT-based
+ * algorithm, something like "switch the signal to frequencies, shift
+ * frequencies, and come back to time", but it does not seem to work
+ * that way... at least not so easily. Or maybe my attempt was buggy.
+ *
+ * Here is the result. It can certainly be improved. 
+ * The result buzzes some time.
+ * Lot of options available so than one can adjust the result.
+ *
+ * so as to lower the pitch, a larger window sounds better (30ms)?
+ * so as to upper the pitch, a smaller window... (10ms)?
+ *
+ * Some speed-optimization could be added at code size expanse/expense?
+ */
+
+#include "st.h"
+
+#include <stdlib.h> /* malloc(), free() */
+
+#include <math.h>   /* cos(), pow() */
+#include <limits.h> /* LONG_MAX */
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+/* float type for the computations.
+   should be common to all effects?
+   I use such trick in vol, pan, speed, pitch and stretch...
+ */
+#ifndef PITCH_FLOAT
+#define PITCH_FLOAT double
+#define PITCH_FLOAT_SCAN "%lf"
+#endif
+
+#define PITCH_USAGE \
+    "Usage: pitch shift width interpole fade" \
+    " (in cents, in ms, cub/lin, cos/ham/lin/trap)" \
+    " (defaults: 0 20 c c)"
+
+/* cross fading options for transitions
+ */
+#define PITCH_FADE_COS 0 /* cosine */
+#define PITCH_FADE_HAM 1 /* Hamming */
+#define PITCH_FADE_LIN 2 /* linear */
+#define PITCH_FADE_TRA 3 /* trapezoid */
+
+#define PITCH_FADE_DEFAULT PITCH_FADE_COS
+
+/* interpolation options
+ */
+#define PITCH_INTERPOLE_CUB 0 /* cubic */
+#define PITCH_INTERPOLE_LIN 1 /* linear */
+
+#define PITCH_INTERPOLE_DEFAULT PITCH_INTERPOLE_CUB
+
+/* default window width
+ */
+#define PITCH_DEFAULT_WIDTH ((PITCH_FLOAT)(20.0e0)) /* 20 ms */
+
+/* constants.
+ */
+#define OCTAVA             ((PITCH_FLOAT)(1200.0e0)) /* in cents */
+#define THOUSAND           ((PITCH_FLOAT)(1000.0e0)) /* in units */
+#define HUNDRED            ((PITCH_FLOAT)(100.0e0))
+#define FOUR               ((PITCH_FLOAT)(4.0e0))
+#define TWO                ((PITCH_FLOAT)(2.0e0))
+#define ONE                ((PITCH_FLOAT)(1.0e0))
+#define HALF               ((PITCH_FLOAT)(0.5e0))
+#define QUARTER            ((PITCH_FLOAT)(0.25e0))
+#define ONESIXTH           ((PITCH_FLOAT)(1.0e0/6.0e0))
+#define ZERO               ((PITCH_FLOAT)(0.0e0))
+
+/* linear factors for the Hamming window
+   0<=i<=n: HAM_n(i) = HAM0 + HAM1*cos(i*PI/n)
+ */
+#define HAM1		   ((PITCH_FLOAT)(0.46e0))
+#define HAM0		   ((PITCH_FLOAT)(0.54e0))
+
+/* state of buffer management... */
+typedef enum { pi_input, pi_compute, pi_output } pitch_state_t;
+
+/* structure hold by the effect descriptor. */
+typedef struct 
+{
+    /* OPTIONS
+     */
+    PITCH_FLOAT shift;   /* shift in cents, >0 to treble, <0 to bass */
+
+    PITCH_FLOAT width;   /* sweep size in ms */
+
+    int interopt;        /* interpole option */
+
+    int fadeopt;         /* fade option */
+    PITCH_FLOAT coef;    /* coefficient used by trapezoid */
+    /* what about coef1/coef2 for hamming... */
+
+    /* COMPUTATION
+     */
+    PITCH_FLOAT rate;    /* sweep rate, around 1.0 */
+
+    int step;            /* size of half a sweep, rounded to integer... */
+    PITCH_FLOAT * fade;  /* fading factors table lookup, ~ 1.0 -> ~ 0.0 */
+
+    int overlap;         /* needed overlap */
+
+    PITCH_FLOAT * tmp;   /* temporary buffer */
+    PITCH_FLOAT * acc;   /* accumulation buffer */
+
+    int iacc;            /* part of acc already output */
+
+    LONG size;           /* size of buffer for processing chunks. */
+    int index;           /* index of next empty input item. */
+    LONG * buf;          /* bufferize input */
+
+    pitch_state_t state; /* buffer management status. */
+
+    int clipped;         /* number of clipped values (i.e. overflows) */
+
+} * pitch_t;
+
+/* // debug functions
+
+static char * fadeoptname(int opt)
+{
+    switch (opt)
+    {
+    case PITCH_FADE_COS: return "cosine";
+    case PITCH_FADE_HAM: return "hamming";
+    case PITCH_FADE_LIN: return "linear";
+    case PITCH_FADE_TRA: return "trapezoid";
+    default: return "UNEXPECTED";
+    }
+}
+
+static void debug(pitch_t pitch, char * where)
+{
+  fprintf(stderr, 
+  "%s: ind=%d sz=%ld step=%d o=%d rate=%f ia=%d st=%d fo=%s\n", 
+  where, pitch->index, pitch->size, pitch->step, pitch->overlap, 
+  pitch->rate, pitch->iacc, pitch->state, fadeoptname(pitch->fadeopt));
+}
+*/
+
+/* compute f(x) as a linear interpolation...
+ */
+static PITCH_FLOAT lin(
+  PITCH_FLOAT f0,  /* f(0)  */
+  PITCH_FLOAT f1,  /* f(1)  */
+  PITCH_FLOAT x)   /* 0.0 <= x < 1.0 */
+{
+    return f0 * (ONE - x) + f1 * x;
+}
+
+/* compute f(x) as a cubic function...
+ */
+static PITCH_FLOAT cub(
+  PITCH_FLOAT fm1, /* f(-1) */
+  PITCH_FLOAT f0,  /* f(0)  */
+  PITCH_FLOAT f1,  /* f(1)  */
+  PITCH_FLOAT f2,  /* f(2)  */
+  PITCH_FLOAT x)   /* 0.0 <= x < 1.0 */
+{
+    /* a x^3 + b x^2 + c x + d */
+    register PITCH_FLOAT a, b, c, d;
+
+    d = f0;
+    b = HALF * (f1+fm1) - f0;
+    a = ONESIXTH * (f2-f1+fm1-f0-FOUR*b);
+    c = f1 - a - b - d;
+    
+    return ((a * x + b) * x + c) * x + d;
+}
+
+/* interpolate a quarter (half a window)
+ *
+ * ibuf buffer of ilen length is swept at rate speed.
+ * result put in output buffer obuf of size olen.
+ */
+static void interpolation(
+  pitch_t pitch,
+  LONG * ibuf, int ilen, 
+  PITCH_FLOAT * out, int olen,
+  PITCH_FLOAT rate) /* signed */
+{
+    register int i, size;
+    register PITCH_FLOAT index;
+
+    size = pitch->step; /* size == olen? */
+
+    if (rate>0) /* sweep forwards */
+    {
+	for (index=ZERO, i=0; i<olen; i++, index+=rate)
+	{
+	    register int ifl = (int) index; /* FLOOR */
+	    register PITCH_FLOAT frac = index - ifl;
+
+	    if (pitch->interopt==PITCH_INTERPOLE_LIN)
+		out[i] = lin((PITCH_FLOAT) ibuf[ifl], 
+			     (PITCH_FLOAT) ibuf[ifl+1],
+			     frac);
+	    else
+		out[i] = cub((PITCH_FLOAT) ibuf[ifl-1], 
+			     (PITCH_FLOAT) ibuf[ifl], 
+			     (PITCH_FLOAT) ibuf[ifl+1], 
+			     (PITCH_FLOAT) ibuf[ifl+2],
+			     frac);
+	}
+    }
+    else /* rate < 0, sweep backwards */
+    {
+	for (index=ilen-1, i=olen-1; i>=0; i--, index+=rate)
+	{
+	    register int ifl = (int) index; /* FLOOR */
+	    register PITCH_FLOAT frac = index - ifl;
+
+	    if (pitch->interopt==PITCH_INTERPOLE_LIN)
+		out[i] = lin((PITCH_FLOAT) ibuf[ifl], 
+			     (PITCH_FLOAT) ibuf[ifl+1],
+			     frac);
+	    else
+		out[i] = cub((PITCH_FLOAT) ibuf[ifl-1], 
+			     (PITCH_FLOAT) ibuf[ifl], 
+			     (PITCH_FLOAT) ibuf[ifl+1], 
+			     (PITCH_FLOAT) ibuf[ifl+2],
+			     frac);
+	}
+    }
+}
+
+/* from input buffer to acc
+ */
+static void process_intput_buffer(pitch_t pitch)
+{
+    register int i, len = pitch->step;
+
+    /* forwards sweep */
+    interpolation(pitch, 
+		  pitch->buf+pitch->overlap, pitch->step+pitch->overlap, 
+		  pitch->tmp, pitch->step,
+		  pitch->rate);
+    
+    for (i=0; i<len; i++)
+	pitch->acc[i] = pitch->fade[i]*pitch->tmp[i];
+
+    /* backwards sweep */
+    interpolation(pitch,
+		  pitch->buf, pitch->step+pitch->overlap,
+		  pitch->tmp, pitch->step,
+		  -pitch->rate);
+    
+    for (i=0; i<len; i++)
+	pitch->acc[i] += pitch->fade[pitch->step-i-1]*pitch->tmp[i];
+}
+
+static LONG clip(pitch_t pitch, PITCH_FLOAT v)
+{
+    if (v < -LONG_MAX)
+    {
+	pitch->clipped++;
+	return -LONG_MAX;
+    }
+    else if (v > LONG_MAX)
+    {
+	pitch->clipped++;
+	return LONG_MAX;
+    }
+    else
+	return (LONG) v;
+}
+
+/*
+ * Process options
+ */
+int st_pitch_getopts(effp, n, argv) 
+eff_t effp;
+int n;
+char **argv;
+{
+    pitch_t pitch = (pitch_t) effp->priv; 
+    
+    /* get pitch shift */
+    pitch->shift = ZERO; /* default is no change */
+
+    if (n && !sscanf(argv[0], PITCH_FLOAT_SCAN, &pitch->shift))
+    {
+	fail(PITCH_USAGE);
+	return ST_EOF;
+    }
+
+    /* sweep size in ms */
+    pitch->width = PITCH_DEFAULT_WIDTH;
+    if (n>1 && !sscanf(argv[1], PITCH_FLOAT_SCAN, &pitch->width))
+    {
+	fail(PITCH_USAGE);
+	return ST_EOF;
+    }
+
+    /* interpole option */
+    pitch->interopt = PITCH_INTERPOLE_DEFAULT;
+    if (n>2)
+    {
+	switch(argv[2][0])
+	{
+	case 'l':
+	case 'L':
+	    pitch->interopt = PITCH_INTERPOLE_LIN;
+	    break;
+	case 'c':
+	case 'C':
+	    pitch->interopt = PITCH_INTERPOLE_CUB;
+	    break;
+	default:
+	    fail(PITCH_USAGE);
+	    return ST_EOF;
+	}
+    }
+
+    /* fade option */
+    pitch->fadeopt = PITCH_FADE_DEFAULT; /* default */
+    if (n>3) 
+    {
+	switch (argv[3][0]) /* what a parser;-) */
+	{
+	case 'l':
+	case 'L':
+	    pitch->fadeopt = PITCH_FADE_LIN;
+	    break;
+	case 't':
+	case 'T':
+	    pitch->fadeopt = PITCH_FADE_TRA;
+	    break;
+	case 'h':
+	case 'H':
+	    pitch->fadeopt = PITCH_FADE_HAM;
+	    break;
+	case 'c':
+	case 'C':
+	    pitch->fadeopt = PITCH_FADE_COS;
+	    break;
+	default:
+	    fail(PITCH_USAGE);
+	    return ST_EOF;
+	}
+    }
+    
+    pitch->coef = QUARTER;
+    if (n>4 && (!sscanf(argv[4], PITCH_FLOAT_SCAN, &pitch->coef) ||
+		pitch->coef<ZERO || pitch->coef>HALF))
+    {
+	fail(PITCH_USAGE);
+	return ST_EOF;
+    }
+
+    pitch->clipped = 0;
+
+    return ST_SUCCESS;
+}
+
+/*
+ * Start processing
+ */
+int st_pitch_start(effp)
+eff_t effp;
+{
+    pitch_t pitch = (pitch_t) effp->priv;
+    register int sample_rate = effp->outinfo.rate, i;
+
+    /* check constraints. sox does already take care of that I guess?
+     */
+    if (effp->outinfo.rate != effp->ininfo.rate)
+    {
+	fail("PITCH cannot handle different rates (in=%ld, out=%ld)"
+	     " use resample or rate", effp->ininfo.rate, effp->outinfo.rate);
+	return ST_EOF;
+    }
+ 
+    if (effp->outinfo.channels != effp->ininfo.channels)
+    {
+	fail("PITCH cannot handle different channels (in=%ld, out=%ld)"
+	     " use avg or pan", effp->ininfo.channels, effp->outinfo.channels);
+	return ST_EOF;
+    }
+
+    /* computer inner stuff... */
+
+    /* Should I trust pow?
+     * BTW, the twelve's root of two is 1.0594630943592952645618252,
+     * if we consider an equal temperament.
+     */
+    pitch->rate = pow(TWO, pitch->shift/OCTAVA);
+
+    /* size is half of the actual target window size, because of symetry.
+     */
+    pitch->step = (int) ((pitch->width*(HALF/THOUSAND))*sample_rate);
+
+    /* make size odd? do we care? */
+    /* if (!(size & 1)) size++; */
+
+    /* security for safe cubic interpolation */
+    if (pitch->rate > ONE)
+	pitch->overlap = (int) ((pitch->rate-ONE)*pitch->step) + 2;
+    else
+	pitch->overlap = 2; 
+
+    pitch->size = pitch->step + 2*pitch->overlap;
+
+    pitch->fade = (PITCH_FLOAT *) malloc(pitch->step*sizeof(PITCH_FLOAT));
+    pitch->tmp  = (PITCH_FLOAT *) malloc(pitch->step*sizeof(PITCH_FLOAT));
+    pitch->acc  = (PITCH_FLOAT *) malloc(pitch->step*sizeof(PITCH_FLOAT));
+    pitch->buf  = (LONG *) malloc(pitch->size*sizeof(LONG));
+
+    if (!pitch->fade || !pitch->tmp || !pitch->acc || !pitch->buf)
+    {
+	fail("malloc failed in st_pitch_start");
+	return ST_EOF;
+    }
+
+    pitch->index = pitch->overlap;
+
+    /* default initial signal */
+    for (i=0; i<pitch->size; i++)
+	pitch->buf[i] = 0;
+
+    if (pitch->fadeopt == PITCH_FADE_HAM)
+    {
+	/* does it make sense to have such an option? */
+	register PITCH_FLOAT pi_step = M_PI / (pitch->step-1);
+	
+	for (i=0; i<pitch->step; i++)
+	    pitch->fade[i] = (PITCH_FLOAT) (HAM0 + HAM1*cos(pi_step*i));
+    }
+    else if (pitch->fadeopt == PITCH_FADE_COS)
+    {
+	register PITCH_FLOAT pi_2_step = M_PI_2 / (pitch->step-1);
+
+	pitch->fade[0] = ONE; /* cos(0) == 1.0 */
+	for (i=1; i<pitch->step-1; i++)
+	    pitch->fade[i]  = (PITCH_FLOAT) cos(pi_2_step*i);
+	pitch->fade[pitch->step-1] = ZERO; /* cos(PI/2) == 0.0 */
+    }
+    else if (pitch->fadeopt == PITCH_FADE_LIN)
+    {
+	register PITCH_FLOAT stepth = ONE / (pitch->step-1);
+
+	pitch->fade[0] = ONE;
+	for (i=1; i<pitch->step-1; i++)
+	    pitch->fade[i] = (pitch->step-i-1) * stepth;
+	pitch->fade[pitch->step-1] = ZERO;
+    }
+    else if (pitch->fadeopt == PITCH_FADE_TRA)
+    {
+	/* 0 <= coef <= 0.5 */
+	register int plat = (int) (pitch->step*pitch->coef);
+	register PITCH_FLOAT slope = ONE / (pitch->step - 2*plat);
+
+	for (i=0; i<plat; i++)
+	    pitch->fade[i] = ONE;
+
+	for (; i<pitch->step-plat; i++)
+	    pitch->fade[i] = slope * (pitch->step-plat-i-1);
+
+	for (; i<pitch->step; i++)
+	    pitch->fade[i] = ZERO;
+    }
+    else
+    {
+	fail("unexpected PITCH_FADE parameter %d", pitch->fadeopt);
+	return ST_EOF;
+    }
+
+    pitch->clipped = 0;
+
+    return ST_SUCCESS;
+}
+
+/* Processes input.
+ */
+int st_pitch_flow(effp, ibuf, obuf, isamp, osamp)
+eff_t effp;
+LONG *ibuf, *obuf;
+LONG *isamp, *osamp;
+{
+    pitch_t pitch = (pitch_t) effp->priv;
+    register int i, len, size, iindex, oindex;
+
+    size = pitch->size;
+    /* size to process */
+    len = MIN(*isamp, *osamp);
+    iindex = 0;
+    oindex = 0;
+
+    /* warning:
+       because of the asynchroneous nature of buffering,
+       the output index can reach the buffer limits before full consumption.
+       I put the input index just in case. 
+       If the code is correct, eithier len or iindex is redundant.
+    */
+    while (len>0 && iindex<*isamp && oindex<*osamp)
+    {
+	if (pitch->state == pi_input)
+	{
+	    register int tocopy = MIN(pitch->size-pitch->index, len);
+
+	    memcpy(pitch->buf+pitch->index, ibuf+iindex, tocopy*sizeof(LONG));
+
+	    len -= tocopy;
+	    pitch->index += tocopy;
+	    iindex += tocopy;
+
+	    if (pitch->index==pitch->size)
+		pitch->state = pi_compute;
+	}
+
+	if (pitch->state == pi_compute)
+	{
+	    process_intput_buffer(pitch);
+	    pitch->state = pi_output;
+	    pitch->iacc = 0;
+	}
+
+	if (pitch->state == pi_output)
+	{
+	    register int toout = MIN(*osamp-oindex, pitch->step-pitch->iacc);
+
+	    for (i=0; i<toout; i++)
+		obuf[oindex++] = clip(pitch, pitch->acc[pitch->iacc++]);
+
+	    if (pitch->iacc == pitch->step)
+	    {
+		pitch->state = pi_input;
+
+		/* shift input buffer. memmove? */
+		for (i=0; i<2*pitch->overlap; i++)
+		    pitch->buf[i] = pitch->buf[i+pitch->step];
+		
+		pitch->index = 2*pitch->overlap;
+	    }
+	}
+    }
+
+    /* report consumption. */
+    *isamp = iindex;
+    *osamp = oindex;
+
+    return ST_SUCCESS;
+}
+
+/* at the end...
+ */
+int st_pitch_drain(effp, obuf, osamp)
+eff_t effp;
+LONG * obuf;
+LONG * osamp;
+{
+    pitch_t pitch = (pitch_t) effp->priv;
+    register int i;
+
+    if (pitch->state == pi_input)
+    {
+	/* complete input buffer content with 0. */
+	for (i=pitch->index; i<pitch->size; i++)
+	    pitch->buf[i] = 0;
+
+	pitch->state = pi_compute;
+    }
+
+    if (pitch->state == pi_compute)
+    {
+	process_intput_buffer(pitch);
+	pitch->state = pi_output;
+	pitch->iacc = 0;
+    }
+
+    /* (pitch->state == pi_output) */
+    for (i=0; i<*osamp && i<pitch->index-pitch->overlap;)
+	obuf[i++] = clip(pitch, pitch->acc[pitch->iacc++]);
+
+    /* report... */
+    *osamp = i;
+
+    return ST_SUCCESS;
+}
+    
+/*
+ * Do anything required when you stop reading samples.  
+ * Don't close input file! 
+ */
+int st_pitch_stop(effp)
+eff_t effp;
+{
+    pitch_t pitch = (pitch_t) effp->priv;
+
+    free(pitch->fade);
+    free(pitch->tmp);
+    free(pitch->acc);
+    free(pitch->buf);
+
+    if (pitch->clipped)
+	warn("PITCH clipped %d values... adjust volume with -v option maybe?", 
+	     pitch->clipped);
+
+    return ST_SUCCESS;
+}
--- /dev/null
+++ b/src/stretch.c
@@ -1,0 +1,440 @@
+/*
+ * (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.h"
+
+#include <stdlib.h> /* malloc and free */
+#include <limits.h> /* LONG_MAX */
+
+#ifndef MIN
+#define MIN(s1,s2) ((s1)<(s2)?(s1):(s2))
+#endif
+
+#ifndef STRETCH_FLOAT
+#define STRETCH_FLOAT float
+#define STRETCH_FLOAT_SCAN "%f"
+#endif
+
+#define STRETCH_USAGE \
+    "Usage: stretch factor [window fade shift fading]\n" \
+    "\t(expansion, frame in ms, lin/..., unit<1.0, unit<0.5)\n" \
+    "\t(defaults: 1.0 20 lin ...)"
+
+/* 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 */
+    LONG * 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);
+}
+*/
+
+/* clip amplitudes and count number of clipped values.
+ */
+static LONG clip(stretch_t stretch, STRETCH_FLOAT v)
+{
+    if (v < -LONG_MAX)
+    {
+	stretch->clipped++;
+	return -LONG_MAX;
+    }
+    else if (v > LONG_MAX)
+    {
+	stretch->clipped++;
+	return LONG_MAX;
+    }
+    else
+    {
+	return (LONG) v;
+    }
+}
+
+/*
+ * Process options
+ */
+int st_stretch_getopts(effp, n, argv) 
+eff_t effp;
+int n;
+char **argv;
+{
+    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))
+    {
+	fail(STRETCH_USAGE "\n\terror while parsing factor");
+	return ST_EOF;
+    }
+
+    if (n>1 && !sscanf(argv[1], STRETCH_FLOAT_SCAN, &stretch->window))
+    {
+	fail(STRETCH_USAGE "\n\terror while parsing window size");
+	return ST_EOF;
+    }
+
+    if (n>2) 
+    {
+	switch (argv[2][0])
+	{
+	case 'l':
+	case 'L':
+	    stretch->fade = st_linear_fading;
+	    break;
+	default:
+	    fail(STRETCH_USAGE "\n\terror while parsing fade type");
+	    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))
+    {
+	fail(STRETCH_USAGE "\n\terror while parsing shift ratio");
+	return ST_EOF;
+    }
+
+    if (stretch->shift > ONE || stretch->shift <= ZERO)
+    {
+	fail(STRETCH_USAGE "\n\terror with shift ratio value");
+	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))
+    {
+	fail(STRETCH_USAGE "\n\terror while parsing fading ratio");
+	return ST_EOF;
+    }
+
+    if (stretch->fading > HALF || stretch->fading < ZERO)
+    {
+	fail(STRETCH_USAGE "\n\terror with fading ratio value");
+	return ST_EOF;
+    }
+
+    return ST_SUCCESS;
+}
+
+/*
+ * Start processing
+ */
+int st_stretch_start(effp)
+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)
+    {
+	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)
+    {
+	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  = (LONG *) malloc(stretch->size * sizeof(LONG));
+
+    /* 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) 
+    {
+	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(effp, ibuf, obuf, isamp, osamp)
+eff_t effp;
+LONG *ibuf, *obuf;
+LONG *isamp, *osamp;
+{
+    stretch_t stretch = (stretch_t) effp->priv;
+    register int iindex, oindex, i;
+
+    iindex = 0;
+    oindex = 0;
+
+    while (iindex<*isamp && oindex<*osamp)
+    {
+	if (stretch->state == input_state)
+	{
+	    register int tocopy = MIN(*isamp-iindex, 
+				      stretch->size-stretch->index);
+
+	    memcpy(stretch->ibuf+stretch->index, 
+		   ibuf+iindex, tocopy*sizeof(LONG));
+
+	    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)
+		obuf[oindex++] = 
+		    clip(stretch, stretch->obuf[stretch->oindex++]);
+
+	    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(effp, obuf, osamp)
+eff_t effp;
+LONG *obuf;
+LONG *osamp;
+{
+    stretch_t stretch = (stretch_t) effp->priv;
+    register int i, 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;
+    }
+
+    if (stretch->state == output_state)
+    {
+	for (; oindex<*osamp && stretch->oindex<stretch->index;)
+	    obuf[oindex++] = clip(stretch, stretch->obuf[stretch->oindex++]);
+    }
+    
+    *osamp = oindex;
+
+    return ST_SUCCESS;
+}
+
+
+/*
+ * Do anything required when you stop reading samples.  
+ * Don't close input file! 
+ */
+int st_stretch_stop(effp)
+eff_t effp;
+{
+    stretch_t stretch = (stretch_t) effp->priv;
+
+    free(stretch->ibuf);
+    free(stretch->obuf);
+    free(stretch->fbuf);
+
+    if (stretch->clipped)
+	warn("STRETCH clipped %d values...", stretch->clipped);
+
+    return ST_SUCCESS;
+}