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