1 /* GStreamer pitch controller element
2 * Copyright (C) 2006 Wouter Paesen <wouter@blue-gate.be>
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.
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.
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
24 /* FIXME: workaround for SoundTouch.h of version 1.3.1 defining those
25 * variables while it shouldn't. */
27 #undef PACKAGE_VERSION
28 #undef PACKAGE_TARNAME
31 #undef PACKAGE_BUGREPORT
34 #include <soundtouch/SoundTouch.h>
37 #include <gst/audio/audio.h>
39 #include "gstpitch.hh"
42 GST_DEBUG_CATEGORY_STATIC (pitch_debug);
43 #define GST_CAT_DEFAULT pitch_debug
45 #define GST_PITCH_GET_PRIVATE(o) (o->priv)
46 struct _GstPitchPrivate
48 gfloat stream_time_ratio;
50 GstEvent *pending_segment;
52 soundtouch::SoundTouch * st;
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
71 #if defined(SOUNDTOUCH_FLOAT_SAMPLES)
72 #define SUPPORTED_CAPS \
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 \
80 "format = (string) " GST_AUDIO_NE (S16) ", " \
81 "rate = (int) [ 8000, MAX ], " \
82 "channels = (int) [ 1, 2 ]"
84 #error "Only integer or float samples are supported"
87 static GstStaticPadTemplate gst_pitch_sink_template =
88 GST_STATIC_PAD_TEMPLATE ("sink",
91 GST_STATIC_CAPS (SUPPORTED_CAPS));
93 static GstStaticPadTemplate gst_pitch_src_template =
94 GST_STATIC_PAD_TEMPLATE ("src",
97 GST_STATIC_CAPS (SUPPORTED_CAPS));
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);
106 static gboolean gst_pitch_setcaps (GstPitch * pitch, GstCaps * caps);
107 static GstFlowReturn gst_pitch_chain (GstPad * pad, GstObject * parent,
109 static GstStateChangeReturn gst_pitch_change_state (GstElement * element,
110 GstStateChange transition);
111 static gboolean gst_pitch_sink_event (GstPad * pad, GstObject * parent,
113 static gboolean gst_pitch_src_event (GstPad * pad, GstObject * parent,
116 static gboolean gst_pitch_src_query (GstPad * pad, GstObject * parent,
119 #define gst_pitch_parent_class parent_class
120 G_DEFINE_TYPE_WITH_PRIVATE (GstPitch, gst_pitch, GST_TYPE_ELEMENT);
123 gst_pitch_class_init (GstPitchClass * klass)
125 GObjectClass *gobject_class;
126 GstElementClass *element_class;
128 gobject_class = G_OBJECT_CLASS (klass);
129 element_class = GST_ELEMENT_CLASS (klass);
131 GST_DEBUG_CATEGORY_INIT (pitch_debug, "pitch", 0,
132 "audio pitch control element");
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);
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)));
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)));
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)));
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)));
162 element_class->change_state = GST_DEBUG_FUNCPTR (gst_pitch_change_state);
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);
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>");
173 gst_pitch_init (GstPitch * pitch)
175 pitch->priv = (GstPitchPrivate *) gst_pitch_get_instance_private (pitch);
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);
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);
195 pitch->priv->st = new soundtouch::SoundTouch ();
199 pitch->out_seg_rate = 1.0;
200 pitch->seg_arate = 1.0;
202 pitch->next_buffer_time = GST_CLOCK_TIME_NONE;
203 pitch->next_buffer_offset = 0;
205 pitch->priv->st->setRate (pitch->rate);
206 pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate);
207 pitch->priv->st->setPitch (pitch->pitch);
209 pitch->priv->stream_time_ratio = 1.0;
210 pitch->min_latency = pitch->max_latency = 0;
215 gst_pitch_dispose (GObject * object)
217 GstPitch *pitch = GST_PITCH (object);
219 if (pitch->priv->st) {
220 delete pitch->priv->st;
222 pitch->priv->st = NULL;
225 G_OBJECT_CLASS (parent_class)->dispose (object);
229 gst_pitch_update_duration (GstPitch * pitch)
233 m = gst_message_new_duration_changed (GST_OBJECT (pitch));
234 gst_element_post_message (GST_ELEMENT (pitch), m);
238 gst_pitch_set_property (GObject * object, guint prop_id,
239 const GValue * value, GParamSpec * pspec)
241 GstPitch *pitch = GST_PITCH (object);
243 GST_OBJECT_LOCK (pitch);
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);
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);
262 /* Has no effect until the next input segment */
263 pitch->out_seg_rate = g_value_get_float (value);
264 GST_OBJECT_UNLOCK (pitch);
267 pitch->pitch = g_value_get_float (value);
268 pitch->priv->st->setPitch (pitch->pitch);
269 GST_OBJECT_UNLOCK (pitch);
272 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
273 GST_OBJECT_UNLOCK (pitch);
279 gst_pitch_get_property (GObject * object, guint prop_id,
280 GValue * value, GParamSpec * pspec)
282 GstPitch *pitch = GST_PITCH (object);
284 GST_OBJECT_LOCK (pitch);
287 g_value_set_float (value, pitch->tempo);
290 g_value_set_float (value, pitch->rate);
293 g_value_set_float (value, pitch->out_seg_rate);
296 g_value_set_float (value, pitch->pitch);
299 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
302 GST_OBJECT_UNLOCK (pitch);
306 gst_pitch_setcaps (GstPitch * pitch, GstCaps * caps)
308 GstPitchPrivate *priv;
310 priv = GST_PITCH_GET_PRIVATE (pitch);
312 if (!gst_audio_info_from_caps (&pitch->info, caps))
315 GST_OBJECT_LOCK (pitch);
317 /* notify the soundtouch instance of this change */
318 priv->st->setSampleRate (pitch->info.rate);
319 priv->st->setChannels (pitch->info.channels);
321 GST_OBJECT_UNLOCK (pitch);
326 /* send a buffer out */
328 gst_pitch_forward_buffer (GstPitch * pitch, GstBuffer * buffer)
332 GST_BUFFER_TIMESTAMP (buffer) = pitch->next_buffer_time;
333 pitch->next_buffer_time += GST_BUFFER_DURATION (buffer);
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;
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);
344 return gst_pad_push (pitch->srcpad, buffer);
347 /* extract a buffer from soundtouch */
349 gst_pitch_prepare_buffer (GstPitch * pitch)
351 GstPitchPrivate *priv;
356 priv = GST_PITCH_GET_PRIVATE (pitch);
358 GST_LOG_OBJECT (pitch, "preparing buffer");
360 samples = pitch->priv->st->numSamples ();
364 buffer = gst_buffer_new_and_alloc (samples * pitch->info.bpf);
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);
371 gst_buffer_unref (buffer);
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;
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. */
387 gst_pitch_flush_buffer (GstPitch * pitch, gboolean send)
391 if (pitch->priv->st->numUnprocessedSamples() != 0) {
392 GST_DEBUG_OBJECT (pitch, "flushing buffer");
393 pitch->priv->st->flush ();
399 buffer = gst_pitch_prepare_buffer (pitch);
404 return gst_pitch_forward_buffer (pitch, buffer);
408 gst_pitch_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
413 pitch = GST_PITCH (parent);
415 GST_DEBUG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
417 switch (GST_EVENT_TYPE (event)) {
418 case GST_EVENT_SEEK:{
419 /* transform the event upstream, according to the playback rate */
423 GstSeekType cur_type, stop_type;
425 gfloat stream_time_ratio;
428 GST_OBJECT_LOCK (pitch);
429 stream_time_ratio = pitch->priv->stream_time_ratio;
430 GST_OBJECT_UNLOCK (pitch);
432 gst_event_parse_seek (event, &rate, &format, &flags,
433 &cur_type, &cur, &stop_type, &stop);
435 seqnum = gst_event_get_seqnum (event);
437 gst_event_unref (event);
439 if (format == GST_FORMAT_TIME || format == GST_FORMAT_DEFAULT) {
440 cur = (gint64) (cur * stream_time_ratio);
442 stop = (gint64) (stop * stream_time_ratio);
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);
449 GST_WARNING_OBJECT (pitch,
450 "Seeking only supported in TIME or DEFAULT format");
456 res = gst_pad_event_default (pad, parent, event);
462 /* generic convert function based on caps, no rate
466 gst_pitch_convert (GstPitch * pitch,
467 GstFormat src_format, gint64 src_value,
468 GstFormat * dst_format, gint64 * dst_value)
474 g_return_val_if_fail (dst_format && dst_value, FALSE);
476 GST_OBJECT_LOCK (pitch);
477 sample_size = pitch->info.bpf;
478 samplerate = pitch->info.rate;
479 GST_OBJECT_UNLOCK (pitch);
481 if (sample_size == 0 || samplerate == 0) {
485 if (src_format == *dst_format || src_value == -1) {
486 *dst_value = src_value;
490 switch (src_format) {
491 case GST_FORMAT_BYTES:
492 switch (*dst_format) {
493 case GST_FORMAT_TIME:
495 gst_util_uint64_scale_int (src_value, GST_SECOND,
496 sample_size * samplerate);
498 case GST_FORMAT_DEFAULT:
499 *dst_value = gst_util_uint64_scale_int (src_value, 1, sample_size);
506 case GST_FORMAT_TIME:
507 switch (*dst_format) {
508 case GST_FORMAT_BYTES:
510 gst_util_uint64_scale_int (src_value, samplerate * sample_size,
513 case GST_FORMAT_DEFAULT:
515 gst_util_uint64_scale_int (src_value, samplerate, GST_SECOND);
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);
527 case GST_FORMAT_TIME:
529 gst_util_uint64_scale_int (src_value, GST_SECOND, samplerate);
545 gst_pitch_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
548 gboolean res = FALSE;
549 gfloat stream_time_ratio;
550 gint64 next_buffer_offset;
551 GstClockTime next_buffer_time;
553 pitch = GST_PITCH (parent);
555 GST_LOG ("%s query", GST_QUERY_TYPE_NAME (query));
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);
563 switch (GST_QUERY_TYPE (query)) {
564 case GST_QUERY_DURATION:{
568 if (!gst_pad_query_default (pad, parent, query)) {
569 GST_DEBUG_OBJECT (pitch, "upstream provided no duration");
573 gst_query_parse_duration (query, &format, &duration);
575 if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) {
576 GST_DEBUG_OBJECT (pitch, "not TIME or DEFAULT format");
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);
586 case GST_QUERY_POSITION:{
587 GstFormat dst_format;
590 gst_query_parse_position (query, &dst_format, &dst_value);
592 if (dst_format != GST_FORMAT_TIME && dst_format != GST_FORMAT_DEFAULT) {
593 GST_DEBUG_OBJECT (pitch, "not TIME or DEFAULT format");
597 if (dst_format == GST_FORMAT_TIME) {
598 dst_value = next_buffer_time;
601 dst_value = next_buffer_offset;
606 GST_LOG_OBJECT (pitch, "our position: %" G_GINT64_FORMAT, dst_value);
607 gst_query_set_position (query, dst_format, dst_value);
611 case GST_QUERY_CONVERT:{
612 GstFormat src_format, dst_format;
613 gint64 src_value, dst_value;
615 gst_query_parse_convert (query, &src_format, &src_value,
618 res = gst_pitch_convert (pitch, src_format, src_value,
619 &dst_format, &dst_value);
622 gst_query_set_convert (query, src_format, src_value,
623 dst_format, dst_value);
627 case GST_QUERY_LATENCY:
629 GstClockTime min, max;
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);
637 GST_DEBUG ("Peer latency: min %"
638 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
639 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
641 /* add our own latency */
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));
648 min += pitch->min_latency;
649 if (max != GST_CLOCK_TIME_NONE)
650 max += pitch->max_latency;
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);
657 gst_object_unref (peer);
662 res = gst_pad_query_default (pad, parent, query);
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
676 gst_pitch_process_segment (GstPitch * pitch, GstEvent ** event)
679 gdouble out_seg_rate, our_arate;
680 gfloat stream_time_ratio;
683 g_return_val_if_fail (event, FALSE);
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);
690 gst_event_copy_segment (*event, &seg);
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;
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;
708 GST_LOG_OBJECT (pitch->sinkpad, "in segment %" GST_SEGMENT_FORMAT, &seg);
710 stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate;
712 if (stream_time_ratio == 0) {
713 GST_LOG_OBJECT (pitch->sinkpad, "stream_time_ratio is zero");
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);
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);
730 GST_LOG_OBJECT (pitch->sinkpad, "out segment %" GST_SEGMENT_FORMAT, &seg);
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);
741 gst_pitch_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
746 pitch = GST_PITCH (parent);
748 GST_LOG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
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;
759 gst_pitch_flush_buffer (pitch, TRUE);
760 pitch->priv->st->clear ();
761 pitch->min_latency = pitch->max_latency = 0;
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;
771 pitch->priv->st->clear ();
772 pitch->min_latency = pitch->max_latency = 0;
778 gst_event_parse_caps (event, &caps);
779 res = gst_pitch_setcaps (pitch, caps);
781 gst_event_unref (event);
791 res = gst_pad_event_default (pad, parent, event);
798 gst_pitch_update_latency (GstPitch * pitch, GstClockTime timestamp)
800 GstClockTimeDiff current_latency, min_latency, max_latency;
803 (GstClockTimeDiff) (timestamp / pitch->priv->stream_time_ratio) -
804 pitch->next_buffer_time;
806 min_latency = MIN (pitch->min_latency, current_latency);
807 max_latency = MAX (pitch->max_latency, current_latency);
809 if (pitch->min_latency != min_latency || pitch->max_latency != max_latency) {
810 pitch->min_latency = min_latency;
811 pitch->max_latency = max_latency;
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?
818 gst_element_post_message (GST_ELEMENT (pitch),
819 gst_message_new_latency (GST_OBJECT (pitch)));
824 gst_pitch_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
827 GstPitchPrivate *priv;
828 GstClockTime timestamp;
831 pitch = GST_PITCH (parent);
832 priv = GST_PITCH_GET_PRIVATE (pitch);
834 timestamp = GST_BUFFER_TIMESTAMP (buffer);
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;
841 GST_OBJECT_LOCK (pitch);
842 stream_time_ratio = priv->stream_time_ratio;
843 GST_OBJECT_UNLOCK (pitch);
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);
850 gst_object_sync_values (GST_OBJECT (pitch), pitch->next_buffer_time);
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));
857 if (GST_PITCH_GET_PRIVATE (pitch)->pending_segment) {
859 gst_event_copy (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
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;
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;
874 gst_event_unref (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
875 GST_PITCH_GET_PRIVATE (pitch)->pending_segment = NULL;
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);
885 /* Calculate latency */
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;
892 out_buffer = gst_pitch_prepare_buffer (pitch);
894 return gst_pitch_forward_buffer (pitch, out_buffer);
900 static GstStateChangeReturn
901 gst_pitch_change_state (GstElement * element, GstStateChange transition)
903 GstStateChangeReturn ret;
904 GstPitch *pitch = GST_PITCH (element);
906 switch (transition) {
907 case GST_STATE_CHANGE_NULL_TO_READY:
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;
915 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
921 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
922 if (ret != GST_STATE_CHANGE_SUCCESS)
925 switch (transition) {
926 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
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;
934 case GST_STATE_CHANGE_READY_TO_NULL: