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