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