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 #define FLOAT_SAMPLES 1
35 #include <soundtouch/SoundTouch.h>
38 #include <gst/controller/gstcontroller.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 #define SUPPORTED_CAPS \
66 "audio/x-raw-float, " \
67 "rate = (int) [ 8000, MAX ], " \
68 "channels = (int) [ 1, 2 ], " \
69 "endianness = (int) BYTE_ORDER, " \
73 static GstStaticPadTemplate gst_pitch_sink_template =
74 GST_STATIC_PAD_TEMPLATE ("sink",
79 static GstStaticPadTemplate gst_pitch_src_template =
80 GST_STATIC_PAD_TEMPLATE ("src",
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);
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);
99 static gboolean gst_pitch_src_query (GstPad * pad, GstQuery * query);
100 static const GstQueryType *gst_pitch_get_query_types (GstPad * pad);
102 GST_BOILERPLATE (GstPitch, gst_pitch, GstElement, GST_TYPE_ELEMENT);
105 gst_pitch_base_init (gpointer g_class)
107 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
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));
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>");
120 gst_pitch_class_init (GstPitchClass * klass)
122 GObjectClass *gobject_class;
123 GstElementClass *element_class;
125 gobject_class = G_OBJECT_CLASS (klass);
126 element_class = GST_ELEMENT_CLASS (klass);
128 GST_DEBUG_CATEGORY_INIT (pitch_debug, "pitch", 0,
129 "audio pitch control element");
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);
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)));
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)));
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)));
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)));
156 g_type_class_add_private (gobject_class, sizeof (GstPitchPrivate));
160 gst_pitch_init (GstPitch * pitch, GstPitchClass * pitch_class)
163 G_TYPE_INSTANCE_GET_PRIVATE ((pitch), GST_TYPE_PITCH, GstPitchPrivate);
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);
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);
191 pitch->priv->st = new soundtouch::SoundTouch ();
195 pitch->out_seg_rate = 1.0;
196 pitch->seg_arate = 1.0;
198 pitch->next_buffer_time = 0;
199 pitch->next_buffer_offset = 0;
201 pitch->priv->st->setRate (pitch->rate);
202 pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate);
203 pitch->priv->st->setPitch (pitch->pitch);
205 pitch->priv->stream_time_ratio = 1.0;
206 pitch->min_latency = pitch->max_latency = 0;
211 gst_pitch_dispose (GObject * object)
213 GstPitch *pitch = GST_PITCH (object);
215 if (pitch->priv->st) {
216 delete pitch->priv->st;
218 pitch->priv->st = NULL;
221 G_OBJECT_CLASS (parent_class)->dispose (object);
225 gst_pitch_update_duration (GstPitch * pitch)
229 m = gst_message_new_duration (GST_OBJECT (pitch), GST_FORMAT_TIME, -1);
230 gst_element_post_message (GST_ELEMENT (pitch), m);
234 gst_pitch_set_property (GObject * object, guint prop_id,
235 const GValue * value, GParamSpec * pspec)
237 GstPitch *pitch = GST_PITCH (object);
239 GST_OBJECT_LOCK (pitch);
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);
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);
256 /* Has no effect until the next input segment */
257 pitch->out_seg_rate = g_value_get_float (value);
258 GST_OBJECT_UNLOCK (pitch);
260 pitch->pitch = g_value_get_float (value);
261 pitch->priv->st->setPitch (pitch->pitch);
262 GST_OBJECT_UNLOCK (pitch);
265 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
266 GST_OBJECT_UNLOCK (pitch);
272 gst_pitch_get_property (GObject * object, guint prop_id,
273 GValue * value, GParamSpec * pspec)
275 GstPitch *pitch = GST_PITCH (object);
277 GST_OBJECT_LOCK (pitch);
280 g_value_set_float (value, pitch->tempo);
283 g_value_set_float (value, pitch->rate);
286 g_value_set_float (value, pitch->out_seg_rate);
289 g_value_set_float (value, pitch->pitch);
292 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
295 GST_OBJECT_UNLOCK (pitch);
299 gst_pitch_sink_setcaps (GstPad * pad, GstCaps * caps)
302 GstPitchPrivate *priv;
303 GstStructure *structure;
307 pitch = GST_PITCH (GST_PAD_PARENT (pad));
308 priv = GST_PITCH_GET_PRIVATE (pitch);
310 otherpad = (pad == pitch->srcpad) ? pitch->sinkpad : pitch->srcpad;
312 if (!gst_pad_set_caps (otherpad, caps))
315 structure = gst_caps_get_structure (caps, 0);
317 if (!gst_structure_get_int (structure, "rate", &rate) ||
318 !gst_structure_get_int (structure, "channels", &channels)) {
322 GST_OBJECT_LOCK (pitch);
324 pitch->samplerate = rate;
325 pitch->channels = channels;
327 /* notify the soundtouch instance of this change */
328 priv->st->setSampleRate (rate);
329 priv->st->setChannels (channels);
331 /* calculate sample size */
332 pitch->sample_size = (sizeof (gfloat) * channels);
334 GST_OBJECT_UNLOCK (pitch);
339 /* send a buffer out */
341 gst_pitch_forward_buffer (GstPitch * pitch, GstBuffer * buffer)
345 GST_BUFFER_TIMESTAMP (buffer) = pitch->next_buffer_time;
346 pitch->next_buffer_time += GST_BUFFER_DURATION (buffer);
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;
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);
357 return gst_pad_push (pitch->srcpad, buffer);
360 /* extract a buffer from soundtouch */
362 gst_pitch_prepare_buffer (GstPitch * pitch)
364 GstPitchPrivate *priv;
368 priv = GST_PITCH_GET_PRIVATE (pitch);
370 GST_LOG_OBJECT (pitch, "preparing buffer");
372 samples = pitch->priv->st->numSamples ();
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)
379 buffer = gst_buffer_new_and_alloc (samples * pitch->sample_size);
380 gst_buffer_set_caps (buffer, GST_PAD_CAPS (pitch->srcpad));
384 priv->st->receiveSamples ((gfloat *) GST_BUFFER_DATA (buffer), samples);
387 gst_buffer_unref (buffer);
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;
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. */
403 gst_pitch_flush_buffer (GstPitch * pitch, gboolean send)
407 GST_DEBUG_OBJECT (pitch, "flushing buffer");
409 if (pitch->next_buffer_offset == 0)
412 pitch->priv->st->flush ();
416 buffer = gst_pitch_prepare_buffer (pitch);
421 return gst_pitch_forward_buffer (pitch, buffer);
425 gst_pitch_src_event (GstPad * pad, GstEvent * event)
430 pitch = GST_PITCH (gst_pad_get_parent (pad));
432 GST_DEBUG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
434 switch (GST_EVENT_TYPE (event)) {
435 case GST_EVENT_SEEK:{
436 /* transform the event upstream, according to the playback rate */
440 GstSeekType cur_type, stop_type;
442 gfloat stream_time_ratio;
444 GST_OBJECT_LOCK (pitch);
445 stream_time_ratio = pitch->priv->stream_time_ratio;
446 GST_OBJECT_UNLOCK (pitch);
448 gst_event_parse_seek (event, &rate, &format, &flags,
449 &cur_type, &cur, &stop_type, &stop);
451 gst_event_unref (event);
453 if (format == GST_FORMAT_TIME || format == GST_FORMAT_DEFAULT) {
454 cur = (gint64) (cur * stream_time_ratio);
456 stop = (gint64) (stop * stream_time_ratio);
458 event = gst_event_new_seek (rate, format, flags,
459 cur_type, cur, stop_type, stop);
460 res = gst_pad_event_default (pad, event);
462 GST_WARNING_OBJECT (pitch,
463 "Seeking only supported in TIME or DEFAULT format");
470 res = gst_pad_event_default (pad, event);
474 gst_object_unref (pitch);
478 /* generic convert function based on caps, no rate
482 gst_pitch_convert (GstPitch * pitch,
483 GstFormat src_format, gint64 src_value,
484 GstFormat * dst_format, gint64 * dst_value)
490 g_return_val_if_fail (dst_format && dst_value, FALSE);
492 GST_OBJECT_LOCK (pitch);
493 sample_size = pitch->sample_size;
494 samplerate = pitch->samplerate;
495 GST_OBJECT_UNLOCK (pitch);
497 if (sample_size == 0 || samplerate == 0) {
501 if (src_format == *dst_format || src_value == -1) {
502 *dst_value = src_value;
506 switch (src_format) {
507 case GST_FORMAT_BYTES:
508 switch (*dst_format) {
509 case GST_FORMAT_TIME:
511 gst_util_uint64_scale_int (src_value, GST_SECOND,
512 sample_size * samplerate);
514 case GST_FORMAT_DEFAULT:
515 *dst_value = gst_util_uint64_scale_int (src_value, 1, sample_size);
522 case GST_FORMAT_TIME:
523 switch (*dst_format) {
524 case GST_FORMAT_BYTES:
526 gst_util_uint64_scale_int (src_value, samplerate * sample_size,
529 case GST_FORMAT_DEFAULT:
531 gst_util_uint64_scale_int (src_value, samplerate, GST_SECOND);
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);
543 case GST_FORMAT_TIME:
545 gst_util_uint64_scale_int (src_value, GST_SECOND, samplerate);
560 static const GstQueryType *
561 gst_pitch_get_query_types (GstPad * pad)
563 static const GstQueryType types[] = {
575 gst_pitch_src_query (GstPad * pad, GstQuery * query)
578 gboolean res = FALSE;
579 gfloat stream_time_ratio;
580 gint64 next_buffer_offset;
581 GstClockTime next_buffer_time;
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);
591 switch (GST_QUERY_TYPE (query)) {
592 case GST_QUERY_DURATION:{
596 if (!gst_pad_query_default (pad, query)) {
597 GST_DEBUG_OBJECT (pitch, "upstream provided no duration");
601 gst_query_parse_duration (query, &format, &duration);
603 if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) {
604 GST_DEBUG_OBJECT (pitch, "not TIME or DEFAULT format");
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);
614 case GST_QUERY_POSITION:{
615 GstFormat dst_format;
618 gst_query_parse_position (query, &dst_format, &dst_value);
620 if (dst_format != GST_FORMAT_TIME && dst_format != GST_FORMAT_DEFAULT) {
621 GST_DEBUG_OBJECT (pitch, "not TIME or DEFAULT format");
625 if (dst_format == GST_FORMAT_TIME) {
626 dst_value = next_buffer_time;
629 dst_value = next_buffer_offset;
634 GST_LOG_OBJECT (pitch, "our position: %" G_GINT64_FORMAT, dst_value);
635 gst_query_set_position (query, dst_format, dst_value);
639 case GST_QUERY_CONVERT:{
640 GstFormat src_format, dst_format;
641 gint64 src_value, dst_value;
643 gst_query_parse_convert (query, &src_format, &src_value,
646 res = gst_pitch_convert (pitch, src_format, src_value,
647 &dst_format, &dst_value);
650 gst_query_set_convert (query, src_format, src_value,
651 dst_format, dst_value);
655 case GST_QUERY_LATENCY:
657 GstClockTime min, max;
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);
665 GST_DEBUG ("Peer latency: min %"
666 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
667 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
669 /* add our own latency */
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));
676 min += pitch->min_latency;
677 if (max != GST_CLOCK_TIME_NONE)
678 max += pitch->max_latency;
680 max = pitch->max_latency;
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);
687 gst_object_unref (peer);
692 res = gst_pad_query_default (pad, query);
696 gst_object_unref (pitch);
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
707 gst_pitch_process_segment (GstPitch * pitch, GstEvent ** event)
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;
716 g_return_val_if_fail (event, FALSE);
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);
723 gst_event_parse_new_segment_full (*event, &update, &rate, &arate, &format, &start_value,
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);
732 gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME, 0, -1, 0);
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 */
744 GST_LOG_OBJECT (pitch->sinkpad,
745 "segment %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%d)", start_value,
748 stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate;
750 if (stream_time_ratio == 0) {
751 GST_LOG_OBJECT (pitch->sinkpad, "stream_time_ratio is zero");
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);
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);
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");
774 conv_format = GST_FORMAT_DEFAULT;
775 if (!gst_pitch_convert (pitch, format, start_value, &conv_format,
777 GST_LOG_OBJECT (pitch->sinkpad,
778 "could not convert segment start value to offset");
782 pitch->next_buffer_time = next_time;
783 pitch->next_buffer_offset = next_offset;
785 gst_event_unref (*event);
786 *event = gst_event_new_new_segment_full (update, rate, arate, format,
787 start_value, stop_value, base);
793 gst_pitch_sink_event (GstPad * pad, GstEvent * event)
798 pitch = GST_PITCH (gst_pad_get_parent (pad));
800 GST_LOG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
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;
811 gst_pitch_flush_buffer (pitch, TRUE);
812 pitch->priv->st->clear ();
813 pitch->min_latency = pitch->max_latency = 0;
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;
823 pitch->priv->st->clear ();
824 pitch->min_latency = pitch->max_latency = 0;
832 res = gst_pad_event_default (pad, event);
834 gst_object_unref (pitch);
839 gst_pitch_update_latency (GstPitch * pitch, GstClockTime timestamp)
841 GstClockTimeDiff current_latency, min_latency, max_latency;
844 (GstClockTimeDiff) (timestamp / pitch->priv->stream_time_ratio) -
845 pitch->next_buffer_time;
847 min_latency = MIN (pitch->min_latency, current_latency);
848 max_latency = MAX (pitch->max_latency, current_latency);
850 if (pitch->min_latency != min_latency || pitch->max_latency != max_latency) {
851 pitch->min_latency = min_latency;
852 pitch->max_latency = max_latency;
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?
859 gst_element_post_message (GST_ELEMENT (pitch),
860 gst_message_new_latency (GST_OBJECT (pitch)));
865 gst_pitch_chain (GstPad * pad, GstBuffer * buffer)
868 GstPitchPrivate *priv;
869 GstClockTime timestamp;
871 pitch = GST_PITCH (GST_PAD_PARENT (pad));
872 priv = GST_PITCH_GET_PRIVATE (pitch);
874 gst_object_sync_values (G_OBJECT (pitch), pitch->next_buffer_time);
876 timestamp = GST_BUFFER_TIMESTAMP (buffer);
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));
882 if (GST_PITCH_GET_PRIVATE (pitch)->pending_segment) {
884 gst_event_copy (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
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;
892 if (!gst_pad_event_default (pitch->sinkpad, event)) {
893 gst_event_unref (event);
894 return GST_FLOW_ERROR;
897 gst_event_unref (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
898 GST_PITCH_GET_PRIVATE (pitch)->pending_segment = NULL;
901 priv->st->putSamples ((gfloat *) GST_BUFFER_DATA (buffer),
902 GST_BUFFER_SIZE (buffer) / pitch->sample_size);
903 gst_buffer_unref (buffer);
905 /* Calculate latency */
907 gst_pitch_update_latency (pitch, timestamp);
909 /* and try to extract some samples from the soundtouch buffer */
910 if (!priv->st->isEmpty ()) {
911 GstBuffer *out_buffer;
913 out_buffer = gst_pitch_prepare_buffer (pitch);
915 return gst_pitch_forward_buffer (pitch, out_buffer);
921 static GstStateChangeReturn
922 gst_pitch_change_state (GstElement * element, GstStateChange transition)
924 GstStateChangeReturn ret;
925 GstPitch *pitch = GST_PITCH (element);
927 switch (transition) {
928 case GST_STATE_CHANGE_NULL_TO_READY:
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;
936 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
942 ret = parent_class->change_state (element, transition);
943 if (ret != GST_STATE_CHANGE_SUCCESS)
946 switch (transition) {
947 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
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;
955 case GST_STATE_CHANGE_READY_TO_NULL: