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