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