Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / ext / soundtouch / gstpitch.cc
1 /* GStreamer pitch controller element
2  * Copyright (C) 2006 Wouter Paesen <wouter@blue-gate.be>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
23
24 /* FIXME: workaround for SoundTouch.h of version 1.3.1 defining those
25  * variables while it shouldn't. */
26 #undef VERSION
27 #undef PACKAGE_VERSION
28 #undef PACKAGE_TARNAME
29 #undef PACKAGE_STRING
30 #undef PACKAGE_NAME
31 #undef PACKAGE_BUGREPORT
32 #undef PACKAGE
33
34 #define FLOAT_SAMPLES 1
35 #include <soundtouch/SoundTouch.h>
36
37 #include <gst/gst.h>
38 #include <gst/controller/gstcontroller.h>
39 #include "gstpitch.hh"
40 #include <math.h>
41
42 GST_DEBUG_CATEGORY_STATIC (pitch_debug);
43 #define GST_CAT_DEFAULT pitch_debug
44
45 #define GST_PITCH_GET_PRIVATE(o) (o->priv)
46 struct _GstPitchPrivate
47 {
48   gfloat stream_time_ratio;
49
50   GstEvent *pending_segment;
51
52     soundtouch::SoundTouch * st;
53 };
54
55 enum
56 {
57   ARG_0,
58   ARG_OUT_RATE,
59   ARG_RATE,
60   ARG_TEMPO,
61   ARG_PITCH
62 };
63
64 #define SUPPORTED_CAPS \
65 GST_STATIC_CAPS( \
66   "audio/x-raw-float, " \
67     "rate = (int) [ 8000, MAX ], " \
68     "channels = (int) [ 1, 2 ], " \
69     "endianness = (int) BYTE_ORDER, " \
70     "width = (int) 32" \
71 )
72
73 static GstStaticPadTemplate gst_pitch_sink_template =
74 GST_STATIC_PAD_TEMPLATE ("sink",
75     GST_PAD_SINK,
76     GST_PAD_ALWAYS,
77     SUPPORTED_CAPS);
78
79 static GstStaticPadTemplate gst_pitch_src_template =
80 GST_STATIC_PAD_TEMPLATE ("src",
81     GST_PAD_SRC,
82     GST_PAD_ALWAYS,
83     SUPPORTED_CAPS);
84
85 static void gst_pitch_dispose (GObject * object);
86 static void gst_pitch_set_property (GObject * object,
87     guint prop_id, const GValue * value, GParamSpec * pspec);
88 static void gst_pitch_get_property (GObject * object,
89     guint prop_id, GValue * value, GParamSpec * pspec);
90
91
92 static gboolean gst_pitch_sink_setcaps (GstPad * pad, GstCaps * caps);
93 static GstFlowReturn gst_pitch_chain (GstPad * pad, GstBuffer * buffer);
94 static GstStateChangeReturn gst_pitch_change_state (GstElement * element,
95     GstStateChange transition);
96 static gboolean gst_pitch_sink_event (GstPad * pad, GstEvent * event);
97 static gboolean gst_pitch_src_event (GstPad * pad, GstEvent * event);
98
99 static gboolean gst_pitch_src_query (GstPad * pad, GstQuery * query);
100 static const GstQueryType *gst_pitch_get_query_types (GstPad * pad);
101
102 GST_BOILERPLATE (GstPitch, gst_pitch, GstElement, GST_TYPE_ELEMENT);
103
104 static void
105 gst_pitch_base_init (gpointer g_class)
106 {
107   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
108
109   gst_element_class_add_pad_template (gstelement_class,
110       gst_static_pad_template_get (&gst_pitch_src_template));
111   gst_element_class_add_pad_template (gstelement_class,
112       gst_static_pad_template_get (&gst_pitch_sink_template));
113
114   gst_element_class_set_details_simple (gstelement_class, "Pitch controller",
115       "Filter/Converter/Audio", "Control the pitch of an audio stream",
116       "Wouter Paesen <wouter@blue-gate.be>");
117 }
118
119 static void
120 gst_pitch_class_init (GstPitchClass * klass)
121 {
122   GObjectClass *gobject_class;
123   GstElementClass *element_class;
124
125   gobject_class = G_OBJECT_CLASS (klass);
126   element_class = GST_ELEMENT_CLASS (klass);
127
128   GST_DEBUG_CATEGORY_INIT (pitch_debug, "pitch", 0,
129       "audio pitch control element");
130
131   gobject_class->set_property = gst_pitch_set_property;
132   gobject_class->get_property = gst_pitch_get_property;
133   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_pitch_dispose);
134   element_class->change_state = GST_DEBUG_FUNCPTR (gst_pitch_change_state);
135
136   g_object_class_install_property (gobject_class, ARG_PITCH,
137       g_param_spec_float ("pitch", "Pitch",
138           "Audio stream pitch", 0.1, 10.0, 1.0,
139           (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)));
140
141   g_object_class_install_property (gobject_class, ARG_TEMPO,
142       g_param_spec_float ("tempo", "Tempo",
143           "Audio stream tempo", 0.1, 10.0, 1.0,
144           (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)));
145
146   g_object_class_install_property (gobject_class, ARG_RATE,
147       g_param_spec_float ("rate", "Rate",
148           "Audio stream rate", 0.1, 10.0, 1.0,
149           (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)));
150
151   g_object_class_install_property (gobject_class, ARG_OUT_RATE,
152       g_param_spec_float ("output-rate", "Output Rate",
153           "Output rate on downstream segment events", 0.1, 10.0, 1.0,
154           (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)));
155
156   g_type_class_add_private (gobject_class, sizeof (GstPitchPrivate));
157 }
158
159 static void
160 gst_pitch_init (GstPitch * pitch, GstPitchClass * pitch_class)
161 {
162   pitch->priv =
163       G_TYPE_INSTANCE_GET_PRIVATE ((pitch), GST_TYPE_PITCH, GstPitchPrivate);
164
165   pitch->sinkpad =
166       gst_pad_new_from_static_template (&gst_pitch_sink_template, "sink");
167   gst_pad_set_chain_function (pitch->sinkpad,
168       GST_DEBUG_FUNCPTR (gst_pitch_chain));
169   gst_pad_set_event_function (pitch->sinkpad,
170       GST_DEBUG_FUNCPTR (gst_pitch_sink_event));
171   gst_pad_set_setcaps_function (pitch->sinkpad,
172       GST_DEBUG_FUNCPTR (gst_pitch_sink_setcaps));
173   gst_pad_set_getcaps_function (pitch->sinkpad,
174       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
175   gst_element_add_pad (GST_ELEMENT (pitch), pitch->sinkpad);
176
177   pitch->srcpad =
178       gst_pad_new_from_static_template (&gst_pitch_src_template, "src");
179   gst_pad_set_event_function (pitch->srcpad,
180       GST_DEBUG_FUNCPTR (gst_pitch_src_event));
181   gst_pad_set_query_type_function (pitch->srcpad,
182       GST_DEBUG_FUNCPTR (gst_pitch_get_query_types));
183   gst_pad_set_query_function (pitch->srcpad,
184       GST_DEBUG_FUNCPTR (gst_pitch_src_query));
185   gst_pad_set_setcaps_function (pitch->srcpad,
186       GST_DEBUG_FUNCPTR (gst_pitch_sink_setcaps));
187   gst_pad_set_getcaps_function (pitch->srcpad,
188       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
189   gst_element_add_pad (GST_ELEMENT (pitch), pitch->srcpad);
190
191   pitch->priv->st = new soundtouch::SoundTouch ();
192
193   pitch->tempo = 1.0;
194   pitch->rate = 1.0;
195   pitch->out_seg_rate = 1.0;
196   pitch->seg_arate = 1.0;
197   pitch->pitch = 1.0;
198   pitch->next_buffer_time = 0;
199   pitch->next_buffer_offset = 0;
200
201   pitch->priv->st->setRate (pitch->rate);
202   pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate);
203   pitch->priv->st->setPitch (pitch->pitch);
204
205   pitch->priv->stream_time_ratio = 1.0;
206   pitch->min_latency = pitch->max_latency = 0;
207 }
208
209
210 static void
211 gst_pitch_dispose (GObject * object)
212 {
213   GstPitch *pitch = GST_PITCH (object);
214
215   if (pitch->priv->st) {
216     delete pitch->priv->st;
217
218     pitch->priv->st = NULL;
219   }
220
221   G_OBJECT_CLASS (parent_class)->dispose (object);
222 }
223
224 static void
225 gst_pitch_update_duration (GstPitch * pitch)
226 {
227   GstMessage *m;
228
229   m = gst_message_new_duration (GST_OBJECT (pitch), GST_FORMAT_TIME, -1);
230   gst_element_post_message (GST_ELEMENT (pitch), m);
231 }
232
233 static void
234 gst_pitch_set_property (GObject * object, guint prop_id,
235     const GValue * value, GParamSpec * pspec)
236 {
237   GstPitch *pitch = GST_PITCH (object);
238
239   GST_OBJECT_LOCK (pitch);
240   switch (prop_id) {
241     case ARG_TEMPO:
242       pitch->tempo = g_value_get_float (value);
243       pitch->priv->stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate;
244       pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate);
245       GST_OBJECT_UNLOCK (pitch);
246       gst_pitch_update_duration (pitch);
247       break;
248     case ARG_RATE:
249       pitch->rate = g_value_get_float (value);
250       pitch->priv->stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate;
251       pitch->priv->st->setRate (pitch->rate);
252       GST_OBJECT_UNLOCK (pitch);
253       gst_pitch_update_duration (pitch);
254       break;
255     case ARG_OUT_RATE:
256       /* Has no effect until the next input segment */
257       pitch->out_seg_rate = g_value_get_float (value);
258       GST_OBJECT_UNLOCK (pitch);
259     case ARG_PITCH:
260       pitch->pitch = g_value_get_float (value);
261       pitch->priv->st->setPitch (pitch->pitch);
262       GST_OBJECT_UNLOCK (pitch);
263       break;
264     default:
265       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
266       GST_OBJECT_UNLOCK (pitch);
267       break;
268   }
269 }
270
271 static void
272 gst_pitch_get_property (GObject * object, guint prop_id,
273     GValue * value, GParamSpec * pspec)
274 {
275   GstPitch *pitch = GST_PITCH (object);
276
277   GST_OBJECT_LOCK (pitch);
278   switch (prop_id) {
279     case ARG_TEMPO:
280       g_value_set_float (value, pitch->tempo);
281       break;
282     case ARG_RATE:
283       g_value_set_float (value, pitch->rate);
284       break;
285     case ARG_OUT_RATE:
286       g_value_set_float (value, pitch->out_seg_rate);
287       break;
288     case ARG_PITCH:
289       g_value_set_float (value, pitch->pitch);
290       break;
291     default:
292       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
293       break;
294   }
295   GST_OBJECT_UNLOCK (pitch);
296 }
297
298 static gboolean
299 gst_pitch_sink_setcaps (GstPad * pad, GstCaps * caps)
300 {
301   GstPitch *pitch;
302   GstPitchPrivate *priv;
303   GstStructure *structure;
304   GstPad *otherpad;
305   gint rate, channels;
306
307   pitch = GST_PITCH (GST_PAD_PARENT (pad));
308   priv = GST_PITCH_GET_PRIVATE (pitch);
309
310   otherpad = (pad == pitch->srcpad) ? pitch->sinkpad : pitch->srcpad;
311
312   if (!gst_pad_set_caps (otherpad, caps))
313     return FALSE;
314
315   structure = gst_caps_get_structure (caps, 0);
316
317   if (!gst_structure_get_int (structure, "rate", &rate) ||
318       !gst_structure_get_int (structure, "channels", &channels)) {
319     return FALSE;
320   }
321
322   GST_OBJECT_LOCK (pitch);
323
324   pitch->samplerate = rate;
325   pitch->channels = channels;
326
327   /* notify the soundtouch instance of this change */
328   priv->st->setSampleRate (rate);
329   priv->st->setChannels (channels);
330
331   /* calculate sample size */
332   pitch->sample_size = (sizeof (gfloat) * channels);
333
334   GST_OBJECT_UNLOCK (pitch);
335
336   return TRUE;
337 }
338
339 /* send a buffer out */
340 static GstFlowReturn
341 gst_pitch_forward_buffer (GstPitch * pitch, GstBuffer * buffer)
342 {
343   gint samples;
344
345   GST_BUFFER_TIMESTAMP (buffer) = pitch->next_buffer_time;
346   pitch->next_buffer_time += GST_BUFFER_DURATION (buffer);
347
348   samples = GST_BUFFER_OFFSET (buffer);
349   GST_BUFFER_OFFSET (buffer) = pitch->next_buffer_offset;
350   pitch->next_buffer_offset += samples;
351   GST_BUFFER_OFFSET_END (buffer) = pitch->next_buffer_offset;
352
353   GST_LOG ("pushing buffer [%" GST_TIME_FORMAT "]-[%" GST_TIME_FORMAT
354       "] (%d samples)", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
355       GST_TIME_ARGS (pitch->next_buffer_time), samples);
356
357   return gst_pad_push (pitch->srcpad, buffer);
358 }
359
360 /* extract a buffer from soundtouch */
361 static GstBuffer *
362 gst_pitch_prepare_buffer (GstPitch * pitch)
363 {
364   GstPitchPrivate *priv;
365   guint samples;
366   GstBuffer *buffer;
367
368   priv = GST_PITCH_GET_PRIVATE (pitch);
369
370   GST_LOG_OBJECT (pitch, "preparing buffer");
371
372   samples = pitch->priv->st->numSamples ();
373   if (samples == 0)
374     return NULL;
375
376   if (gst_pad_alloc_buffer_and_set_caps (pitch->srcpad, GST_BUFFER_OFFSET_NONE,
377           samples * pitch->sample_size, GST_PAD_CAPS (pitch->srcpad), &buffer)
378       != GST_FLOW_OK) {
379     buffer = gst_buffer_new_and_alloc (samples * pitch->sample_size);
380     gst_buffer_set_caps (buffer, GST_PAD_CAPS (pitch->srcpad));
381   }
382
383   samples =
384       priv->st->receiveSamples ((gfloat *) GST_BUFFER_DATA (buffer), samples);
385
386   if (samples <= 0) {
387     gst_buffer_unref (buffer);
388     return NULL;
389   }
390
391   GST_BUFFER_DURATION (buffer) =
392       gst_util_uint64_scale (samples, GST_SECOND, pitch->samplerate);
393   /* temporary store samples here, to avoid having to recalculate this */
394   GST_BUFFER_OFFSET (buffer) = (gint64) samples;
395
396   return buffer;
397 }
398
399 /* process the last samples, in a later stage we should make sure no more
400  * samples are sent out here as strictly necessary, because soundtouch could
401  * append zero samples, which could disturb looping.  */
402 static GstFlowReturn
403 gst_pitch_flush_buffer (GstPitch * pitch, gboolean send)
404 {
405   GstBuffer *buffer;
406
407   GST_DEBUG_OBJECT (pitch, "flushing buffer");
408
409   if (pitch->next_buffer_offset == 0)
410     return GST_FLOW_OK;
411
412   pitch->priv->st->flush ();
413   if (!send)
414     return GST_FLOW_OK;
415
416   buffer = gst_pitch_prepare_buffer (pitch);
417
418   if (!buffer)
419     return GST_FLOW_OK;
420
421   return gst_pitch_forward_buffer (pitch, buffer);
422 }
423
424 static gboolean
425 gst_pitch_src_event (GstPad * pad, GstEvent * event)
426 {
427   GstPitch *pitch;
428   gboolean res;
429
430   pitch = GST_PITCH (gst_pad_get_parent (pad));
431
432   GST_DEBUG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
433
434   switch (GST_EVENT_TYPE (event)) {
435     case GST_EVENT_SEEK:{
436       /* transform the event upstream, according to the playback rate */
437       gdouble rate;
438       GstFormat format;
439       GstSeekFlags flags;
440       GstSeekType cur_type, stop_type;
441       gint64 cur, stop;
442       gfloat stream_time_ratio;
443
444       GST_OBJECT_LOCK (pitch);
445       stream_time_ratio = pitch->priv->stream_time_ratio;
446       GST_OBJECT_UNLOCK (pitch);
447
448       gst_event_parse_seek (event, &rate, &format, &flags,
449           &cur_type, &cur, &stop_type, &stop);
450
451       gst_event_unref (event);
452
453       if (format == GST_FORMAT_TIME || format == GST_FORMAT_DEFAULT) {
454         cur = (gint64) (cur * stream_time_ratio);
455         if (stop != -1)
456           stop = (gint64) (stop * stream_time_ratio);
457
458         event = gst_event_new_seek (rate, format, flags,
459             cur_type, cur, stop_type, stop);
460         res = gst_pad_event_default (pad, event);
461       } else {
462         GST_WARNING_OBJECT (pitch,
463             "Seeking only supported in TIME or DEFAULT format");
464         res = FALSE;
465       }
466
467       break;
468     }
469     default:
470       res = gst_pad_event_default (pad, event);
471       break;
472   }
473
474   gst_object_unref (pitch);
475   return res;
476 }
477
478 /* generic convert function based on caps, no rate 
479  * used here 
480  */
481 static gboolean
482 gst_pitch_convert (GstPitch * pitch,
483     GstFormat src_format, gint64 src_value,
484     GstFormat * dst_format, gint64 * dst_value)
485 {
486   gboolean res = TRUE;
487   guint sample_size;
488   gint samplerate;
489
490   g_return_val_if_fail (dst_format && dst_value, FALSE);
491
492   GST_OBJECT_LOCK (pitch);
493   sample_size = pitch->sample_size;
494   samplerate = pitch->samplerate;
495   GST_OBJECT_UNLOCK (pitch);
496
497   if (sample_size == 0 || samplerate == 0) {
498     return FALSE;
499   }
500
501   if (src_format == *dst_format || src_value == -1) {
502     *dst_value = src_value;
503     return TRUE;
504   }
505
506   switch (src_format) {
507     case GST_FORMAT_BYTES:
508       switch (*dst_format) {
509         case GST_FORMAT_TIME:
510           *dst_value =
511               gst_util_uint64_scale_int (src_value, GST_SECOND,
512               sample_size * samplerate);
513           break;
514         case GST_FORMAT_DEFAULT:
515           *dst_value = gst_util_uint64_scale_int (src_value, 1, sample_size);
516           break;
517         default:
518           res = FALSE;
519           break;
520       }
521       break;
522     case GST_FORMAT_TIME:
523       switch (*dst_format) {
524         case GST_FORMAT_BYTES:
525           *dst_value =
526               gst_util_uint64_scale_int (src_value, samplerate * sample_size,
527               GST_SECOND);
528           break;
529         case GST_FORMAT_DEFAULT:
530           *dst_value =
531               gst_util_uint64_scale_int (src_value, samplerate, GST_SECOND);
532           break;
533         default:
534           res = FALSE;
535           break;
536       }
537       break;
538     case GST_FORMAT_DEFAULT:
539       switch (*dst_format) {
540         case GST_FORMAT_BYTES:
541           *dst_value = gst_util_uint64_scale_int (src_value, sample_size, 1);
542           break;
543         case GST_FORMAT_TIME:
544           *dst_value =
545               gst_util_uint64_scale_int (src_value, GST_SECOND, samplerate);
546           break;
547         default:
548           res = FALSE;
549           break;
550       }
551       break;
552     default:
553       res = FALSE;
554       break;
555   }
556
557   return res;
558 }
559
560 static const GstQueryType *
561 gst_pitch_get_query_types (GstPad * pad)
562 {
563   static const GstQueryType types[] = {
564     GST_QUERY_POSITION,
565     GST_QUERY_DURATION,
566     GST_QUERY_CONVERT,
567     GST_QUERY_LATENCY,
568     GST_QUERY_NONE
569   };
570
571   return types;
572 }
573
574 static gboolean
575 gst_pitch_src_query (GstPad * pad, GstQuery * query)
576 {
577   GstPitch *pitch;
578   gboolean res = FALSE;
579   gfloat stream_time_ratio;
580   gint64 next_buffer_offset;
581   GstClockTime next_buffer_time;
582
583   pitch = GST_PITCH (gst_pad_get_parent (pad));
584   GST_LOG ("%s query", GST_QUERY_TYPE_NAME (query));
585   GST_OBJECT_LOCK (pitch);
586   stream_time_ratio = pitch->priv->stream_time_ratio;
587   next_buffer_time = pitch->next_buffer_time;
588   next_buffer_offset = pitch->next_buffer_offset;
589   GST_OBJECT_UNLOCK (pitch);
590
591   switch (GST_QUERY_TYPE (query)) {
592     case GST_QUERY_DURATION:{
593       GstFormat format;
594       gint64 duration;
595
596       if (!gst_pad_query_default (pad, query)) {
597         GST_DEBUG_OBJECT (pitch, "upstream provided no duration");
598         break;
599       }
600
601       gst_query_parse_duration (query, &format, &duration);
602
603       if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) {
604         GST_DEBUG_OBJECT (pitch, "not TIME or DEFAULT format");
605         break;
606       }
607       GST_LOG_OBJECT (pitch, "upstream duration: %" G_GINT64_FORMAT, duration);
608       duration = (gint64) (duration / stream_time_ratio);
609       GST_LOG_OBJECT (pitch, "our duration: %" G_GINT64_FORMAT, duration);
610       gst_query_set_duration (query, format, duration);
611       res = TRUE;
612       break;
613     }
614     case GST_QUERY_POSITION:{
615       GstFormat dst_format;
616       gint64 dst_value;
617
618       gst_query_parse_position (query, &dst_format, &dst_value);
619
620       if (dst_format != GST_FORMAT_TIME && dst_format != GST_FORMAT_DEFAULT) {
621         GST_DEBUG_OBJECT (pitch, "not TIME or DEFAULT format");
622         break;
623       }
624
625       if (dst_format == GST_FORMAT_TIME) {
626         dst_value = next_buffer_time;
627         res = TRUE;
628       } else {
629         dst_value = next_buffer_offset;
630         res = TRUE;
631       }
632
633       if (res) {
634         GST_LOG_OBJECT (pitch, "our position: %" G_GINT64_FORMAT, dst_value);
635         gst_query_set_position (query, dst_format, dst_value);
636       }
637       break;
638     }
639     case GST_QUERY_CONVERT:{
640       GstFormat src_format, dst_format;
641       gint64 src_value, dst_value;
642
643       gst_query_parse_convert (query, &src_format, &src_value,
644           &dst_format, NULL);
645
646       res = gst_pitch_convert (pitch, src_format, src_value,
647           &dst_format, &dst_value);
648
649       if (res) {
650         gst_query_set_convert (query, src_format, src_value,
651             dst_format, dst_value);
652       }
653       break;
654     }
655     case GST_QUERY_LATENCY:
656     {
657       GstClockTime min, max;
658       gboolean live;
659       GstPad *peer;
660
661       if ((peer = gst_pad_get_peer (pitch->sinkpad))) {
662         if ((res = gst_pad_query (peer, query))) {
663           gst_query_parse_latency (query, &live, &min, &max);
664
665           GST_DEBUG ("Peer latency: min %"
666               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
667               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
668
669           /* add our own latency */
670
671           GST_DEBUG ("Our latency: min %" GST_TIME_FORMAT
672               ", max %" GST_TIME_FORMAT,
673               GST_TIME_ARGS (pitch->min_latency),
674               GST_TIME_ARGS (pitch->max_latency));
675
676           min += pitch->min_latency;
677           if (max != GST_CLOCK_TIME_NONE)
678             max += pitch->max_latency;
679           else
680             max = pitch->max_latency;
681
682           GST_DEBUG ("Calculated total latency : min %"
683               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
684               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
685           gst_query_set_latency (query, live, min, max);
686         }
687         gst_object_unref (peer);
688       }
689       break;
690     }
691     default:
692       res = gst_pad_query_default (pad, query);
693       break;
694   }
695
696   gst_object_unref (pitch);
697   return res;
698 }
699
700 /* this function returns FALSE if not enough data is known to transform the
701  * segment into proper downstream values.  If the function does return false
702  * the sgement should be stalled until enough information is available.
703  * If the funtion returns TRUE, event will be replaced by the new downstream
704  * compatible event.
705  */
706 static gboolean
707 gst_pitch_process_segment (GstPitch * pitch, GstEvent ** event)
708 {
709   GstFormat format, conv_format;
710   gint64 start_value, stop_value, base;
711   gint64 next_offset = 0, next_time = 0;
712   gboolean update = FALSE;
713   gdouble rate, out_seg_rate, arate, our_arate;
714   gfloat stream_time_ratio;
715
716   g_return_val_if_fail (event, FALSE);
717
718   GST_OBJECT_LOCK (pitch);
719   stream_time_ratio = pitch->priv->stream_time_ratio;
720   out_seg_rate = pitch->out_seg_rate;
721   GST_OBJECT_UNLOCK (pitch);
722
723   gst_event_parse_new_segment_full (*event, &update, &rate, &arate, &format, &start_value,
724       &stop_value, &base);
725
726   if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) {
727     GST_WARNING_OBJECT (pitch,
728         "Only NEWSEGMENT in TIME or DEFAULT format supported, sending"
729         "open ended NEWSEGMENT in TIME format.");
730     gst_event_unref (*event);
731     *event =
732         gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME, 0, -1, 0);
733     start_value = 0;
734     stop_value = -1;
735     base = 0;
736   }
737
738   /* Figure out how much of the incoming 'rate' we'll apply ourselves */
739   our_arate = rate / out_seg_rate;
740   /* update the output rate variables */
741   rate = out_seg_rate;
742   arate *= our_arate;
743
744   GST_LOG_OBJECT (pitch->sinkpad,
745       "segment %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%d)", start_value,
746       stop_value, format);
747
748   stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate;
749
750   if (stream_time_ratio == 0) {
751     GST_LOG_OBJECT (pitch->sinkpad, "stream_time_ratio is zero");
752     return FALSE;
753   }
754
755   /* Update the playback rate */
756   GST_OBJECT_LOCK (pitch);
757   pitch->seg_arate = our_arate;
758   pitch->priv->stream_time_ratio = stream_time_ratio;
759   pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate);
760   GST_OBJECT_UNLOCK (pitch);
761
762   start_value = (gint64) (start_value / stream_time_ratio);
763   if (stop_value != -1)
764     stop_value = (gint64) (stop_value / stream_time_ratio);
765   base = (gint64) (base / stream_time_ratio);
766
767   conv_format = GST_FORMAT_TIME;
768   if (!gst_pitch_convert (pitch, format, start_value, &conv_format, &next_time)) {
769     GST_LOG_OBJECT (pitch->sinkpad,
770         "could not convert segment start value to time");
771     return FALSE;
772   }
773
774   conv_format = GST_FORMAT_DEFAULT;
775   if (!gst_pitch_convert (pitch, format, start_value, &conv_format,
776           &next_offset)) {
777     GST_LOG_OBJECT (pitch->sinkpad,
778         "could not convert segment start value to offset");
779     return FALSE;
780   }
781
782   pitch->next_buffer_time = next_time;
783   pitch->next_buffer_offset = next_offset;
784
785   gst_event_unref (*event);
786   *event = gst_event_new_new_segment_full (update, rate, arate, format,
787        start_value, stop_value, base);
788
789   return TRUE;
790 }
791
792 static gboolean
793 gst_pitch_sink_event (GstPad * pad, GstEvent * event)
794 {
795   gboolean res = TRUE;
796   GstPitch *pitch;
797
798   pitch = GST_PITCH (gst_pad_get_parent (pad));
799
800   GST_LOG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
801
802   switch (GST_EVENT_TYPE (event)) {
803     case GST_EVENT_FLUSH_STOP:
804       gst_pitch_flush_buffer (pitch, FALSE);
805       pitch->priv->st->clear ();
806       pitch->next_buffer_offset = 0;
807       pitch->next_buffer_time = 0;
808       pitch->min_latency = pitch->max_latency = 0;
809       break;
810     case GST_EVENT_EOS:
811       gst_pitch_flush_buffer (pitch, TRUE);
812       pitch->priv->st->clear ();
813       pitch->min_latency = pitch->max_latency = 0;
814       break;
815     case GST_EVENT_NEWSEGMENT:
816       if (!gst_pitch_process_segment (pitch, &event)) {
817         GST_LOG_OBJECT (pad, "not enough data known, stalling segment");
818         if (GST_PITCH_GET_PRIVATE (pitch)->pending_segment)
819           gst_event_unref (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
820         GST_PITCH_GET_PRIVATE (pitch)->pending_segment = event;
821         event = NULL;
822       }
823       pitch->priv->st->clear ();
824       pitch->min_latency = pitch->max_latency = 0;
825       break;
826     default:
827       break;
828   }
829
830   /* and forward it */
831   if (event)
832     res = gst_pad_event_default (pad, event);
833
834   gst_object_unref (pitch);
835   return res;
836 }
837
838 static void
839 gst_pitch_update_latency (GstPitch * pitch, GstClockTime timestamp)
840 {
841   GstClockTimeDiff current_latency, min_latency, max_latency;
842
843   current_latency =
844       (GstClockTimeDiff) (timestamp / pitch->priv->stream_time_ratio) -
845       pitch->next_buffer_time;
846
847   min_latency = MIN (pitch->min_latency, current_latency);
848   max_latency = MAX (pitch->max_latency, current_latency);
849
850   if (pitch->min_latency != min_latency || pitch->max_latency != max_latency) {
851     pitch->min_latency = min_latency;
852     pitch->max_latency = max_latency;
853
854     /* FIXME: what about the LATENCY event? It only has
855      * one latency value, should it be current, min or max?
856      * Should it include upstream latencies?
857      */
858
859     gst_element_post_message (GST_ELEMENT (pitch),
860         gst_message_new_latency (GST_OBJECT (pitch)));
861   }
862 }
863
864 static GstFlowReturn
865 gst_pitch_chain (GstPad * pad, GstBuffer * buffer)
866 {
867   GstPitch *pitch;
868   GstPitchPrivate *priv;
869   GstClockTime timestamp;
870
871   pitch = GST_PITCH (GST_PAD_PARENT (pad));
872   priv = GST_PITCH_GET_PRIVATE (pitch);
873
874   gst_object_sync_values (G_OBJECT (pitch), pitch->next_buffer_time);
875
876   timestamp = GST_BUFFER_TIMESTAMP (buffer);
877
878   /* push the received samples on the soundtouch buffer */
879   GST_LOG_OBJECT (pitch, "incoming buffer (%d samples)",
880       (gint) (GST_BUFFER_SIZE (buffer) / pitch->sample_size));
881
882   if (GST_PITCH_GET_PRIVATE (pitch)->pending_segment) {
883     GstEvent *event =
884         gst_event_copy (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
885
886     GST_LOG_OBJECT (pitch, "processing stalled segment");
887     if (!gst_pitch_process_segment (pitch, &event)) {
888       gst_event_unref (event);
889       return GST_FLOW_ERROR;
890     }
891
892     if (!gst_pad_event_default (pitch->sinkpad, event)) {
893       gst_event_unref (event);
894       return GST_FLOW_ERROR;
895     }
896
897     gst_event_unref (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
898     GST_PITCH_GET_PRIVATE (pitch)->pending_segment = NULL;
899   }
900
901   priv->st->putSamples ((gfloat *) GST_BUFFER_DATA (buffer),
902       GST_BUFFER_SIZE (buffer) / pitch->sample_size);
903   gst_buffer_unref (buffer);
904
905   /* Calculate latency */
906
907   gst_pitch_update_latency (pitch, timestamp);
908
909   /* and try to extract some samples from the soundtouch buffer */
910   if (!priv->st->isEmpty ()) {
911     GstBuffer *out_buffer;
912
913     out_buffer = gst_pitch_prepare_buffer (pitch);
914     if (out_buffer)
915       return gst_pitch_forward_buffer (pitch, out_buffer);
916   }
917
918   return GST_FLOW_OK;
919 }
920
921 static GstStateChangeReturn
922 gst_pitch_change_state (GstElement * element, GstStateChange transition)
923 {
924   GstStateChangeReturn ret;
925   GstPitch *pitch = GST_PITCH (element);
926
927   switch (transition) {
928     case GST_STATE_CHANGE_NULL_TO_READY:
929       break;
930     case GST_STATE_CHANGE_READY_TO_PAUSED:
931       pitch->next_buffer_time = 0;
932       pitch->next_buffer_offset = 0;
933       pitch->priv->st->clear ();
934       pitch->min_latency = pitch->max_latency = 0;
935       break;
936     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
937       break;
938     default:
939       break;
940   }
941
942   ret = parent_class->change_state (element, transition);
943   if (ret != GST_STATE_CHANGE_SUCCESS)
944     return ret;
945
946   switch (transition) {
947     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
948       break;
949     case GST_STATE_CHANGE_PAUSED_TO_READY:
950       if (GST_PITCH_GET_PRIVATE (pitch)->pending_segment) {
951         gst_event_unref (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
952         GST_PITCH_GET_PRIVATE (pitch)->pending_segment = NULL;
953       }
954       break;
955     case GST_STATE_CHANGE_READY_TO_NULL:
956     default:
957       break;
958   }
959
960   return ret;
961 }