audioresample: provide as much valid output ts and offset as valid input
[platform/upstream/gstreamer.git] / gst / audioresample / gstaudioresample.c
1 /* GStreamer
2  * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2003,2004 David A. Schleef <ds@schleef.org>
4  * Copyright (C) 2007-2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:element-audioresample
24  *
25  * audioresample resamples raw audio buffers to different sample rates using
26  * a configurable windowing function to enhance quality.
27  *
28  * <refsect2>
29  * <title>Example launch line</title>
30  * |[
31  * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! audio/x-raw-int, rate=8000 ! alsasink
32  * ]| Decode an Ogg/Vorbis downsample to 8Khz and play sound through alsa.
33  * To create the Ogg/Vorbis file refer to the documentation of vorbisenc.
34  * </refsect2>
35  */
36
37 /* TODO:
38  *  - Enable SSE/ARM optimizations and select at runtime
39  */
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #include <string.h>
46 #include <math.h>
47
48 #include "gstaudioresample.h"
49 #include <gst/gstutils.h>
50 #include <gst/audio/audio.h>
51 #include <gst/base/gstbasetransform.h>
52
53 #ifndef DISABLE_ORC
54 #include <orc/orc.h>
55 #include <orc-test/orctest.h>
56 #include <orc-test/orcprofile.h>
57 #endif
58
59 GST_DEBUG_CATEGORY (audio_resample_debug);
60 #define GST_CAT_DEFAULT audio_resample_debug
61
62 enum
63 {
64   PROP_0,
65   PROP_QUALITY,
66   PROP_FILTER_LENGTH
67 };
68
69 #define SUPPORTED_CAPS \
70 GST_STATIC_CAPS ( \
71     "audio/x-raw-float, " \
72       "rate = (int) [ 1, MAX ], "       \
73       "channels = (int) [ 1, MAX ], " \
74       "endianness = (int) BYTE_ORDER, " \
75       "width = (int) { 32, 64 }; " \
76     "audio/x-raw-int, " \
77       "rate = (int) [ 1, MAX ], " \
78       "channels = (int) [ 1, MAX ], " \
79       "endianness = (int) BYTE_ORDER, " \
80       "width = (int) 32, " \
81       "depth = (int) 32, " \
82       "signed = (boolean) true; " \
83     "audio/x-raw-int, " \
84       "rate = (int) [ 1, MAX ], " \
85       "channels = (int) [ 1, MAX ], " \
86       "endianness = (int) BYTE_ORDER, " \
87       "width = (int) 24, " \
88       "depth = (int) 24, " \
89       "signed = (boolean) true; " \
90     "audio/x-raw-int, " \
91       "rate = (int) [ 1, MAX ], " \
92       "channels = (int) [ 1, MAX ], " \
93       "endianness = (int) BYTE_ORDER, " \
94       "width = (int) 16, " \
95       "depth = (int) 16, " \
96       "signed = (boolean) true; " \
97     "audio/x-raw-int, " \
98       "rate = (int) [ 1, MAX ], " \
99       "channels = (int) [ 1, MAX ], " \
100       "endianness = (int) BYTE_ORDER, " \
101       "width = (int) 8, " \
102       "depth = (int) 8, " \
103       "signed = (boolean) true" \
104 )
105
106 /* If TRUE integer arithmetic resampling is faster and will be used if appropiate */
107 #if defined AUDIORESAMPLE_FORMAT_INT
108 static gboolean gst_audio_resample_use_int = TRUE;
109 #elif defined AUDIORESAMPLE_FORMAT_FLOAT
110 static gboolean gst_audio_resample_use_int = FALSE;
111 #else
112 static gboolean gst_audio_resample_use_int = FALSE;
113 #endif
114
115 static GstStaticPadTemplate gst_audio_resample_sink_template =
116 GST_STATIC_PAD_TEMPLATE ("sink",
117     GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS);
118
119 static GstStaticPadTemplate gst_audio_resample_src_template =
120 GST_STATIC_PAD_TEMPLATE ("src",
121     GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS);
122
123 static void gst_audio_resample_set_property (GObject * object,
124     guint prop_id, const GValue * value, GParamSpec * pspec);
125 static void gst_audio_resample_get_property (GObject * object,
126     guint prop_id, GValue * value, GParamSpec * pspec);
127
128 /* vmethods */
129 static gboolean gst_audio_resample_get_unit_size (GstBaseTransform * base,
130     GstCaps * caps, guint * size);
131 static GstCaps *gst_audio_resample_transform_caps (GstBaseTransform * base,
132     GstPadDirection direction, GstCaps * caps);
133 static void gst_audio_resample_fixate_caps (GstBaseTransform * base,
134     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
135 static gboolean gst_audio_resample_transform_size (GstBaseTransform * trans,
136     GstPadDirection direction, GstCaps * incaps, guint insize,
137     GstCaps * outcaps, guint * outsize);
138 static gboolean gst_audio_resample_set_caps (GstBaseTransform * base,
139     GstCaps * incaps, GstCaps * outcaps);
140 static GstFlowReturn gst_audio_resample_transform (GstBaseTransform * base,
141     GstBuffer * inbuf, GstBuffer * outbuf);
142 static gboolean gst_audio_resample_event (GstBaseTransform * base,
143     GstEvent * event);
144 static gboolean gst_audio_resample_start (GstBaseTransform * base);
145 static gboolean gst_audio_resample_stop (GstBaseTransform * base);
146 static gboolean gst_audio_resample_query (GstPad * pad, GstQuery * query);
147 static const GstQueryType *gst_audio_resample_query_type (GstPad * pad);
148
149 GST_BOILERPLATE (GstAudioResample, gst_audio_resample, GstBaseTransform,
150     GST_TYPE_BASE_TRANSFORM);
151
152 static void
153 gst_audio_resample_base_init (gpointer g_class)
154 {
155   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
156
157   gst_element_class_add_pad_template (gstelement_class,
158       gst_static_pad_template_get (&gst_audio_resample_src_template));
159   gst_element_class_add_pad_template (gstelement_class,
160       gst_static_pad_template_get (&gst_audio_resample_sink_template));
161
162   gst_element_class_set_details_simple (gstelement_class, "Audio resampler",
163       "Filter/Converter/Audio", "Resamples audio",
164       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
165 }
166
167 static void
168 gst_audio_resample_class_init (GstAudioResampleClass * klass)
169 {
170   GObjectClass *gobject_class = (GObjectClass *) klass;
171
172   gobject_class->set_property = gst_audio_resample_set_property;
173   gobject_class->get_property = gst_audio_resample_get_property;
174
175   g_object_class_install_property (gobject_class, PROP_QUALITY,
176       g_param_spec_int ("quality", "Quality", "Resample quality with 0 being "
177           "the lowest and 10 being the best",
178           SPEEX_RESAMPLER_QUALITY_MIN, SPEEX_RESAMPLER_QUALITY_MAX,
179           SPEEX_RESAMPLER_QUALITY_DEFAULT,
180           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
181
182   /* FIXME 0.11: Remove this property, it's just for compatibility
183    * with old audioresample
184    */
185   /**
186    * GstAudioResample:filter-length:
187    *
188    * Length of the resample filter
189    *
190    * Deprectated: Use #GstAudioResample:quality property instead
191    */
192   g_object_class_install_property (gobject_class, PROP_FILTER_LENGTH,
193       g_param_spec_int ("filter-length", "Filter length",
194           "Length of the resample filter", 0, G_MAXINT, 64,
195           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
196
197   GST_BASE_TRANSFORM_CLASS (klass)->start =
198       GST_DEBUG_FUNCPTR (gst_audio_resample_start);
199   GST_BASE_TRANSFORM_CLASS (klass)->stop =
200       GST_DEBUG_FUNCPTR (gst_audio_resample_stop);
201   GST_BASE_TRANSFORM_CLASS (klass)->transform_size =
202       GST_DEBUG_FUNCPTR (gst_audio_resample_transform_size);
203   GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size =
204       GST_DEBUG_FUNCPTR (gst_audio_resample_get_unit_size);
205   GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
206       GST_DEBUG_FUNCPTR (gst_audio_resample_transform_caps);
207   GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps =
208       GST_DEBUG_FUNCPTR (gst_audio_resample_fixate_caps);
209   GST_BASE_TRANSFORM_CLASS (klass)->set_caps =
210       GST_DEBUG_FUNCPTR (gst_audio_resample_set_caps);
211   GST_BASE_TRANSFORM_CLASS (klass)->transform =
212       GST_DEBUG_FUNCPTR (gst_audio_resample_transform);
213   GST_BASE_TRANSFORM_CLASS (klass)->event =
214       GST_DEBUG_FUNCPTR (gst_audio_resample_event);
215
216   GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE;
217 }
218
219 static void
220 gst_audio_resample_init (GstAudioResample * resample,
221     GstAudioResampleClass * klass)
222 {
223   GstBaseTransform *trans = GST_BASE_TRANSFORM (resample);
224
225   resample->quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
226
227   gst_pad_set_query_function (trans->srcpad, gst_audio_resample_query);
228   gst_pad_set_query_type_function (trans->srcpad,
229       gst_audio_resample_query_type);
230 }
231
232 /* vmethods */
233 static gboolean
234 gst_audio_resample_start (GstBaseTransform * base)
235 {
236   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
237
238   resample->need_discont = TRUE;
239
240   resample->t0 = GST_CLOCK_TIME_NONE;
241   resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
242   resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
243   resample->samples_in = 0;
244   resample->samples_out = 0;
245
246   resample->tmp_in = NULL;
247   resample->tmp_in_size = 0;
248   resample->tmp_out = NULL;
249   resample->tmp_out_size = 0;
250
251   return TRUE;
252 }
253
254 static gboolean
255 gst_audio_resample_stop (GstBaseTransform * base)
256 {
257   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
258
259   if (resample->state) {
260     resample->funcs->destroy (resample->state);
261     resample->state = NULL;
262   }
263
264   resample->funcs = NULL;
265
266   g_free (resample->tmp_in);
267   resample->tmp_in = NULL;
268   resample->tmp_in_size = 0;
269
270   g_free (resample->tmp_out);
271   resample->tmp_out = NULL;
272   resample->tmp_out_size = 0;
273
274   gst_caps_replace (&resample->sinkcaps, NULL);
275   gst_caps_replace (&resample->srccaps, NULL);
276
277   return TRUE;
278 }
279
280 static gboolean
281 gst_audio_resample_get_unit_size (GstBaseTransform * base, GstCaps * caps,
282     guint * size)
283 {
284   gint width, channels;
285   GstStructure *structure;
286   gboolean ret;
287
288   g_return_val_if_fail (size != NULL, FALSE);
289
290   /* this works for both float and int */
291   structure = gst_caps_get_structure (caps, 0);
292   ret = gst_structure_get_int (structure, "width", &width);
293   ret &= gst_structure_get_int (structure, "channels", &channels);
294
295   if (G_UNLIKELY (!ret))
296     return FALSE;
297
298   *size = (width / 8) * channels;
299
300   return TRUE;
301 }
302
303 static GstCaps *
304 gst_audio_resample_transform_caps (GstBaseTransform * base,
305     GstPadDirection direction, GstCaps * caps)
306 {
307   const GValue *val;
308   GstStructure *s;
309   GstCaps *res;
310
311   /* transform single caps into input_caps + input_caps with the rate
312    * field set to our supported range. This ensures that upstream knows
313    * about downstream's prefered rate(s) and can negotiate accordingly. */
314   res = gst_caps_copy (caps);
315
316   /* first, however, check if the caps contain a range for the rate field, in
317    * which case that side isn't going to care much about the exact sample rate
318    * chosen and we should just assume things will get fixated to something sane
319    * and we may just as well offer our full range instead of the range in the
320    * caps. If the rate is not an int range value, it's likely to express a
321    * real preference or limitation and we should maintain that structure as
322    * preference by putting it first into the transformed caps, and only add
323    * our full rate range as second option  */
324   s = gst_caps_get_structure (res, 0);
325   val = gst_structure_get_value (s, "rate");
326   if (val == NULL || GST_VALUE_HOLDS_INT_RANGE (val)) {
327     /* overwrite existing range, or add field if it doesn't exist yet */
328     gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
329   } else {
330     /* append caps with full range to existing caps with non-range rate field */
331     s = gst_structure_copy (s);
332     gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
333     gst_caps_append_structure (res, s);
334   }
335
336   return res;
337 }
338
339 /* Fixate rate to the allowed rate that has the smallest difference */
340 static void
341 gst_audio_resample_fixate_caps (GstBaseTransform * base,
342     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
343 {
344   GstStructure *s;
345   gint rate;
346
347   s = gst_caps_get_structure (caps, 0);
348   if (G_UNLIKELY (!gst_structure_get_int (s, "rate", &rate)))
349     return;
350
351   s = gst_caps_get_structure (othercaps, 0);
352   gst_structure_fixate_field_nearest_int (s, "rate", rate);
353 }
354
355 static const SpeexResampleFuncs *
356 gst_audio_resample_get_funcs (gint width, gboolean fp)
357 {
358   const SpeexResampleFuncs *funcs = NULL;
359
360   if (gst_audio_resample_use_int && (width == 8 || width == 16) && !fp)
361     funcs = &int_funcs;
362   else if ((!gst_audio_resample_use_int && (width == 8 || width == 16) && !fp)
363       || (width == 32 && fp))
364     funcs = &float_funcs;
365   else if ((width == 64 && fp) || ((width == 32 || width == 24) && !fp))
366     funcs = &double_funcs;
367   else
368     g_assert_not_reached ();
369
370   return funcs;
371 }
372
373 static SpeexResamplerState *
374 gst_audio_resample_init_state (GstAudioResample * resample, gint width,
375     gint channels, gint inrate, gint outrate, gint quality, gboolean fp)
376 {
377   SpeexResamplerState *ret = NULL;
378   gint err = RESAMPLER_ERR_SUCCESS;
379   const SpeexResampleFuncs *funcs = gst_audio_resample_get_funcs (width, fp);
380
381   ret = funcs->init (channels, inrate, outrate, quality, &err);
382
383   if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) {
384     GST_ERROR_OBJECT (resample, "Failed to create resampler state: %s",
385         funcs->strerror (err));
386     return NULL;
387   }
388
389   funcs->skip_zeros (ret);
390
391   return ret;
392 }
393
394 static gboolean
395 gst_audio_resample_update_state (GstAudioResample * resample, gint width,
396     gint channels, gint inrate, gint outrate, gint quality, gboolean fp)
397 {
398   gboolean ret = TRUE;
399   gboolean updated_latency = FALSE;
400
401   updated_latency = (resample->inrate != inrate
402       || quality != resample->quality) && resample->state != NULL;
403
404   if (resample->state == NULL) {
405     ret = TRUE;
406   } else if (resample->channels != channels || fp != resample->fp
407       || width != resample->width) {
408     resample->funcs->destroy (resample->state);
409     resample->state =
410         gst_audio_resample_init_state (resample, width, channels, inrate,
411         outrate, quality, fp);
412
413     resample->funcs = gst_audio_resample_get_funcs (width, fp);
414     ret = (resample->state != NULL);
415   } else if (resample->inrate != inrate || resample->outrate != outrate) {
416     gint err = RESAMPLER_ERR_SUCCESS;
417
418     err = resample->funcs->set_rate (resample->state, inrate, outrate);
419
420     if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS))
421       GST_ERROR_OBJECT (resample, "Failed to update rate: %s",
422           resample->funcs->strerror (err));
423
424     ret = (err == RESAMPLER_ERR_SUCCESS);
425   } else if (quality != resample->quality) {
426     gint err = RESAMPLER_ERR_SUCCESS;
427
428     err = resample->funcs->set_quality (resample->state, quality);
429
430     if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS))
431       GST_ERROR_OBJECT (resample, "Failed to update quality: %s",
432           resample->funcs->strerror (err));
433
434     ret = (err == RESAMPLER_ERR_SUCCESS);
435   }
436
437   resample->width = width;
438   resample->channels = channels;
439   resample->fp = fp;
440   resample->quality = quality;
441   resample->inrate = inrate;
442   resample->outrate = outrate;
443
444   if (updated_latency)
445     gst_element_post_message (GST_ELEMENT (resample),
446         gst_message_new_latency (GST_OBJECT (resample)));
447
448   return ret;
449 }
450
451 static void
452 gst_audio_resample_reset_state (GstAudioResample * resample)
453 {
454   if (resample->state)
455     resample->funcs->reset_mem (resample->state);
456 }
457
458 static gboolean
459 gst_audio_resample_parse_caps (GstCaps * incaps,
460     GstCaps * outcaps, gint * width, gint * channels, gint * inrate,
461     gint * outrate, gboolean * fp)
462 {
463   GstStructure *structure;
464   gboolean ret;
465   gint mywidth, myinrate, myoutrate, mychannels;
466   gboolean myfp;
467
468   GST_DEBUG ("incaps %" GST_PTR_FORMAT ", outcaps %"
469       GST_PTR_FORMAT, incaps, outcaps);
470
471   structure = gst_caps_get_structure (incaps, 0);
472
473   if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float"))
474     myfp = TRUE;
475   else
476     myfp = FALSE;
477
478   ret = gst_structure_get_int (structure, "rate", &myinrate);
479   ret &= gst_structure_get_int (structure, "channels", &mychannels);
480   ret &= gst_structure_get_int (structure, "width", &mywidth);
481   if (G_UNLIKELY (!ret))
482     goto no_in_rate_channels;
483
484   structure = gst_caps_get_structure (outcaps, 0);
485   ret = gst_structure_get_int (structure, "rate", &myoutrate);
486   if (G_UNLIKELY (!ret))
487     goto no_out_rate;
488
489   if (channels)
490     *channels = mychannels;
491   if (inrate)
492     *inrate = myinrate;
493   if (outrate)
494     *outrate = myoutrate;
495   if (width)
496     *width = mywidth;
497   if (fp)
498     *fp = myfp;
499
500   return TRUE;
501
502   /* ERRORS */
503 no_in_rate_channels:
504   {
505     GST_DEBUG ("could not get input rate and channels");
506     return FALSE;
507   }
508 no_out_rate:
509   {
510     GST_DEBUG ("could not get output rate");
511     return FALSE;
512   }
513 }
514
515 static gint
516 _gcd (gint a, gint b)
517 {
518   while (b != 0) {
519     int temp = a;
520
521     a = b;
522     b = temp % b;
523   }
524
525   return ABS (a);
526 }
527
528 static gboolean
529 gst_audio_resample_transform_size (GstBaseTransform * base,
530     GstPadDirection direction, GstCaps * caps, guint size, GstCaps * othercaps,
531     guint * othersize)
532 {
533   gboolean ret = TRUE;
534   guint32 ratio_den, ratio_num;
535   gint inrate, outrate, gcd;
536   gint bytes_per_samp, channels;
537
538   GST_LOG_OBJECT (base, "asked to transform size %d in direction %s",
539       size, direction == GST_PAD_SINK ? "SINK" : "SRC");
540
541   /* Get sample width -> bytes_per_samp, channels, inrate, outrate */
542   ret =
543       gst_audio_resample_parse_caps (caps, othercaps, &bytes_per_samp,
544       &channels, &inrate, &outrate, NULL);
545   if (G_UNLIKELY (!ret)) {
546     GST_ERROR_OBJECT (base, "Wrong caps");
547     return FALSE;
548   }
549   /* Number of samples in either buffer is size / (width*channels) ->
550    * calculate the factor */
551   bytes_per_samp = bytes_per_samp * channels / 8;
552   /* Convert source buffer size to samples */
553   size /= bytes_per_samp;
554
555   /* Simplify the conversion ratio factors */
556   gcd = _gcd (inrate, outrate);
557   ratio_num = inrate / gcd;
558   ratio_den = outrate / gcd;
559
560   if (direction == GST_PAD_SINK) {
561     /* asked to convert size of an incoming buffer. Round up the output size */
562     *othersize = gst_util_uint64_scale_int_ceil (size, ratio_den, ratio_num);
563     *othersize *= bytes_per_samp;
564   } else {
565     /* asked to convert size of an outgoing buffer. Round down the input size */
566     *othersize = gst_util_uint64_scale_int (size, ratio_num, ratio_den);
567     *othersize *= bytes_per_samp;
568   }
569
570   GST_LOG_OBJECT (base, "transformed size %d to %d", size * bytes_per_samp,
571       *othersize);
572
573   return ret;
574 }
575
576 static gboolean
577 gst_audio_resample_set_caps (GstBaseTransform * base, GstCaps * incaps,
578     GstCaps * outcaps)
579 {
580   gboolean ret;
581   gint width = 0, inrate = 0, outrate = 0, channels = 0;
582   gboolean fp;
583   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
584
585   GST_LOG ("incaps %" GST_PTR_FORMAT ", outcaps %"
586       GST_PTR_FORMAT, incaps, outcaps);
587
588   ret = gst_audio_resample_parse_caps (incaps, outcaps,
589       &width, &channels, &inrate, &outrate, &fp);
590
591   if (G_UNLIKELY (!ret))
592     return FALSE;
593
594   ret =
595       gst_audio_resample_update_state (resample, width, channels, inrate,
596       outrate, resample->quality, fp);
597
598   if (G_UNLIKELY (!ret))
599     return FALSE;
600
601   /* save caps so we can short-circuit in the size_transform if the caps
602    * are the same */
603   gst_caps_replace (&resample->sinkcaps, incaps);
604   gst_caps_replace (&resample->srccaps, outcaps);
605
606   return TRUE;
607 }
608
609 #define GST_MAXINT24 (8388607)
610 #define GST_MININT24 (-8388608)
611
612 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
613 #define GST_READ_UINT24 GST_READ_UINT24_LE
614 #define GST_WRITE_UINT24 GST_WRITE_UINT24_LE
615 #else
616 #define GST_READ_UINT24 GST_READ_UINT24_BE
617 #define GST_WRITE_UINT24 GST_WRITE_UINT24_BE
618 #endif
619
620 static void
621 gst_audio_resample_convert_buffer (GstAudioResample * resample,
622     const guint8 * in, guint8 * out, guint len, gboolean inverse)
623 {
624   len *= resample->channels;
625
626   if (inverse) {
627     if (gst_audio_resample_use_int && resample->width == 8 && !resample->fp) {
628       gint8 *o = (gint8 *) out;
629       gint16 *i = (gint16 *) in;
630       gint32 tmp;
631
632       while (len) {
633         tmp = *i + (G_MAXINT8 >> 1);
634         *o = CLAMP (tmp >> 8, G_MININT8, G_MAXINT8);
635         o++;
636         i++;
637         len--;
638       }
639     } else if (!gst_audio_resample_use_int && resample->width == 8
640         && !resample->fp) {
641       gint8 *o = (gint8 *) out;
642       gfloat *i = (gfloat *) in;
643       gfloat tmp;
644
645       while (len) {
646         tmp = *i;
647         *o = (gint8) CLAMP (tmp * G_MAXINT8 + 0.5, G_MININT8, G_MAXINT8);
648         o++;
649         i++;
650         len--;
651       }
652     } else if (!gst_audio_resample_use_int && resample->width == 16
653         && !resample->fp) {
654       gint16 *o = (gint16 *) out;
655       gfloat *i = (gfloat *) in;
656       gfloat tmp;
657
658       while (len) {
659         tmp = *i;
660         *o = (gint16) CLAMP (tmp * G_MAXINT16 + 0.5, G_MININT16, G_MAXINT16);
661         o++;
662         i++;
663         len--;
664       }
665     } else if (resample->width == 24 && !resample->fp) {
666       guint8 *o = (guint8 *) out;
667       gdouble *i = (gdouble *) in;
668       gdouble tmp;
669
670       while (len) {
671         tmp = *i;
672         GST_WRITE_UINT24 (o, (gint32) CLAMP (tmp * GST_MAXINT24 + 0.5,
673                 GST_MININT24, GST_MAXINT24));
674         o += 3;
675         i++;
676         len--;
677       }
678     } else if (resample->width == 32 && !resample->fp) {
679       gint32 *o = (gint32 *) out;
680       gdouble *i = (gdouble *) in;
681       gdouble tmp;
682
683       while (len) {
684         tmp = *i;
685         *o = (gint32) CLAMP (tmp * G_MAXINT32 + 0.5, G_MININT32, G_MAXINT32);
686         o++;
687         i++;
688         len--;
689       }
690     } else {
691       g_assert_not_reached ();
692     }
693   } else {
694     if (gst_audio_resample_use_int && resample->width == 8 && !resample->fp) {
695       gint8 *i = (gint8 *) in;
696       gint16 *o = (gint16 *) out;
697       gint32 tmp;
698
699       while (len) {
700         tmp = *i;
701         *o = tmp << 8;
702         o++;
703         i++;
704         len--;
705       }
706     } else if (!gst_audio_resample_use_int && resample->width == 8
707         && !resample->fp) {
708       gint8 *i = (gint8 *) in;
709       gfloat *o = (gfloat *) out;
710       gfloat tmp;
711
712       while (len) {
713         tmp = *i;
714         *o = tmp / G_MAXINT8;
715         o++;
716         i++;
717         len--;
718       }
719     } else if (!gst_audio_resample_use_int && resample->width == 16
720         && !resample->fp) {
721       gint16 *i = (gint16 *) in;
722       gfloat *o = (gfloat *) out;
723       gfloat tmp;
724
725       while (len) {
726         tmp = *i;
727         *o = tmp / G_MAXINT16;
728         o++;
729         i++;
730         len--;
731       }
732     } else if (resample->width == 24 && !resample->fp) {
733       guint8 *i = (guint8 *) in;
734       gdouble *o = (gdouble *) out;
735       gdouble tmp;
736       guint32 tmp2;
737
738       while (len) {
739         tmp2 = GST_READ_UINT24 (i);
740         if (tmp2 & 0x00800000)
741           tmp2 |= 0xff000000;
742         tmp = (gint32) tmp2;
743         *o = tmp / GST_MAXINT24;
744         o++;
745         i += 3;
746         len--;
747       }
748     } else if (resample->width == 32 && !resample->fp) {
749       gint32 *i = (gint32 *) in;
750       gdouble *o = (gdouble *) out;
751       gdouble tmp;
752
753       while (len) {
754         tmp = *i;
755         *o = tmp / G_MAXINT32;
756         o++;
757         i++;
758         len--;
759       }
760     } else {
761       g_assert_not_reached ();
762     }
763   }
764 }
765
766 static guint8 *
767 gst_audio_resample_workspace_realloc (guint8 ** workspace, guint * size,
768     guint new_size)
769 {
770   guint8 *new;
771   if (new_size <= *size)
772     /* no need to resize */
773     return *workspace;
774   new = g_realloc (*workspace, new_size);
775   if (!new)
776     /* failure (re)allocating memeory */
777     return NULL;
778   /* success */
779   *workspace = new;
780   *size = new_size;
781   return *workspace;
782 }
783
784 static void
785 gst_audio_resample_push_drain (GstAudioResample * resample)
786 {
787   GstBuffer *outbuf;
788   GstFlowReturn res;
789   gint outsize;
790   guint history_len, out_len, out_processed;
791   gint err;
792   guint num, den;
793
794   if (!resample->state)
795     return;
796
797   /* Don't drain samples if we were reset. */
798   if (!GST_CLOCK_TIME_IS_VALID (resample->t0))
799     return;
800
801   resample->funcs->get_ratio (resample->state, &num, &den);
802
803   history_len = resample->funcs->get_input_latency (resample->state);
804   out_len = out_processed =
805       gst_util_uint64_scale_int_ceil (history_len, den, num);
806   outsize = out_len * resample->channels * (resample->width / 8);
807
808   res =
809       gst_pad_alloc_buffer_and_set_caps (GST_BASE_TRANSFORM_SRC_PAD (resample),
810       GST_BUFFER_OFFSET_NONE, outsize,
811       GST_PAD_CAPS (GST_BASE_TRANSFORM_SRC_PAD (resample)), &outbuf);
812   if (G_UNLIKELY (res != GST_FLOW_OK)) {
813     GST_WARNING_OBJECT (resample, "failed allocating buffer of %d bytes",
814         outsize);
815     return;
816   }
817
818   if (resample->funcs->width != resample->width) {
819     /* need to convert data format;  allocate workspace */
820     if (!gst_audio_resample_workspace_realloc (&resample->tmp_out,
821             &resample->tmp_out_size, (resample->funcs->width / 8) * out_len *
822             resample->channels)) {
823       GST_ERROR_OBJECT (resample, "failed to allocate workspace");
824       return;
825     }
826
827     /* process */
828     err = resample->funcs->process (resample->state, NULL, &history_len,
829         resample->tmp_out, &out_processed);
830
831     /* convert output format */
832     gst_audio_resample_convert_buffer (resample, resample->tmp_out,
833         GST_BUFFER_DATA (outbuf), out_processed, TRUE);
834   } else {
835     /* don't need to convert data format;  process */
836     err = resample->funcs->process (resample->state, NULL, &history_len,
837         GST_BUFFER_DATA (outbuf), &out_processed);
838   }
839
840   /* If we wrote more than allocated something is really wrong now
841    * and we should better abort immediately */
842   g_assert (out_len >= out_processed);
843
844   if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) {
845     GST_WARNING_OBJECT (resample, "Failed to process drain: %s",
846         resample->funcs->strerror (err));
847     gst_buffer_unref (outbuf);
848     return;
849   }
850
851   if (G_UNLIKELY (out_processed == 0)) {
852     GST_WARNING_OBJECT (resample, "Failed to get drain, dropping buffer");
853     gst_buffer_unref (outbuf);
854     return;
855   }
856
857   /* time */
858   if (GST_CLOCK_TIME_IS_VALID (resample->t0)) {
859     GST_BUFFER_TIMESTAMP (outbuf) = resample->t0 +
860         gst_util_uint64_scale_int_round (resample->samples_out, GST_SECOND,
861         resample->outrate);
862     GST_BUFFER_DURATION (outbuf) =
863         gst_util_uint64_scale_int_round (resample->samples_out + out_processed,
864         GST_SECOND, resample->outrate) - GST_BUFFER_TIMESTAMP (outbuf);
865   } else {
866     GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
867     GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
868   }
869   /* offset */
870   if (resample->out_offset0 != GST_BUFFER_OFFSET_NONE) {
871     GST_BUFFER_OFFSET (outbuf) = resample->out_offset0 + resample->samples_out;
872     GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_processed;
873   } else {
874     GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE;
875     GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;
876   }
877   /* move along */
878   resample->samples_out += out_processed;
879   resample->samples_in += 0;
880
881   GST_BUFFER_SIZE (outbuf) =
882       out_processed * resample->channels * (resample->width / 8);
883
884   GST_LOG_OBJECT (resample,
885       "Pushing drain buffer of %u bytes with timestamp %" GST_TIME_FORMAT
886       " duration %" GST_TIME_FORMAT " offset %" G_GUINT64_FORMAT " offset_end %"
887       G_GUINT64_FORMAT, GST_BUFFER_SIZE (outbuf),
888       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
889       GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
890       GST_BUFFER_OFFSET_END (outbuf));
891
892   res = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (resample), outbuf);
893
894   if (G_UNLIKELY (res != GST_FLOW_OK))
895     GST_WARNING_OBJECT (resample, "Failed to push drain: %s",
896         gst_flow_get_name (res));
897
898   return;
899 }
900
901 static gboolean
902 gst_audio_resample_event (GstBaseTransform * base, GstEvent * event)
903 {
904   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
905
906   switch (GST_EVENT_TYPE (event)) {
907     case GST_EVENT_FLUSH_STOP:
908       gst_audio_resample_reset_state (resample);
909       resample->t0 = GST_CLOCK_TIME_NONE;
910       resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
911       resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
912       resample->samples_in = 0;
913       resample->samples_out = 0;
914       resample->need_discont = TRUE;
915       break;
916     case GST_EVENT_NEWSEGMENT:
917       gst_audio_resample_push_drain (resample);
918       gst_audio_resample_reset_state (resample);
919       resample->t0 = GST_CLOCK_TIME_NONE;
920       resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
921       resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
922       resample->samples_in = 0;
923       resample->samples_out = 0;
924       resample->need_discont = TRUE;
925       break;
926     case GST_EVENT_EOS:
927       gst_audio_resample_push_drain (resample);
928       gst_audio_resample_reset_state (resample);
929       break;
930     default:
931       break;
932   }
933
934   return parent_class->event (base, event);
935 }
936
937 static gboolean
938 gst_audio_resample_check_discont (GstAudioResample * resample, GstBuffer * buf)
939 {
940   guint64 offset;
941   guint64 delta;
942
943   /* is the incoming buffer a discontinuity? */
944   if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (buf)))
945     return TRUE;
946
947   /* no valid timestamps or offsets to compare --> no discontinuity */
948   if (G_UNLIKELY (!(GST_BUFFER_TIMESTAMP_IS_VALID (buf) &&
949               GST_CLOCK_TIME_IS_VALID (resample->t0))))
950     return FALSE;
951
952   /* convert the inbound timestamp to an offset. */
953   offset =
954       gst_util_uint64_scale_int_round (GST_BUFFER_TIMESTAMP (buf) -
955       resample->t0, resample->inrate, GST_SECOND);
956
957   /* many elements generate imperfect streams due to rounding errors, so we
958    * permit a small error (up to one sample) without triggering a filter
959    * flush/restart (if triggered incorrectly, this will be audible) */
960   delta = ABS ((gint64) (offset - resample->samples_in));
961   if (delta <= 1)
962     return FALSE;
963
964   GST_WARNING_OBJECT (resample,
965       "encountered timestamp discontinuity of %" G_GUINT64_FORMAT " samples = %"
966       GST_TIME_FORMAT, delta,
967       GST_TIME_ARGS (gst_util_uint64_scale_int_round (delta, GST_SECOND,
968               resample->inrate)));
969   return TRUE;
970 }
971
972 static GstFlowReturn
973 gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
974     GstBuffer * outbuf)
975 {
976   guint32 in_len, in_processed;
977   guint32 out_len, out_processed;
978   gint err;
979
980   in_len = GST_BUFFER_SIZE (inbuf) / resample->channels;
981   out_len = GST_BUFFER_SIZE (outbuf) / resample->channels;
982
983   in_len /= (resample->width / 8);
984   out_len /= (resample->width / 8);
985
986   in_processed = in_len;
987   out_processed = out_len;
988
989   if (resample->funcs->width != resample->width) {
990     /* need to convert data format for processing;  ensure we have enough
991      * workspace available */
992     if (!gst_audio_resample_workspace_realloc (&resample->tmp_in,
993             &resample->tmp_in_size, in_len * resample->channels *
994             (resample->funcs->width / 8)) ||
995         !gst_audio_resample_workspace_realloc (&resample->tmp_out,
996             &resample->tmp_out_size, out_len * resample->channels *
997             (resample->funcs->width / 8))) {
998       GST_ERROR_OBJECT (resample, "failed to allocate workspace");
999       return GST_FLOW_ERROR;
1000     }
1001
1002     /* convert input */
1003     gst_audio_resample_convert_buffer (resample, GST_BUFFER_DATA (inbuf),
1004         resample->tmp_in, in_len, FALSE);
1005
1006     /* process */
1007     err = resample->funcs->process (resample->state,
1008         resample->tmp_in, &in_processed, resample->tmp_out, &out_processed);
1009
1010     /* convert output */
1011     gst_audio_resample_convert_buffer (resample, resample->tmp_out,
1012         GST_BUFFER_DATA (outbuf), out_processed, TRUE);
1013   } else {
1014     /* no format conversion required;  process */
1015     err = resample->funcs->process (resample->state,
1016         GST_BUFFER_DATA (inbuf), &in_processed,
1017         GST_BUFFER_DATA (outbuf), &out_processed);
1018   }
1019
1020   /* If we wrote more than allocated something is really wrong now and we
1021    * should better abort immediately */
1022   g_assert (out_len >= out_processed);
1023
1024   if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) {
1025     GST_ERROR_OBJECT (resample, "Failed to convert data: %s",
1026         resample->funcs->strerror (err));
1027     return GST_FLOW_ERROR;
1028   }
1029
1030   if (G_UNLIKELY (in_len != in_processed)) {
1031     GST_WARNING_OBJECT (resample, "converted %d of %d input samples",
1032         in_processed, in_len);
1033   }
1034
1035   /* time */
1036   if (GST_CLOCK_TIME_IS_VALID (resample->t0)) {
1037     GST_BUFFER_TIMESTAMP (outbuf) = resample->t0 +
1038         gst_util_uint64_scale_int_round (resample->samples_out, GST_SECOND,
1039         resample->outrate);
1040     GST_BUFFER_DURATION (outbuf) =
1041         gst_util_uint64_scale_int_round (resample->samples_out + out_processed,
1042         GST_SECOND, resample->outrate) - GST_BUFFER_TIMESTAMP (outbuf);
1043   } else {
1044     GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
1045     GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
1046   }
1047   /* offset */
1048   if (resample->out_offset0 != GST_BUFFER_OFFSET_NONE) {
1049     GST_BUFFER_OFFSET (outbuf) = resample->out_offset0 + resample->samples_out;
1050     GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_processed;
1051   } else {
1052     GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE;
1053     GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;
1054   }
1055   /* move along */
1056   resample->samples_out += out_processed;
1057   resample->samples_in += in_len;
1058
1059   GST_BUFFER_SIZE (outbuf) =
1060       out_processed * resample->channels * (resample->width / 8);
1061
1062   GST_LOG_OBJECT (resample,
1063       "Converted to buffer of %" G_GUINT32_FORMAT
1064       " samples (%u bytes) with timestamp %" GST_TIME_FORMAT ", duration %"
1065       GST_TIME_FORMAT ", offset %" G_GUINT64_FORMAT ", offset_end %"
1066       G_GUINT64_FORMAT, out_processed, GST_BUFFER_SIZE (outbuf),
1067       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
1068       GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)),
1069       GST_BUFFER_OFFSET (outbuf), GST_BUFFER_OFFSET_END (outbuf));
1070
1071   if (out_processed == 0) {
1072     GST_DEBUG_OBJECT (resample, "buffer dropped");
1073     return GST_BASE_TRANSFORM_FLOW_DROPPED;
1074   }
1075   return GST_FLOW_OK;
1076 }
1077
1078 static GstFlowReturn
1079 gst_audio_resample_transform (GstBaseTransform * base, GstBuffer * inbuf,
1080     GstBuffer * outbuf)
1081 {
1082   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
1083   gulong size;
1084   GstFlowReturn ret;
1085
1086   if (resample->state == NULL) {
1087     if (G_UNLIKELY (!(resample->state =
1088                 gst_audio_resample_init_state (resample, resample->width,
1089                     resample->channels, resample->inrate, resample->outrate,
1090                     resample->quality, resample->fp))))
1091       return GST_FLOW_ERROR;
1092
1093     resample->funcs =
1094         gst_audio_resample_get_funcs (resample->width, resample->fp);
1095   }
1096
1097   size = GST_BUFFER_SIZE (inbuf);
1098
1099   GST_LOG_OBJECT (resample, "transforming buffer of %ld bytes, ts %"
1100       GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT ", offset %"
1101       G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT,
1102       size, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)),
1103       GST_TIME_ARGS (GST_BUFFER_DURATION (inbuf)),
1104       GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf));
1105
1106   /* check for timestamp discontinuities;  flush/reset if needed, and set
1107    * flag to resync timestamp and offset counters and send event
1108    * downstream */
1109   if (G_UNLIKELY (gst_audio_resample_check_discont (resample, inbuf))) {
1110     gst_audio_resample_reset_state (resample);
1111     resample->need_discont = TRUE;
1112   }
1113
1114   /* handle discontinuity */
1115   if (G_UNLIKELY (resample->need_discont)) {
1116     /* reset */
1117     resample->samples_in = 0;
1118     resample->samples_out = 0;
1119     GST_DEBUG_OBJECT (resample, "found discontinuity; resyncing");
1120     /* resync the timestamp and offset counters if possible */
1121     if (GST_BUFFER_TIMESTAMP_IS_VALID (inbuf)) {
1122       resample->t0 = GST_BUFFER_TIMESTAMP (inbuf);
1123     } else {
1124       GST_DEBUG_OBJECT (resample, "... but new timestamp is invalid");
1125       resample->t0 = GST_CLOCK_TIME_NONE;
1126     }
1127     if (GST_BUFFER_OFFSET_IS_VALID (inbuf)) {
1128       resample->in_offset0 = GST_BUFFER_OFFSET (inbuf);
1129       resample->out_offset0 =
1130           gst_util_uint64_scale_int_round (resample->in_offset0,
1131           resample->outrate, resample->inrate);
1132     } else {
1133       GST_DEBUG_OBJECT (resample, "... but new offset is invalid");
1134       resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
1135       resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
1136     }
1137     /* set DISCONT flag on output buffer */
1138     GST_DEBUG_OBJECT (resample, "marking this buffer with the DISCONT flag");
1139     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
1140     resample->need_discont = FALSE;
1141   }
1142
1143   ret = gst_audio_resample_process (resample, inbuf, outbuf);
1144   if (G_UNLIKELY (ret != GST_FLOW_OK))
1145     return ret;
1146
1147   GST_DEBUG_OBJECT (resample, "input = samples [%" G_GUINT64_FORMAT ", %"
1148       G_GUINT64_FORMAT ") = [%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT
1149       ") ns;  output = samples [%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT
1150       ") = [%" G_GUINT64_FORMAT ", %" G_GUINT64_FORMAT ") ns",
1151       GST_BUFFER_OFFSET (inbuf), GST_BUFFER_OFFSET_END (inbuf),
1152       GST_BUFFER_TIMESTAMP (inbuf), GST_BUFFER_TIMESTAMP (inbuf) +
1153       GST_BUFFER_DURATION (inbuf), GST_BUFFER_OFFSET (outbuf),
1154       GST_BUFFER_OFFSET_END (outbuf), GST_BUFFER_TIMESTAMP (outbuf),
1155       GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf));
1156
1157   return GST_FLOW_OK;
1158 }
1159
1160 static gboolean
1161 gst_audio_resample_query (GstPad * pad, GstQuery * query)
1162 {
1163   GstAudioResample *resample = GST_AUDIO_RESAMPLE (gst_pad_get_parent (pad));
1164   GstBaseTransform *trans = GST_BASE_TRANSFORM (resample);
1165   gboolean res = TRUE;
1166
1167   switch (GST_QUERY_TYPE (query)) {
1168     case GST_QUERY_LATENCY:
1169     {
1170       GstClockTime min, max;
1171       gboolean live;
1172       guint64 latency;
1173       GstPad *peer;
1174       gint rate = resample->inrate;
1175       gint resampler_latency;
1176
1177       if (resample->state)
1178         resampler_latency =
1179             resample->funcs->get_input_latency (resample->state);
1180       else
1181         resampler_latency = 0;
1182
1183       if (gst_base_transform_is_passthrough (trans))
1184         resampler_latency = 0;
1185
1186       if ((peer = gst_pad_get_peer (GST_BASE_TRANSFORM_SINK_PAD (trans)))) {
1187         if ((res = gst_pad_query (peer, query))) {
1188           gst_query_parse_latency (query, &live, &min, &max);
1189
1190           GST_DEBUG_OBJECT (resample, "Peer latency: min %"
1191               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1192               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1193
1194           /* add our own latency */
1195           if (rate != 0 && resampler_latency != 0)
1196             latency = gst_util_uint64_scale_round (resampler_latency,
1197                 GST_SECOND, rate);
1198           else
1199             latency = 0;
1200
1201           GST_DEBUG_OBJECT (resample, "Our latency: %" GST_TIME_FORMAT,
1202               GST_TIME_ARGS (latency));
1203
1204           min += latency;
1205           if (GST_CLOCK_TIME_IS_VALID (max))
1206             max += latency;
1207
1208           GST_DEBUG_OBJECT (resample, "Calculated total latency : min %"
1209               GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1210               GST_TIME_ARGS (min), GST_TIME_ARGS (max));
1211
1212           gst_query_set_latency (query, live, min, max);
1213         }
1214         gst_object_unref (peer);
1215       }
1216       break;
1217     }
1218     default:
1219       res = gst_pad_query_default (pad, query);
1220       break;
1221   }
1222   gst_object_unref (resample);
1223   return res;
1224 }
1225
1226 static const GstQueryType *
1227 gst_audio_resample_query_type (GstPad * pad)
1228 {
1229   static const GstQueryType types[] = {
1230     GST_QUERY_LATENCY,
1231     0
1232   };
1233
1234   return types;
1235 }
1236
1237 static void
1238 gst_audio_resample_set_property (GObject * object, guint prop_id,
1239     const GValue * value, GParamSpec * pspec)
1240 {
1241   GstAudioResample *resample;
1242
1243   resample = GST_AUDIO_RESAMPLE (object);
1244
1245   switch (prop_id) {
1246     case PROP_QUALITY:
1247       GST_BASE_TRANSFORM_LOCK (resample);
1248       resample->quality = g_value_get_int (value);
1249       GST_DEBUG_OBJECT (resample, "new quality %d", resample->quality);
1250
1251       gst_audio_resample_update_state (resample, resample->width,
1252           resample->channels, resample->inrate, resample->outrate,
1253           resample->quality, resample->fp);
1254       GST_BASE_TRANSFORM_UNLOCK (resample);
1255       break;
1256     case PROP_FILTER_LENGTH:{
1257       gint filter_length = g_value_get_int (value);
1258
1259       GST_BASE_TRANSFORM_LOCK (resample);
1260       if (filter_length <= 8)
1261         resample->quality = 0;
1262       else if (filter_length <= 16)
1263         resample->quality = 1;
1264       else if (filter_length <= 32)
1265         resample->quality = 2;
1266       else if (filter_length <= 48)
1267         resample->quality = 3;
1268       else if (filter_length <= 64)
1269         resample->quality = 4;
1270       else if (filter_length <= 80)
1271         resample->quality = 5;
1272       else if (filter_length <= 96)
1273         resample->quality = 6;
1274       else if (filter_length <= 128)
1275         resample->quality = 7;
1276       else if (filter_length <= 160)
1277         resample->quality = 8;
1278       else if (filter_length <= 192)
1279         resample->quality = 9;
1280       else
1281         resample->quality = 10;
1282
1283       GST_DEBUG_OBJECT (resample, "new quality %d", resample->quality);
1284
1285       gst_audio_resample_update_state (resample, resample->width,
1286           resample->channels, resample->inrate, resample->outrate,
1287           resample->quality, resample->fp);
1288       GST_BASE_TRANSFORM_UNLOCK (resample);
1289       break;
1290     }
1291     default:
1292       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1293       break;
1294   }
1295 }
1296
1297 static void
1298 gst_audio_resample_get_property (GObject * object, guint prop_id,
1299     GValue * value, GParamSpec * pspec)
1300 {
1301   GstAudioResample *resample;
1302
1303   resample = GST_AUDIO_RESAMPLE (object);
1304
1305   switch (prop_id) {
1306     case PROP_QUALITY:
1307       g_value_set_int (value, resample->quality);
1308       break;
1309     case PROP_FILTER_LENGTH:
1310       switch (resample->quality) {
1311         case 0:
1312           g_value_set_int (value, 8);
1313           break;
1314         case 1:
1315           g_value_set_int (value, 16);
1316           break;
1317         case 2:
1318           g_value_set_int (value, 32);
1319           break;
1320         case 3:
1321           g_value_set_int (value, 48);
1322           break;
1323         case 4:
1324           g_value_set_int (value, 64);
1325           break;
1326         case 5:
1327           g_value_set_int (value, 80);
1328           break;
1329         case 6:
1330           g_value_set_int (value, 96);
1331           break;
1332         case 7:
1333           g_value_set_int (value, 128);
1334           break;
1335         case 8:
1336           g_value_set_int (value, 160);
1337           break;
1338         case 9:
1339           g_value_set_int (value, 192);
1340           break;
1341         case 10:
1342           g_value_set_int (value, 256);
1343           break;
1344       }
1345       break;
1346     default:
1347       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1348       break;
1349   }
1350 }
1351
1352 /* FIXME: should have a benchmark fallback for the case where orc is disabled */
1353 #if defined(AUDIORESAMPLE_FORMAT_AUTO) && !defined(DISABLE_ORC)
1354
1355 #define BENCHMARK_SIZE 512
1356
1357 static gboolean
1358 _benchmark_int_float (SpeexResamplerState * st)
1359 {
1360   gint16 in[BENCHMARK_SIZE] = { 0, }, out[BENCHMARK_SIZE / 2];
1361   gfloat in_tmp[BENCHMARK_SIZE], out_tmp[BENCHMARK_SIZE / 2];
1362   gint i;
1363   guint32 inlen = BENCHMARK_SIZE, outlen = BENCHMARK_SIZE / 2;
1364
1365   for (i = 0; i < BENCHMARK_SIZE; i++) {
1366     gfloat tmp = in[i];
1367     in_tmp[i] = tmp / G_MAXINT16;
1368   }
1369
1370   resample_float_resampler_process_interleaved_float (st,
1371       (const guint8 *) in_tmp, &inlen, (guint8 *) out_tmp, &outlen);
1372
1373   if (outlen == 0) {
1374     GST_ERROR ("Failed to use float resampler");
1375     return FALSE;
1376   }
1377
1378   for (i = 0; i < outlen; i++) {
1379     gfloat tmp = out_tmp[i];
1380     out[i] = CLAMP (tmp * G_MAXINT16 + 0.5, G_MININT16, G_MAXINT16);
1381   }
1382
1383   return TRUE;
1384 }
1385
1386 static gboolean
1387 _benchmark_int_int (SpeexResamplerState * st)
1388 {
1389   gint16 in[BENCHMARK_SIZE] = { 0, }, out[BENCHMARK_SIZE / 2];
1390   guint32 inlen = BENCHMARK_SIZE, outlen = BENCHMARK_SIZE / 2;
1391
1392   resample_int_resampler_process_interleaved_int (st, (const guint8 *) in,
1393       &inlen, (guint8 *) out, &outlen);
1394
1395   if (outlen == 0) {
1396     GST_ERROR ("Failed to use int resampler");
1397     return FALSE;
1398   }
1399
1400   return TRUE;
1401 }
1402
1403 static gboolean
1404 _benchmark_integer_resampling (void)
1405 {
1406   OrcProfile a, b;
1407   gdouble av, bv;
1408   SpeexResamplerState *sta, *stb;
1409   int i;
1410
1411   orc_profile_init (&a);
1412   orc_profile_init (&b);
1413
1414   sta = resample_float_resampler_init (1, 48000, 24000, 4, NULL);
1415   if (sta == NULL) {
1416     GST_ERROR ("Failed to create float resampler state");
1417     return FALSE;
1418   }
1419
1420   stb = resample_int_resampler_init (1, 48000, 24000, 4, NULL);
1421   if (stb == NULL) {
1422     resample_float_resampler_destroy (sta);
1423     GST_ERROR ("Failed to create int resampler state");
1424     return FALSE;
1425   }
1426
1427   /* Benchmark */
1428   for (i = 0; i < 10; i++) {
1429     orc_profile_start (&a);
1430     if (!_benchmark_int_float (sta))
1431       goto error;
1432     orc_profile_stop (&a);
1433   }
1434
1435   /* Benchmark */
1436   for (i = 0; i < 10; i++) {
1437     orc_profile_start (&b);
1438     if (!_benchmark_int_int (stb))
1439       goto error;
1440     orc_profile_stop (&b);
1441   }
1442
1443   /* Handle results */
1444   orc_profile_get_ave_std (&a, &av, NULL);
1445   orc_profile_get_ave_std (&b, &bv, NULL);
1446
1447   /* Remember benchmark result in global variable */
1448   gst_audio_resample_use_int = (av > bv);
1449   resample_float_resampler_destroy (sta);
1450   resample_int_resampler_destroy (stb);
1451
1452   if (av > bv)
1453     GST_INFO ("Using integer resampler if appropiate: %lf < %lf", bv, av);
1454   else
1455     GST_INFO ("Using float resampler for everything: %lf <= %lf", av, bv);
1456
1457   return TRUE;
1458
1459 error:
1460   resample_float_resampler_destroy (sta);
1461   resample_int_resampler_destroy (stb);
1462
1463   return FALSE;
1464 }
1465 #endif /* defined(AUDIORESAMPLE_FORMAT_AUTO) && !defined(DISABLE_ORC) */
1466
1467 static gboolean
1468 plugin_init (GstPlugin * plugin)
1469 {
1470   GST_DEBUG_CATEGORY_INIT (audio_resample_debug, "audioresample", 0,
1471       "audio resampling element");
1472
1473 #if defined(AUDIORESAMPLE_FORMAT_AUTO) && !defined(DISABLE_ORC)
1474   if (!_benchmark_integer_resampling ())
1475     return FALSE;
1476 #else
1477   GST_WARNING ("Orc disabled, can't benchmark int vs. float resampler");
1478   {
1479     GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
1480     GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
1481     GST_CAT_WARNING (GST_CAT_PERFORMANCE, "orc disabled, no benchmarking done");
1482   }
1483 #endif
1484
1485   if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY,
1486           GST_TYPE_AUDIO_RESAMPLE)) {
1487     return FALSE;
1488   }
1489
1490   return TRUE;
1491 }
1492
1493 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1494     GST_VERSION_MINOR,
1495     "audioresample",
1496     "Resamples audio", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
1497     GST_PACKAGE_ORIGIN);