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
26 * Scale tempo while maintaining pitch
27 * (WSOLA-like technique with cross correlation)
28 * Inspired by SoundTouch library by Olli Parviainen
31 * Use Sceletempo to apply playback rates without the chipmunk effect.
33 * <title>Example pipelines</title>
36 * filesrc location=media.ext ! decodebin name=d \
37 * d. ! queue ! audioconvert ! audioresample ! scaletempo ! audioconvert ! audioresample ! autoaudiosink \
38 * d. ! queue ! ffmpegcolorspace ! autovideosink
42 * playbin uri=... audio_sink="scaletempo ! audioconvert ! audioresample ! autoaudiosink"
44 * When an application sends a seek event with rate != 1.0, Scaletempo applies
45 * the rate change by scaling the tempo without scaling the pitch.
48 * Scaletempo works by producing audio in constant sized chunks (a "stride") but
49 * consuming chunks proportional to the playback rate.
52 * Scaletempo then smooths the output by blending the end of one stride with
53 * the next ("overlap").
56 * Scaletempo smooths the overlap further by searching within the input buffer
57 * for the best overlap position. Scaletempo uses a statistical cross correlation
58 * (roughly a dot-product). Scaletempo consumes most of its CPU cycles here.
64 * Note: frame = audio key unit (i.e. one sample for each channel)
72 #include <gst/base/gstbasetransform.h>
73 #include <string.h> /* for memset */
75 #include "gstscaletempo.h"
77 GST_DEBUG_CATEGORY_STATIC (gst_scaletempo_debug
);
78 #define GST_CAT_DEFAULT gst_scaletempo_debug
80 /* Filter signals and args */
93 #define SUPPORTED_CAPS \
95 "audio/x-raw-float, " \
96 "rate = (int) [ 1, MAX ], " \
97 "channels = (int) [ 1, MAX ], " \
98 "endianness = (int) BYTE_ORDER, " \
100 "audio/x-raw-int, " \
101 "rate = (int) [ 1, MAX ], " \
102 "channels = (int) [ 1, MAX ], " \
103 "endianness = (int) BYTE_ORDER, " \
104 "width = (int) 16, " \
105 "depth = (int) 16, " \
106 "signed = (boolean) true;" \
109 static GstStaticPadTemplate sink_template
=
110 GST_STATIC_PAD_TEMPLATE (
117 static GstStaticPadTemplate src_template
=
118 GST_STATIC_PAD_TEMPLATE (
125 #define DEBUG_INIT(bla) GST_DEBUG_CATEGORY_INIT (gst_scaletempo_debug, "scaletempo", 0, "scaletempo element");
127 GST_BOILERPLATE_FULL (GstScaletempo
, gst_scaletempo
, GstBaseTransform
, GST_TYPE_BASE_TRANSFORM
, DEBUG_INIT
);
129 typedef struct _GstScaletempoPrivate
{
133 gdouble percent_overlap
;
137 guint samples_per_frame
; /* AKA number of channels */
138 guint bytes_per_sample
;
139 guint bytes_per_frame
;
142 gdouble frames_stride_scaled
;
143 gdouble frames_stride_error
;
145 gdouble bytes_stride_scaled
;
146 guint bytes_queue_max
;
148 guint bytes_to_slide
;
151 guint samples_overlap
;
152 guint samples_standing
;
154 guint bytes_standing
;
155 gpointer buf_overlap
;
156 gpointer table_blend
;
157 void (*output_overlap
)(GstScaletempo
*scaletempo
, gpointer out_buf
, guint bytes_off
);
160 gpointer buf_pre_corr
;
161 gpointer table_window
;
162 guint (*best_overlap_offset
)(GstScaletempo
*scaletempo
);
164 gint64 segment_start
;
166 gboolean reinit_buffers
;
167 } GstScaletempoPrivate
;
168 #define GST_SCALETEMPO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_SCALETEMPO, GstScaletempoPrivate))
172 best_overlap_offset_float (GstScaletempo
*scaletempo
)
174 GstScaletempoPrivate
*p
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
175 gfloat
*pw
, *po
, *ppc
, *search_start
;
176 gfloat best_corr
= G_MININT
;
180 pw
= p
->table_window
;
182 po
+= p
->samples_per_frame
;
183 ppc
= p
->buf_pre_corr
;
184 for (i
= p
->samples_per_frame
; i
< p
->samples_overlap
; i
++) {
185 *ppc
++ = *pw
++ * *po
++;
188 search_start
= (gfloat
*)p
->buf_queue
+ p
->samples_per_frame
;
189 for (off
= 0; off
< p
->frames_search
; off
++) {
191 gfloat
*ps
= search_start
;
192 ppc
= p
->buf_pre_corr
;
193 for (i
= p
->samples_per_frame
; i
< p
->samples_overlap
; i
++) {
194 corr
+= *ppc
++ * *ps
++;
196 if (corr
> best_corr
) {
200 search_start
+= p
->samples_per_frame
;
203 return best_off
* p
->bytes_per_frame
;
206 /* buffer padding for loop optimization: sizeof(gint32) * (loop_size - 1) */
207 #define UNROLL_PADDING (4*3)
209 best_overlap_offset_s16 (GstScaletempo
*scaletempo
)
211 GstScaletempoPrivate
*p
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
213 gint16
*po
, *search_start
;
214 gint64 best_corr
= G_MININT64
;
219 pw
= p
->table_window
;
221 po
+= p
->samples_per_frame
;
222 ppc
= p
->buf_pre_corr
;
223 for (i
= p
->samples_per_frame
; i
< p
->samples_overlap
; i
++) {
224 *ppc
++ = ( *pw
++ * *po
++ ) >> 15;
227 search_start
= (gint16
*)p
->buf_queue
+ p
->samples_per_frame
;
228 for (off
= 0; off
< p
->frames_search
; off
++) {
230 gint16
*ps
= search_start
;
231 ppc
= p
->buf_pre_corr
;
232 ppc
+= p
->samples_overlap
- p
->samples_per_frame
;
233 ps
+= p
->samples_overlap
- p
->samples_per_frame
;
234 i
= -(p
->samples_overlap
- p
->samples_per_frame
);
236 corr
+= ppc
[i
+0] * ps
[i
+0];
237 corr
+= ppc
[i
+1] * ps
[i
+1];
238 corr
+= ppc
[i
+2] * ps
[i
+2];
239 corr
+= ppc
[i
+3] * ps
[i
+3];
242 if (corr
> best_corr
) {
246 search_start
+= p
->samples_per_frame
;
249 return best_off
* p
->bytes_per_frame
;
253 output_overlap_float (GstScaletempo
*scaletempo
,
257 GstScaletempoPrivate
*p
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
258 gfloat
*pout
= buf_out
;
259 gfloat
*pb
= p
->table_blend
;
260 gfloat
*po
= p
->buf_overlap
;
261 gfloat
*pin
= (gfloat
*)(p
->buf_queue
+ bytes_off
);
263 for (i
= 0; i
< p
->samples_overlap
; i
++) {
264 *pout
++ = *po
- *pb
++ * ( *po
- *pin
++ ); po
++;
269 output_overlap_s16 (GstScaletempo
*scaletempo
,
273 GstScaletempoPrivate
*p
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
274 gint16
*pout
= buf_out
;
275 gint32
*pb
= p
->table_blend
;
276 gint16
*po
= p
->buf_overlap
;
277 gint16
*pin
= (gint16
*)(p
->buf_queue
+ bytes_off
);
279 for (i
= 0; i
< p
->samples_overlap
; i
++) {
280 *pout
++ = *po
- ( ( *pb
++ * ( *po
- *pin
++ ) ) >> 16 ); po
++;
285 fill_queue (GstScaletempo
*scaletempo
,
289 GstScaletempoPrivate
*p
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
290 guint bytes_in
= GST_BUFFER_SIZE (buf_in
) - offset
;
291 guint offset_unchanged
= offset
;
293 if (p
->bytes_to_slide
> 0) {
294 if (p
->bytes_to_slide
< p
->bytes_queued
) {
295 guint bytes_in_move
= p
->bytes_queued
- p
->bytes_to_slide
;
296 memmove (p
->buf_queue
,
297 p
->buf_queue
+ p
->bytes_to_slide
,
299 p
->bytes_to_slide
= 0;
300 p
->bytes_queued
= bytes_in_move
;
303 p
->bytes_to_slide
-= p
->bytes_queued
;
304 bytes_in_skip
= MIN (p
->bytes_to_slide
, bytes_in
);
306 p
->bytes_to_slide
-= bytes_in_skip
;
307 offset
+= bytes_in_skip
;
308 bytes_in
-= bytes_in_skip
;
313 guint bytes_in_copy
= MIN (p
->bytes_queue_max
- p
->bytes_queued
, bytes_in
);
314 memcpy (p
->buf_queue
+ p
->bytes_queued
,
315 GST_BUFFER_DATA (buf_in
) + offset
,
317 p
->bytes_queued
+= bytes_in_copy
;
318 offset
+= bytes_in_copy
;
321 return offset
- offset_unchanged
;
325 reinit_buffers (GstScaletempo
*scaletempo
)
327 GstScaletempoPrivate
*p
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
330 guint frames_stride
= p
->ms_stride
* p
->sample_rate
/ 1000.0;
331 p
->bytes_stride
= frames_stride
* p
->bytes_per_frame
;
334 guint frames_overlap
= frames_stride
* p
->percent_overlap
;
335 if (frames_overlap
< 1) { /* if no overlap */
336 p
->bytes_overlap
= 0;
337 p
->bytes_standing
= p
->bytes_stride
;
338 p
->samples_standing
= p
->bytes_standing
/ p
->bytes_per_sample
;
339 p
->output_overlap
= NULL
;
341 guint prev_overlap
= p
->bytes_overlap
;
342 p
->bytes_overlap
= frames_overlap
* p
->bytes_per_frame
;
343 p
->samples_overlap
= frames_overlap
* p
->samples_per_frame
;
344 p
->bytes_standing
= p
->bytes_stride
- p
->bytes_overlap
;
345 p
->samples_standing
= p
->bytes_standing
/ p
->bytes_per_sample
;
346 p
->buf_overlap
= g_realloc (p
->buf_overlap
, p
->bytes_overlap
);
347 p
->table_blend
= g_realloc (p
->table_blend
, p
->samples_overlap
* 4); /* sizeof (gint32|gfloat) */
348 if (p
->bytes_overlap
> prev_overlap
) {
349 memset (p
->buf_overlap
+ prev_overlap
, 0, p
->bytes_overlap
- prev_overlap
);
352 gint32
*pb
= p
->table_blend
;
354 for (i
=0; i
<frames_overlap
; i
++) {
355 gint32 v
= blend
/ frames_overlap
;
356 for (j
=0; j
< p
->samples_per_frame
; j
++) {
359 blend
+= 65535; /* 2^16 */
361 p
->output_overlap
= output_overlap_s16
;
363 gfloat
*pb
= p
->table_blend
;
364 gfloat t
= (gfloat
)frames_overlap
;
365 for (i
=0; i
<frames_overlap
; i
++) {
367 for (j
=0; j
< p
->samples_per_frame
; j
++) {
371 p
->output_overlap
= output_overlap_float
;
376 p
->frames_search
= (frames_overlap
<= 1) ? 0 : p
->ms_search
* p
->sample_rate
/ 1000.0;
377 if (p
->frames_search
< 1) { /* if no search */
378 p
->best_overlap_offset
= NULL
;
380 guint bytes_pre_corr
= (p
->samples_overlap
- p
->samples_per_frame
) * 4; /* sizeof (gint32|gfloat) */
381 p
->buf_pre_corr
= g_realloc (p
->buf_pre_corr
, bytes_pre_corr
+ UNROLL_PADDING
);
382 p
->table_window
= g_realloc (p
->table_window
, bytes_pre_corr
);
384 gint64 t
= frames_overlap
;
385 gint32 n
= 8589934588LL / (t
* t
); /* 4 * (2^31 - 1) / t^2 */
386 memset (p
->buf_pre_corr
+ bytes_pre_corr
, 0, UNROLL_PADDING
);
387 gint32
*pw
= p
->table_window
;
388 for (i
=1; i
<frames_overlap
; i
++) {
389 gint32 v
= (i
* (t
- i
) * n
) >> 15;
390 for (j
=0; j
< p
->samples_per_frame
; j
++) {
394 p
->best_overlap_offset
= best_overlap_offset_s16
;
396 gfloat
*pw
= p
->table_window
;
397 for (i
=1; i
<frames_overlap
; i
++) {
398 gfloat v
= i
* (frames_overlap
- i
);
399 for (j
=0; j
< p
->samples_per_frame
; j
++) {
403 p
->best_overlap_offset
= best_overlap_offset_float
;
407 guint new_size
= (p
->frames_search
+ frames_stride
+ frames_overlap
) * p
->bytes_per_frame
;
408 if (p
->bytes_queued
> new_size
) {
409 if (p
->bytes_to_slide
> p
->bytes_queued
) {
410 p
->bytes_to_slide
-= p
->bytes_queued
;
413 guint new_queued
= MIN (p
->bytes_queued
- p
->bytes_to_slide
, new_size
);
414 memmove (p
->buf_queue
,
415 p
->buf_queue
+ p
->bytes_queued
- new_queued
,
417 p
->bytes_to_slide
= 0;
418 p
->bytes_queued
= new_queued
;
421 p
->bytes_queue_max
= new_size
;
422 p
->buf_queue
= g_realloc (p
->buf_queue
, p
->bytes_queue_max
);
424 p
->bytes_stride_scaled
= p
->bytes_stride
* p
->scale
;
425 p
->frames_stride_scaled
= p
->bytes_stride_scaled
/ p
->bytes_per_frame
;
427 GST_DEBUG ("%.3f scale, %.3f stride_in, %i stride_out, %i standing, %i overlap, %i search, %i queue, %s mode",
429 p
->frames_stride_scaled
,
430 (gint
)(p
->bytes_stride
/ p
->bytes_per_frame
),
431 (gint
)(p
->bytes_standing
/ p
->bytes_per_frame
),
432 (gint
)(p
->bytes_overlap
/ p
->bytes_per_frame
),
434 (gint
)(p
->bytes_queue_max
/ p
->bytes_per_frame
),
435 (p
->use_int
?"s16":"float"));
437 p
->reinit_buffers
= FALSE
;
441 /* GstBaseTransform vmethod implementations */
443 gst_scaletempo_transform (GstBaseTransform
*trans
,
447 GstScaletempo
*scaletempo
= GST_SCALETEMPO (trans
);
448 GstScaletempoPrivate
*p
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
450 gint8
*pout
= (gint8
*)GST_BUFFER_DATA (outbuf
);
451 guint offset_in
= fill_queue (scaletempo
, inbuf
, 0);
453 while (p
->bytes_queued
>= p
->bytes_queue_max
) {
457 if (p
->output_overlap
) {
458 if (p
->best_overlap_offset
) {
459 bytes_off
= p
->best_overlap_offset (scaletempo
);
461 p
->output_overlap (scaletempo
, pout
, bytes_off
);
463 memcpy (pout
+ p
->bytes_overlap
,
464 p
->buf_queue
+ bytes_off
+ p
->bytes_overlap
,
466 pout
+= p
->bytes_stride
;
467 bytes_out
+= p
->bytes_stride
;
470 memcpy (p
->buf_overlap
,
471 p
->buf_queue
+ bytes_off
+ p
->bytes_stride
,
473 gdouble frames_to_slide
= p
->frames_stride_scaled
+ p
->frames_stride_error
;
474 guint frames_to_stride_whole
= (gint
)frames_to_slide
;
475 p
->bytes_to_slide
= frames_to_stride_whole
* p
->bytes_per_frame
;
476 p
->frames_stride_error
= frames_to_slide
- frames_to_stride_whole
;
478 offset_in
+= fill_queue (scaletempo
, inbuf
, offset_in
);
481 GST_BUFFER_SIZE (outbuf
) = bytes_out
;
482 GST_BUFFER_TIMESTAMP (outbuf
) = (GST_BUFFER_TIMESTAMP (outbuf
) - p
->segment_start
) / p
->scale
+ p
->segment_start
;
483 //GST_BUFFER_DURATION (outbuf) = bytes_out * GST_SECOND / (p->bytes_per_frame * p->sample_rate);
488 gst_scaletempo_transform_size (GstBaseTransform
*trans
,
489 GstPadDirection direction
,
495 if (direction
== GST_PAD_SINK
) {
496 GstScaletempo
*scaletempo
= GST_SCALETEMPO (trans
);
497 GstScaletempoPrivate
*priv
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
499 if (priv
->reinit_buffers
)
500 reinit_buffers (scaletempo
);
502 gint bytes_to_out
= size
+ priv
->bytes_queued
- priv
->bytes_to_slide
;
503 if (bytes_to_out
< (gint
)priv
->bytes_queue_max
) {
506 /* while (total_buffered - stride_length * n <= queue_max) n++ */
507 *othersize
= priv
->bytes_stride
* ((guint
)(
508 (bytes_to_out
- priv
->bytes_queue_max
+ /* rounding protection */ priv
->bytes_per_frame
)
509 / priv
->bytes_stride_scaled
) + 1);
518 gst_scaletempo_sink_event (GstBaseTransform
*trans
,
521 if (GST_EVENT_TYPE (event
) == GST_EVENT_NEWSEGMENT
) {
522 GstScaletempo
*scaletempo
= GST_SCALETEMPO (trans
);
523 GstScaletempoPrivate
*priv
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
526 gdouble rate
, applied_rate
;
528 gint64 start
, stop
, position
;
530 gst_event_parse_new_segment_full (event
, &update
, &rate
, &applied_rate
,
531 &format
, &start
, &stop
, &position
);
533 if (priv
->scale
!= rate
) {
534 if (ABS (rate
- 1.0) < 1e-10) {
536 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (scaletempo
), TRUE
);
538 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (scaletempo
), FALSE
);
540 priv
->bytes_stride_scaled
= priv
->bytes_stride
* priv
->scale
;
541 priv
->frames_stride_scaled
= priv
->bytes_stride_scaled
/ priv
->bytes_per_frame
;
542 GST_DEBUG ("%.3f scale, %.3f stride_in, %i stride_out",
544 priv
->frames_stride_scaled
,
545 (gint
)(priv
->bytes_stride
/ priv
->bytes_per_frame
));
547 priv
->bytes_to_slide
= 0;
551 if (priv
->scale
!= 1.0) {
552 priv
->segment_start
= start
;
553 applied_rate
= priv
->scale
;
555 gst_event_unref (event
);
556 event
= gst_event_new_new_segment_full (update
, rate
, applied_rate
,
557 format
, start
, stop
, position
);
558 gst_pad_push_event (GST_BASE_TRANSFORM_SRC_PAD (trans
), event
);
562 parent_class
->event(trans
, event
);
567 gst_scaletempo_set_caps (GstBaseTransform
*trans
,
571 GstScaletempo
*scaletempo
= GST_SCALETEMPO (trans
);
572 GstScaletempoPrivate
*priv
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
573 GstStructure
*s
= gst_caps_get_structure (incaps
, 0);
575 gint width
, bps
, nch
, rate
;
577 const gchar
*type
= gst_structure_get_name (s
);
578 if (g_str_equal (type
, "audio/x-raw-int")) {
580 gst_structure_get_int (s
, "depth", &width
);
581 } else if (g_str_equal (type
, "audio/x-raw-float")) {
583 gst_structure_get_int (s
, "width", &width
);
589 gst_structure_get_int (s
, "channels", &nch
);
590 gst_structure_get_int (s
, "rate", &rate
);
592 GST_DEBUG ("caps: %s seek, "
593 "%5" G_GUINT32_FORMAT
" rate, "
594 "%2" G_GUINT32_FORMAT
" nch, "
595 "%2" G_GUINT32_FORMAT
" bps",
596 type
, rate
, nch
, bps
);
598 if ( rate
!= priv
->sample_rate
599 || nch
!= priv
->samples_per_frame
600 || bps
!= priv
->bytes_per_sample
601 || use_int
!= priv
->use_int
) {
602 priv
->sample_rate
= rate
;
603 priv
->samples_per_frame
= nch
;
604 priv
->bytes_per_sample
= bps
;
605 priv
->bytes_per_frame
= nch
* bps
;
606 priv
->use_int
= use_int
;
607 priv
->reinit_buffers
= TRUE
;
614 /* GObject vmethod implementations */
616 gst_scaletempo_get_property (GObject
*object
,
621 GstScaletempo
*scaletempo
= GST_SCALETEMPO (object
);
622 GstScaletempoPrivate
*priv
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
626 g_value_set_double (value
, priv
->scale
);
629 g_value_set_uint (value
, priv
->ms_stride
);
632 g_value_set_double (value
, priv
->percent_overlap
);
635 g_value_set_uint (value
, priv
->ms_search
);
638 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
644 gst_scaletempo_set_property (GObject
*object
,
649 GstScaletempo
*scaletempo
= GST_SCALETEMPO (object
);
650 GstScaletempoPrivate
*priv
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
654 guint new_value
= g_value_get_uint (value
);
655 if (priv
->ms_stride
!= new_value
) {
656 priv
->ms_stride
= new_value
;
657 priv
->reinit_buffers
= TRUE
;
662 gdouble new_value
= g_value_get_double (value
);
663 if (priv
->percent_overlap
!= new_value
) {
664 priv
->percent_overlap
= new_value
;
665 priv
->reinit_buffers
= TRUE
;
670 guint new_value
= g_value_get_uint (value
);
671 if (priv
->ms_search
!= new_value
) {
672 priv
->ms_search
= new_value
;
673 priv
->reinit_buffers
= TRUE
;
678 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
684 gst_scaletempo_base_init (gpointer klass
)
686 static GstElementDetails element_details
= {
688 "Filter/Effect/Rate",
689 "Sync audio tempo with playback rate",
690 "Rov Juvano <rovjuvano@users.sourceforge.net>"
692 GstElementClass
*element_class
= GST_ELEMENT_CLASS (klass
);
694 gst_element_class_add_pad_template (element_class
,
695 gst_static_pad_template_get (&src_template
));
696 gst_element_class_add_pad_template (element_class
,
697 gst_static_pad_template_get (&sink_template
));
698 gst_element_class_set_details (element_class
, &element_details
);
702 gst_scaletempo_class_init (GstScaletempoClass
*klass
)
704 g_type_class_add_private (klass
, sizeof (GstScaletempoPrivate
));
706 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
707 gobject_class
->get_property
= GST_DEBUG_FUNCPTR (gst_scaletempo_get_property
);
708 gobject_class
->set_property
= GST_DEBUG_FUNCPTR (gst_scaletempo_set_property
);
710 g_object_class_install_property (gobject_class
, PROP_RATE
,
711 g_param_spec_double ("rate", "Playback Rate", "Current playback rate",
712 G_MININT
, G_MAXINT
, 1.0, G_PARAM_READABLE
));
714 g_object_class_install_property (gobject_class
, PROP_STRIDE
,
715 g_param_spec_uint ("stride", "Stride Length", "Length in milliseconds to output each stride",
716 1, 10000, 60, G_PARAM_READWRITE
));
718 g_object_class_install_property (gobject_class
, PROP_OVERLAP
,
719 g_param_spec_double ("overlap", "Overlap Length", "Percentage of stride to overlap",
720 0, 1, .2, G_PARAM_READWRITE
));
722 g_object_class_install_property (gobject_class
, PROP_SEARCH
,
723 g_param_spec_uint ("search", "Search Length", "Length in milliseconds to search for best overlap position",
724 0, 10000, 14, G_PARAM_READWRITE
));
726 GstBaseTransformClass
*basetransform_class
= GST_BASE_TRANSFORM_CLASS (klass
);
727 basetransform_class
->event
= GST_DEBUG_FUNCPTR (gst_scaletempo_sink_event
);
728 basetransform_class
->set_caps
= GST_DEBUG_FUNCPTR (gst_scaletempo_set_caps
);
729 basetransform_class
->transform_size
= GST_DEBUG_FUNCPTR (gst_scaletempo_transform_size
);
730 basetransform_class
->transform
= GST_DEBUG_FUNCPTR (gst_scaletempo_transform
);
734 gst_scaletempo_init (GstScaletempo
*scaletempo
,
735 GstScaletempoClass
*klass
)
737 GstScaletempoPrivate
*priv
= GST_SCALETEMPO_GET_PRIVATE (scaletempo
);
739 priv
->ms_stride
= 60;
740 priv
->percent_overlap
= .2;
741 priv
->ms_search
= 14;
745 priv
->sample_rate
= 0;
746 priv
->frames_stride_error
= 0;
747 priv
->bytes_stride
= 0;
748 priv
->bytes_queued
= 0;
749 priv
->bytes_to_slide
= 0;
750 priv
->segment_start
= 0;