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
|
|
82 int fill_queue(struct af_instance_s* af, af_data_t* data, int offset) {
|
|
83 af_scaletempo_t* s = af->setup;
|
|
84 int bytes_in = data->len - offset;
|
|
85 int offset_unchanged = offset;
|
|
86
|
|
87 if (s->bytes_to_slide > 0) {
|
|
88 if (s->bytes_to_slide < s->bytes_queued) {
|
|
89 int bytes_move = s->bytes_queued - s->bytes_to_slide;
|
|
90 memmove(s->buf_queue,
|
|
91 s->buf_queue + s->bytes_to_slide,
|
|
92 bytes_move);
|
|
93 s->bytes_to_slide = 0;
|
|
94 s->bytes_queued = bytes_move;
|
|
95 } else {
|
|
96 int bytes_skip;
|
|
97 s->bytes_to_slide -= s->bytes_queued;
|
|
98 bytes_skip = FFMIN(s->bytes_to_slide, bytes_in);
|
|
99 s->bytes_queued = 0;
|
|
100 s->bytes_to_slide -= bytes_skip;
|
|
101 offset += bytes_skip;
|
|
102 bytes_in -= bytes_skip;
|
|
103 }
|
|
104 }
|
|
105
|
|
106 if (bytes_in > 0) {
|
|
107 int bytes_copy = FFMIN(s->bytes_queue - s->bytes_queued, bytes_in);
|
|
108 memcpy(s->buf_queue + s->bytes_queued,
|
|
109 (int8_t*)data->audio + offset,
|
|
110 bytes_copy);
|
|
111 s->bytes_queued += bytes_copy;
|
|
112 offset += bytes_copy;
|
|
113 }
|
|
114
|
|
115 return offset - offset_unchanged;
|
|
116 }
|
|
117
|
|
118 int _best_overlap_offset_float(af_scaletempo_t* s) {
|
|
119 float *pw, *po, *ppc, *search_start;
|
|
120 float best_corr = INT_MIN;
|
|
121 int best_off = 0;
|
|
122 int i, off;
|
|
123
|
|
124 pw = (float*)s->table_window;
|
|
125 po = (float*)s->buf_overlap + s->num_channels;
|
|
126 ppc = (float*)s->buf_pre_corr;
|
|
127 for (i=s->num_channels; i<s->samples_overlap; i++) {
|
|
128 *ppc++ = *pw++ * *po++;
|
|
129 }
|
|
130
|
|
131 search_start = (float*)s->buf_queue + s->num_channels;
|
|
132 for (off=0; off<s->frames_search; off++) {
|
|
133 float corr = 0;
|
|
134 float* ps = search_start;
|
|
135 ppc = (float*)s->buf_pre_corr;
|
|
136 for (i=s->num_channels; i<s->samples_overlap; i++) {
|
|
137 corr += *ppc++ * *ps++;
|
|
138 }
|
|
139 if (corr > best_corr) {
|
|
140 best_corr = corr;
|
|
141 best_off = off;
|
|
142 }
|
|
143 search_start += s->num_channels;
|
|
144 }
|
|
145
|
24897
|
146 return best_off * 4 * s->num_channels;
|
24896
|
147 }
|
|
148
|
|
149 int _best_overlap_offset_s16(af_scaletempo_t* s) {
|
|
150 int32_t *pw, *ppc;
|
|
151 int16_t *po, *search_start;
|
|
152 int32_t best_corr = INT_MIN;
|
|
153 int best_off = 0;
|
|
154 int i, off;
|
|
155
|
|
156 pw = (int32_t*)s->table_window;
|
|
157 po = (int16_t*)s->buf_overlap + s->num_channels;
|
|
158 ppc = (int32_t*)s->buf_pre_corr;
|
|
159 for (i=s->num_channels; i<s->samples_overlap; i++) {
|
|
160 *ppc++ = ( *pw++ * *po++ ) >> 15;
|
|
161 }
|
|
162
|
|
163 search_start = (int16_t*)s->buf_queue + s->num_channels;
|
|
164 for (off=0; off<s->frames_search; off++) {
|
|
165 int32_t corr = 0;
|
|
166 int16_t* ps = search_start;
|
|
167 ppc = (int32_t*)s->buf_pre_corr;
|
|
168 for (i=s->num_channels; i<s->samples_overlap; i++) {
|
|
169 corr += ( *ppc++ * *ps++ ) >> s->shift_corr;
|
|
170 }
|
|
171 if (corr > best_corr) {
|
|
172 best_corr = corr;
|
|
173 best_off = off;
|
|
174 }
|
|
175 search_start += s->num_channels;
|
|
176 }
|
|
177
|
24897
|
178 return best_off * 2 * s->num_channels;
|
24896
|
179 }
|
|
180
|
|
181 void _output_overlap_float(af_scaletempo_t* s, int8_t* buf_out, int bytes_off) {
|
|
182 float* pout = (float*)buf_out;
|
|
183 float* pb = (float*)s->table_blend;
|
|
184 float* po = (float*)s->buf_overlap;
|
|
185 float* pin = (float*)(s->buf_queue + bytes_off);
|
|
186 int i;
|
|
187 for (i=0; i<s->samples_overlap; i++) {
|
|
188 *pout++ = *po - *pb++ * ( *po - *pin++ ); po++;
|
|
189 }
|
|
190 }
|
|
191 void _output_overlap_s16(af_scaletempo_t* s, int8_t* buf_out, int bytes_off) {
|
|
192 int16_t* pout = (int16_t*)buf_out;
|
|
193 int32_t* pb = (int32_t*)s->table_blend;
|
|
194 int16_t* po = (int16_t*)s->buf_overlap;
|
|
195 int16_t* pin = (int16_t*)(s->buf_queue + bytes_off);
|
|
196 int i;
|
|
197 for (i=0; i<s->samples_overlap; i++) {
|
|
198 *pout++ = *po - ( ( *pb++ * ( *po - *pin++ ) ) >> 16 ); po++;
|
|
199 }
|
|
200 }
|
|
201
|
|
202 // Filter data through filter
|
|
203 static af_data_t* play(struct af_instance_s* af, af_data_t* data)
|
|
204 {
|
|
205 af_scaletempo_t* s = af->setup;
|
|
206 int offset_in;
|
|
207 int max_bytes_out;
|
|
208 int8_t* pout;
|
|
209
|
|
210 if (s->scale == 1.0) {
|
|
211 return data;
|
|
212 }
|
|
213
|
|
214 // RESIZE_LOCAL_BUFFER - can't use macro
|
|
215 max_bytes_out = ((int)(data->len / s->bytes_stride_scaled) + 1) * s->bytes_stride;
|
|
216 if (max_bytes_out > af->data->len) {
|
|
217 af_msg(AF_MSG_VERBOSE, "[libaf] Reallocating memory in module %s, "
|
|
218 "old len = %i, new len = %i\n",af->info->name,af->data->len,max_bytes_out);
|
|
219 af->data->audio = realloc(af->data->audio, max_bytes_out);
|
|
220 if (!af->data->audio) {
|
|
221 af_msg(AF_MSG_FATAL, "[libaf] Could not allocate memory\n");
|
|
222 return NULL;
|
|
223 }
|
|
224 af->data->len = max_bytes_out;
|
|
225 }
|
|
226
|
|
227 offset_in = fill_queue(af, data, 0);
|
|
228 pout = af->data->audio;
|
|
229 while (s->bytes_queued >= s->bytes_queue) {
|
|
230 int ti;
|
|
231 float tf;
|
|
232 int bytes_off = 0;
|
|
233
|
|
234 // output stride
|
|
235 if (s->output_overlap) {
|
|
236 if (s->best_overlap_offset)
|
|
237 bytes_off = s->best_overlap_offset(s);
|
|
238 s->output_overlap(s, pout, bytes_off);
|
|
239 }
|
|
240 memcpy(pout + s->bytes_overlap,
|
|
241 s->buf_queue + bytes_off + s->bytes_overlap,
|
|
242 s->bytes_standing);
|
|
243 pout += s->bytes_stride;
|
|
244
|
|
245 // input stride
|
|
246 memcpy(s->buf_overlap,
|
|
247 s->buf_queue + bytes_off + s->bytes_stride,
|
|
248 s->bytes_overlap);
|
|
249 tf = s->frames_stride_scaled + s->frames_stride_error;
|
|
250 ti = (int)tf;
|
|
251 s->frames_stride_error = tf - ti;
|
|
252 s->bytes_to_slide = ti * s->bytes_per_frame;
|
|
253
|
|
254 offset_in += fill_queue(af, data, offset_in);
|
|
255 }
|
|
256
|
|
257 data->audio = af->data->audio;
|
|
258 data->len = (int)pout - (int)af->data->audio;
|
|
259 return data;
|
|
260 }
|
|
261
|
|
262 // Initialization and runtime control
|
|
263 static int control(struct af_instance_s* af, int cmd, void* arg)
|
|
264 {
|
|
265 af_scaletempo_t* s = af->setup;
|
|
266 switch(cmd){
|
|
267 case AF_CONTROL_REINIT:{
|
|
268 af_data_t* data = (af_data_t*)arg;
|
|
269 float srate = data->rate / 1000;
|
|
270 int nch = data->nch;
|
|
271 int bps;
|
|
272 int use_int = 0;
|
|
273 int frames_stride, frames_overlap;
|
|
274 int i, j;
|
|
275
|
|
276 af_msg(AF_MSG_VERBOSE,
|
|
277 "[scaletempo] %.3f speed * %.3f scale_nominal = %.3f\n",
|
|
278 s->speed, s->scale_nominal, s->scale);
|
|
279
|
|
280 if (s->scale == 1.0) {
|
|
281 if (s->speed_tempo && s->speed_pitch)
|
|
282 return AF_DETACH;
|
|
283 memcpy(af->data, data, sizeof(af_data_t));
|
|
284 return af_test_output(af, data);
|
|
285 }
|
|
286
|
|
287 af->data->rate = data->rate;
|
|
288 af->data->nch = data->nch;
|
|
289 if ( data->format == AF_FORMAT_S16_LE
|
|
290 || data->format == AF_FORMAT_S16_BE ) {
|
|
291 use_int = 1;
|
|
292 af->data->format = AF_FORMAT_S16_NE;
|
|
293 af->data->bps = bps = 2;
|
|
294 } else {
|
|
295 af->data->format = AF_FORMAT_FLOAT_NE;
|
|
296 af->data->bps = bps = 4;
|
|
297 }
|
|
298
|
|
299 frames_stride = srate * s->ms_stride;
|
|
300 s->bytes_stride = frames_stride * bps * nch;
|
|
301 s->bytes_stride_scaled = s->scale * s->bytes_stride;
|
|
302 s->frames_stride_scaled = s->scale * frames_stride;
|
|
303 s->frames_stride_error = 0;
|
|
304 af->mul = (double)s->bytes_stride / s->bytes_stride_scaled;
|
|
305
|
|
306 frames_overlap = frames_stride * s->percent_overlap;
|
|
307 if (frames_overlap <= 0) {
|
|
308 s->bytes_standing = s->bytes_stride;
|
|
309 s->samples_standing = s->bytes_standing / bps;
|
|
310 s->output_overlap = NULL;
|
|
311 } else {
|
|
312 s->samples_overlap = frames_overlap * nch;
|
|
313 s->bytes_overlap = frames_overlap * nch * bps;
|
|
314 s->bytes_standing = s->bytes_stride - s->bytes_overlap;
|
|
315 s->samples_standing = s->bytes_standing / bps;
|
|
316 s->buf_overlap = realloc(s->buf_overlap, s->bytes_overlap);
|
|
317 s->table_blend = realloc(s->table_blend, s->bytes_overlap * 4);
|
|
318 if(!s->buf_overlap || !s->table_blend) {
|
|
319 af_msg(AF_MSG_FATAL, "[scaletempo] Out of memory\n");
|
|
320 return AF_ERROR;
|
|
321 }
|
|
322 bzero(s->buf_overlap, s->bytes_overlap);
|
|
323 if (use_int) {
|
|
324 int32_t* pb = (int32_t*)s->table_blend;
|
|
325 int64_t blend = 0;
|
|
326 for (i=0; i<frames_overlap; i++) {
|
|
327 int32_t v = blend / frames_overlap;
|
|
328 for (j=0; j<nch; j++) {
|
|
329 *pb++ = v;
|
|
330 }
|
|
331 blend += 65536; // 2^16
|
|
332 }
|
|
333 s->output_overlap = _output_overlap_s16;
|
|
334 } else {
|
|
335 float* pb = (float*)s->table_blend;
|
|
336 for (i=0; i<frames_overlap; i++) {
|
|
337 float v = i / (float)frames_overlap;
|
|
338 for (j=0; j<nch; j++) {
|
|
339 *pb++ = v;
|
|
340 }
|
|
341 }
|
|
342 s->output_overlap = _output_overlap_float;
|
|
343 }
|
|
344 }
|
|
345
|
|
346 s->frames_search = (frames_overlap > 1) ? srate * s->ms_search : 0;
|
|
347 if (s->frames_search <= 0) {
|
|
348 s->best_overlap_offset = NULL;
|
|
349 } else {
|
|
350 if (use_int) {
|
|
351 int64_t t = frames_overlap;
|
|
352 int32_t n = 8589934588LL / (t * t); // 4 * (2^31 - 1) / t^2
|
|
353 int32_t* pw;
|
|
354 s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap * 2);
|
|
355 s->table_window = realloc(s->table_window, s->bytes_overlap * 2 - nch * bps * 2);
|
|
356 if(!s->buf_pre_corr && !s->table_window) {
|
|
357 af_msg(AF_MSG_FATAL, "[scaletempo] Out of memory\n");
|
|
358 return AF_ERROR;
|
|
359 }
|
|
360 pw = (int32_t*)s->table_window;
|
|
361 for (i=1; i<frames_overlap; i++) {
|
|
362 int32_t v = ( i * (t - i) * n ) >> 15;
|
|
363 for (j=0; j<nch; j++) {
|
|
364 *pw++ = v;
|
|
365 }
|
|
366 }
|
|
367 s->shift_corr = av_log2( 2*(s->samples_overlap - nch) - 1 );
|
|
368 s->best_overlap_offset = _best_overlap_offset_s16;
|
|
369 } else {
|
|
370 float* pw;
|
|
371 s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap);
|
|
372 s->table_window = realloc(s->table_window, s->bytes_overlap - nch * bps);
|
|
373 if(!s->buf_pre_corr || !s->table_window) {
|
|
374 af_msg(AF_MSG_FATAL, "[scaletempo] Out of memory\n");
|
|
375 return AF_ERROR;
|
|
376 }
|
|
377 pw = (float*)s->table_window;
|
|
378 for (i=1; i<frames_overlap; i++) {
|
|
379 float v = i * (frames_overlap - i);
|
|
380 for (j=0; j<nch; j++) {
|
|
381 *pw++ = v;
|
|
382 }
|
|
383 }
|
|
384 s->best_overlap_offset = _best_overlap_offset_float;
|
|
385 }
|
|
386 }
|
|
387
|
|
388 s->bytes_per_frame = bps * nch;
|
|
389 s->num_channels = nch;
|
|
390
|
|
391 s->bytes_queue
|
|
392 = (s->frames_search + frames_stride + frames_overlap) * bps * nch;
|
|
393 s->buf_queue = realloc(s->buf_queue, s->bytes_queue);
|
|
394 if(!s->buf_queue) {
|
|
395 af_msg(AF_MSG_FATAL, "[scaletempo] Out of memory\n");
|
|
396 return AF_ERROR;
|
|
397 }
|
|
398
|
|
399 af_msg (AF_MSG_DEBUG0, "[scaletempo] "
|
|
400 "%.2f stride_in, %i stride_out, %i standing, "
|
|
401 "%i overlap, %i search, %i queue, %s mode\n",
|
|
402 s->frames_stride_scaled,
|
|
403 (int)(s->bytes_stride / nch / bps),
|
|
404 (int)(s->bytes_standing / nch / bps),
|
|
405 (int)(s->bytes_overlap / nch / bps),
|
|
406 s->frames_search,
|
|
407 (int)(s->bytes_queue / nch / bps),
|
|
408 (use_int?"s16":"float"));
|
|
409
|
|
410 return af_test_output(af, (af_data_t*)arg);
|
|
411 }
|
|
412 case AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET:{
|
|
413 if (s->speed_tempo) {
|
|
414 if (s->speed_pitch) {
|
|
415 break;
|
|
416 }
|
|
417 s->speed = *(float*)arg;
|
|
418 s->scale = s->speed * s->scale_nominal;
|
|
419 } else {
|
|
420 if (s->speed_pitch) {
|
|
421 s->speed = 1 / *(float*)arg;
|
|
422 s->scale = s->speed * s->scale_nominal;
|
|
423 break;
|
|
424 }
|
|
425 }
|
|
426 return AF_OK;
|
|
427 }
|
|
428 case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_SET:{
|
|
429 s->scale = *(float*)arg;
|
|
430 s->scale = s->speed * s->scale_nominal;
|
|
431 return AF_OK;
|
|
432 }
|
|
433 case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_GET:
|
|
434 *(float*)arg = s->scale;
|
|
435 return AF_OK;
|
|
436 case AF_CONTROL_COMMAND_LINE:{
|
|
437 strarg_t speed;
|
|
438 opt_t subopts[] = {
|
|
439 {"scale", OPT_ARG_FLOAT, &s->scale_nominal, NULL},
|
|
440 {"stride", OPT_ARG_FLOAT, &s->ms_stride, NULL},
|
|
441 {"overlap", OPT_ARG_FLOAT, &s->percent_overlap, NULL},
|
|
442 {"search", OPT_ARG_FLOAT, &s->ms_search, NULL},
|
|
443 {"speed", OPT_ARG_STR, &speed, NULL},
|
|
444 {NULL},
|
|
445 };
|
|
446 if (subopt_parse(arg, subopts) != 0) {
|
|
447 return AF_ERROR;
|
|
448 }
|
|
449 if (s->scale_nominal <= 0) {
|
|
450 af_msg(AF_MSG_ERROR, "[scaletempo] "
|
|
451 MSGTR_ErrorParsingCommandLine ": " MSGTR_AF_ValueOutOfRange
|
|
452 ": scale > 0\n");
|
|
453 return AF_ERROR;
|
|
454 }
|
|
455 if (s->ms_stride <= 0) {
|
|
456 af_msg(AF_MSG_ERROR, "[scaletempo] "
|
|
457 MSGTR_ErrorParsingCommandLine ": " MSGTR_AF_ValueOutOfRange
|
|
458 ": stride > 0\n");
|
|
459 return AF_ERROR;
|
|
460 }
|
|
461 if (s->percent_overlap < 0 || s->percent_overlap > 1) {
|
|
462 af_msg(AF_MSG_ERROR, "[scaletempo] "
|
|
463 MSGTR_ErrorParsingCommandLine ": " MSGTR_AF_ValueOutOfRange
|
|
464 ": 0 <= overlap <= 1\n");
|
|
465 return AF_ERROR;
|
|
466 }
|
|
467 if (s->ms_search < 0) {
|
|
468 af_msg(AF_MSG_ERROR, "[scaletempo] "
|
|
469 MSGTR_ErrorParsingCommandLine ": " MSGTR_AF_ValueOutOfRange
|
|
470 ": search >= 0\n");
|
|
471 return AF_ERROR;
|
|
472 }
|
|
473 if (speed.len > 0) {
|
|
474 if (strcmp(speed.str, "pitch") == 0) {
|
|
475 s->speed_tempo = 0;
|
|
476 s->speed_pitch = 1;
|
|
477 } else if (strcmp(speed.str, "tempo") == 0) {
|
|
478 s->speed_tempo = 1;
|
|
479 s->speed_pitch = 0;
|
|
480 } else if (strcmp(speed.str, "none") == 0) {
|
|
481 s->speed_tempo = 0;
|
|
482 s->speed_pitch = 0;
|
|
483 } else if (strcmp(speed.str, "both") == 0) {
|
|
484 s->speed_tempo = 1;
|
|
485 s->speed_pitch = 1;
|
|
486 } else {
|
|
487 af_msg(AF_MSG_ERROR, "[scaletempo] "
|
|
488 MSGTR_ErrorParsingCommandLine ": " MSGTR_AF_ValueOutOfRange
|
|
489 ": speed=[pitch|tempo|none|both]\n");
|
|
490 return AF_ERROR;
|
|
491 }
|
|
492 }
|
|
493 s->scale = s->speed * s->scale_nominal;
|
|
494 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")));
|
|
495 return AF_OK;
|
|
496 }
|
|
497 }
|
|
498 return AF_UNKNOWN;
|
|
499 }
|
|
500
|
|
501 // Deallocate memory
|
|
502 static void uninit(struct af_instance_s* af)
|
|
503 {
|
|
504 af_scaletempo_t* s = af->setup;
|
|
505 free(af->data->audio);
|
|
506 free(af->data);
|
|
507 free(s->buf_queue);
|
|
508 free(s->buf_overlap);
|
|
509 free(s->buf_pre_corr);
|
|
510 free(s->table_blend);
|
|
511 free(s->table_window);
|
|
512 free(af->setup);
|
|
513 }
|
|
514
|
|
515 // Allocate memory and set function pointers
|
|
516 static int af_open(af_instance_t* af){
|
|
517 af_scaletempo_t* s;
|
|
518
|
|
519 af->control = control;
|
|
520 af->uninit = uninit;
|
|
521 af->play = play;
|
|
522 af->mul = 1;
|
|
523 af->data = calloc(1,sizeof(af_data_t));
|
|
524 af->setup = calloc(1,sizeof(af_scaletempo_t));
|
|
525 if(af->data == NULL || af->setup == NULL)
|
|
526 return AF_ERROR;
|
|
527
|
|
528 s = af->setup;
|
|
529 s->scale = s->speed = s->scale_nominal = 1.0;
|
|
530 s->speed_tempo = 1;
|
|
531 s->speed_pitch = 0;
|
|
532 s->ms_stride = 60;
|
|
533 s->percent_overlap = .20;
|
|
534 s->ms_search = 14;
|
|
535
|
|
536 return AF_OK;
|
|
537 }
|
|
538
|
|
539 // Description of this filter
|
|
540 af_info_t af_info_scaletempo = {
|
|
541 "Scale audio tempo while maintaining pitch",
|
|
542 "scaletempo",
|
|
543 "Robert Juliano",
|
|
544 "",
|
|
545 AF_FLAGS_REENTRANT,
|
|
546 af_open
|
|
547 };
|