Release 1.1.4
[platform/upstream/gst-plugins-good.git] / gst / audiofx / gstscaletempo.c
1 /*
2  * GStreamer
3  * Copyright (C) 2008 Rov Juvano <rovjuvano@users.sourceforge.net>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 /**
22  * SECTION:element-scaletempo
23  *
24  * Scale tempo while maintaining pitch
25  * (WSOLA-like technique with cross correlation)
26  * Inspired by SoundTouch library by Olli Parviainen
27  *
28  * Use Sceletempo to apply playback rates without the chipmunk effect.
29  *
30  * <refsect2>
31  * <title>Example pipelines</title>
32  * <para>
33  * |[
34  * filesrc location=media.ext ! decodebin name=d \
35  *     d. ! queue ! audioconvert ! audioresample ! scaletempo ! audioconvert ! audioresample ! autoaudiosink \
36  *     d. ! queue ! videoconvert ! autovideosink
37  * ]|
38  * OR
39  * |[
40  * playbin uri=... audio_sink="scaletempo ! audioconvert ! audioresample ! autoaudiosink"
41  * ]|
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.
44  *
45  * Scaletempo works by producing audio in constant sized chunks
46  * (#GstScaletempo:stride) but consuming chunks proportional to the playback
47  * rate.
48  *
49  * Scaletempo then smooths the output by blending the end of one stride with
50  * the next (#GstScaletempo:overlap).
51  *
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
56  * the algoritm looks.
57  * </para>
58  * </refsect2>
59  */
60
61 /*
62  * Note: frame = audio key unit (i.e. one sample for each channel)
63  */
64
65 #ifdef HAVE_CONFIG_H
66 #include "config.h"
67 #endif
68
69 #include <gst/gst.h>
70 #include <gst/base/gstbasetransform.h>
71 #include <gst/audio/audio.h>
72 #include <string.h>             /* for memset */
73
74 #include "gstscaletempo.h"
75
76 GST_DEBUG_CATEGORY_STATIC (gst_scaletempo_debug);
77 #define GST_CAT_DEFAULT gst_scaletempo_debug
78
79 /* Filter signals and args */
80 enum
81 {
82   LAST_SIGNAL
83 };
84
85 enum
86 {
87   PROP_0,
88   PROP_RATE,
89   PROP_STRIDE,
90   PROP_OVERLAP,
91   PROP_SEARCH,
92 };
93
94 #define SUPPORTED_CAPS \
95 GST_STATIC_CAPS ( \
96     GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (F32)) "; " \
97     GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (S16)) \
98 )
99
100 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
101     GST_PAD_SINK,
102     GST_PAD_ALWAYS,
103     SUPPORTED_CAPS);
104
105 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
106     GST_PAD_SRC,
107     GST_PAD_ALWAYS,
108     SUPPORTED_CAPS);
109
110 #define DEBUG_INIT(bla) GST_DEBUG_CATEGORY_INIT (gst_scaletempo_debug, "scaletempo", 0, "scaletempo element");
111
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));
115
116 static guint
117 best_overlap_offset_float (GstScaletempo * st)
118 {
119   gfloat *pw, *po, *ppc, *search_start;
120   gfloat best_corr = G_MININT;
121   guint best_off = 0;
122   gint i, off;
123
124   pw = st->table_window;
125   po = st->buf_overlap;
126   po += st->samples_per_frame;
127   ppc = st->buf_pre_corr;
128   for (i = st->samples_per_frame; i < st->samples_overlap; i++) {
129     *ppc++ = *pw++ * *po++;
130   }
131
132   search_start = (gfloat *) st->buf_queue + st->samples_per_frame;
133   for (off = 0; off < st->frames_search; off++) {
134     gfloat corr = 0;
135     gfloat *ps = search_start;
136     ppc = st->buf_pre_corr;
137     for (i = st->samples_per_frame; i < st->samples_overlap; i++) {
138       corr += *ppc++ * *ps++;
139     }
140     if (corr > best_corr) {
141       best_corr = corr;
142       best_off = off;
143     }
144     search_start += st->samples_per_frame;
145   }
146
147   return best_off * st->bytes_per_frame;
148 }
149
150 /* buffer padding for loop optimization: sizeof(gint32) * (loop_size - 1) */
151 #define UNROLL_PADDING (4*3)
152 static guint
153 best_overlap_offset_s16 (GstScaletempo * st)
154 {
155   gint32 *pw, *ppc;
156   gint16 *po, *search_start;
157   gint64 best_corr = G_MININT64;
158   guint best_off = 0;
159   guint off;
160   glong i;
161
162   pw = st->table_window;
163   po = st->buf_overlap;
164   po += st->samples_per_frame;
165   ppc = st->buf_pre_corr;
166   for (i = st->samples_per_frame; i < st->samples_overlap; i++) {
167     *ppc++ = (*pw++ * *po++) >> 15;
168   }
169
170   search_start = (gint16 *) st->buf_queue + st->samples_per_frame;
171   for (off = 0; off < st->frames_search; off++) {
172     gint64 corr = 0;
173     gint16 *ps = search_start;
174     ppc = st->buf_pre_corr;
175     ppc += st->samples_overlap - st->samples_per_frame;
176     ps += st->samples_overlap - st->samples_per_frame;
177     i = -((glong) st->samples_overlap - (glong) st->samples_per_frame);
178     do {
179       corr += ppc[i + 0] * ps[i + 0];
180       corr += ppc[i + 1] * ps[i + 1];
181       corr += ppc[i + 2] * ps[i + 2];
182       corr += ppc[i + 3] * ps[i + 3];
183       i += 4;
184     } while (i < 0);
185     if (corr > best_corr) {
186       best_corr = corr;
187       best_off = off;
188     }
189     search_start += st->samples_per_frame;
190   }
191
192   return best_off * st->bytes_per_frame;
193 }
194
195 static void
196 output_overlap_float (GstScaletempo * st, gpointer buf_out, guint bytes_off)
197 {
198   gfloat *pout = buf_out;
199   gfloat *pb = st->table_blend;
200   gfloat *po = st->buf_overlap;
201   gfloat *pin = (gfloat *) (st->buf_queue + bytes_off);
202   gint i;
203   for (i = 0; i < st->samples_overlap; i++) {
204     *pout++ = *po - *pb++ * (*po - *pin++);
205     po++;
206   }
207 }
208
209 static void
210 output_overlap_s16 (GstScaletempo * st, gpointer buf_out, guint bytes_off)
211 {
212   gint16 *pout = buf_out;
213   gint32 *pb = st->table_blend;
214   gint16 *po = st->buf_overlap;
215   gint16 *pin = (gint16 *) (st->buf_queue + bytes_off);
216   gint i;
217   for (i = 0; i < st->samples_overlap; i++) {
218     *pout++ = *po - ((*pb++ * (*po - *pin++)) >> 16);
219     po++;
220   }
221 }
222
223 static guint
224 fill_queue (GstScaletempo * st, GstBuffer * buf_in, guint offset)
225 {
226   guint bytes_in = gst_buffer_get_size (buf_in) - offset;
227   guint offset_unchanged = offset;
228   GstMapInfo map;
229
230   gst_buffer_map (buf_in, &map, GST_MAP_READ);
231   if (st->bytes_to_slide > 0) {
232     if (st->bytes_to_slide < st->bytes_queued) {
233       guint bytes_in_move = st->bytes_queued - st->bytes_to_slide;
234       memmove (st->buf_queue, st->buf_queue + st->bytes_to_slide,
235           bytes_in_move);
236       st->bytes_to_slide = 0;
237       st->bytes_queued = bytes_in_move;
238     } else {
239       guint bytes_in_skip;
240       st->bytes_to_slide -= st->bytes_queued;
241       bytes_in_skip = MIN (st->bytes_to_slide, bytes_in);
242       st->bytes_queued = 0;
243       st->bytes_to_slide -= bytes_in_skip;
244       offset += bytes_in_skip;
245       bytes_in -= bytes_in_skip;
246     }
247   }
248
249   if (bytes_in > 0) {
250     guint bytes_in_copy =
251         MIN (st->bytes_queue_max - st->bytes_queued, bytes_in);
252     memcpy (st->buf_queue + st->bytes_queued, map.data + offset, bytes_in_copy);
253     st->bytes_queued += bytes_in_copy;
254     offset += bytes_in_copy;
255   }
256   gst_buffer_unmap (buf_in, &map);
257
258   return offset - offset_unchanged;
259 }
260
261 static void
262 reinit_buffers (GstScaletempo * st)
263 {
264   gint i, j;
265   guint frames_overlap;
266   guint new_size;
267   GstClockTime latency;
268
269   guint frames_stride = st->ms_stride * st->sample_rate / 1000.0;
270   st->bytes_stride = frames_stride * st->bytes_per_frame;
271
272   /* overlap */
273   frames_overlap = frames_stride * st->percent_overlap;
274   if (frames_overlap < 1) {     /* if no overlap */
275     st->bytes_overlap = 0;
276     st->bytes_standing = st->bytes_stride;
277     st->samples_standing = st->bytes_standing / st->bytes_per_sample;
278     st->output_overlap = NULL;
279   } else {
280     guint prev_overlap = st->bytes_overlap;
281     st->bytes_overlap = frames_overlap * st->bytes_per_frame;
282     st->samples_overlap = frames_overlap * st->samples_per_frame;
283     st->bytes_standing = st->bytes_stride - st->bytes_overlap;
284     st->samples_standing = st->bytes_standing / st->bytes_per_sample;
285     st->buf_overlap = g_realloc (st->buf_overlap, st->bytes_overlap);
286     st->table_blend = g_realloc (st->table_blend, st->samples_overlap * 4);     /* sizeof (gint32|gfloat) */
287     if (st->bytes_overlap > prev_overlap) {
288       memset ((guint8 *) st->buf_overlap + prev_overlap, 0,
289           st->bytes_overlap - prev_overlap);
290     }
291     if (st->use_int) {
292       gint32 *pb = st->table_blend;
293       gint64 blend = 0;
294       for (i = 0; i < frames_overlap; i++) {
295         gint32 v = blend / frames_overlap;
296         for (j = 0; j < st->samples_per_frame; j++) {
297           *pb++ = v;
298         }
299         blend += 65535;         /* 2^16 */
300       }
301       st->output_overlap = output_overlap_s16;
302     } else {
303       gfloat *pb = st->table_blend;
304       gfloat t = (gfloat) frames_overlap;
305       for (i = 0; i < frames_overlap; i++) {
306         gfloat v = i / t;
307         for (j = 0; j < st->samples_per_frame; j++) {
308           *pb++ = v;
309         }
310       }
311       st->output_overlap = output_overlap_float;
312     }
313   }
314
315   /* best overlap */
316   st->frames_search =
317       (frames_overlap <= 1) ? 0 : st->ms_search * st->sample_rate / 1000.0;
318   if (st->frames_search < 1) {  /* if no search */
319     st->best_overlap_offset = NULL;
320   } else {
321     guint bytes_pre_corr = (st->samples_overlap - st->samples_per_frame) * 4;   /* sizeof (gint32|gfloat) */
322     st->buf_pre_corr =
323         g_realloc (st->buf_pre_corr, bytes_pre_corr + UNROLL_PADDING);
324     st->table_window = g_realloc (st->table_window, bytes_pre_corr);
325     if (st->use_int) {
326       gint64 t = frames_overlap;
327       gint32 n = 8589934588LL / (t * t);        /* 4 * (2^31 - 1) / t^2 */
328       gint32 *pw;
329
330       memset ((guint8 *) st->buf_pre_corr + bytes_pre_corr, 0, UNROLL_PADDING);
331       pw = st->table_window;
332       for (i = 1; i < frames_overlap; i++) {
333         gint32 v = (i * (t - i) * n) >> 15;
334         for (j = 0; j < st->samples_per_frame; j++) {
335           *pw++ = v;
336         }
337       }
338       st->best_overlap_offset = best_overlap_offset_s16;
339     } else {
340       gfloat *pw = st->table_window;
341       for (i = 1; i < frames_overlap; i++) {
342         gfloat v = i * (frames_overlap - i);
343         for (j = 0; j < st->samples_per_frame; j++) {
344           *pw++ = v;
345         }
346       }
347       st->best_overlap_offset = best_overlap_offset_float;
348     }
349   }
350
351   new_size =
352       (st->frames_search + frames_stride +
353       frames_overlap) * st->bytes_per_frame;
354   if (st->bytes_queued > new_size) {
355     if (st->bytes_to_slide > st->bytes_queued) {
356       st->bytes_to_slide -= st->bytes_queued;
357       st->bytes_queued = 0;
358     } else {
359       guint new_queued = MIN (st->bytes_queued - st->bytes_to_slide, new_size);
360       memmove (st->buf_queue,
361           st->buf_queue + st->bytes_queued - new_queued, new_queued);
362       st->bytes_to_slide = 0;
363       st->bytes_queued = new_queued;
364     }
365   }
366
367   st->bytes_queue_max = new_size;
368   st->buf_queue = g_realloc (st->buf_queue, st->bytes_queue_max);
369
370   latency =
371       gst_util_uint64_scale (st->bytes_queue_max, GST_SECOND,
372       st->bytes_per_frame * st->sample_rate);
373   if (st->latency != latency) {
374     st->latency = latency;
375     gst_element_post_message (GST_ELEMENT (st),
376         gst_message_new_latency (GST_OBJECT (st)));
377   }
378
379   st->bytes_stride_scaled = st->bytes_stride * st->scale;
380   st->frames_stride_scaled = st->bytes_stride_scaled / st->bytes_per_frame;
381
382   GST_DEBUG
383       ("%.3f scale, %.3f stride_in, %i stride_out, %i standing, %i overlap, %i search, %i queue, %s mode",
384       st->scale, st->frames_stride_scaled,
385       (gint) (st->bytes_stride / st->bytes_per_frame),
386       (gint) (st->bytes_standing / st->bytes_per_frame),
387       (gint) (st->bytes_overlap / st->bytes_per_frame), st->frames_search,
388       (gint) (st->bytes_queue_max / st->bytes_per_frame),
389       (st->use_int ? "s16" : "float"));
390
391   st->reinit_buffers = FALSE;
392 }
393
394
395 /* GstBaseTransform vmethod implementations */
396 static GstFlowReturn
397 gst_scaletempo_transform (GstBaseTransform * trans,
398     GstBuffer * inbuf, GstBuffer * outbuf)
399 {
400   GstScaletempo *st = GST_SCALETEMPO (trans);
401   gint8 *pout;
402   guint offset_in, bytes_out;
403   GstMapInfo omap;
404   GstClockTime timestamp;
405
406   gst_buffer_map (outbuf, &omap, GST_MAP_WRITE);
407   pout = (gint8 *) omap.data;
408   offset_in = fill_queue (st, inbuf, 0);
409   bytes_out = 0;
410   while (st->bytes_queued >= st->bytes_queue_max) {
411     guint bytes_off = 0;
412     gdouble frames_to_slide;
413     guint frames_to_stride_whole;
414
415     /* output stride */
416     if (st->output_overlap) {
417       if (st->best_overlap_offset) {
418         bytes_off = st->best_overlap_offset (st);
419       }
420       st->output_overlap (st, pout, bytes_off);
421     }
422     memcpy (pout + st->bytes_overlap,
423         st->buf_queue + bytes_off + st->bytes_overlap, st->bytes_standing);
424     pout += st->bytes_stride;
425     bytes_out += st->bytes_stride;
426
427     /* input stride */
428     memcpy (st->buf_overlap,
429         st->buf_queue + bytes_off + st->bytes_stride, st->bytes_overlap);
430     frames_to_slide = st->frames_stride_scaled + st->frames_stride_error;
431     frames_to_stride_whole = (gint) frames_to_slide;
432     st->bytes_to_slide = frames_to_stride_whole * st->bytes_per_frame;
433     st->frames_stride_error = frames_to_slide - frames_to_stride_whole;
434
435     offset_in += fill_queue (st, inbuf, offset_in);
436   }
437
438   gst_buffer_unmap (outbuf, &omap);
439
440   timestamp = GST_BUFFER_TIMESTAMP (inbuf) - st->segment_start;
441   if (timestamp < st->latency)
442     timestamp = 0;
443   else
444     timestamp -= st->latency;
445   GST_BUFFER_TIMESTAMP (outbuf) = timestamp / st->scale + st->segment_start;
446   GST_BUFFER_DURATION (outbuf) =
447       gst_util_uint64_scale (bytes_out, GST_SECOND,
448       st->bytes_per_frame * st->sample_rate);
449   gst_buffer_set_size (outbuf, bytes_out);
450
451   return GST_FLOW_OK;
452 }
453
454 static gboolean
455 gst_scaletempo_transform_size (GstBaseTransform * trans,
456     GstPadDirection direction,
457     GstCaps * caps, gsize size, GstCaps * othercaps, gsize * othersize)
458 {
459   if (direction == GST_PAD_SINK) {
460     GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
461     gint bytes_to_out;
462
463     if (scaletempo->reinit_buffers)
464       reinit_buffers (scaletempo);
465
466     bytes_to_out = size + scaletempo->bytes_queued - scaletempo->bytes_to_slide;
467     if (bytes_to_out < (gint) scaletempo->bytes_queue_max) {
468       *othersize = 0;
469     } else {
470       /* while (total_buffered - stride_length * n >= queue_max) n++ */
471       *othersize = scaletempo->bytes_stride * ((guint) (
472               (bytes_to_out - scaletempo->bytes_queue_max +
473                   /* rounding protection */ scaletempo->bytes_per_frame)
474               / scaletempo->bytes_stride_scaled) + 1);
475     }
476
477     return TRUE;
478   }
479   return FALSE;
480 }
481
482 static gboolean
483 gst_scaletempo_sink_event (GstBaseTransform * trans, GstEvent * event)
484 {
485   if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
486     GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
487     GstSegment segment;
488
489     gst_event_copy_segment (event, &segment);
490
491     if (scaletempo->scale != segment.rate) {
492       if (ABS (segment.rate - 1.0) < 1e-10) {
493         scaletempo->scale = 1.0;
494         gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (scaletempo),
495             TRUE);
496       } else {
497         gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (scaletempo),
498             FALSE);
499         scaletempo->scale = segment.rate;
500         scaletempo->bytes_stride_scaled =
501             scaletempo->bytes_stride * scaletempo->scale;
502         scaletempo->frames_stride_scaled =
503             scaletempo->bytes_stride_scaled / scaletempo->bytes_per_frame;
504         GST_DEBUG ("%.3f scale, %.3f stride_in, %i stride_out",
505             scaletempo->scale, scaletempo->frames_stride_scaled,
506             (gint) (scaletempo->bytes_stride / scaletempo->bytes_per_frame));
507
508         scaletempo->bytes_to_slide = 0;
509       }
510     }
511
512     if (scaletempo->scale != 1.0) {
513       scaletempo->segment_start = segment.start;
514       segment.applied_rate = scaletempo->scale;
515       segment.rate = 1.0;
516       gst_event_unref (event);
517
518       if (segment.stop != -1) {
519         segment.stop = (segment.stop - segment.start) / segment.applied_rate +
520             segment.start;
521       }
522
523       event = gst_event_new_segment (&segment);
524       gst_pad_push_event (GST_BASE_TRANSFORM_SRC_PAD (trans), event);
525       return TRUE;
526     }
527   }
528   return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
529 }
530
531 static gboolean
532 gst_scaletempo_set_caps (GstBaseTransform * trans,
533     GstCaps * incaps, GstCaps * outcaps)
534 {
535   GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
536
537   gint width, bps, nch, rate;
538   gboolean use_int;
539   GstAudioInfo info;
540
541   if (!gst_audio_info_from_caps (&info, incaps))
542     return FALSE;
543
544   nch = GST_AUDIO_INFO_CHANNELS (&info);
545   rate = GST_AUDIO_INFO_RATE (&info);
546   width = GST_AUDIO_INFO_WIDTH (&info);
547   use_int = GST_AUDIO_INFO_IS_INTEGER (&info);
548
549   bps = width / 8;
550
551   GST_DEBUG ("caps: %" GST_PTR_FORMAT ", %d bps", incaps, bps);
552
553   if (rate != scaletempo->sample_rate
554       || nch != scaletempo->samples_per_frame
555       || bps != scaletempo->bytes_per_sample
556       || use_int != scaletempo->use_int) {
557     scaletempo->sample_rate = rate;
558     scaletempo->samples_per_frame = nch;
559     scaletempo->bytes_per_sample = bps;
560     scaletempo->bytes_per_frame = nch * bps;
561     scaletempo->use_int = use_int;
562     scaletempo->reinit_buffers = TRUE;
563   }
564
565   return TRUE;
566 }
567
568 static gboolean
569 gst_scaletempo_query (GstBaseTransform * trans, GstPadDirection direction,
570     GstQuery * query)
571 {
572   GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
573
574   if (direction == GST_PAD_SRC) {
575     switch (GST_QUERY_TYPE (query)) {
576       case GST_QUERY_LATENCY:{
577         GstPad *peer;
578         gboolean res;
579
580         if ((peer = gst_pad_get_peer (GST_BASE_TRANSFORM_SINK_PAD (trans)))) {
581           if ((res = gst_pad_query (peer, query))) {
582             GstClockTime min, max;
583             gboolean live;
584
585             gst_query_parse_latency (query, &live, &min, &max);
586
587             GST_DEBUG_OBJECT (scaletempo, "Peer latency: min %"
588                 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
589                 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
590
591             /* add our own latency */
592             GST_DEBUG_OBJECT (scaletempo, "Our latency: %" GST_TIME_FORMAT,
593                 GST_TIME_ARGS (scaletempo->latency));
594             min += scaletempo->latency;
595             if (max != GST_CLOCK_TIME_NONE)
596               max += scaletempo->latency;
597
598             GST_DEBUG_OBJECT (scaletempo, "Calculated total latency : min %"
599                 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
600                 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
601             gst_query_set_latency (query, live, min, max);
602           }
603           gst_object_unref (peer);
604         }
605
606         return TRUE;
607         break;
608       }
609       default:{
610         return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
611             query);
612         break;
613       }
614     }
615   } else {
616     return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
617         query);
618   }
619 }
620
621 /* GObject vmethod implementations */
622 static void
623 gst_scaletempo_get_property (GObject * object,
624     guint prop_id, GValue * value, GParamSpec * pspec)
625 {
626   GstScaletempo *scaletempo = GST_SCALETEMPO (object);
627
628   switch (prop_id) {
629     case PROP_RATE:
630       g_value_set_double (value, scaletempo->scale);
631       break;
632     case PROP_STRIDE:
633       g_value_set_uint (value, scaletempo->ms_stride);
634       break;
635     case PROP_OVERLAP:
636       g_value_set_double (value, scaletempo->percent_overlap);
637       break;
638     case PROP_SEARCH:
639       g_value_set_uint (value, scaletempo->ms_search);
640       break;
641     default:
642       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
643       break;
644   }
645 }
646
647 static void
648 gst_scaletempo_set_property (GObject * object,
649     guint prop_id, const GValue * value, GParamSpec * pspec)
650 {
651   GstScaletempo *scaletempo = GST_SCALETEMPO (object);
652
653   switch (prop_id) {
654     case PROP_STRIDE:{
655       guint new_value = g_value_get_uint (value);
656       if (scaletempo->ms_stride != new_value) {
657         scaletempo->ms_stride = new_value;
658         scaletempo->reinit_buffers = TRUE;
659       }
660       break;
661     }
662     case PROP_OVERLAP:{
663       gdouble new_value = g_value_get_double (value);
664       if (scaletempo->percent_overlap != new_value) {
665         scaletempo->percent_overlap = new_value;
666         scaletempo->reinit_buffers = TRUE;
667       }
668       break;
669     }
670     case PROP_SEARCH:{
671       guint new_value = g_value_get_uint (value);
672       if (scaletempo->ms_search != new_value) {
673         scaletempo->ms_search = new_value;
674         scaletempo->reinit_buffers = TRUE;
675       }
676       break;
677     }
678     default:
679       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
680       break;
681   }
682 }
683
684 static void
685 gst_scaletempo_class_init (GstScaletempoClass * klass)
686 {
687   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
688   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
689   GstBaseTransformClass *basetransform_class = GST_BASE_TRANSFORM_CLASS (klass);
690
691   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_scaletempo_get_property);
692   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_scaletempo_set_property);
693
694   g_object_class_install_property (gobject_class, PROP_RATE,
695       g_param_spec_double ("rate", "Playback Rate", "Current playback rate",
696           G_MININT, G_MAXINT, 1.0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
697
698   g_object_class_install_property (gobject_class, PROP_STRIDE,
699       g_param_spec_uint ("stride", "Stride Length",
700           "Length in milliseconds to output each stride", 1, 5000, 30,
701           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
702
703   g_object_class_install_property (gobject_class, PROP_OVERLAP,
704       g_param_spec_double ("overlap", "Overlap Length",
705           "Percentage of stride to overlap", 0, 1, .2,
706           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
707
708   g_object_class_install_property (gobject_class, PROP_SEARCH,
709       g_param_spec_uint ("search", "Search Length",
710           "Length in milliseconds to search for best overlap position", 0, 500,
711           14, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
712
713   gst_element_class_add_pad_template (gstelement_class,
714       gst_static_pad_template_get (&src_template));
715   gst_element_class_add_pad_template (gstelement_class,
716       gst_static_pad_template_get (&sink_template));
717   gst_element_class_set_static_metadata (gstelement_class, "Scaletempo",
718       "Filter/Effect/Rate",
719       "Sync audio tempo with playback rate",
720       "Rov Juvano <rovjuvano@users.sourceforge.net>");
721
722   basetransform_class->sink_event =
723       GST_DEBUG_FUNCPTR (gst_scaletempo_sink_event);
724   basetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_scaletempo_set_caps);
725   basetransform_class->transform_size =
726       GST_DEBUG_FUNCPTR (gst_scaletempo_transform_size);
727   basetransform_class->transform = GST_DEBUG_FUNCPTR (gst_scaletempo_transform);
728   basetransform_class->query = GST_DEBUG_FUNCPTR (gst_scaletempo_query);
729 }
730
731 static void
732 gst_scaletempo_init (GstScaletempo * scaletempo)
733 {
734   /* defaults */
735   scaletempo->ms_stride = 30;
736   scaletempo->percent_overlap = .2;
737   scaletempo->ms_search = 14;
738
739   /* uninitialized */
740   scaletempo->scale = 0;
741   scaletempo->sample_rate = 0;
742   scaletempo->frames_stride_error = 0;
743   scaletempo->bytes_stride = 0;
744   scaletempo->bytes_queued = 0;
745   scaletempo->bytes_to_slide = 0;
746   scaletempo->segment_start = 0;
747 }