24896
|
1 /*
|
|
2 * scaletempo audio filter
|
|
3 * Copyright (c) 2007 Robert Juliano
|
|
4 *
|
|
5 * This file is part of MPlayer.
|
|
6 *
|
|
7 * MPlayer is free software; you can redistribute it and/or modify
|
|
8 * it under the terms of the GNU General Public License as published by
|
|
9 * the Free Software Foundation; either version 2 of the License, or
|
|
10 * (at your option) any later version.
|
|
11 *
|
|
12 * MPlayer is distributed in the hope that it will be useful,
|
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15 * GNU General Public License for more details.
|
|
16 *
|
|
17 * You should have received a copy of the GNU General Public License
|
|
18 * along with MPlayer; if not, write to the Free Software
|
|
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
20 *
|
|
21 * scale tempo while maintaining pitch
|
|
22 * (WSOLA technique with cross correlation)
|
|
23 * inspired by SoundTouch library by Olli Parviainen
|
|
24 *
|
|
25 * basic algorithm
|
|
26 * - produce 'stride' output samples per loop
|
|
27 * - consume stride*scale input samples per loop
|
|
28 *
|
|
29 * to produce smoother transitions between strides, blend next overlap
|
|
30 * samples from last stride with correlated samples of current input
|
|
31 *
|
|
32 */
|
|
33
|
|
34 #include <stdlib.h>
|
|
35 #include <string.h>
|
|
36 #include <limits.h>
|
|
37
|
|
38 #include "af.h"
|
|
39 #include "libavutil/common.h"
|
|
40 #include "subopt-helper.h"
|
|
41 #include "help_mp.h"
|
|
42
|
|
43 // Data for specific instances of this filter
|
|
44 typedef struct af_scaletempo_s
|
|
45 {
|
|
46 // stride
|
|
47 float scale;
|
|
48 float speed;
|
|
49 float frames_stride_scaled;
|
|
50 float frames_stride_error;
|
|
51 int bytes_per_frame;
|
|
52 int bytes_stride;
|
|
53 float bytes_stride_scaled;
|
|
54 int bytes_queue;
|
|
55 int bytes_queued;
|
|
56 int bytes_to_slide;
|
|
57 int8_t* buf_queue;
|
|
58 // overlap
|
|
59 int samples_overlap;
|
|
60 int samples_standing;
|
|
61 int bytes_overlap;
|
|
62 int bytes_standing;
|
|
63 int8_t* buf_overlap;
|
|
64 int8_t* table_blend;
|
|
65 void (*output_overlap)(struct af_scaletempo_s* s, int8_t* out_buf, int bytes_off);
|
|
66 // best overlap
|
|
67 int frames_search;
|
|
68 int num_channels;
|
|
69 int8_t* buf_pre_corr;
|
|
70 int8_t* table_window;
|
|
71 int (*best_overlap_offset)(struct af_scaletempo_s* s);
|
|
72 short shift_corr;
|
|
73 // command line
|
|
74 float scale_nominal;
|
|
75 float ms_stride;
|
|
76 float percent_overlap;
|
|
77 float ms_search;
|
|
78 short speed_tempo;
|
|
79 short speed_pitch;
|
|
80 } af_scaletempo_t;
|
|
81
|
24899
|
82 static int fill_queue(struct af_instance_s* af, af_data_t* data, int offset)
|
|
83 {
|
24896
|
84 af_scaletempo_t* s = af->setup;
|
|
85 int bytes_in = data->len - offset;
|
|
86 int offset_unchanged = offset;
|
|
87
|
|
88 if (s->bytes_to_slide > 0) {
|
|
89 if (s->bytes_to_slide < s->bytes_queued) {
|
|
90 int bytes_move = s->bytes_queued - s->bytes_to_slide;
|
|
91 memmove(s->buf_queue,
|
|
92 s->buf_queue + s->bytes_to_slide,
|
|
93 bytes_move);
|
|
94 s->bytes_to_slide = 0;
|
|
95 s->bytes_queued = bytes_move;
|
|
96 } else {
|
|
97 int bytes_skip;
|
|
98 s->bytes_to_slide -= s->bytes_queued;
|
|
99 bytes_skip = FFMIN(s->bytes_to_slide, bytes_in);
|
|
100 s->bytes_queued = 0;
|
|
101 s->bytes_to_slide -= bytes_skip;
|
|
102 offset += bytes_skip;
|
|
103 bytes_in -= bytes_skip;
|
|
104 }
|
|
105 }
|
|
106
|
|
107 if (bytes_in > 0) {
|
|
108 int bytes_copy = FFMIN(s->bytes_queue - s->bytes_queued, bytes_in);
|
|
109 memcpy(s->buf_queue + s->bytes_queued,
|
|
110 (int8_t*)data->audio + offset,
|
|
111 bytes_copy);
|
|
112 s->bytes_queued += bytes_copy;
|
|
113 offset += bytes_copy;
|
|
114 }
|
|
115
|
|
116 return offset - offset_unchanged;
|
|
117 }
|
|
118
|
24899
|
119 static int best_overlap_offset_float(af_scaletempo_t* s)
|
|
120 {
|
24896
|
121 float *pw, *po, *ppc, *search_start;
|
|
122 float best_corr = INT_MIN;
|
|
123 int best_off = 0;
|
|
124 int i, off;
|
|
125
|
|
126 pw = (float*)s->table_window;
|
|
127 po = (float*)s->buf_overlap + s->num_channels;
|
|
128 ppc = (float*)s->buf_pre_corr;
|
|
129 for (i=s->num_channels; i<s->samples_overlap; i++) {
|
|
130 *ppc++ = *pw++ * *po++;
|
|
131 }
|
|
132
|
|
133 search_start = (float*)s->buf_queue + s->num_channels;
|
|
134 for (off=0; off<s->frames_search; off++) {
|
|
135 float corr = 0;
|
|
136 float* ps = search_start;
|
|
137 ppc = (float*)s->buf_pre_corr;
|
|
138 for (i=s->num_channels; i<s->samples_overlap; i++) {
|
|
139 corr += *ppc++ * *ps++;
|
|
140 }
|
|
141 if (corr > best_corr) {
|
|
142 best_corr = corr;
|
|
143 best_off = off;
|
|
144 }
|
|
145 search_start += s->num_channels;
|
|
146 }
|
|
147
|
24897
|
148 return best_off * 4 * s->num_channels;
|
24896
|
149 }
|
|
150
|
24899
|
151 static int best_overlap_offset_s16(af_scaletempo_t* s)
|
|
152 {
|
24896
|
153 int32_t *pw, *ppc;
|
|
154 int16_t *po, *search_start;
|
|
155 int32_t best_corr = INT_MIN;
|
|
156 int best_off = 0;
|
|
157 int i, off;
|
|
158
|
|
159 pw = (int32_t*)s->table_window;
|
|
160 po = (int16_t*)s->buf_overlap + s->num_channels;
|
|
161 ppc = (int32_t*)s->buf_pre_corr;
|
|
162 for (i=s->num_channels; i<s->samples_overlap; i++) {
|
|
163 *ppc++ = ( *pw++ * *po++ ) >> 15;
|
|
164 }
|
|
165
|
|
166 search_start = (int16_t*)s->buf_queue + s->num_channels;
|
|
167 for (off=0; off<s->frames_search; off++) {
|
|
168 int32_t corr = 0;
|
|
169 int16_t* ps = search_start;
|
|
170 ppc = (int32_t*)s->buf_pre_corr;
|
|
171 for (i=s->num_channels; i<s->samples_overlap; i++) {
|
|
172 corr += ( *ppc++ * *ps++ ) >> s->shift_corr;
|
|
173 }
|
|
174 if (corr > best_corr) {
|
|
175 best_corr = corr;
|
|
176 best_off = off;
|
|
177 }
|
|
178 search_start += s->num_channels;
|
|
179 }
|
|
180
|
24897
|
181 return best_off * 2 * s->num_channels;
|
24896
|
182 }
|
|
183
|
24899
|
184 static void output_overlap_float(af_scaletempo_t* s, int8_t* buf_out,
|
|
185 int bytes_off)
|
|
186 {
|
24896
|
187 float* pout = (float*)buf_out;
|
|
188 float* pb = (float*)s->table_blend;
|
|
189 float* po = (float*)s->buf_overlap;
|
|
190 float* pin = (float*)(s->buf_queue + bytes_off);
|
|
191 int i;
|
|
192 for (i=0; i<s->samples_overlap; i++) {
|
|
193 *pout++ = *po - *pb++ * ( *po - *pin++ ); po++;
|
|
194 }
|
|
195 }
|
24899
|
196 static void output_overlap_s16(af_scaletempo_t* s, int8_t* buf_out,
|
|
197 int bytes_off)
|
|
198 {
|
24896
|
199 int16_t* pout = (int16_t*)buf_out;
|
|
200 int32_t* pb = (int32_t*)s->table_blend;
|
|
201 int16_t* po = (int16_t*)s->buf_overlap;
|
|
202 int16_t* pin = (int16_t*)(s->buf_queue + bytes_off);
|
|
203 int i;
|
|
204 for (i=0; i<s->samples_overlap; i++) {
|
|
205 *pout++ = *po - ( ( *pb++ * ( *po - *pin++ ) ) >> 16 ); po++;
|
|
206 }
|
|
207 }
|
|
208
|
|
209 // Filter data through filter
|
|
210 static af_data_t* play(struct af_instance_s* af, af_data_t* data)
|
|
211 {
|
|
212 af_scaletempo_t* s = af->setup;
|
|
213 int offset_in;
|
|
214 int max_bytes_out;
|
|
215 int8_t* pout;
|
|
216
|
|
217 if (s->scale == 1.0) {
|
|
218 return data;
|
|
219 }
|
|
220
|
|
221 // RESIZE_LOCAL_BUFFER - can't use macro
|
|
222 max_bytes_out = ((int)(data->len / s->bytes_stride_scaled) + 1) * s->bytes_stride;
|
|
223 if (max_bytes_out > af->data->len) {
|
|
224 af_msg(AF_MSG_VERBOSE, "[libaf] Reallocating memory in module %s, "
|
|
225 "old len = %i, new len = %i\n",af->info->name,af->data->len,max_bytes_out);
|
|
226 af->data->audio = realloc(af->data->audio, max_bytes_out);
|
|
227 if (!af->data->audio) {
|
|
228 af_msg(AF_MSG_FATAL, "[libaf] Could not allocate memory\n");
|
|
229 return NULL;
|
|
230 }
|
|
231 af->data->len = max_bytes_out;
|
|
232 }
|
|
233
|
|
234 offset_in = fill_queue(af, data, 0);
|
|
235 pout = af->data->audio;
|
|
236 while (s->bytes_queued >= s->bytes_queue) {
|
|
237 int ti;
|
|
238 float tf;
|
|
239 int bytes_off = 0;
|
|
240
|
|
241 // output stride
|
|
242 if (s->output_overlap) {
|
|
243 if (s->best_overlap_offset)
|
|
244 bytes_off = s->best_overlap_offset(s);
|
|
245 s->output_overlap(s, pout, bytes_off);
|
|
246 }
|
|
247 memcpy(pout + s->bytes_overlap,
|
|
248 s->buf_queue + bytes_off + s->bytes_overlap,
|
|
249 s->bytes_standing);
|
|
250 pout += s->bytes_stride;
|
|
251
|
|
252 // input stride
|
|
253 memcpy(s->buf_overlap,
|
|
254 s->buf_queue + bytes_off + s->bytes_stride,
|
|
255 s->bytes_overlap);
|
|
256 tf = s->frames_stride_scaled + s->frames_stride_error;
|
|
257 ti = (int)tf;
|
|
258 s->frames_stride_error = tf - ti;
|
|
259 s->bytes_to_slide = ti * s->bytes_per_frame;
|
|
260
|
|
261 offset_in += fill_queue(af, data, offset_in);
|
|
262 }
|
|
263
|
24900
|
264 // This filter can have a negative delay when scale > 1:
|
|
265 // output corresponding to some length of input can be decided and written
|
|
266 // after receiving only a part of that input.
|
|
267 af->delay = s->bytes_queued - s->bytes_to_slide;
|
|
268
|
24896
|
269 data->audio = af->data->audio;
|
24899
|
270 data->len = pout - (int8_t *)af->data->audio;
|
24896
|
271 return data;
|
|
272 }
|
|
273
|
|
274 // Initialization and runtime control
|
|
275 static int control(struct af_instance_s* af, int cmd, void* arg)
|
|
276 {
|
|
277 af_scaletempo_t* s = af->setup;
|
|
278 switch(cmd){
|
|
279 case AF_CONTROL_REINIT:{
|
|
280 af_data_t* data = (af_data_t*)arg;
|
|
281 float srate = data->rate / 1000;
|
|
282 int nch = data->nch;
|
|
283 int bps;
|
|
284 int use_int = 0;
|
|
285 int frames_stride, frames_overlap;
|
|
286 int i, j;
|
|
287
|
|
288 af_msg(AF_MSG_VERBOSE,
|
|
289 "[scaletempo] %.3f speed * %.3f scale_nominal = %.3f\n",
|
|
290 s->speed, s->scale_nominal, s->scale);
|
|
291
|
|
292 if (s->scale == 1.0) {
|
|
293 if (s->speed_tempo && s->speed_pitch)
|
|
294 return AF_DETACH;
|
|
295 memcpy(af->data, data, sizeof(af_data_t));
|
|
296 return af_test_output(af, data);
|
|
297 }
|
|
298
|
|
299 af->data->rate = data->rate;
|
|
300 af->data->nch = data->nch;
|
|
301 if ( data->format == AF_FORMAT_S16_LE
|
|
302 || data->format == AF_FORMAT_S16_BE ) {
|
|
303 use_int = 1;
|
|
304 af->data->format = AF_FORMAT_S16_NE;
|
|
305 af->data->bps = bps = 2;
|
|
306 } else {
|
|
307 af->data->format = AF_FORMAT_FLOAT_NE;
|
|
308 af->data->bps = bps = 4;
|
|
309 }
|
|
310
|
|
311 frames_stride = srate * s->ms_stride;
|
|
312 s->bytes_stride = frames_stride * bps * nch;
|
|
313 s->bytes_stride_scaled = s->scale * s->bytes_stride;
|
|
314 s->frames_stride_scaled = s->scale * frames_stride;
|
|
315 s->frames_stride_error = 0;
|
|
316 af->mul = (double)s->bytes_stride / s->bytes_stride_scaled;
|
|
317
|
|
318 frames_overlap = frames_stride * s->percent_overlap;
|
|
319 if (frames_overlap <= 0) {
|
|
320 s->bytes_standing = s->bytes_stride;
|
|
321 s->samples_standing = s->bytes_standing / bps;
|
|
322 s->output_overlap = NULL;
|
|
323 } else {
|
|
324 s->samples_overlap = frames_overlap * nch;
|
|
325 s->bytes_overlap = frames_overlap * nch * bps;
|
|
326 s->bytes_standing = s->bytes_stride - s->bytes_overlap;
|
|
327 s->samples_standing = s->bytes_standing / bps;
|
|
328 s->buf_overlap = realloc(s->buf_overlap, s->bytes_overlap);
|
|
329 s->table_blend = realloc(s->table_blend, s->bytes_overlap * 4);
|
|
330 if(!s->buf_overlap || !s->table_blend) {
|
|
331 af_msg(AF_MSG_FATAL, "[scaletempo] Out of memory\n");
|
|
332 return AF_ERROR;
|
|
333 }
|
|
334 bzero(s->buf_overlap, s->bytes_overlap);
|
|
335 if (use_int) {
|
|
336 int32_t* pb = (int32_t*)s->table_blend;
|
|
337 int64_t blend = 0;
|
|
338 for (i=0; i<frames_overlap; i++) {
|
|
339 int32_t v = blend / frames_overlap;
|
|
340 for (j=0; j<nch; j++) {
|
|
341 *pb++ = v;
|
|
342 }
|
|
343 blend += 65536; // 2^16
|
|
344 }
|
24899
|
345 s->output_overlap = output_overlap_s16;
|
24896
|
346 } else {
|
|
347 float* pb = (float*)s->table_blend;
|
|
348 for (i=0; i<frames_overlap; i++) {
|
|
349 float v = i / (float)frames_overlap;
|
|
350 for (j=0; j<nch; j++) {
|
|
351 *pb++ = v;
|
|
352 }
|
|
353 }
|
24899
|
354 s->output_overlap = output_overlap_float;
|
24896
|
355 }
|
|
356 }
|
|
357
|
|
358 s->frames_search = (frames_overlap > 1) ? srate * s->ms_search : 0;
|
|
359 if (s->frames_search <= 0) {
|
|
360 s->best_overlap_offset = NULL;
|
|
361 } else {
|
|
362 if (use_int) {
|
|
363 int64_t t = frames_overlap;
|
|
364 int32_t n = 8589934588LL / (t * t); // 4 * (2^31 - 1) / t^2
|
|
365 int32_t* pw;
|
|
366 s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap * 2);
|
|
367 s->table_window = realloc(s->table_window, s->bytes_overlap * 2 - nch * bps * 2);
|
|
368 if(!s->buf_pre_corr && !s->table_window) {
|
|
369 af_msg(AF_MSG_FATAL, "[scaletempo] Out of memory\n");
|
|
370 return AF_ERROR;
|
|
371 }
|
|
372 pw = (int32_t*)s->table_window;
|
|
373 for (i=1; i<frames_overlap; i++) {
|
|
374 int32_t v = ( i * (t - i) * n ) >> 15;
|
|
375 for (j=0; j<nch; j++) {
|
|
376 *pw++ = v;
|
|
377 }
|
|
378 }
|
|
379 s->shift_corr = av_log2( 2*(s->samples_overlap - nch) - 1 );
|
24899
|
380 s->best_overlap_offset = best_overlap_offset_s16;
|
24896
|
381 } else {
|
|
382 float* pw;
|
|
383 s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap);
|
|
384 s->table_window = realloc(s->table_window, s->bytes_overlap - nch * bps);
|
|
385 if(!s->buf_pre_corr || !s->table_window) {
|
|
386 af_msg(AF_MSG_FATAL, "[scaletempo] Out of memory\n");
|
|
387 return AF_ERROR;
|
|
388 }
|
|
389 pw = (float*)s->table_window;
|
|
390 for (i=1; i<frames_overlap; i++) {
|
|
391 float v = i * (frames_overlap - i);
|
|
392 for (j=0; j<nch; j++) {
|
|
393 *pw++ = v;
|
|
394 }
|
|
395 }
|
24899
|
396 s->best_overlap_offset = best_overlap_offset_float;
|
24896
|
397 }
|
|
398 }
|
|
399
|
|
400 s->bytes_per_frame = bps * nch;
|
|
401 s->num_channels = nch;
|
|
402
|
|
403 s->bytes_queue
|
|
404 = (s->frames_search + frames_stride + frames_overlap) * bps * nch;
|
|
405 s->buf_queue = realloc(s->buf_queue, s->bytes_queue);
|
|
406 if(!s->buf_queue) {
|
|
407 af_msg(AF_MSG_FATAL, "[scaletempo] Out of memory\n");
|
|
408 return AF_ERROR;
|
|
409 }
|
|
410
|
|
411 af_msg (AF_MSG_DEBUG0, "[scaletempo] "
|
|
412 "%.2f stride_in, %i stride_out, %i standing, "
|
|
413 "%i overlap, %i search, %i queue, %s mode\n",
|
|
414 s->frames_stride_scaled,
|
|
415 (int)(s->bytes_stride / nch / bps),
|
|
416 (int)(s->bytes_standing / nch / bps),
|
|
417 (int)(s->bytes_overlap / nch / bps),
|
|
418 s->frames_search,
|
|
419 (int)(s->bytes_queue / nch / bps),
|
|
420 (use_int?"s16":"float"));
|
|
421
|
|
422 return af_test_output(af, (af_data_t*)arg);
|
|
423 }
|
|
424 case AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET:{
|
|
425 if (s->speed_tempo) {
|
|
426 if (s->speed_pitch) {
|
|
427 break;
|
|
428 }
|
|
429 s->speed = *(float*)arg;
|
|
430 s->scale = s->speed * s->scale_nominal;
|
|
431 } else {
|
|
432 if (s->speed_pitch) {
|
|
433 s->speed = 1 / *(float*)arg;
|
|
434 s->scale = s->speed * s->scale_nominal;
|
|
435 break;
|
|
436 }
|
|
437 }
|
|
438 return AF_OK;
|
|
439 }
|
|
440 case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_SET:{
|
|
441 s->scale = *(float*)arg;
|
|
442 s->scale = s->speed * s->scale_nominal;
|
|
443 return AF_OK;
|
|
444 }
|
|
445 case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_GET:
|
|
446 *(float*)arg = s->scale;
|
|
447 return AF_OK;
|
|
448 case AF_CONTROL_COMMAND_LINE:{
|
24898
|
449 strarg_t speed = {};
|
24896
|
450 opt_t subopts[] = {
|
|
451 {"scale", OPT_ARG_FLOAT, &s->scale_nominal, NULL},
|
|
452 {"stride", OPT_ARG_FLOAT, &s->ms_stride, NULL},
|
|
453 {"overlap", OPT_ARG_FLOAT, &s->percent_overlap, NULL},
|
|
454 {"search", OPT_ARG_FLOAT, &s->ms_search, NULL},
|
|
455 {"speed", OPT_ARG_STR, &speed, NULL},
|
|
456 {NULL},
|
|
457 };
|
|
458 if (subopt_parse(arg, subopts) != 0) {
|
|
459 return AF_ERROR;
|
|
460 }
|
|
461 if (s->scale_nominal <= 0) {
|
|
462 af_msg(AF_MSG_ERROR, "[scaletempo] "
|
|
463 MSGTR_ErrorParsingCommandLine ": " MSGTR_AF_ValueOutOfRange
|
|
464 ": scale > 0\n");
|
|
465 return AF_ERROR;
|
|
466 }
|
|
467 if (s->ms_stride <= 0) {
|
|
468 af_msg(AF_MSG_ERROR, "[scaletempo] "
|
|
469 MSGTR_ErrorParsingCommandLine ": " MSGTR_AF_ValueOutOfRange
|
|
470 ": stride > 0\n");
|
|
471 return AF_ERROR;
|
|
472 }
|
|
473 if (s->percent_overlap < 0 || s->percent_overlap > 1) {
|
|
474 af_msg(AF_MSG_ERROR, "[scaletempo] "
|
|
475 MSGTR_ErrorParsingCommandLine ": " MSGTR_AF_ValueOutOfRange
|
|
476 ": 0 <= overlap <= 1\n");
|
|
477 return AF_ERROR;
|
|
478 }
|
|
479 if (s->ms_search < 0) {
|
|
480 af_msg(AF_MSG_ERROR, "[scaletempo] "
|
|
481 MSGTR_ErrorParsingCommandLine ": " MSGTR_AF_ValueOutOfRange
|
|
482 ": search >= 0\n");
|
|
483 return AF_ERROR;
|
|
484 }
|
|
485 if (speed.len > 0) {
|
|
486 if (strcmp(speed.str, "pitch") == 0) {
|
|
487 s->speed_tempo = 0;
|
|
488 s->speed_pitch = 1;
|
|
489 } else if (strcmp(speed.str, "tempo") == 0) {
|
|
490 s->speed_tempo = 1;
|
|
491 s->speed_pitch = 0;
|
|
492 } else if (strcmp(speed.str, "none") == 0) {
|
|
493 s->speed_tempo = 0;
|
|
494 s->speed_pitch = 0;
|
|
495 } else if (strcmp(speed.str, "both") == 0) {
|
|
496 s->speed_tempo = 1;
|
|
497 s->speed_pitch = 1;
|
|
498 } else {
|
|
499 af_msg(AF_MSG_ERROR, "[scaletempo] "
|
|
500 MSGTR_ErrorParsingCommandLine ": " MSGTR_AF_ValueOutOfRange
|
|
501 ": speed=[pitch|tempo|none|both]\n");
|
|
502 return AF_ERROR;
|
|
503 }
|
|
504 }
|
|
505 s->scale = s->speed * s->scale_nominal;
|
|
506 af_msg(AF_MSG_DEBUG0, "[scaletempo] %6.3f scale, %6.2f stride, %6.2f overlap, %6.2f search, speed = %s\n", s->scale_nominal, s->ms_stride, s->percent_overlap, s->ms_search, (s->speed_tempo?(s->speed_pitch?"tempo and speed":"tempo"):(s->speed_pitch?"pitch":"none")));
|
|
507 return AF_OK;
|
|
508 }
|
|
509 }
|
|
510 return AF_UNKNOWN;
|
|
511 }
|
|
512
|
|
513 // Deallocate memory
|
|
514 static void uninit(struct af_instance_s* af)
|
|
515 {
|
|
516 af_scaletempo_t* s = af->setup;
|
|
517 free(af->data->audio);
|
|
518 free(af->data);
|
|
519 free(s->buf_queue);
|
|
520 free(s->buf_overlap);
|
|
521 free(s->buf_pre_corr);
|
|
522 free(s->table_blend);
|
|
523 free(s->table_window);
|
|
524 free(af->setup);
|
|
525 }
|
|
526
|
|
527 // Allocate memory and set function pointers
|
|
528 static int af_open(af_instance_t* af){
|
|
529 af_scaletempo_t* s;
|
|
530
|
|
531 af->control = control;
|
|
532 af->uninit = uninit;
|
|
533 af->play = play;
|
|
534 af->mul = 1;
|
|
535 af->data = calloc(1,sizeof(af_data_t));
|
|
536 af->setup = calloc(1,sizeof(af_scaletempo_t));
|
|
537 if(af->data == NULL || af->setup == NULL)
|
|
538 return AF_ERROR;
|
|
539
|
|
540 s = af->setup;
|
|
541 s->scale = s->speed = s->scale_nominal = 1.0;
|
|
542 s->speed_tempo = 1;
|
|
543 s->speed_pitch = 0;
|
|
544 s->ms_stride = 60;
|
|
545 s->percent_overlap = .20;
|
|
546 s->ms_search = 14;
|
|
547
|
|
548 return AF_OK;
|
|
549 }
|
|
550
|
|
551 // Description of this filter
|
|
552 af_info_t af_info_scaletempo = {
|
|
553 "Scale audio tempo while maintaining pitch",
|
|
554 "scaletempo",
|
|
555 "Robert Juliano",
|
|
556 "",
|
|
557 AF_FLAGS_REENTRANT,
|
|
558 af_open
|
|
559 };
|