3 * Copyright (C) 2008 Rov Juvano <rovjuvano@users.sourceforge.net>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-scaletempo
24 * Scale tempo while maintaining pitch
25 * (WSOLA-like technique with cross correlation)
26 * Inspired by SoundTouch library by Olli Parviainen
28 * Use Sceletempo to apply playback rates without the chipmunk effect.
31 * <title>Example pipelines</title>
34 * filesrc location=media.ext ! decodebin name=d \
35 * d. ! queue ! audioconvert ! audioresample ! scaletempo ! audioconvert ! audioresample ! autoaudiosink \
36 * d. ! queue ! videoconvert ! autovideosink
40 * playbin uri=... audio_sink="scaletempo ! audioconvert ! audioresample ! autoaudiosink"
42 * When an application sends a seek event with rate != 1.0, Scaletempo applies
43 * the rate change by scaling the tempo without scaling the pitch.
45 * Scaletempo works by producing audio in constant sized chunks
46 * (#GstScaletempo:stride) but consuming chunks proportional to the playback
49 * Scaletempo then smooths the output by blending the end of one stride with
50 * the next (#GstScaletempo:overlap).
52 * Scaletempo smooths the overlap further by searching within the input buffer
53 * for the best overlap position. Scaletempo uses a statistical cross
54 * correlation (roughly a dot-product). Scaletempo consumes most of its CPU
55 * cycles here. One can use the #GstScaletempo:search propery to tune how far
62 * Note: frame = audio key unit (i.e. one sample for each channel)
70 #include <gst/base/gstbasetransform.h>
71 #include <gst/audio/audio.h>
72 #include <string.h> /* for memset */
74 #include "gstscaletempo.h"
76 GST_DEBUG_CATEGORY_STATIC (gst_scaletempo_debug);
77 #define GST_CAT_DEFAULT gst_scaletempo_debug
79 /* Filter signals and args */
94 #define SUPPORTED_CAPS \
96 GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (F32)) "; " \
97 GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (S16)) \
100 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
105 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
110 #define DEBUG_INIT(bla) GST_DEBUG_CATEGORY_INIT (gst_scaletempo_debug, "scaletempo", 0, "scaletempo element");
112 #define gst_scaletempo_parent_class parent_class
113 G_DEFINE_TYPE_WITH_CODE (GstScaletempo, gst_scaletempo,
114 GST_TYPE_BASE_TRANSFORM, DEBUG_INIT (0));
117 best_overlap_offset_float (GstScaletempo * st)
119 gfloat *pw, *po, *ppc, *search_start;
120 gfloat best_corr = G_MININT;
124 pw = st->table_window;
125 po = st->buf_overlap;
126 po += st->samples_per_frame;
127 ppc = st->buf_pre_corr;
128 for (i = st->samples_per_frame; i < st->samples_overlap; i++) {
129 *ppc++ = *pw++ * *po++;
132 search_start = (gfloat *) st->buf_queue + st->samples_per_frame;
133 for (off = 0; off < st->frames_search; off++) {
135 gfloat *ps = search_start;
136 ppc = st->buf_pre_corr;
137 for (i = st->samples_per_frame; i < st->samples_overlap; i++) {
138 corr += *ppc++ * *ps++;
140 if (corr > best_corr) {
144 search_start += st->samples_per_frame;
147 return best_off * st->bytes_per_frame;
150 /* buffer padding for loop optimization: sizeof(gint32) * (loop_size - 1) */
151 #define UNROLL_PADDING (4*3)
153 best_overlap_offset_s16 (GstScaletempo * st)
156 gint16 *po, *search_start;
157 gint64 best_corr = G_MININT64;
162 pw = st->table_window;
163 po = st->buf_overlap;
164 po += st->samples_per_frame;
165 ppc = st->buf_pre_corr;
166 for (i = st->samples_per_frame; i < st->samples_overlap; i++) {
167 *ppc++ = (*pw++ * *po++) >> 15;
170 search_start = (gint16 *) st->buf_queue + st->samples_per_frame;
171 for (off = 0; off < st->frames_search; off++) {
173 gint16 *ps = search_start;
174 ppc = st->buf_pre_corr;
175 ppc += st->samples_overlap - st->samples_per_frame;
176 ps += st->samples_overlap - st->samples_per_frame;
177 i = -((glong) st->samples_overlap - (glong) st->samples_per_frame);
179 corr += ppc[i + 0] * ps[i + 0];
180 corr += ppc[i + 1] * ps[i + 1];
181 corr += ppc[i + 2] * ps[i + 2];
182 corr += ppc[i + 3] * ps[i + 3];
185 if (corr > best_corr) {
189 search_start += st->samples_per_frame;
192 return best_off * st->bytes_per_frame;
196 output_overlap_float (GstScaletempo * st, gpointer buf_out, guint bytes_off)
198 gfloat *pout = buf_out;
199 gfloat *pb = st->table_blend;
200 gfloat *po = st->buf_overlap;
201 gfloat *pin = (gfloat *) (st->buf_queue + bytes_off);
203 for (i = 0; i < st->samples_overlap; i++) {
204 *pout++ = *po - *pb++ * (*po - *pin++);
210 output_overlap_s16 (GstScaletempo * st, gpointer buf_out, guint bytes_off)
212 gint16 *pout = buf_out;
213 gint32 *pb = st->table_blend;
214 gint16 *po = st->buf_overlap;
215 gint16 *pin = (gint16 *) (st->buf_queue + bytes_off);
217 for (i = 0; i < st->samples_overlap; i++) {
218 *pout++ = *po - ((*pb++ * (*po - *pin++)) >> 16);
224 fill_queue (GstScaletempo * st, GstBuffer * buf_in, guint offset)
226 guint bytes_in = gst_buffer_get_size (buf_in) - offset;
227 guint offset_unchanged = offset;
230 gst_buffer_map (buf_in, &map, GST_MAP_READ);
231 if (st->bytes_to_slide > 0) {
232 if (st->bytes_to_slide < st->bytes_queued) {
233 guint bytes_in_move = st->bytes_queued - st->bytes_to_slide;
234 memmove (st->buf_queue, st->buf_queue + st->bytes_to_slide,
236 st->bytes_to_slide = 0;
237 st->bytes_queued = bytes_in_move;
240 st->bytes_to_slide -= st->bytes_queued;
241 bytes_in_skip = MIN (st->bytes_to_slide, bytes_in);
242 st->bytes_queued = 0;
243 st->bytes_to_slide -= bytes_in_skip;
244 offset += bytes_in_skip;
245 bytes_in -= bytes_in_skip;
250 guint bytes_in_copy =
251 MIN (st->bytes_queue_max - st->bytes_queued, bytes_in);
252 memcpy (st->buf_queue + st->bytes_queued, map.data + offset, bytes_in_copy);
253 st->bytes_queued += bytes_in_copy;
254 offset += bytes_in_copy;
256 gst_buffer_unmap (buf_in, &map);
258 return offset - offset_unchanged;
262 reinit_buffers (GstScaletempo * st)
265 guint frames_overlap;
267 GstClockTime latency;
269 guint frames_stride = st->ms_stride * st->sample_rate / 1000.0;
270 st->bytes_stride = frames_stride * st->bytes_per_frame;
273 frames_overlap = frames_stride * st->percent_overlap;
274 if (frames_overlap < 1) { /* if no overlap */
275 st->bytes_overlap = 0;
276 st->bytes_standing = st->bytes_stride;
277 st->samples_standing = st->bytes_standing / st->bytes_per_sample;
278 st->output_overlap = NULL;
280 guint prev_overlap = st->bytes_overlap;
281 st->bytes_overlap = frames_overlap * st->bytes_per_frame;
282 st->samples_overlap = frames_overlap * st->samples_per_frame;
283 st->bytes_standing = st->bytes_stride - st->bytes_overlap;
284 st->samples_standing = st->bytes_standing / st->bytes_per_sample;
285 st->buf_overlap = g_realloc (st->buf_overlap, st->bytes_overlap);
286 st->table_blend = g_realloc (st->table_blend, st->samples_overlap * 4); /* sizeof (gint32|gfloat) */
287 if (st->bytes_overlap > prev_overlap) {
288 memset ((guint8 *) st->buf_overlap + prev_overlap, 0,
289 st->bytes_overlap - prev_overlap);
292 gint32 *pb = st->table_blend;
294 for (i = 0; i < frames_overlap; i++) {
295 gint32 v = blend / frames_overlap;
296 for (j = 0; j < st->samples_per_frame; j++) {
299 blend += 65535; /* 2^16 */
301 st->output_overlap = output_overlap_s16;
303 gfloat *pb = st->table_blend;
304 gfloat t = (gfloat) frames_overlap;
305 for (i = 0; i < frames_overlap; i++) {
307 for (j = 0; j < st->samples_per_frame; j++) {
311 st->output_overlap = output_overlap_float;
317 (frames_overlap <= 1) ? 0 : st->ms_search * st->sample_rate / 1000.0;
318 if (st->frames_search < 1) { /* if no search */
319 st->best_overlap_offset = NULL;
321 guint bytes_pre_corr = (st->samples_overlap - st->samples_per_frame) * 4; /* sizeof (gint32|gfloat) */
323 g_realloc (st->buf_pre_corr, bytes_pre_corr + UNROLL_PADDING);
324 st->table_window = g_realloc (st->table_window, bytes_pre_corr);
326 gint64 t = frames_overlap;
327 gint32 n = 8589934588LL / (t * t); /* 4 * (2^31 - 1) / t^2 */
330 memset ((guint8 *) st->buf_pre_corr + bytes_pre_corr, 0, UNROLL_PADDING);
331 pw = st->table_window;
332 for (i = 1; i < frames_overlap; i++) {
333 gint32 v = (i * (t - i) * n) >> 15;
334 for (j = 0; j < st->samples_per_frame; j++) {
338 st->best_overlap_offset = best_overlap_offset_s16;
340 gfloat *pw = st->table_window;
341 for (i = 1; i < frames_overlap; i++) {
342 gfloat v = i * (frames_overlap - i);
343 for (j = 0; j < st->samples_per_frame; j++) {
347 st->best_overlap_offset = best_overlap_offset_float;
352 (st->frames_search + frames_stride +
353 frames_overlap) * st->bytes_per_frame;
354 if (st->bytes_queued > new_size) {
355 if (st->bytes_to_slide > st->bytes_queued) {
356 st->bytes_to_slide -= st->bytes_queued;
357 st->bytes_queued = 0;
359 guint new_queued = MIN (st->bytes_queued - st->bytes_to_slide, new_size);
360 memmove (st->buf_queue,
361 st->buf_queue + st->bytes_queued - new_queued, new_queued);
362 st->bytes_to_slide = 0;
363 st->bytes_queued = new_queued;
367 st->bytes_queue_max = new_size;
368 st->buf_queue = g_realloc (st->buf_queue, st->bytes_queue_max);
371 gst_util_uint64_scale (st->bytes_queue_max, GST_SECOND,
372 st->bytes_per_frame * st->sample_rate);
373 if (st->latency != latency) {
374 st->latency = latency;
375 gst_element_post_message (GST_ELEMENT (st),
376 gst_message_new_latency (GST_OBJECT (st)));
379 st->bytes_stride_scaled = st->bytes_stride * st->scale;
380 st->frames_stride_scaled = st->bytes_stride_scaled / st->bytes_per_frame;
383 ("%.3f scale, %.3f stride_in, %i stride_out, %i standing, %i overlap, %i search, %i queue, %s mode",
384 st->scale, st->frames_stride_scaled,
385 (gint) (st->bytes_stride / st->bytes_per_frame),
386 (gint) (st->bytes_standing / st->bytes_per_frame),
387 (gint) (st->bytes_overlap / st->bytes_per_frame), st->frames_search,
388 (gint) (st->bytes_queue_max / st->bytes_per_frame),
389 (st->use_int ? "s16" : "float"));
391 st->reinit_buffers = FALSE;
395 /* GstBaseTransform vmethod implementations */
397 gst_scaletempo_transform (GstBaseTransform * trans,
398 GstBuffer * inbuf, GstBuffer * outbuf)
400 GstScaletempo *st = GST_SCALETEMPO (trans);
402 guint offset_in, bytes_out;
404 GstClockTime timestamp;
406 gst_buffer_map (outbuf, &omap, GST_MAP_WRITE);
407 pout = (gint8 *) omap.data;
408 offset_in = fill_queue (st, inbuf, 0);
410 while (st->bytes_queued >= st->bytes_queue_max) {
412 gdouble frames_to_slide;
413 guint frames_to_stride_whole;
416 if (st->output_overlap) {
417 if (st->best_overlap_offset) {
418 bytes_off = st->best_overlap_offset (st);
420 st->output_overlap (st, pout, bytes_off);
422 memcpy (pout + st->bytes_overlap,
423 st->buf_queue + bytes_off + st->bytes_overlap, st->bytes_standing);
424 pout += st->bytes_stride;
425 bytes_out += st->bytes_stride;
428 memcpy (st->buf_overlap,
429 st->buf_queue + bytes_off + st->bytes_stride, st->bytes_overlap);
430 frames_to_slide = st->frames_stride_scaled + st->frames_stride_error;
431 frames_to_stride_whole = (gint) frames_to_slide;
432 st->bytes_to_slide = frames_to_stride_whole * st->bytes_per_frame;
433 st->frames_stride_error = frames_to_slide - frames_to_stride_whole;
435 offset_in += fill_queue (st, inbuf, offset_in);
438 gst_buffer_unmap (outbuf, &omap);
440 timestamp = GST_BUFFER_TIMESTAMP (inbuf) - st->segment_start;
441 if (timestamp < st->latency)
444 timestamp -= st->latency;
445 GST_BUFFER_TIMESTAMP (outbuf) = timestamp / st->scale + st->segment_start;
446 GST_BUFFER_DURATION (outbuf) =
447 gst_util_uint64_scale (bytes_out, GST_SECOND,
448 st->bytes_per_frame * st->sample_rate);
449 gst_buffer_set_size (outbuf, bytes_out);
455 gst_scaletempo_transform_size (GstBaseTransform * trans,
456 GstPadDirection direction,
457 GstCaps * caps, gsize size, GstCaps * othercaps, gsize * othersize)
459 if (direction == GST_PAD_SINK) {
460 GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
463 if (scaletempo->reinit_buffers)
464 reinit_buffers (scaletempo);
466 bytes_to_out = size + scaletempo->bytes_queued - scaletempo->bytes_to_slide;
467 if (bytes_to_out < (gint) scaletempo->bytes_queue_max) {
470 /* while (total_buffered - stride_length * n >= queue_max) n++ */
471 *othersize = scaletempo->bytes_stride * ((guint) (
472 (bytes_to_out - scaletempo->bytes_queue_max +
473 /* rounding protection */ scaletempo->bytes_per_frame)
474 / scaletempo->bytes_stride_scaled) + 1);
483 gst_scaletempo_sink_event (GstBaseTransform * trans, GstEvent * event)
485 if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
486 GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
489 gst_event_copy_segment (event, &segment);
491 if (scaletempo->scale != segment.rate) {
492 if (ABS (segment.rate - 1.0) < 1e-10) {
493 scaletempo->scale = 1.0;
494 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (scaletempo),
497 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (scaletempo),
499 scaletempo->scale = segment.rate;
500 scaletempo->bytes_stride_scaled =
501 scaletempo->bytes_stride * scaletempo->scale;
502 scaletempo->frames_stride_scaled =
503 scaletempo->bytes_stride_scaled / scaletempo->bytes_per_frame;
504 GST_DEBUG ("%.3f scale, %.3f stride_in, %i stride_out",
505 scaletempo->scale, scaletempo->frames_stride_scaled,
506 (gint) (scaletempo->bytes_stride / scaletempo->bytes_per_frame));
508 scaletempo->bytes_to_slide = 0;
512 if (scaletempo->scale != 1.0) {
513 scaletempo->segment_start = segment.start;
514 segment.applied_rate = scaletempo->scale;
516 gst_event_unref (event);
518 if (segment.stop != -1) {
519 segment.stop = (segment.stop - segment.start) / segment.applied_rate +
523 event = gst_event_new_segment (&segment);
524 gst_pad_push_event (GST_BASE_TRANSFORM_SRC_PAD (trans), event);
528 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
532 gst_scaletempo_set_caps (GstBaseTransform * trans,
533 GstCaps * incaps, GstCaps * outcaps)
535 GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
537 gint width, bps, nch, rate;
541 if (!gst_audio_info_from_caps (&info, incaps))
544 nch = GST_AUDIO_INFO_CHANNELS (&info);
545 rate = GST_AUDIO_INFO_RATE (&info);
546 width = GST_AUDIO_INFO_WIDTH (&info);
547 use_int = GST_AUDIO_INFO_IS_INTEGER (&info);
551 GST_DEBUG ("caps: %" GST_PTR_FORMAT ", %d bps", incaps, bps);
553 if (rate != scaletempo->sample_rate
554 || nch != scaletempo->samples_per_frame
555 || bps != scaletempo->bytes_per_sample
556 || use_int != scaletempo->use_int) {
557 scaletempo->sample_rate = rate;
558 scaletempo->samples_per_frame = nch;
559 scaletempo->bytes_per_sample = bps;
560 scaletempo->bytes_per_frame = nch * bps;
561 scaletempo->use_int = use_int;
562 scaletempo->reinit_buffers = TRUE;
569 gst_scaletempo_query (GstBaseTransform * trans, GstPadDirection direction,
572 GstScaletempo *scaletempo = GST_SCALETEMPO (trans);
574 if (direction == GST_PAD_SRC) {
575 switch (GST_QUERY_TYPE (query)) {
576 case GST_QUERY_LATENCY:{
580 if ((peer = gst_pad_get_peer (GST_BASE_TRANSFORM_SINK_PAD (trans)))) {
581 if ((res = gst_pad_query (peer, query))) {
582 GstClockTime min, max;
585 gst_query_parse_latency (query, &live, &min, &max);
587 GST_DEBUG_OBJECT (scaletempo, "Peer latency: min %"
588 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
589 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
591 /* add our own latency */
592 GST_DEBUG_OBJECT (scaletempo, "Our latency: %" GST_TIME_FORMAT,
593 GST_TIME_ARGS (scaletempo->latency));
594 min += scaletempo->latency;
595 if (max != GST_CLOCK_TIME_NONE)
596 max += scaletempo->latency;
598 GST_DEBUG_OBJECT (scaletempo, "Calculated total latency : min %"
599 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
600 GST_TIME_ARGS (min), GST_TIME_ARGS (max));
601 gst_query_set_latency (query, live, min, max);
603 gst_object_unref (peer);
610 return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
616 return GST_BASE_TRANSFORM_CLASS (parent_class)->query (trans, direction,
621 /* GObject vmethod implementations */
623 gst_scaletempo_get_property (GObject * object,
624 guint prop_id, GValue * value, GParamSpec * pspec)
626 GstScaletempo *scaletempo = GST_SCALETEMPO (object);
630 g_value_set_double (value, scaletempo->scale);
633 g_value_set_uint (value, scaletempo->ms_stride);
636 g_value_set_double (value, scaletempo->percent_overlap);
639 g_value_set_uint (value, scaletempo->ms_search);
642 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
648 gst_scaletempo_set_property (GObject * object,
649 guint prop_id, const GValue * value, GParamSpec * pspec)
651 GstScaletempo *scaletempo = GST_SCALETEMPO (object);
655 guint new_value = g_value_get_uint (value);
656 if (scaletempo->ms_stride != new_value) {
657 scaletempo->ms_stride = new_value;
658 scaletempo->reinit_buffers = TRUE;
663 gdouble new_value = g_value_get_double (value);
664 if (scaletempo->percent_overlap != new_value) {
665 scaletempo->percent_overlap = new_value;
666 scaletempo->reinit_buffers = TRUE;
671 guint new_value = g_value_get_uint (value);
672 if (scaletempo->ms_search != new_value) {
673 scaletempo->ms_search = new_value;
674 scaletempo->reinit_buffers = TRUE;
679 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
685 gst_scaletempo_class_init (GstScaletempoClass * klass)
687 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
688 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
689 GstBaseTransformClass *basetransform_class = GST_BASE_TRANSFORM_CLASS (klass);
691 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_scaletempo_get_property);
692 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_scaletempo_set_property);
694 g_object_class_install_property (gobject_class, PROP_RATE,
695 g_param_spec_double ("rate", "Playback Rate", "Current playback rate",
696 G_MININT, G_MAXINT, 1.0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
698 g_object_class_install_property (gobject_class, PROP_STRIDE,
699 g_param_spec_uint ("stride", "Stride Length",
700 "Length in milliseconds to output each stride", 1, 5000, 30,
701 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
703 g_object_class_install_property (gobject_class, PROP_OVERLAP,
704 g_param_spec_double ("overlap", "Overlap Length",
705 "Percentage of stride to overlap", 0, 1, .2,
706 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
708 g_object_class_install_property (gobject_class, PROP_SEARCH,
709 g_param_spec_uint ("search", "Search Length",
710 "Length in milliseconds to search for best overlap position", 0, 500,
711 14, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
713 gst_element_class_add_pad_template (gstelement_class,
714 gst_static_pad_template_get (&src_template));
715 gst_element_class_add_pad_template (gstelement_class,
716 gst_static_pad_template_get (&sink_template));
717 gst_element_class_set_static_metadata (gstelement_class, "Scaletempo",
718 "Filter/Effect/Rate",
719 "Sync audio tempo with playback rate",
720 "Rov Juvano <rovjuvano@users.sourceforge.net>");
722 basetransform_class->sink_event =
723 GST_DEBUG_FUNCPTR (gst_scaletempo_sink_event);
724 basetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_scaletempo_set_caps);
725 basetransform_class->transform_size =
726 GST_DEBUG_FUNCPTR (gst_scaletempo_transform_size);
727 basetransform_class->transform = GST_DEBUG_FUNCPTR (gst_scaletempo_transform);
728 basetransform_class->query = GST_DEBUG_FUNCPTR (gst_scaletempo_query);
732 gst_scaletempo_init (GstScaletempo * scaletempo)
735 scaletempo->ms_stride = 30;
736 scaletempo->percent_overlap = .2;
737 scaletempo->ms_search = 14;
740 scaletempo->scale = 0;
741 scaletempo->sample_rate = 0;
742 scaletempo->frames_stride_error = 0;
743 scaletempo->bytes_stride = 0;
744 scaletempo->bytes_queued = 0;
745 scaletempo->bytes_to_slide = 0;
746 scaletempo->segment_start = 0;