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