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/audio/audio.h>
40 #include "gstpitch.hh"
43 GST_DEBUG_CATEGORY_STATIC (pitch_debug);
44 #define GST_CAT_DEFAULT pitch_debug
46 #define GST_PITCH_GET_PRIVATE(o) (o->priv)
47 struct _GstPitchPrivate
49 gfloat stream_time_ratio;
51 GstEvent *pending_segment;
53 soundtouch::SoundTouch * st;
65 #define SUPPORTED_CAPS \
67 "format = (string) " GST_AUDIO_NE (F32) ", " \
68 "rate = (int) [ 8000, MAX ], " \
69 "channels = (int) [ 1, 2 ]"
71 static GstStaticPadTemplate gst_pitch_sink_template =
72 GST_STATIC_PAD_TEMPLATE ("sink",
75 GST_STATIC_CAPS (SUPPORTED_CAPS));
77 static GstStaticPadTemplate gst_pitch_src_template =
78 GST_STATIC_PAD_TEMPLATE ("src",
81 GST_STATIC_CAPS (SUPPORTED_CAPS));
83 static void gst_pitch_dispose (GObject * object);
84 static void gst_pitch_set_property (GObject * object,
85 guint prop_id, const GValue * value, GParamSpec * pspec);
86 static void gst_pitch_get_property (GObject * object,
87 guint prop_id, GValue * value, GParamSpec * pspec);
90 static gboolean gst_pitch_setcaps (GstPitch * pitch, GstCaps * caps);
91 static GstFlowReturn gst_pitch_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer);
92 static GstStateChangeReturn gst_pitch_change_state (GstElement * element,
93 GstStateChange transition);
94 static gboolean gst_pitch_sink_event (GstPad * pad, GstObject * parent, GstEvent * event);
95 static gboolean gst_pitch_src_event (GstPad * pad, GstObject * parent, GstEvent * event);
97 static gboolean gst_pitch_src_query (GstPad * pad, GstObject * parent, GstQuery * query);
99 #define gst_pitch_parent_class parent_class
100 G_DEFINE_TYPE (GstPitch, gst_pitch, GST_TYPE_ELEMENT);
103 gst_pitch_class_init (GstPitchClass * klass)
105 GObjectClass *gobject_class;
106 GstElementClass *element_class;
108 gobject_class = G_OBJECT_CLASS (klass);
109 element_class = GST_ELEMENT_CLASS (klass);
111 GST_DEBUG_CATEGORY_INIT (pitch_debug, "pitch", 0,
112 "audio pitch control element");
114 g_type_class_add_private (gobject_class, sizeof (GstPitchPrivate));
116 gobject_class->set_property = gst_pitch_set_property;
117 gobject_class->get_property = gst_pitch_get_property;
118 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_pitch_dispose);
120 g_object_class_install_property (gobject_class, ARG_PITCH,
121 g_param_spec_float ("pitch", "Pitch",
122 "Audio stream pitch", 0.1, 10.0, 1.0,
123 (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)));
125 g_object_class_install_property (gobject_class, ARG_TEMPO,
126 g_param_spec_float ("tempo", "Tempo",
127 "Audio stream tempo", 0.1, 10.0, 1.0,
128 (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)));
130 g_object_class_install_property (gobject_class, ARG_RATE,
131 g_param_spec_float ("rate", "Rate",
132 "Audio stream rate", 0.1, 10.0, 1.0,
133 (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)));
135 g_object_class_install_property (gobject_class, ARG_OUT_RATE,
136 g_param_spec_float ("output-rate", "Output Rate",
137 "Output rate on downstream segment events", 0.1, 10.0, 1.0,
138 (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)));
140 element_class->change_state = GST_DEBUG_FUNCPTR (gst_pitch_change_state);
142 gst_element_class_add_pad_template (element_class,
143 gst_static_pad_template_get (&gst_pitch_src_template));
144 gst_element_class_add_pad_template (element_class,
145 gst_static_pad_template_get (&gst_pitch_sink_template));
147 gst_element_class_set_details_simple (element_class, "Pitch controller",
148 "Filter/Effect/Audio", "Control the pitch of an audio stream",
149 "Wouter Paesen <wouter@blue-gate.be>");
153 gst_pitch_init (GstPitch * pitch)
156 G_TYPE_INSTANCE_GET_PRIVATE ((pitch), GST_TYPE_PITCH, GstPitchPrivate);
159 gst_pad_new_from_static_template (&gst_pitch_sink_template, "sink");
160 gst_pad_set_chain_function (pitch->sinkpad,
161 GST_DEBUG_FUNCPTR (gst_pitch_chain));
162 gst_pad_set_event_function (pitch->sinkpad,
163 GST_DEBUG_FUNCPTR (gst_pitch_sink_event));
164 GST_PAD_SET_PROXY_CAPS (pitch->sinkpad);
165 gst_element_add_pad (GST_ELEMENT (pitch), pitch->sinkpad);
168 gst_pad_new_from_static_template (&gst_pitch_src_template, "src");
169 gst_pad_set_event_function (pitch->srcpad,
170 GST_DEBUG_FUNCPTR (gst_pitch_src_event));
171 gst_pad_set_query_function (pitch->srcpad,
172 GST_DEBUG_FUNCPTR (gst_pitch_src_query));
173 GST_PAD_SET_PROXY_CAPS (pitch->sinkpad);
174 gst_element_add_pad (GST_ELEMENT (pitch), pitch->srcpad);
176 pitch->priv->st = new soundtouch::SoundTouch ();
180 pitch->out_seg_rate = 1.0;
181 pitch->seg_arate = 1.0;
183 pitch->next_buffer_time = GST_CLOCK_TIME_NONE;
184 pitch->next_buffer_offset = 0;
186 pitch->priv->st->setRate (pitch->rate);
187 pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate);
188 pitch->priv->st->setPitch (pitch->pitch);
190 pitch->priv->stream_time_ratio = 1.0;
191 pitch->min_latency = pitch->max_latency = 0;
196 gst_pitch_dispose (GObject * object)
198 GstPitch *pitch = GST_PITCH (object);
200 if (pitch->priv->st) {
201 delete pitch->priv->st;
203 pitch->priv->st = NULL;
206 G_OBJECT_CLASS (parent_class)->dispose (object);
210 gst_pitch_update_duration (GstPitch * pitch)
214 m = gst_message_new_duration (GST_OBJECT (pitch), GST_FORMAT_TIME, -1);
215 gst_element_post_message (GST_ELEMENT (pitch), m);
219 gst_pitch_set_property (GObject * object, guint prop_id,
220 const GValue * value, GParamSpec * pspec)
222 GstPitch *pitch = GST_PITCH (object);
224 GST_OBJECT_LOCK (pitch);
227 pitch->tempo = g_value_get_float (value);
228 pitch->priv->stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate;
229 pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate);
230 GST_OBJECT_UNLOCK (pitch);
231 gst_pitch_update_duration (pitch);
234 pitch->rate = g_value_get_float (value);
235 pitch->priv->stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate;
236 pitch->priv->st->setRate (pitch->rate);
237 GST_OBJECT_UNLOCK (pitch);
238 gst_pitch_update_duration (pitch);
241 /* Has no effect until the next input segment */
242 pitch->out_seg_rate = g_value_get_float (value);
243 GST_OBJECT_UNLOCK (pitch);
245 pitch->pitch = g_value_get_float (value);
246 pitch->priv->st->setPitch (pitch->pitch);
247 GST_OBJECT_UNLOCK (pitch);
250 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
251 GST_OBJECT_UNLOCK (pitch);
257 gst_pitch_get_property (GObject * object, guint prop_id,
258 GValue * value, GParamSpec * pspec)
260 GstPitch *pitch = GST_PITCH (object);
262 GST_OBJECT_LOCK (pitch);
265 g_value_set_float (value, pitch->tempo);
268 g_value_set_float (value, pitch->rate);
271 g_value_set_float (value, pitch->out_seg_rate);
274 g_value_set_float (value, pitch->pitch);
277 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
280 GST_OBJECT_UNLOCK (pitch);
284 gst_pitch_setcaps (GstPitch * pitch, GstCaps * caps)
286 GstPitchPrivate *priv;
287 GstStructure *structure;
290 priv = GST_PITCH_GET_PRIVATE (pitch);
292 structure = gst_caps_get_structure (caps, 0);
294 if (!gst_structure_get_int (structure, "rate", &rate) ||
295 !gst_structure_get_int (structure, "channels", &channels)) {
299 GST_OBJECT_LOCK (pitch);
301 pitch->samplerate = rate;
302 pitch->channels = channels;
304 /* notify the soundtouch instance of this change */
305 priv->st->setSampleRate (rate);
306 priv->st->setChannels (channels);
308 /* calculate sample size */
309 pitch->sample_size = (sizeof (gfloat) * channels);
311 GST_OBJECT_UNLOCK (pitch);
316 /* send a buffer out */
318 gst_pitch_forward_buffer (GstPitch * pitch, GstBuffer * buffer)
322 GST_BUFFER_TIMESTAMP (buffer) = pitch->next_buffer_time;
323 pitch->next_buffer_time += GST_BUFFER_DURATION (buffer);
325 samples = GST_BUFFER_OFFSET (buffer);
326 GST_BUFFER_OFFSET (buffer) = pitch->next_buffer_offset;
327 pitch->next_buffer_offset += samples;
328 GST_BUFFER_OFFSET_END (buffer) = pitch->next_buffer_offset;
330 GST_LOG ("pushing buffer [%" GST_TIME_FORMAT "]-[%" GST_TIME_FORMAT
331 "] (%d samples)", GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
332 GST_TIME_ARGS (pitch->next_buffer_time), samples);
334 return gst_pad_push (pitch->srcpad, buffer);
337 /* extract a buffer from soundtouch */
339 gst_pitch_prepare_buffer (GstPitch * pitch)
341 GstPitchPrivate *priv;
346 priv = GST_PITCH_GET_PRIVATE (pitch);
348 GST_LOG_OBJECT (pitch, "preparing buffer");
350 samples = pitch->priv->st->numSamples ();
354 buffer = gst_buffer_new_and_alloc (samples * pitch->sample_size);
356 gst_buffer_map (buffer, &info, (GstMapFlags) GST_MAP_READWRITE);
358 priv->st->receiveSamples ((gfloat *) info.data, samples);
359 gst_buffer_unmap (buffer, &info);
362 gst_buffer_unref (buffer);
366 GST_BUFFER_DURATION (buffer) =
367 gst_util_uint64_scale (samples, GST_SECOND, pitch->samplerate);
368 /* temporary store samples here, to avoid having to recalculate this */
369 GST_BUFFER_OFFSET (buffer) = (gint64) samples;
374 /* process the last samples, in a later stage we should make sure no more
375 * samples are sent out here as strictly necessary, because soundtouch could
376 * append zero samples, which could disturb looping. */
378 gst_pitch_flush_buffer (GstPitch * pitch, gboolean send)
382 GST_DEBUG_OBJECT (pitch, "flushing buffer");
384 if (pitch->next_buffer_offset == 0)
387 pitch->priv->st->flush ();
391 buffer = gst_pitch_prepare_buffer (pitch);
396 return gst_pitch_forward_buffer (pitch, buffer);
400 gst_pitch_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
405 pitch = GST_PITCH (parent);
407 GST_DEBUG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
409 switch (GST_EVENT_TYPE (event)) {
410 case GST_EVENT_SEEK:{
411 /* transform the event upstream, according to the playback rate */
415 GstSeekType cur_type, stop_type;
417 gfloat stream_time_ratio;
419 GST_OBJECT_LOCK (pitch);
420 stream_time_ratio = pitch->priv->stream_time_ratio;
421 GST_OBJECT_UNLOCK (pitch);
423 gst_event_parse_seek (event, &rate, &format, &flags,
424 &cur_type, &cur, &stop_type, &stop);
426 gst_event_unref (event);
428 if (format == GST_FORMAT_TIME || format == GST_FORMAT_DEFAULT) {
429 cur = (gint64) (cur * stream_time_ratio);
431 stop = (gint64) (stop * stream_time_ratio);
433 event = gst_event_new_seek (rate, format, flags,
434 cur_type, cur, stop_type, stop);
435 res = gst_pad_event_default (pad, parent, event);
437 GST_WARNING_OBJECT (pitch,
438 "Seeking only supported in TIME or DEFAULT format");
444 res = gst_pad_event_default (pad, parent, event);
450 /* generic convert function based on caps, no rate
454 gst_pitch_convert (GstPitch * pitch,
455 GstFormat src_format, gint64 src_value,
456 GstFormat * dst_format, gint64 * dst_value)
462 g_return_val_if_fail (dst_format && dst_value, FALSE);
464 GST_OBJECT_LOCK (pitch);
465 sample_size = pitch->sample_size;
466 samplerate = pitch->samplerate;
467 GST_OBJECT_UNLOCK (pitch);
469 if (sample_size == 0 || samplerate == 0) {
473 if (src_format == *dst_format || src_value == -1) {
474 *dst_value = src_value;
478 switch (src_format) {
479 case GST_FORMAT_BYTES:
480 switch (*dst_format) {
481 case GST_FORMAT_TIME:
483 gst_util_uint64_scale_int (src_value, GST_SECOND,
484 sample_size * samplerate);
486 case GST_FORMAT_DEFAULT:
487 *dst_value = gst_util_uint64_scale_int (src_value, 1, sample_size);
494 case GST_FORMAT_TIME:
495 switch (*dst_format) {
496 case GST_FORMAT_BYTES:
498 gst_util_uint64_scale_int (src_value, samplerate * sample_size,
501 case GST_FORMAT_DEFAULT:
503 gst_util_uint64_scale_int (src_value, samplerate, GST_SECOND);
510 case GST_FORMAT_DEFAULT:
511 switch (*dst_format) {
512 case GST_FORMAT_BYTES:
513 *dst_value = gst_util_uint64_scale_int (src_value, sample_size, 1);
515 case GST_FORMAT_TIME:
517 gst_util_uint64_scale_int (src_value, GST_SECOND, samplerate);
533 gst_pitch_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
536 gboolean res = FALSE;
537 gfloat stream_time_ratio;
538 gint64 next_buffer_offset;
539 GstClockTime next_buffer_time;
541 pitch = GST_PITCH (parent);
543 GST_LOG ("%s query", GST_QUERY_TYPE_NAME (query));
545 GST_OBJECT_LOCK (pitch);
546 stream_time_ratio = pitch->priv->stream_time_ratio;
547 next_buffer_time = pitch->next_buffer_time;
548 next_buffer_offset = pitch->next_buffer_offset;
549 GST_OBJECT_UNLOCK (pitch);
551 switch (GST_QUERY_TYPE (query)) {
552 case GST_QUERY_DURATION:{
556 if (!gst_pad_query_default (pad, parent, query)) {
557 GST_DEBUG_OBJECT (pitch, "upstream provided no duration");
561 gst_query_parse_duration (query, &format, &duration);
563 if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) {
564 GST_DEBUG_OBJECT (pitch, "not TIME or DEFAULT format");
567 GST_LOG_OBJECT (pitch, "upstream duration: %" G_GINT64_FORMAT, duration);
568 duration = (gint64) (duration / stream_time_ratio);
569 GST_LOG_OBJECT (pitch, "our duration: %" G_GINT64_FORMAT, duration);
570 gst_query_set_duration (query, format, duration);
574 case GST_QUERY_POSITION:{
575 GstFormat dst_format;
578 gst_query_parse_position (query, &dst_format, &dst_value);
580 if (dst_format != GST_FORMAT_TIME && dst_format != GST_FORMAT_DEFAULT) {
581 GST_DEBUG_OBJECT (pitch, "not TIME or DEFAULT format");
585 if (dst_format == GST_FORMAT_TIME) {
586 dst_value = next_buffer_time;
589 dst_value = next_buffer_offset;
594 GST_LOG_OBJECT (pitch, "our position: %" G_GINT64_FORMAT, dst_value);
595 gst_query_set_position (query, dst_format, dst_value);
599 case GST_QUERY_CONVERT:{
600 GstFormat src_format, dst_format;
601 gint64 src_value, dst_value;
603 gst_query_parse_convert (query, &src_format, &src_value,
606 res = gst_pitch_convert (pitch, src_format, src_value,
607 &dst_format, &dst_value);
610 gst_query_set_convert (query, src_format, src_value,
611 dst_format, dst_value);
615 case GST_QUERY_LATENCY:
617 GstClockTime min, max;
621 if ((peer = gst_pad_get_peer (pitch->sinkpad))) {
622 if ((res = gst_pad_query (peer, query))) {
623 gst_query_parse_latency (query, &live, &min, &max);
625 GST_DEBUG ("Peer latency: min %"
626 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
627 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
629 /* add our own latency */
631 GST_DEBUG ("Our latency: min %" GST_TIME_FORMAT
632 ", max %" GST_TIME_FORMAT,
633 GST_TIME_ARGS (pitch->min_latency),
634 GST_TIME_ARGS (pitch->max_latency));
636 min += pitch->min_latency;
637 if (max != GST_CLOCK_TIME_NONE)
638 max += pitch->max_latency;
640 max = pitch->max_latency;
642 GST_DEBUG ("Calculated total latency : min %"
643 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
644 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
645 gst_query_set_latency (query, live, min, max);
647 gst_object_unref (peer);
652 res = gst_pad_query_default (pad, parent, query);
659 /* this function returns FALSE if not enough data is known to transform the
660 * segment into proper downstream values. If the function does return false
661 * the sgement should be stalled until enough information is available.
662 * If the funtion returns TRUE, event will be replaced by the new downstream
666 gst_pitch_process_segment (GstPitch * pitch, GstEvent ** event)
668 gdouble out_seg_rate, our_arate;
669 gfloat stream_time_ratio;
672 g_return_val_if_fail (event, FALSE);
674 GST_OBJECT_LOCK (pitch);
675 stream_time_ratio = pitch->priv->stream_time_ratio;
676 out_seg_rate = pitch->out_seg_rate;
677 GST_OBJECT_UNLOCK (pitch);
679 gst_event_copy_segment (*event, &seg);
681 if (seg.format != GST_FORMAT_TIME && seg.format != GST_FORMAT_DEFAULT) {
682 GST_WARNING_OBJECT (pitch,
683 "Only NEWSEGMENT in TIME or DEFAULT format supported, sending"
684 "open ended NEWSEGMENT in TIME format.");
685 seg.format = GST_FORMAT_TIME;
691 /* Figure out how much of the incoming 'rate' we'll apply ourselves */
692 our_arate = seg.rate / out_seg_rate;
693 /* update the output rate variables */
694 seg.rate = out_seg_rate;
695 seg.applied_rate *= our_arate;
697 GST_LOG_OBJECT (pitch->sinkpad,
698 "segment %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%d)", seg.start,
699 seg.stop, seg.format);
701 stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate;
703 if (stream_time_ratio == 0) {
704 GST_LOG_OBJECT (pitch->sinkpad, "stream_time_ratio is zero");
708 /* Update the playback rate */
709 GST_OBJECT_LOCK (pitch);
710 pitch->seg_arate = our_arate;
711 pitch->priv->stream_time_ratio = stream_time_ratio;
712 pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate);
713 GST_OBJECT_UNLOCK (pitch);
715 seg.start = (gint64) (seg.start / stream_time_ratio);
716 if (seg.stop != (guint64) - 1)
717 seg.stop = (gint64) (seg.stop / stream_time_ratio);
718 seg.time = (gint64) (seg.time / stream_time_ratio);
720 gst_event_unref (*event);
721 *event = gst_event_new_segment (&seg);
727 gst_pitch_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
732 pitch = GST_PITCH (parent);
734 GST_LOG_OBJECT (pad, "received %s event", GST_EVENT_TYPE_NAME (event));
736 switch (GST_EVENT_TYPE (event)) {
737 case GST_EVENT_FLUSH_STOP:
738 gst_pitch_flush_buffer (pitch, FALSE);
739 pitch->priv->st->clear ();
740 pitch->next_buffer_offset = 0;
741 pitch->next_buffer_time = GST_CLOCK_TIME_NONE;
742 pitch->min_latency = pitch->max_latency = 0;
745 gst_pitch_flush_buffer (pitch, TRUE);
746 pitch->priv->st->clear ();
747 pitch->min_latency = pitch->max_latency = 0;
749 case GST_EVENT_SEGMENT:
750 if (!gst_pitch_process_segment (pitch, &event)) {
751 GST_LOG_OBJECT (pad, "not enough data known, stalling segment");
752 if (GST_PITCH_GET_PRIVATE (pitch)->pending_segment)
753 gst_event_unref (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
754 GST_PITCH_GET_PRIVATE (pitch)->pending_segment = event;
757 pitch->priv->st->clear ();
758 pitch->min_latency = pitch->max_latency = 0;
764 gst_event_parse_caps (event, &caps);
765 res = gst_pitch_setcaps (pitch, caps);
767 gst_event_unref (event);
777 res = gst_pad_event_default (pad, parent, event);
784 gst_pitch_update_latency (GstPitch * pitch, GstClockTime timestamp)
786 GstClockTimeDiff current_latency, min_latency, max_latency;
789 (GstClockTimeDiff) (timestamp / pitch->priv->stream_time_ratio) -
790 pitch->next_buffer_time;
792 min_latency = MIN (pitch->min_latency, current_latency);
793 max_latency = MAX (pitch->max_latency, current_latency);
795 if (pitch->min_latency != min_latency || pitch->max_latency != max_latency) {
796 pitch->min_latency = min_latency;
797 pitch->max_latency = max_latency;
799 /* FIXME: what about the LATENCY event? It only has
800 * one latency value, should it be current, min or max?
801 * Should it include upstream latencies?
804 gst_element_post_message (GST_ELEMENT (pitch),
805 gst_message_new_latency (GST_OBJECT (pitch)));
810 gst_pitch_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
813 GstPitchPrivate *priv;
814 GstClockTime timestamp;
817 pitch = GST_PITCH (parent);
818 priv = GST_PITCH_GET_PRIVATE (pitch);
820 timestamp = GST_BUFFER_TIMESTAMP (buffer);
822 // Remember the first time and corresponding offset
823 if (!GST_CLOCK_TIME_IS_VALID (pitch->next_buffer_time)) {
824 GstFormat out_format = GST_FORMAT_DEFAULT;
825 pitch->next_buffer_time = timestamp;
826 gst_pitch_convert (pitch, GST_FORMAT_TIME, timestamp, &out_format,
827 &pitch->next_buffer_offset);
830 gst_object_sync_values (GST_OBJECT (pitch), pitch->next_buffer_time);
832 /* push the received samples on the soundtouch buffer */
833 GST_LOG_OBJECT (pitch, "incoming buffer (%d samples) %" GST_TIME_FORMAT,
834 (gint) (gst_buffer_get_size (buffer) / pitch->sample_size),
835 GST_TIME_ARGS (timestamp));
837 if (GST_PITCH_GET_PRIVATE (pitch)->pending_segment) {
839 gst_event_copy (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
841 GST_LOG_OBJECT (pitch, "processing stalled segment");
842 if (!gst_pitch_process_segment (pitch, &event)) {
843 gst_buffer_unref (buffer);
844 gst_event_unref (event);
845 return GST_FLOW_ERROR;
848 if (!gst_pad_event_default (pitch->sinkpad, parent, event)) {
849 gst_buffer_unref (buffer);
850 gst_event_unref (event);
851 return GST_FLOW_ERROR;
854 gst_event_unref (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
855 GST_PITCH_GET_PRIVATE (pitch)->pending_segment = NULL;
858 gst_buffer_map (buffer, &info, GST_MAP_READ);
859 priv->st->putSamples ((gfloat *) info.data, info.size / pitch->sample_size);
860 gst_buffer_unmap (buffer, &info);
861 gst_buffer_unref (buffer);
863 /* Calculate latency */
865 gst_pitch_update_latency (pitch, timestamp);
866 /* and try to extract some samples from the soundtouch buffer */
867 if (!priv->st->isEmpty ()) {
868 GstBuffer *out_buffer;
870 out_buffer = gst_pitch_prepare_buffer (pitch);
872 return gst_pitch_forward_buffer (pitch, out_buffer);
878 static GstStateChangeReturn
879 gst_pitch_change_state (GstElement * element, GstStateChange transition)
881 GstStateChangeReturn ret;
882 GstPitch *pitch = GST_PITCH (element);
884 switch (transition) {
885 case GST_STATE_CHANGE_NULL_TO_READY:
887 case GST_STATE_CHANGE_READY_TO_PAUSED:
888 pitch->next_buffer_time = 0;
889 pitch->next_buffer_offset = 0;
890 pitch->priv->st->clear ();
891 pitch->min_latency = pitch->max_latency = 0;
893 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
899 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
900 if (ret != GST_STATE_CHANGE_SUCCESS)
903 switch (transition) {
904 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
906 case GST_STATE_CHANGE_PAUSED_TO_READY:
907 if (GST_PITCH_GET_PRIVATE (pitch)->pending_segment) {
908 gst_event_unref (GST_PITCH_GET_PRIVATE (pitch)->pending_segment);
909 GST_PITCH_GET_PRIVATE (pitch)->pending_segment = NULL;
912 case GST_STATE_CHANGE_READY_TO_NULL: