scaletempo: Fix timestamp tracking
[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 struct _GstScaletempoPrivate
117 {
118   gdouble scale;
119   /* parameters */
120   guint ms_stride;
121   gdouble percent_overlap;
122   guint ms_search;
123   /* caps */
124   gboolean use_int;
125   guint samples_per_frame;      /* AKA number of channels */
126   guint bytes_per_sample;
127   guint bytes_per_frame;
128   guint sample_rate;
129   /* stride */
130   gdouble frames_stride_scaled;
131   gdouble frames_stride_error;
132   guint bytes_stride;
133   gdouble bytes_stride_scaled;
134   guint bytes_queue_max;
135   guint bytes_queued;
136   guint bytes_to_slide;
137   gint8 *buf_queue;
138   /* overlap */
139   guint samples_overlap;
140   guint samples_standing;
141   guint bytes_overlap;
142   guint bytes_standing;
143   gpointer buf_overlap;
144   gpointer table_blend;
145   void (*output_overlap) (GstScaletempo * scaletempo, gpointer out_buf,
146       guint bytes_off);
147   /* best overlap */
148   guint frames_search;
149   gpointer buf_pre_corr;
150   gpointer table_window;
151     guint (*best_overlap_offset) (GstScaletempo * scaletempo);
152   /* gstreamer */
153   gint64 segment_start;
154   GstClockTime latency;
155   /* threads */
156   gboolean reinit_buffers;
157 };
158 #define GST_SCALETEMPO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_SCALETEMPO, GstScaletempoPrivate))
159
160
161 static guint
162 best_overlap_offset_float (GstScaletempo * scaletempo)
163 {
164   GstScaletempoPrivate *p = scaletempo->priv;
165   gfloat *pw, *po, *ppc, *search_start;
166   gfloat best_corr = G_MININT;
167   guint best_off = 0;
168   gint i, off;
169
170   pw = p->table_window;
171   po = p->buf_overlap;
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++;
176   }
177
178   search_start = (gfloat *) p->buf_queue + p->samples_per_frame;
179   for (off = 0; off < p->frames_search; off++) {
180     gfloat corr = 0;
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++;
185     }
186     if (corr > best_corr) {
187       best_corr = corr;
188       best_off = off;
189     }
190     search_start += p->samples_per_frame;
191   }
192
193   return best_off * p->bytes_per_frame;
194 }
195
196 /* buffer padding for loop optimization: sizeof(gint32) * (loop_size - 1) */
197 #define UNROLL_PADDING (4*3)
198 static guint
199 best_overlap_offset_s16 (GstScaletempo * scaletempo)
200 {
201   GstScaletempoPrivate *p = scaletempo->priv;
202   gint32 *pw, *ppc;
203   gint16 *po, *search_start;
204   gint64 best_corr = G_MININT64;
205   guint best_off = 0;
206   guint off;
207   glong i;
208
209   pw = p->table_window;
210   po = p->buf_overlap;
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;
215   }
216
217   search_start = (gint16 *) p->buf_queue + p->samples_per_frame;
218   for (off = 0; off < p->frames_search; off++) {
219     gint64 corr = 0;
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);
225     do {
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];
230       i += 4;
231     } while (i < 0);
232     if (corr > best_corr) {
233       best_corr = corr;
234       best_off = off;
235     }
236     search_start += p->samples_per_frame;
237   }
238
239   return best_off * p->bytes_per_frame;
240 }
241
242 static void
243 output_overlap_float (GstScaletempo * scaletempo,
244     gpointer buf_out, guint bytes_off)
245 {
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);
251   gint i;
252   for (i = 0; i < p->samples_overlap; i++) {
253     *pout++ = *po - *pb++ * (*po - *pin++);
254     po++;
255   }
256 }
257
258 static void
259 output_overlap_s16 (GstScaletempo * scaletempo,
260     gpointer buf_out, guint bytes_off)
261 {
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);
267   gint i;
268   for (i = 0; i < p->samples_overlap; i++) {
269     *pout++ = *po - ((*pb++ * (*po - *pin++)) >> 16);
270     po++;
271   }
272 }
273
274 static guint
275 fill_queue (GstScaletempo * scaletempo, GstBuffer * buf_in, guint offset)
276 {
277   GstScaletempoPrivate *p = scaletempo->priv;
278   guint bytes_in = gst_buffer_get_size (buf_in) - offset;
279   guint offset_unchanged = offset;
280   GstMapInfo map;
281
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;
289     } else {
290       guint bytes_in_skip;
291       p->bytes_to_slide -= p->bytes_queued;
292       bytes_in_skip = MIN (p->bytes_to_slide, bytes_in);
293       p->bytes_queued = 0;
294       p->bytes_to_slide -= bytes_in_skip;
295       offset += bytes_in_skip;
296       bytes_in -= bytes_in_skip;
297     }
298   }
299
300   if (bytes_in > 0) {
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;
305   }
306   gst_buffer_unmap (buf_in, &map);
307
308   return offset - offset_unchanged;
309 }
310
311 static void
312 reinit_buffers (GstScaletempo * scaletempo)
313 {
314   GstScaletempoPrivate *p = scaletempo->priv;
315   gint i, j;
316   guint frames_overlap;
317   guint new_size;
318   GstClockTime latency;
319
320   guint frames_stride = p->ms_stride * p->sample_rate / 1000.0;
321   p->bytes_stride = frames_stride * p->bytes_per_frame;
322
323   /* overlap */
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;
330   } else {
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);
341     }
342     if (p->use_int) {
343       gint32 *pb = p->table_blend;
344       gint64 blend = 0;
345       for (i = 0; i < frames_overlap; i++) {
346         gint32 v = blend / frames_overlap;
347         for (j = 0; j < p->samples_per_frame; j++) {
348           *pb++ = v;
349         }
350         blend += 65535;         /* 2^16 */
351       }
352       p->output_overlap = output_overlap_s16;
353     } else {
354       gfloat *pb = p->table_blend;
355       gfloat t = (gfloat) frames_overlap;
356       for (i = 0; i < frames_overlap; i++) {
357         gfloat v = i / t;
358         for (j = 0; j < p->samples_per_frame; j++) {
359           *pb++ = v;
360         }
361       }
362       p->output_overlap = output_overlap_float;
363     }
364   }
365
366   /* best overlap */
367   p->frames_search =
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;
371   } else {
372     guint bytes_pre_corr = (p->samples_overlap - p->samples_per_frame) * 4;     /* sizeof (gint32|gfloat) */
373     p->buf_pre_corr =
374         g_realloc (p->buf_pre_corr, bytes_pre_corr + UNROLL_PADDING);
375     p->table_window = g_realloc (p->table_window, bytes_pre_corr);
376     if (p->use_int) {
377       gint64 t = frames_overlap;
378       gint32 n = 8589934588LL / (t * t);        /* 4 * (2^31 - 1) / t^2 */
379       gint32 *pw;
380
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++) {
386           *pw++ = v;
387         }
388       }
389       p->best_overlap_offset = best_overlap_offset_s16;
390     } else {
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++) {
395           *pw++ = v;
396         }
397       }
398       p->best_overlap_offset = best_overlap_offset_float;
399     }
400   }
401
402   new_size =
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;
407       p->bytes_queued = 0;
408     } else {
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;
414     }
415   }
416
417   p->bytes_queue_max = new_size;
418   p->buf_queue = g_realloc (p->buf_queue, p->bytes_queue_max);
419
420   latency =
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)));
427   }
428
429   p->bytes_stride_scaled = p->bytes_stride * p->scale;
430   p->frames_stride_scaled = p->bytes_stride_scaled / p->bytes_per_frame;
431
432   GST_DEBUG
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"));
440
441   p->reinit_buffers = FALSE;
442 }
443
444
445 /* GstBaseTransform vmethod implementations */
446 static GstFlowReturn
447 gst_scaletempo_transform (GstBaseTransform * trans,
448     GstBuffer * inbuf, GstBuffer * outbuf)
449 {
450   GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
451   GstScaletempoPrivate *p = scaletempo->priv;
452   gint8 *pout;
453   guint offset_in, bytes_out;
454   GstMapInfo omap;
455   GstClockTime timestamp;
456
457   gst_buffer_map (outbuf, &omap, GST_MAP_WRITE);
458   pout = (gint8 *) omap.data;
459   offset_in = fill_queue (scaletempo, inbuf, 0);
460   bytes_out = 0;
461   while (p->bytes_queued >= p->bytes_queue_max) {
462     guint bytes_off = 0;
463     gdouble frames_to_slide;
464     guint frames_to_stride_whole;
465
466     // output stride
467     if (p->output_overlap) {
468       if (p->best_overlap_offset) {
469         bytes_off = p->best_overlap_offset (scaletempo);
470       }
471       p->output_overlap (scaletempo, pout, bytes_off);
472     }
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;
477
478     // input 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;
485
486     offset_in += fill_queue (scaletempo, inbuf, offset_in);
487   }
488
489   gst_buffer_unmap (outbuf, &omap);
490
491   timestamp = GST_BUFFER_TIMESTAMP (inbuf) - p->segment_start;
492   if (timestamp < p->latency)
493     timestamp = 0;
494   else
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);
501
502   return GST_FLOW_OK;
503 }
504
505 static gboolean
506 gst_scaletempo_transform_size (GstBaseTransform * trans,
507     GstPadDirection direction,
508     GstCaps * caps, gsize size, GstCaps * othercaps, gsize * othersize)
509 {
510   if (direction == GST_PAD_SINK) {
511     GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
512     GstScaletempoPrivate *priv = scaletempo->priv;
513     gint bytes_to_out;
514
515     if (priv->reinit_buffers)
516       reinit_buffers (scaletempo);
517
518     bytes_to_out = size + priv->bytes_queued - priv->bytes_to_slide;
519     if (bytes_to_out < (gint) priv->bytes_queue_max) {
520       *othersize = 0;
521     } else {
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);
527     }
528
529     return TRUE;
530   }
531   return FALSE;
532 }
533
534 static gboolean
535 gst_scaletempo_sink_event (GstBaseTransform * trans, GstEvent * event)
536 {
537   if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
538     GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
539     GstScaletempoPrivate *priv = scaletempo->priv;
540     GstSegment segment;
541
542     gst_event_copy_segment (event, &segment);
543
544     if (priv->scale != segment.rate) {
545       if (ABS (segment.rate - 1.0) < 1e-10) {
546         priv->scale = 1.0;
547         gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (scaletempo),
548             TRUE);
549       } else {
550         gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (scaletempo),
551             FALSE);
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));
559
560         priv->bytes_to_slide = 0;
561       }
562     }
563
564     if (priv->scale != 1.0) {
565       priv->segment_start = segment.start;
566       segment.applied_rate = priv->scale;
567       segment.rate = 1.0;
568       //gst_event_unref (event);
569
570       if (segment.stop != -1) {
571         segment.stop = (segment.stop - segment.start) / segment.applied_rate +
572             segment.start;
573       }
574
575       event = gst_event_new_segment (&segment);
576       gst_pad_push_event (GST_BASE_TRANSFORM_SRC_PAD (trans), event);
577       return TRUE;
578     }
579   }
580   return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
581 }
582
583 static gboolean
584 gst_scaletempo_set_caps (GstBaseTransform * trans,
585     GstCaps * incaps, GstCaps * outcaps)
586 {
587   GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
588   GstScaletempoPrivate *priv = scaletempo->priv;
589
590   gint width, bps, nch, rate;
591   gboolean use_int;
592   GstAudioInfo info;
593
594   if (!gst_audio_info_from_caps (&info, incaps))
595     return FALSE;
596
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);
601
602   bps = width / 8;
603
604   GST_DEBUG ("caps: %" GST_PTR_FORMAT ", %d bps", incaps, bps);
605
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;
615   }
616
617   return TRUE;
618 }
619
620 static gboolean
621 gst_scaletempo_query (GstBaseTransform * trans, GstPadDirection direction,
622     GstQuery * query)
623 {
624   GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
625   GstScaletempoPrivate *p = scaletempo->priv;
626
627   if (direction == GST_PAD_SRC) {
628     switch (GST_QUERY_TYPE (query)) {
629       case GST_QUERY_LATENCY:{
630         GstPad *peer;
631         gboolean res;
632
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;
636             gboolean live;
637
638             gst_query_parse_latency (query, &live, &min, &max);
639
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));
643
644             /* add our own latency */
645             GST_DEBUG_OBJECT (scaletempo, "Our latency: %" GST_TIME_FORMAT,
646                 GST_TIME_ARGS (p->latency));
647             min += p->latency;
648             if (max != GST_CLOCK_TIME_NONE)
649               max += p->latency;
650
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);
655           }
656           gst_object_unref (peer);
657         }
658
659         return TRUE;
660         break;
661       }
662       default:{
663         return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
664             query);
665         break;
666       }
667     }
668   } else {
669     return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
670         query);
671   }
672 }
673
674 /* GObject vmethod implementations */
675 static void
676 gst_scaletempo_get_property (GObject * object,
677     guint prop_id, GValue * value, GParamSpec * pspec)
678 {
679   GstScaletempo *scaletempo = GST_SCALETEMPO (object);
680   GstScaletempoPrivate *priv = scaletempo->priv;
681
682   switch (prop_id) {
683     case PROP_RATE:
684       g_value_set_double (value, priv->scale);
685       break;
686     case PROP_STRIDE:
687       g_value_set_uint (value, priv->ms_stride);
688       break;
689     case PROP_OVERLAP:
690       g_value_set_double (value, priv->percent_overlap);
691       break;
692     case PROP_SEARCH:
693       g_value_set_uint (value, priv->ms_search);
694       break;
695     default:
696       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
697       break;
698   }
699 }
700
701 static void
702 gst_scaletempo_set_property (GObject * object,
703     guint prop_id, const GValue * value, GParamSpec * pspec)
704 {
705   GstScaletempo *scaletempo = GST_SCALETEMPO (object);
706   GstScaletempoPrivate *priv = scaletempo->priv;
707
708   switch (prop_id) {
709     case PROP_STRIDE:{
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;
714       }
715       break;
716     }
717     case PROP_OVERLAP:{
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;
722       }
723       break;
724     }
725     case PROP_SEARCH:{
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;
730       }
731       break;
732     }
733     default:
734       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
735       break;
736   }
737 }
738
739 static void
740 gst_scaletempo_class_init (GstScaletempoClass * klass)
741 {
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);
745
746   g_type_class_add_private (klass, sizeof (GstScaletempoPrivate));
747
748   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_scaletempo_get_property);
749   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_scaletempo_set_property);
750
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));
754
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));
759
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));
764
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));
769
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>");
778
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);
786 }
787
788 static void
789 gst_scaletempo_init (GstScaletempo * scaletempo)
790 {
791   GstScaletempoPrivate *priv;
792
793   scaletempo->priv = priv = GST_SCALETEMPO_GET_PRIVATE (scaletempo);
794
795   /* defaults */
796   priv->ms_stride = 30;
797   priv->percent_overlap = .2;
798   priv->ms_search = 14;
799
800   /* uninitialized */
801   priv->scale = 0;
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;
808 }