1 /* libSoX Basic time stretcher.
2 * (c) march/april 2000 Fabien COELHO <fabien@coelho.net> for sox.
4 * cross fade samples so as to go slower or faster.
6 * The filter is based on 6 parameters:
11 * - steady state of window s, ss = s*w
13 * I decided of the default values of these parameters based
14 * on some small non extensive tests. maybe better defaults
23 #define DEFAULT_SLOW_SHIFT_RATIO 0.8
24 #define DEFAULT_FAST_SHIFT_RATIO 1.0
26 #define DEFAULT_STRETCH_WINDOW 20.0 /* ms */
28 typedef enum { input_state
, output_state
} stretch_status_t
;
32 * FIXME: maybe shift could be allowed > 1.0 with factor < 1.0 ???
34 double factor
; /* strech factor. 1.0 means copy. */
35 double window
; /* window in ms */
36 double shift
; /* shift ratio wrt window. <1.0 */
37 double fading
; /* fading ratio wrt window. <0.5 */
40 stretch_status_t state
; /* automaton status */
42 size_t segment
; /* buffer size */
43 size_t index
; /* next available element */
44 sox_sample_t
*ibuf
; /* input buffer */
45 size_t ishift
; /* input shift */
47 size_t oindex
; /* next evailable element */
48 double * obuf
; /* output buffer */
49 size_t oshift
; /* output shift */
51 size_t overlap
; /* fading size */
52 double * fade_coefs
; /* fading, 1.0 -> 0.0 */
59 static int getopts(sox_effect_t
* effp
, int argc
, char **argv
)
61 priv_t
* p
= (priv_t
*) effp
->priv
;
65 p
->factor
= 1.0; /* default is no change */
66 p
->window
= DEFAULT_STRETCH_WINDOW
;
68 if (argc
> 0 && !sscanf(argv
[0], "%lf", &p
->factor
)) {
69 lsx_fail("error while parsing factor");
70 return lsx_usage(effp
);
73 if (argc
> 1 && !sscanf(argv
[1], "%lf", &p
->window
)) {
74 lsx_fail("error while parsing window size");
75 return lsx_usage(effp
);
84 lsx_fail("error while parsing fade type");
85 return lsx_usage(effp
);
89 /* default shift depends whether we go slower or faster */
90 p
->shift
= (p
->factor
<= 1.0) ?
91 DEFAULT_FAST_SHIFT_RATIO
: DEFAULT_SLOW_SHIFT_RATIO
;
93 if (argc
> 3 && !sscanf(argv
[3], "%lf", &p
->shift
)) {
94 lsx_fail("error while parsing shift ratio");
95 return lsx_usage(effp
);
98 if (p
->shift
> 1.0 || p
->shift
<= 0.0) {
99 lsx_fail("error with shift ratio value");
100 return lsx_usage(effp
);
103 /* default fading stuff...
104 it makes sense for factor >= 0.5 */
106 p
->fading
= 1.0 - (p
->factor
* p
->shift
);
108 p
->fading
= 1.0 - p
->shift
;
112 if (argc
> 4 && !sscanf(argv
[4], "%lf", &p
->fading
)) {
113 lsx_fail("error while parsing fading ratio");
114 return lsx_usage(effp
);
117 if (p
->fading
> 0.5 || p
->fading
< 0.0) {
118 lsx_fail("error with fading ratio value");
119 return lsx_usage(effp
);
128 static int start(sox_effect_t
* effp
)
130 priv_t
* p
= (priv_t
*)effp
->priv
;
136 p
->state
= input_state
;
138 p
->segment
= (int)(effp
->out_signal
.rate
* 0.001 * p
->window
);
139 /* start in the middle of an input to avoid initial fading... */
140 p
->index
= p
->segment
/ 2;
141 p
->ibuf
= lsx_malloc(p
->segment
* sizeof(sox_sample_t
));
143 /* the shift ratio deal with the longest of ishift/oshift
144 hence ishift<=segment and oshift<=segment. */
145 if (p
->factor
< 1.0) {
146 p
->ishift
= p
->shift
* p
->segment
;
147 p
->oshift
= p
->factor
* p
->ishift
;
149 p
->oshift
= p
->shift
* p
->segment
;
150 p
->ishift
= p
->oshift
/ p
->factor
;
152 assert(p
->ishift
<= p
->segment
);
153 assert(p
->oshift
<= p
->segment
);
155 p
->oindex
= p
->index
; /* start as synchronized */
156 p
->obuf
= lsx_malloc(p
->segment
* sizeof(double));
157 p
->overlap
= (int)(p
->fading
* p
->segment
);
158 p
->fade_coefs
= lsx_malloc(p
->overlap
* sizeof(double));
160 /* initialize buffers */
161 for (i
= 0; i
<p
->segment
; i
++)
164 for (i
= 0; i
<p
->segment
; i
++)
168 double slope
= 1.0 / (p
->overlap
- 1);
169 p
->fade_coefs
[0] = 1.0;
170 for (i
= 1; i
< p
->overlap
- 1; i
++)
171 p
->fade_coefs
[i
] = slope
* (p
->overlap
- i
- 1);
172 p
->fade_coefs
[p
->overlap
- 1] = 0.0;
173 } else if (p
->overlap
== 1)
174 p
->fade_coefs
[0] = 1.0;
176 lsx_debug("start: (factor=%g segment=%g shift=%g overlap=%g)\nstate=%d\n"
177 "segment=%" PRIuPTR
"\nindex=%" PRIuPTR
"\n"
178 "ishift=%" PRIuPTR
"\noindex=%" PRIuPTR
"\n"
179 "oshift=%" PRIuPTR
"\noverlap=%" PRIuPTR
,
180 p
->factor
, p
->window
, p
->shift
, p
->fading
, p
->state
,
181 p
->segment
, p
->index
, p
->ishift
, p
->oindex
, p
->oshift
, p
->overlap
);
183 effp
->out_signal
.length
= SOX_UNKNOWN_LEN
; /* TODO: calculate actual length */
187 /* accumulates input ibuf to output obuf with fading fade_coefs */
188 static void combine(priv_t
* p
)
193 for (i
= 0; i
< p
->overlap
; i
++)
194 p
->obuf
[i
] += p
->fade_coefs
[p
->overlap
- 1 - i
] * p
->ibuf
[i
];
197 for (; i
< p
->segment
- p
->overlap
; i
++)
198 p
->obuf
[i
] += p
->ibuf
[i
];
201 for (; i
<p
->segment
; i
++)
202 p
->obuf
[i
] += p
->fade_coefs
[i
- p
->segment
+ p
->overlap
] * p
->ibuf
[i
];
208 static int flow(sox_effect_t
* effp
, const sox_sample_t
*ibuf
, sox_sample_t
*obuf
,
209 size_t *isamp
, size_t *osamp
)
211 priv_t
* p
= (priv_t
*) effp
->priv
;
212 size_t iindex
= 0, oindex
= 0;
215 while (iindex
<*isamp
&& oindex
<*osamp
) {
216 if (p
->state
== input_state
) {
217 size_t tocopy
= min(*isamp
-iindex
,
218 p
->segment
-p
->index
);
220 memcpy(p
->ibuf
+ p
->index
, ibuf
+ iindex
, tocopy
* sizeof(sox_sample_t
));
225 if (p
->index
== p
->segment
) {
230 for (i
= 0; i
+ p
->ishift
< p
->segment
; i
++)
231 p
->ibuf
[i
] = p
->ibuf
[i
+p
->ishift
];
233 p
->index
-= p
->ishift
;
235 /* switch to output state */
236 p
->state
= output_state
;
240 if (p
->state
== output_state
) {
241 while (p
->oindex
< p
->oshift
&& oindex
< *osamp
) {
243 f
= p
->obuf
[p
->oindex
++];
244 SOX_SAMPLE_CLIP_COUNT(f
, effp
->clips
);
248 if (p
->oindex
>= p
->oshift
&& oindex
<*osamp
) {
249 p
->oindex
-= p
->oshift
;
251 /* shift internal output buffer */
252 for (i
= 0; i
+ p
->oshift
< p
->segment
; i
++)
253 p
->obuf
[i
] = p
->obuf
[i
+ p
->oshift
];
256 for (; i
< p
->segment
; i
++)
259 p
->state
= input_state
;
272 * Drain buffer at the end
273 * maybe not correct ? end might be artificially faded?
275 static int drain(sox_effect_t
* effp
, sox_sample_t
*obuf
, size_t *osamp
)
277 priv_t
* p
= (priv_t
*) effp
->priv
;
281 if (p
->state
== input_state
) {
282 for (i
=p
->index
; i
<p
->segment
; i
++)
287 p
->state
= output_state
;
290 while (oindex
<*osamp
&& p
->oindex
<p
->index
) {
291 float f
= p
->obuf
[p
->oindex
++];
292 SOX_SAMPLE_CLIP_COUNT(f
, effp
->clips
);
298 if (p
->oindex
== p
->index
)
305 static int stop(sox_effect_t
* effp
)
307 priv_t
* p
= (priv_t
*) effp
->priv
;
315 const sox_effect_handler_t
*lsx_stretch_effect_fn(void)
317 static const sox_effect_handler_t handler
= {
319 "factor [window fade shift fading]\n"
320 " (expansion, frame in ms, lin/..., unit<1.0, unit<0.5)\n"
321 " (defaults: 1.0 20 lin ...)",
323 getopts
, start
, flow
, drain
, stop
, NULL
, sizeof(priv_t
)