3 * Copyright (C) 2008 Rov Juvano <rovjuvano@users.sourceforge.net>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-scaletempo
24 * Scale tempo while maintaining pitch
25 * (WSOLA-like technique with cross correlation)
26 * Inspired by SoundTouch library by Olli Parviainen
28 * Use Sceletempo to apply playback rates without the chipmunk effect.
31 * <title>Example pipelines</title>
34 * filesrc location=media.ext ! decodebin name=d \
35 * d. ! queue ! audioconvert ! audioresample ! scaletempo ! audioconvert ! audioresample ! autoaudiosink \
36 * d. ! queue ! videoconvert ! autovideosink
40 * playbin uri=... audio_sink="scaletempo ! audioconvert ! audioresample ! autoaudiosink"
42 * When an application sends a seek event with rate != 1.0, Scaletempo applies
43 * the rate change by scaling the tempo without scaling the pitch.
45 * Scaletempo works by producing audio in constant sized chunks
46 * (#GstScaletempo:stride) but consuming chunks proportional to the playback
49 * Scaletempo then smooths the output by blending the end of one stride with
50 * the next (#GstScaletempo:overlap).
52 * Scaletempo smooths the overlap further by searching within the input buffer
53 * for the best overlap position. Scaletempo uses a statistical cross
54 * correlation (roughly a dot-product). Scaletempo consumes most of its CPU
55 * cycles here. One can use the #GstScaletempo:search propery to tune how far
62 * Note: frame = audio key unit (i.e. one sample for each channel)
70 #include <gst/base/gstbasetransform.h>
71 #include <gst/audio/audio.h>
72 #include <string.h> /* for memset */
74 #include "gstscaletempo.h"
76 GST_DEBUG_CATEGORY_STATIC (gst_scaletempo_debug);
77 #define GST_CAT_DEFAULT gst_scaletempo_debug
79 /* Filter signals and args */
94 #define SUPPORTED_CAPS \
96 GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (F32)) "; " \
97 GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (S16)) \
100 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
105 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
110 #define DEBUG_INIT(bla) GST_DEBUG_CATEGORY_INIT (gst_scaletempo_debug, "scaletempo", 0, "scaletempo element");
112 #define gst_scaletempo_parent_class parent_class
113 G_DEFINE_TYPE_WITH_CODE (GstScaletempo, gst_scaletempo,
114 GST_TYPE_BASE_TRANSFORM, DEBUG_INIT (0));
116 struct _GstScaletempoPrivate
121 gdouble percent_overlap;
125 guint samples_per_frame; /* AKA number of channels */
126 guint bytes_per_sample;
127 guint bytes_per_frame;
130 gdouble frames_stride_scaled;
131 gdouble frames_stride_error;
133 gdouble bytes_stride_scaled;
134 guint bytes_queue_max;
136 guint bytes_to_slide;
139 guint samples_overlap;
140 guint samples_standing;
142 guint bytes_standing;
143 gpointer buf_overlap;
144 gpointer table_blend;
145 void (*output_overlap) (GstScaletempo * scaletempo, gpointer out_buf,
149 gpointer buf_pre_corr;
150 gpointer table_window;
151 guint (*best_overlap_offset) (GstScaletempo * scaletempo);
153 gint64 segment_start;
154 GstClockTime latency;
156 gboolean reinit_buffers;
158 #define GST_SCALETEMPO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_SCALETEMPO, GstScaletempoPrivate))
162 best_overlap_offset_float (GstScaletempo * scaletempo)
164 GstScaletempoPrivate *p = scaletempo->priv;
165 gfloat *pw, *po, *ppc, *search_start;
166 gfloat best_corr = G_MININT;
170 pw = p->table_window;
172 po += p->samples_per_frame;
173 ppc = p->buf_pre_corr;
174 for (i = p->samples_per_frame; i < p->samples_overlap; i++) {
175 *ppc++ = *pw++ * *po++;
178 search_start = (gfloat *) p->buf_queue + p->samples_per_frame;
179 for (off = 0; off < p->frames_search; off++) {
181 gfloat *ps = search_start;
182 ppc = p->buf_pre_corr;
183 for (i = p->samples_per_frame; i < p->samples_overlap; i++) {
184 corr += *ppc++ * *ps++;
186 if (corr > best_corr) {
190 search_start += p->samples_per_frame;
193 return best_off * p->bytes_per_frame;
196 /* buffer padding for loop optimization: sizeof(gint32) * (loop_size - 1) */
197 #define UNROLL_PADDING (4*3)
199 best_overlap_offset_s16 (GstScaletempo * scaletempo)
201 GstScaletempoPrivate *p = scaletempo->priv;
203 gint16 *po, *search_start;
204 gint64 best_corr = G_MININT64;
209 pw = p->table_window;
211 po += p->samples_per_frame;
212 ppc = p->buf_pre_corr;
213 for (i = p->samples_per_frame; i < p->samples_overlap; i++) {
214 *ppc++ = (*pw++ * *po++) >> 15;
217 search_start = (gint16 *) p->buf_queue + p->samples_per_frame;
218 for (off = 0; off < p->frames_search; off++) {
220 gint16 *ps = search_start;
221 ppc = p->buf_pre_corr;
222 ppc += p->samples_overlap - p->samples_per_frame;
223 ps += p->samples_overlap - p->samples_per_frame;
224 i = -((glong) p->samples_overlap - (glong) p->samples_per_frame);
226 corr += ppc[i + 0] * ps[i + 0];
227 corr += ppc[i + 1] * ps[i + 1];
228 corr += ppc[i + 2] * ps[i + 2];
229 corr += ppc[i + 3] * ps[i + 3];
232 if (corr > best_corr) {
236 search_start += p->samples_per_frame;
239 return best_off * p->bytes_per_frame;
243 output_overlap_float (GstScaletempo * scaletempo,
244 gpointer buf_out, guint bytes_off)
246 GstScaletempoPrivate *p = scaletempo->priv;
247 gfloat *pout = buf_out;
248 gfloat *pb = p->table_blend;
249 gfloat *po = p->buf_overlap;
250 gfloat *pin = (gfloat *) (p->buf_queue + bytes_off);
252 for (i = 0; i < p->samples_overlap; i++) {
253 *pout++ = *po - *pb++ * (*po - *pin++);
259 output_overlap_s16 (GstScaletempo * scaletempo,
260 gpointer buf_out, guint bytes_off)
262 GstScaletempoPrivate *p = scaletempo->priv;
263 gint16 *pout = buf_out;
264 gint32 *pb = p->table_blend;
265 gint16 *po = p->buf_overlap;
266 gint16 *pin = (gint16 *) (p->buf_queue + bytes_off);
268 for (i = 0; i < p->samples_overlap; i++) {
269 *pout++ = *po - ((*pb++ * (*po - *pin++)) >> 16);
275 fill_queue (GstScaletempo * scaletempo, GstBuffer * buf_in, guint offset)
277 GstScaletempoPrivate *p = scaletempo->priv;
278 guint bytes_in = gst_buffer_get_size (buf_in) - offset;
279 guint offset_unchanged = offset;
282 gst_buffer_map (buf_in, &map, GST_MAP_READ);
283 if (p->bytes_to_slide > 0) {
284 if (p->bytes_to_slide < p->bytes_queued) {
285 guint bytes_in_move = p->bytes_queued - p->bytes_to_slide;
286 memmove (p->buf_queue, p->buf_queue + p->bytes_to_slide, bytes_in_move);
287 p->bytes_to_slide = 0;
288 p->bytes_queued = bytes_in_move;
291 p->bytes_to_slide -= p->bytes_queued;
292 bytes_in_skip = MIN (p->bytes_to_slide, bytes_in);
294 p->bytes_to_slide -= bytes_in_skip;
295 offset += bytes_in_skip;
296 bytes_in -= bytes_in_skip;
301 guint bytes_in_copy = MIN (p->bytes_queue_max - p->bytes_queued, bytes_in);
302 memcpy (p->buf_queue + p->bytes_queued, map.data + offset, bytes_in_copy);
303 p->bytes_queued += bytes_in_copy;
304 offset += bytes_in_copy;
306 gst_buffer_unmap (buf_in, &map);
308 return offset - offset_unchanged;
312 reinit_buffers (GstScaletempo * scaletempo)
314 GstScaletempoPrivate *p = scaletempo->priv;
316 guint frames_overlap;
318 GstClockTime latency;
320 guint frames_stride = p->ms_stride * p->sample_rate / 1000.0;
321 p->bytes_stride = frames_stride * p->bytes_per_frame;
324 frames_overlap = frames_stride * p->percent_overlap;
325 if (frames_overlap < 1) { /* if no overlap */
326 p->bytes_overlap = 0;
327 p->bytes_standing = p->bytes_stride;
328 p->samples_standing = p->bytes_standing / p->bytes_per_sample;
329 p->output_overlap = NULL;
331 guint prev_overlap = p->bytes_overlap;
332 p->bytes_overlap = frames_overlap * p->bytes_per_frame;
333 p->samples_overlap = frames_overlap * p->samples_per_frame;
334 p->bytes_standing = p->bytes_stride - p->bytes_overlap;
335 p->samples_standing = p->bytes_standing / p->bytes_per_sample;
336 p->buf_overlap = g_realloc (p->buf_overlap, p->bytes_overlap);
337 p->table_blend = g_realloc (p->table_blend, p->samples_overlap * 4); /* sizeof (gint32|gfloat) */
338 if (p->bytes_overlap > prev_overlap) {
339 memset ((guint8 *) p->buf_overlap + prev_overlap, 0,
340 p->bytes_overlap - prev_overlap);
343 gint32 *pb = p->table_blend;
345 for (i = 0; i < frames_overlap; i++) {
346 gint32 v = blend / frames_overlap;
347 for (j = 0; j < p->samples_per_frame; j++) {
350 blend += 65535; /* 2^16 */
352 p->output_overlap = output_overlap_s16;
354 gfloat *pb = p->table_blend;
355 gfloat t = (gfloat) frames_overlap;
356 for (i = 0; i < frames_overlap; i++) {
358 for (j = 0; j < p->samples_per_frame; j++) {
362 p->output_overlap = output_overlap_float;
368 (frames_overlap <= 1) ? 0 : p->ms_search * p->sample_rate / 1000.0;
369 if (p->frames_search < 1) { /* if no search */
370 p->best_overlap_offset = NULL;
372 guint bytes_pre_corr = (p->samples_overlap - p->samples_per_frame) * 4; /* sizeof (gint32|gfloat) */
374 g_realloc (p->buf_pre_corr, bytes_pre_corr + UNROLL_PADDING);
375 p->table_window = g_realloc (p->table_window, bytes_pre_corr);
377 gint64 t = frames_overlap;
378 gint32 n = 8589934588LL / (t * t); /* 4 * (2^31 - 1) / t^2 */
381 memset ((guint8 *) p->buf_pre_corr + bytes_pre_corr, 0, UNROLL_PADDING);
382 pw = p->table_window;
383 for (i = 1; i < frames_overlap; i++) {
384 gint32 v = (i * (t - i) * n) >> 15;
385 for (j = 0; j < p->samples_per_frame; j++) {
389 p->best_overlap_offset = best_overlap_offset_s16;
391 gfloat *pw = p->table_window;
392 for (i = 1; i < frames_overlap; i++) {
393 gfloat v = i * (frames_overlap - i);
394 for (j = 0; j < p->samples_per_frame; j++) {
398 p->best_overlap_offset = best_overlap_offset_float;
403 (p->frames_search + frames_stride + frames_overlap) * p->bytes_per_frame;
404 if (p->bytes_queued > new_size) {
405 if (p->bytes_to_slide > p->bytes_queued) {
406 p->bytes_to_slide -= p->bytes_queued;
409 guint new_queued = MIN (p->bytes_queued - p->bytes_to_slide, new_size);
410 memmove (p->buf_queue,
411 p->buf_queue + p->bytes_queued - new_queued, new_queued);
412 p->bytes_to_slide = 0;
413 p->bytes_queued = new_queued;
417 p->bytes_queue_max = new_size;
418 p->buf_queue = g_realloc (p->buf_queue, p->bytes_queue_max);
421 gst_util_uint64_scale (p->bytes_queue_max, GST_SECOND,
422 p->bytes_per_frame * p->sample_rate);
423 if (p->latency != latency) {
424 p->latency = latency;
425 gst_element_post_message (GST_ELEMENT (scaletempo),
426 gst_message_new_latency (GST_OBJECT (scaletempo)));
429 p->bytes_stride_scaled = p->bytes_stride * p->scale;
430 p->frames_stride_scaled = p->bytes_stride_scaled / p->bytes_per_frame;
433 ("%.3f scale, %.3f stride_in, %i stride_out, %i standing, %i overlap, %i search, %i queue, %s mode",
434 p->scale, p->frames_stride_scaled,
435 (gint) (p->bytes_stride / p->bytes_per_frame),
436 (gint) (p->bytes_standing / p->bytes_per_frame),
437 (gint) (p->bytes_overlap / p->bytes_per_frame), p->frames_search,
438 (gint) (p->bytes_queue_max / p->bytes_per_frame),
439 (p->use_int ? "s16" : "float"));
441 p->reinit_buffers = FALSE;
445 /* GstBaseTransform vmethod implementations */
447 gst_scaletempo_transform (GstBaseTransform * trans,
448 GstBuffer * inbuf, GstBuffer * outbuf)
450 GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
451 GstScaletempoPrivate *p = scaletempo->priv;
453 guint offset_in, bytes_out;
455 GstClockTime timestamp;
457 gst_buffer_map (outbuf, &omap, GST_MAP_WRITE);
458 pout = (gint8 *) omap.data;
459 offset_in = fill_queue (scaletempo, inbuf, 0);
461 while (p->bytes_queued >= p->bytes_queue_max) {
463 gdouble frames_to_slide;
464 guint frames_to_stride_whole;
467 if (p->output_overlap) {
468 if (p->best_overlap_offset) {
469 bytes_off = p->best_overlap_offset (scaletempo);
471 p->output_overlap (scaletempo, pout, bytes_off);
473 memcpy (pout + p->bytes_overlap,
474 p->buf_queue + bytes_off + p->bytes_overlap, p->bytes_standing);
475 pout += p->bytes_stride;
476 bytes_out += p->bytes_stride;
479 memcpy (p->buf_overlap,
480 p->buf_queue + bytes_off + p->bytes_stride, p->bytes_overlap);
481 frames_to_slide = p->frames_stride_scaled + p->frames_stride_error;
482 frames_to_stride_whole = (gint) frames_to_slide;
483 p->bytes_to_slide = frames_to_stride_whole * p->bytes_per_frame;
484 p->frames_stride_error = frames_to_slide - frames_to_stride_whole;
486 offset_in += fill_queue (scaletempo, inbuf, offset_in);
489 gst_buffer_unmap (outbuf, &omap);
491 timestamp = GST_BUFFER_TIMESTAMP (inbuf) - p->segment_start;
492 if (timestamp < p->latency)
495 timestamp -= p->latency;
496 GST_BUFFER_TIMESTAMP (outbuf) = timestamp / p->scale + p->segment_start;
497 GST_BUFFER_DURATION (outbuf) =
498 gst_util_uint64_scale (bytes_out, GST_SECOND,
499 p->bytes_per_frame * p->sample_rate);
500 gst_buffer_set_size (outbuf, bytes_out);
506 gst_scaletempo_transform_size (GstBaseTransform * trans,
507 GstPadDirection direction,
508 GstCaps * caps, gsize size, GstCaps * othercaps, gsize * othersize)
510 if (direction == GST_PAD_SINK) {
511 GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
512 GstScaletempoPrivate *priv = scaletempo->priv;
515 if (priv->reinit_buffers)
516 reinit_buffers (scaletempo);
518 bytes_to_out = size + priv->bytes_queued - priv->bytes_to_slide;
519 if (bytes_to_out < (gint) priv->bytes_queue_max) {
522 /* while (total_buffered - stride_length * n >= queue_max) n++ */
523 *othersize = priv->bytes_stride * ((guint) (
524 (bytes_to_out - priv->bytes_queue_max +
525 /* rounding protection */ priv->bytes_per_frame)
526 / priv->bytes_stride_scaled) + 1);
535 gst_scaletempo_sink_event (GstBaseTransform * trans, GstEvent * event)
537 if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
538 GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
539 GstScaletempoPrivate *priv = scaletempo->priv;
542 gst_event_copy_segment (event, &segment);
544 if (priv->scale != segment.rate) {
545 if (ABS (segment.rate - 1.0) < 1e-10) {
547 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (scaletempo),
550 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (scaletempo),
552 priv->scale = segment.rate;
553 priv->bytes_stride_scaled = priv->bytes_stride * priv->scale;
554 priv->frames_stride_scaled =
555 priv->bytes_stride_scaled / priv->bytes_per_frame;
556 GST_DEBUG ("%.3f scale, %.3f stride_in, %i stride_out", priv->scale,
557 priv->frames_stride_scaled,
558 (gint) (priv->bytes_stride / priv->bytes_per_frame));
560 priv->bytes_to_slide = 0;
564 if (priv->scale != 1.0) {
565 priv->segment_start = segment.start;
566 segment.applied_rate = priv->scale;
568 //gst_event_unref (event);
570 if (segment.stop != -1) {
571 segment.stop = (segment.stop - segment.start) / segment.applied_rate +
575 event = gst_event_new_segment (&segment);
576 gst_pad_push_event (GST_BASE_TRANSFORM_SRC_PAD (trans), event);
580 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
584 gst_scaletempo_set_caps (GstBaseTransform * trans,
585 GstCaps * incaps, GstCaps * outcaps)
587 GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
588 GstScaletempoPrivate *priv = scaletempo->priv;
590 gint width, bps, nch, rate;
594 if (!gst_audio_info_from_caps (&info, incaps))
597 nch = GST_AUDIO_INFO_CHANNELS (&info);
598 rate = GST_AUDIO_INFO_RATE (&info);
599 width = GST_AUDIO_INFO_WIDTH (&info);
600 use_int = GST_AUDIO_INFO_IS_INTEGER (&info);
604 GST_DEBUG ("caps: %" GST_PTR_FORMAT ", %d bps", incaps, bps);
606 if (rate != priv->sample_rate
607 || nch != priv->samples_per_frame
608 || bps != priv->bytes_per_sample || use_int != priv->use_int) {
609 priv->sample_rate = rate;
610 priv->samples_per_frame = nch;
611 priv->bytes_per_sample = bps;
612 priv->bytes_per_frame = nch * bps;
613 priv->use_int = use_int;
614 priv->reinit_buffers = TRUE;
621 gst_scaletempo_query (GstBaseTransform * trans, GstPadDirection direction,
624 GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
625 GstScaletempoPrivate *p = scaletempo->priv;
627 if (direction == GST_PAD_SRC) {
628 switch (GST_QUERY_TYPE (query)) {
629 case GST_QUERY_LATENCY:{
633 if ((peer = gst_pad_get_peer (GST_BASE_TRANSFORM_SINK_PAD (trans)))) {
634 if ((res = gst_pad_query (peer, query))) {
635 GstClockTime min, max;
638 gst_query_parse_latency (query, &live, &min, &max);
640 GST_DEBUG_OBJECT (scaletempo, "Peer latency: min %"
641 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
642 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
644 /* add our own latency */
645 GST_DEBUG_OBJECT (scaletempo, "Our latency: %" GST_TIME_FORMAT,
646 GST_TIME_ARGS (p->latency));
648 if (max != GST_CLOCK_TIME_NONE)
651 GST_DEBUG_OBJECT (scaletempo, "Calculated total latency : min %"
652 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
653 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
654 gst_query_set_latency (query, live, min, max);
656 gst_object_unref (peer);
663 return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
669 return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
674 /* GObject vmethod implementations */
676 gst_scaletempo_get_property (GObject * object,
677 guint prop_id, GValue * value, GParamSpec * pspec)
679 GstScaletempo *scaletempo = GST_SCALETEMPO (object);
680 GstScaletempoPrivate *priv = scaletempo->priv;
684 g_value_set_double (value, priv->scale);
687 g_value_set_uint (value, priv->ms_stride);
690 g_value_set_double (value, priv->percent_overlap);
693 g_value_set_uint (value, priv->ms_search);
696 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
702 gst_scaletempo_set_property (GObject * object,
703 guint prop_id, const GValue * value, GParamSpec * pspec)
705 GstScaletempo *scaletempo = GST_SCALETEMPO (object);
706 GstScaletempoPrivate *priv = scaletempo->priv;
710 guint new_value = g_value_get_uint (value);
711 if (priv->ms_stride != new_value) {
712 priv->ms_stride = new_value;
713 priv->reinit_buffers = TRUE;
718 gdouble new_value = g_value_get_double (value);
719 if (priv->percent_overlap != new_value) {
720 priv->percent_overlap = new_value;
721 priv->reinit_buffers = TRUE;
726 guint new_value = g_value_get_uint (value);
727 if (priv->ms_search != new_value) {
728 priv->ms_search = new_value;
729 priv->reinit_buffers = TRUE;
734 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
740 gst_scaletempo_class_init (GstScaletempoClass * klass)
742 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
743 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
744 GstBaseTransformClass *basetransform_class = GST_BASE_TRANSFORM_CLASS (klass);
746 g_type_class_add_private (klass, sizeof (GstScaletempoPrivate));
748 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_scaletempo_get_property);
749 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_scaletempo_set_property);
751 g_object_class_install_property (gobject_class, PROP_RATE,
752 g_param_spec_double ("rate", "Playback Rate", "Current playback rate",
753 G_MININT, G_MAXINT, 1.0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
755 g_object_class_install_property (gobject_class, PROP_STRIDE,
756 g_param_spec_uint ("stride", "Stride Length",
757 "Length in milliseconds to output each stride", 1, 5000, 30,
758 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
760 g_object_class_install_property (gobject_class, PROP_OVERLAP,
761 g_param_spec_double ("overlap", "Overlap Length",
762 "Percentage of stride to overlap", 0, 1, .2,
763 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
765 g_object_class_install_property (gobject_class, PROP_SEARCH,
766 g_param_spec_uint ("search", "Search Length",
767 "Length in milliseconds to search for best overlap position", 0, 500,
768 14, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
770 gst_element_class_add_pad_template (gstelement_class,
771 gst_static_pad_template_get (&src_template));
772 gst_element_class_add_pad_template (gstelement_class,
773 gst_static_pad_template_get (&sink_template));
774 gst_element_class_set_static_metadata (gstelement_class, "Scaletempo",
775 "Filter/Effect/Rate",
776 "Sync audio tempo with playback rate",
777 "Rov Juvano <rovjuvano@users.sourceforge.net>");
779 basetransform_class->sink_event =
780 GST_DEBUG_FUNCPTR (gst_scaletempo_sink_event);
781 basetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_scaletempo_set_caps);
782 basetransform_class->transform_size =
783 GST_DEBUG_FUNCPTR (gst_scaletempo_transform_size);
784 basetransform_class->transform = GST_DEBUG_FUNCPTR (gst_scaletempo_transform);
785 basetransform_class->query = GST_DEBUG_FUNCPTR (gst_scaletempo_query);
789 gst_scaletempo_init (GstScaletempo * scaletempo)
791 GstScaletempoPrivate *priv;
793 scaletempo->priv = priv = GST_SCALETEMPO_GET_PRIVATE (scaletempo);
796 priv->ms_stride = 30;
797 priv->percent_overlap = .2;
798 priv->ms_search = 14;
802 priv->sample_rate = 0;
803 priv->frames_stride_error = 0;
804 priv->bytes_stride = 0;
805 priv->bytes_queued = 0;
806 priv->bytes_to_slide = 0;
807 priv->segment_start = 0;