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