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;
+}