audioresample: On the first buffer we need discont handling
[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 #if defined AUDIORESAMPLE_FORMAT_AUTO
54 #define OIL_ENABLE_UNSTABLE_API
55 #include <liboil/liboilprofile.h>
56 #include <liboil/liboil.h>
57 #endif
58
59 GST_DEBUG_CATEGORY (audio_resample_debug);
60 #define GST_CAT_DEFAULT audio_resample_debug
61
62 enum
63 {
64   PROP_0,
65   PROP_QUALITY,
66   PROP_FILTER_LENGTH
67 };
68
69 #define SUPPORTED_CAPS \
70 GST_STATIC_CAPS ( \
71     "audio/x-raw-float, " \
72       "rate = (int) [ 1, MAX ], "       \
73       "channels = (int) [ 1, MAX ], " \
74       "endianness = (int) BYTE_ORDER, " \
75       "width = (int) { 32, 64 }; " \
76     "audio/x-raw-int, " \
77       "rate = (int) [ 1, MAX ], " \
78       "channels = (int) [ 1, MAX ], " \
79       "endianness = (int) BYTE_ORDER, " \
80       "width = (int) 32, " \
81       "depth = (int) 32, " \
82       "signed = (boolean) true; " \
83     "audio/x-raw-int, " \
84       "rate = (int) [ 1, MAX ], " \
85       "channels = (int) [ 1, MAX ], " \
86       "endianness = (int) BYTE_ORDER, " \
87       "width = (int) 24, " \
88       "depth = (int) 24, " \
89       "signed = (boolean) true; " \
90     "audio/x-raw-int, " \
91       "rate = (int) [ 1, MAX ], " \
92       "channels = (int) [ 1, MAX ], " \
93       "endianness = (int) BYTE_ORDER, " \
94       "width = (int) 16, " \
95       "depth = (int) 16, " \
96       "signed = (boolean) true; " \
97     "audio/x-raw-int, " \
98       "rate = (int) [ 1, MAX ], " \
99       "channels = (int) [ 1, MAX ], " \
100       "endianness = (int) BYTE_ORDER, " \
101       "width = (int) 8, " \
102       "depth = (int) 8, " \
103       "signed = (boolean) true" \
104 )
105
106 /* If TRUE integer arithmetic resampling is faster and will be used if appropiate */
107 #if defined AUDIORESAMPLE_FORMAT_INT
108 static gboolean gst_audio_resample_use_int = TRUE;
109 #elif defined AUDIORESAMPLE_FORMAT_FLOAT
110 static gboolean gst_audio_resample_use_int = FALSE;
111 #else
112 static gboolean gst_audio_resample_use_int = FALSE;
113 #endif
114
115 static GstStaticPadTemplate gst_audio_resample_sink_template =
116 GST_STATIC_PAD_TEMPLATE ("sink",
117     GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS);
118
119 static GstStaticPadTemplate gst_audio_resample_src_template =
120 GST_STATIC_PAD_TEMPLATE ("src",
121     GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS);
122
123 static void gst_audio_resample_set_property (GObject * object,
124     guint prop_id, const GValue * value, GParamSpec * pspec);
125 static void gst_audio_resample_get_property (GObject * object,
126     guint prop_id, GValue * value, GParamSpec * pspec);
127
128 /* vmethods */
129 static gboolean gst_audio_resample_get_unit_size (GstBaseTransform * base,
130     GstCaps * caps, guint * size);
131 static GstCaps *gst_audio_resample_transform_caps (GstBaseTransform * base,
132     GstPadDirection direction, GstCaps * caps);
133 static void gst_audio_resample_fixate_caps (GstBaseTransform * base,
134     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps);
135 static gboolean gst_audio_resample_transform_size (GstBaseTransform * trans,
136     GstPadDirection direction, GstCaps * incaps, guint insize,
137     GstCaps * outcaps, guint * outsize);
138 static gboolean gst_audio_resample_set_caps (GstBaseTransform * base,
139     GstCaps * incaps, GstCaps * outcaps);
140 static GstFlowReturn gst_audio_resample_transform (GstBaseTransform * base,
141     GstBuffer * inbuf, GstBuffer * outbuf);
142 static gboolean gst_audio_resample_event (GstBaseTransform * base,
143     GstEvent * event);
144 static gboolean gst_audio_resample_start (GstBaseTransform * base);
145 static gboolean gst_audio_resample_stop (GstBaseTransform * base);
146 static gboolean gst_audio_resample_query (GstPad * pad, GstQuery * query);
147 static const GstQueryType *gst_audio_resample_query_type (GstPad * pad);
148
149 GST_BOILERPLATE (GstAudioResample, gst_audio_resample, GstBaseTransform,
150     GST_TYPE_BASE_TRANSFORM);
151
152 static void
153 gst_audio_resample_base_init (gpointer g_class)
154 {
155   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
156
157   gst_element_class_add_pad_template (gstelement_class,
158       gst_static_pad_template_get (&gst_audio_resample_src_template));
159   gst_element_class_add_pad_template (gstelement_class,
160       gst_static_pad_template_get (&gst_audio_resample_sink_template));
161
162   gst_element_class_set_details_simple (gstelement_class, "Audio resampler",
163       "Filter/Converter/Audio", "Resamples audio",
164       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
165 }
166
167 static void
168 gst_audio_resample_class_init (GstAudioResampleClass * klass)
169 {
170   GObjectClass *gobject_class = (GObjectClass *) klass;
171
172   gobject_class->set_property = gst_audio_resample_set_property;
173   gobject_class->get_property = gst_audio_resample_get_property;
174
175   g_object_class_install_property (gobject_class, PROP_QUALITY,
176       g_param_spec_int ("quality", "Quality", "Resample quality with 0 being "
177           "the lowest and 10 being the best",
178           SPEEX_RESAMPLER_QUALITY_MIN, SPEEX_RESAMPLER_QUALITY_MAX,
179           SPEEX_RESAMPLER_QUALITY_DEFAULT,
180           G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
181
182   /* FIXME 0.11: Remove this property, it's just for compatibility
183    * with old audioresample
184    */
185   /**
186    * GstAudioResample:filter-length:
187    *
188    * Length of the resample filter
189    *
190    * Deprectated: Use #GstAudioResample:quality property instead
191    */
192   g_object_class_install_property (gobject_class, PROP_FILTER_LENGTH,
193       g_param_spec_int ("filter-length", "Filter length",
194           "Length of the resample filter", 0, G_MAXINT, 64, G_PARAM_READWRITE));
195
196   GST_BASE_TRANSFORM_CLASS (klass)->start =
197       GST_DEBUG_FUNCPTR (gst_audio_resample_start);
198   GST_BASE_TRANSFORM_CLASS (klass)->stop =
199       GST_DEBUG_FUNCPTR (gst_audio_resample_stop);
200   GST_BASE_TRANSFORM_CLASS (klass)->transform_size =
201       GST_DEBUG_FUNCPTR (gst_audio_resample_transform_size);
202   GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size =
203       GST_DEBUG_FUNCPTR (gst_audio_resample_get_unit_size);
204   GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
205       GST_DEBUG_FUNCPTR (gst_audio_resample_transform_caps);
206   GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps =
207       GST_DEBUG_FUNCPTR (gst_audio_resample_fixate_caps);
208   GST_BASE_TRANSFORM_CLASS (klass)->set_caps =
209       GST_DEBUG_FUNCPTR (gst_audio_resample_set_caps);
210   GST_BASE_TRANSFORM_CLASS (klass)->transform =
211       GST_DEBUG_FUNCPTR (gst_audio_resample_transform);
212   GST_BASE_TRANSFORM_CLASS (klass)->event =
213       GST_DEBUG_FUNCPTR (gst_audio_resample_event);
214
215   GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE;
216 }
217
218 static void
219 gst_audio_resample_init (GstAudioResample * resample,
220     GstAudioResampleClass * klass)
221 {
222   GstBaseTransform *trans = GST_BASE_TRANSFORM (resample);
223
224   resample->quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
225
226   gst_pad_set_query_function (trans->srcpad, gst_audio_resample_query);
227   gst_pad_set_query_type_function (trans->srcpad,
228       gst_audio_resample_query_type);
229 }
230
231 /* vmethods */
232 static gboolean
233 gst_audio_resample_start (GstBaseTransform * base)
234 {
235   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
236
237   resample->need_discont = TRUE;
238
239   resample->t0 = GST_CLOCK_TIME_NONE;
240   resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
241   resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
242   resample->next_in_offset = GST_BUFFER_OFFSET_NONE;
243   resample->next_out_offset = GST_BUFFER_OFFSET_NONE;
244
245   resample->tmp_in = NULL;
246   resample->tmp_in_size = 0;
247   resample->tmp_out = NULL;
248   resample->tmp_out_size = 0;
249
250   return TRUE;
251 }
252
253 static gboolean
254 gst_audio_resample_stop (GstBaseTransform * base)
255 {
256   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
257
258   if (resample->state) {
259     resample->funcs->destroy (resample->state);
260     resample->state = NULL;
261   }
262
263   resample->funcs = NULL;
264
265   g_free (resample->tmp_in);
266   resample->tmp_in = NULL;
267   resample->tmp_in_size = 0;
268
269   g_free (resample->tmp_out);
270   resample->tmp_out = NULL;
271   resample->tmp_out_size = 0;
272
273   gst_caps_replace (&resample->sinkcaps, NULL);
274   gst_caps_replace (&resample->srccaps, NULL);
275
276   return TRUE;
277 }
278
279 static gboolean
280 gst_audio_resample_get_unit_size (GstBaseTransform * base, GstCaps * caps,
281     guint * size)
282 {
283   gint width, channels;
284   GstStructure *structure;
285   gboolean ret;
286
287   g_return_val_if_fail (size != NULL, FALSE);
288
289   /* this works for both float and int */
290   structure = gst_caps_get_structure (caps, 0);
291   ret = gst_structure_get_int (structure, "width", &width);
292   ret &= gst_structure_get_int (structure, "channels", &channels);
293
294   if (G_UNLIKELY (!ret))
295     return FALSE;
296
297   *size = (width / 8) * channels;
298
299   return TRUE;
300 }
301
302 static GstCaps *
303 gst_audio_resample_transform_caps (GstBaseTransform * base,
304     GstPadDirection direction, GstCaps * caps)
305 {
306   const GValue *val;
307   GstStructure *s;
308   GstCaps *res;
309
310   /* transform single caps into input_caps + input_caps with the rate
311    * field set to our supported range. This ensures that upstream knows
312    * about downstream's prefered rate(s) and can negotiate accordingly. */
313   res = gst_caps_copy (caps);
314
315   /* first, however, check if the caps contain a range for the rate field, in
316    * which case that side isn't going to care much about the exact sample rate
317    * chosen and we should just assume things will get fixated to something sane
318    * and we may just as well offer our full range instead of the range in the
319    * caps. If the rate is not an int range value, it's likely to express a
320    * real preference or limitation and we should maintain that structure as
321    * preference by putting it first into the transformed caps, and only add
322    * our full rate range as second option  */
323   s = gst_caps_get_structure (res, 0);
324   val = gst_structure_get_value (s, "rate");
325   if (val == NULL || GST_VALUE_HOLDS_INT_RANGE (val)) {
326     /* overwrite existing range, or add field if it doesn't exist yet */
327     gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
328   } else {
329     /* append caps with full range to existing caps with non-range rate field */
330     s = gst_structure_copy (s);
331     gst_structure_set (s, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
332     gst_caps_append_structure (res, s);
333   }
334
335   return res;
336 }
337
338 /* Fixate rate to the allowed rate that has the smallest difference */
339 static void
340 gst_audio_resample_fixate_caps (GstBaseTransform * base,
341     GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
342 {
343   GstStructure *s;
344   gint rate;
345
346   s = gst_caps_get_structure (caps, 0);
347   if (G_UNLIKELY (!gst_structure_get_int (s, "rate", &rate)))
348     return;
349
350   s = gst_caps_get_structure (othercaps, 0);
351   gst_structure_fixate_field_nearest_int (s, "rate", rate);
352 }
353
354 static const SpeexResampleFuncs *
355 gst_audio_resample_get_funcs (gint width, gboolean fp)
356 {
357   const SpeexResampleFuncs *funcs = NULL;
358
359   if (gst_audio_resample_use_int && (width == 8 || width == 16) && !fp)
360     funcs = &int_funcs;
361   else if ((!gst_audio_resample_use_int && (width == 8 || width == 16) && !fp)
362       || (width == 32 && fp))
363     funcs = &float_funcs;
364   else if ((width == 64 && fp) || ((width == 32 || width == 24) && !fp))
365     funcs = &double_funcs;
366   else
367     g_assert_not_reached ();
368
369   return funcs;
370 }
371
372 static SpeexResamplerState *
373 gst_audio_resample_init_state (GstAudioResample * resample, gint width,
374     gint channels, gint inrate, gint outrate, gint quality, gboolean fp)
375 {
376   SpeexResamplerState *ret = NULL;
377   gint err = RESAMPLER_ERR_SUCCESS;
378   const SpeexResampleFuncs *funcs = gst_audio_resample_get_funcs (width, fp);
379
380   ret = funcs->init (channels, inrate, outrate, quality, &err);
381
382   if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) {
383     GST_ERROR_OBJECT (resample, "Failed to create resampler state: %s",
384         funcs->strerror (err));
385     return NULL;
386   }
387
388   funcs->skip_zeros (ret);
389
390   return ret;
391 }
392
393 static gboolean
394 gst_audio_resample_update_state (GstAudioResample * resample, gint width,
395     gint channels, gint inrate, gint outrate, gint quality, gboolean fp)
396 {
397   gboolean ret = TRUE;
398   gboolean updated_latency = FALSE;
399
400   updated_latency = (resample->inrate != inrate
401       || quality != resample->quality) && resample->state != NULL;
402
403   if (resample->state == NULL) {
404     ret = TRUE;
405   } else if (resample->channels != channels || fp != resample->fp
406       || width != resample->width) {
407     resample->funcs->destroy (resample->state);
408     resample->state =
409         gst_audio_resample_init_state (resample, width, channels, inrate,
410         outrate, quality, fp);
411
412     resample->funcs = gst_audio_resample_get_funcs (width, fp);
413     ret = (resample->state != NULL);
414   } else if (resample->inrate != inrate || resample->outrate != outrate) {
415     gint err = RESAMPLER_ERR_SUCCESS;
416
417     err = resample->funcs->set_rate (resample->state, inrate, outrate);
418
419     if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS))
420       GST_ERROR_OBJECT (resample, "Failed to update rate: %s",
421           resample->funcs->strerror (err));
422
423     ret = (err == RESAMPLER_ERR_SUCCESS);
424   } else if (quality != resample->quality) {
425     gint err = RESAMPLER_ERR_SUCCESS;
426
427     err = resample->funcs->set_quality (resample->state, quality);
428
429     if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS))
430       GST_ERROR_OBJECT (resample, "Failed to update quality: %s",
431           resample->funcs->strerror (err));
432
433     ret = (err == RESAMPLER_ERR_SUCCESS);
434   }
435
436   resample->width = width;
437   resample->channels = channels;
438   resample->fp = fp;
439   resample->quality = quality;
440   resample->inrate = inrate;
441   resample->outrate = outrate;
442
443   if (updated_latency)
444     gst_element_post_message (GST_ELEMENT (resample),
445         gst_message_new_latency (GST_OBJECT (resample)));
446
447   return ret;
448 }
449
450 static void
451 gst_audio_resample_reset_state (GstAudioResample * resample)
452 {
453   if (resample->state)
454     resample->funcs->reset_mem (resample->state);
455 }
456
457 static gboolean
458 gst_audio_resample_parse_caps (GstCaps * incaps,
459     GstCaps * outcaps, gint * width, gint * channels, gint * inrate,
460     gint * outrate, gboolean * fp)
461 {
462   GstStructure *structure;
463   gboolean ret;
464   gint mywidth, myinrate, myoutrate, mychannels;
465   gboolean myfp;
466
467   GST_DEBUG ("incaps %" GST_PTR_FORMAT ", outcaps %"
468       GST_PTR_FORMAT, incaps, outcaps);
469
470   structure = gst_caps_get_structure (incaps, 0);
471
472   if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float"))
473     myfp = TRUE;
474   else
475     myfp = FALSE;
476
477   ret = gst_structure_get_int (structure, "rate", &myinrate);
478   ret &= gst_structure_get_int (structure, "channels", &mychannels);
479   ret &= gst_structure_get_int (structure, "width", &mywidth);
480   if (G_UNLIKELY (!ret))
481     goto no_in_rate_channels;
482
483   structure = gst_caps_get_structure (outcaps, 0);
484   ret = gst_structure_get_int (structure, "rate", &myoutrate);
485   if (G_UNLIKELY (!ret))
486     goto no_out_rate;
487
488   if (channels)
489     *channels = mychannels;
490   if (inrate)
491     *inrate = myinrate;
492   if (outrate)
493     *outrate = myoutrate;
494   if (width)
495     *width = mywidth;
496   if (fp)
497     *fp = myfp;
498
499   return TRUE;
500
501   /* ERRORS */
502 no_in_rate_channels:
503   {
504     GST_DEBUG ("could not get input rate and channels");
505     return FALSE;
506   }
507 no_out_rate:
508   {
509     GST_DEBUG ("could not get output rate");
510     return FALSE;
511   }
512 }
513
514 static gint
515 _gcd (gint a, gint b)
516 {
517   while (b != 0) {
518     int temp = a;
519
520     a = b;
521     b = temp % b;
522   }
523
524   return ABS (a);
525 }
526
527 static gboolean
528 gst_audio_resample_transform_size (GstBaseTransform * base,
529     GstPadDirection direction, GstCaps * caps, guint size, GstCaps * othercaps,
530     guint * othersize)
531 {
532   gboolean ret = TRUE;
533   guint32 ratio_den, ratio_num;
534   gint inrate, outrate, gcd;
535   gint bytes_per_samp, channels;
536
537   GST_LOG_OBJECT (base, "asked to transform size %d in direction %s",
538       size, direction == GST_PAD_SINK ? "SINK" : "SRC");
539
540   /* Get sample width -> bytes_per_samp, channels, inrate, outrate */
541   ret =
542       gst_audio_resample_parse_caps (caps, othercaps, &bytes_per_samp,
543       &channels, &inrate, &outrate, NULL);
544   if (G_UNLIKELY (!ret)) {
545     GST_ERROR_OBJECT (base, "Wrong caps");
546     return FALSE;
547   }
548   /* Number of samples in either buffer is size / (width*channels) ->
549    * calculate the factor */
550   bytes_per_samp = bytes_per_samp * channels / 8;
551   /* Convert source buffer size to samples */
552   size /= bytes_per_samp;
553
554   /* Simplify the conversion ratio factors */
555   gcd = _gcd (inrate, outrate);
556   ratio_num = inrate / gcd;
557   ratio_den = outrate / gcd;
558
559   if (direction == GST_PAD_SINK) {
560     /* asked to convert size of an incoming buffer. Round up the output size */
561     *othersize = gst_util_uint64_scale_int_ceil (size, ratio_den, ratio_num);
562     *othersize *= bytes_per_samp;
563   } else {
564     /* asked to convert size of an outgoing buffer. Round down the input size */
565     *othersize = gst_util_uint64_scale_int (size, ratio_num, ratio_den);
566     *othersize *= bytes_per_samp;
567   }
568
569   GST_LOG_OBJECT (base, "transformed size %d to %d", size * bytes_per_samp,
570       *othersize);
571
572   return ret;
573 }
574
575 static gboolean
576 gst_audio_resample_set_caps (GstBaseTransform * base, GstCaps * incaps,
577     GstCaps * outcaps)
578 {
579   gboolean ret;
580   gint width = 0, inrate = 0, outrate = 0, channels = 0;
581   gboolean fp;
582   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
583
584   GST_LOG ("incaps %" GST_PTR_FORMAT ", outcaps %"
585       GST_PTR_FORMAT, incaps, outcaps);
586
587   ret = gst_audio_resample_parse_caps (incaps, outcaps,
588       &width, &channels, &inrate, &outrate, &fp);
589
590   if (G_UNLIKELY (!ret))
591     return FALSE;
592
593   ret =
594       gst_audio_resample_update_state (resample, width, channels, inrate,
595       outrate, resample->quality, fp);
596
597   if (G_UNLIKELY (!ret))
598     return FALSE;
599
600   /* save caps so we can short-circuit in the size_transform if the caps
601    * are the same */
602   gst_caps_replace (&resample->sinkcaps, incaps);
603   gst_caps_replace (&resample->srccaps, outcaps);
604
605   return TRUE;
606 }
607
608 #define GST_MAXINT24 (8388607)
609 #define GST_MININT24 (-8388608)
610
611 #if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
612 #define GST_READ_UINT24 GST_READ_UINT24_LE
613 #define GST_WRITE_UINT24 GST_WRITE_UINT24_LE
614 #else
615 #define GST_READ_UINT24 GST_READ_UINT24_BE
616 #define GST_WRITE_UINT24 GST_WRITE_UINT24_BE
617 #endif
618
619 static void
620 gst_audio_resample_convert_buffer (GstAudioResample * resample,
621     const guint8 * in, guint8 * out, guint len, gboolean inverse)
622 {
623   len *= resample->channels;
624
625   if (inverse) {
626     if (gst_audio_resample_use_int && resample->width == 8 && !resample->fp) {
627       gint8 *o = (gint8 *) out;
628       gint16 *i = (gint16 *) in;
629       gint32 tmp;
630
631       while (len) {
632         tmp = *i + (G_MAXINT8 >> 1);
633         *o = CLAMP (tmp >> 8, G_MININT8, G_MAXINT8);
634         o++;
635         i++;
636         len--;
637       }
638     } else if (!gst_audio_resample_use_int && resample->width == 8
639         && !resample->fp) {
640       gint8 *o = (gint8 *) out;
641       gfloat *i = (gfloat *) in;
642       gfloat tmp;
643
644       while (len) {
645         tmp = *i;
646         *o = (gint8) CLAMP (tmp * G_MAXINT8 + 0.5, G_MININT8, G_MAXINT8);
647         o++;
648         i++;
649         len--;
650       }
651     } else if (!gst_audio_resample_use_int && resample->width == 16
652         && !resample->fp) {
653       gint16 *o = (gint16 *) out;
654       gfloat *i = (gfloat *) in;
655       gfloat tmp;
656
657       while (len) {
658         tmp = *i;
659         *o = (gint16) CLAMP (tmp * G_MAXINT16 + 0.5, G_MININT16, G_MAXINT16);
660         o++;
661         i++;
662         len--;
663       }
664     } else if (resample->width == 24 && !resample->fp) {
665       guint8 *o = (guint8 *) out;
666       gdouble *i = (gdouble *) in;
667       gdouble tmp;
668
669       while (len) {
670         tmp = *i;
671         GST_WRITE_UINT24 (o, (gint32) CLAMP (tmp * GST_MAXINT24 + 0.5,
672                 GST_MININT24, GST_MAXINT24));
673         o += 3;
674         i++;
675         len--;
676       }
677     } else if (resample->width == 32 && !resample->fp) {
678       gint32 *o = (gint32 *) out;
679       gdouble *i = (gdouble *) in;
680       gdouble tmp;
681
682       while (len) {
683         tmp = *i;
684         *o = (gint32) CLAMP (tmp * G_MAXINT32 + 0.5, G_MININT32, G_MAXINT32);
685         o++;
686         i++;
687         len--;
688       }
689     } else {
690       g_assert_not_reached ();
691     }
692   } else {
693     if (gst_audio_resample_use_int && resample->width == 8 && !resample->fp) {
694       gint8 *i = (gint8 *) in;
695       gint16 *o = (gint16 *) out;
696       gint32 tmp;
697
698       while (len) {
699         tmp = *i;
700         *o = tmp << 8;
701         o++;
702         i++;
703         len--;
704       }
705     } else if (!gst_audio_resample_use_int && resample->width == 8
706         && !resample->fp) {
707       gint8 *i = (gint8 *) in;
708       gfloat *o = (gfloat *) out;
709       gfloat tmp;
710
711       while (len) {
712         tmp = *i;
713         *o = tmp / G_MAXINT8;
714         o++;
715         i++;
716         len--;
717       }
718     } else if (!gst_audio_resample_use_int && resample->width == 16
719         && !resample->fp) {
720       gint16 *i = (gint16 *) in;
721       gfloat *o = (gfloat *) out;
722       gfloat tmp;
723
724       while (len) {
725         tmp = *i;
726         *o = tmp / G_MAXINT16;
727         o++;
728         i++;
729         len--;
730       }
731     } else if (resample->width == 24 && !resample->fp) {
732       guint8 *i = (guint8 *) in;
733       gdouble *o = (gdouble *) out;
734       gdouble tmp;
735       guint32 tmp2;
736
737       while (len) {
738         tmp2 = GST_READ_UINT24 (i);
739         if (tmp2 & 0x00800000)
740           tmp2 |= 0xff000000;
741         tmp = (gint32) tmp2;
742         *o = tmp / GST_MAXINT24;
743         o++;
744         i += 3;
745         len--;
746       }
747     } else if (resample->width == 32 && !resample->fp) {
748       gint32 *i = (gint32 *) in;
749       gdouble *o = (gdouble *) out;
750       gdouble tmp;
751
752       while (len) {
753         tmp = *i;
754         *o = tmp / G_MAXINT32;
755         o++;
756         i++;
757         len--;
758       }
759     } else {
760       g_assert_not_reached ();
761     }
762   }
763 }
764
765 static guint8 *
766 gst_audio_resample_workspace_realloc (guint8 ** workspace, guint * size,
767     guint new_size)
768 {
769   guint8 *new;
770   if (new_size <= *size)
771     /* no need to resize */
772     return *workspace;
773   new = g_realloc (*workspace, new_size);
774   if (!new)
775     /* failure (re)allocating memeory */
776     return NULL;
777   /* success */
778   *workspace = new;
779   *size = new_size;
780   return *workspace;
781 }
782
783 static void
784 gst_audio_resample_push_drain (GstAudioResample * resample)
785 {
786   GstBuffer *outbuf;
787   GstFlowReturn res;
788   gint outsize;
789   guint history_len, out_len, out_processed;
790   gint err;
791   guint num, den;
792
793   if (!resample->state)
794     return;
795
796   /* Don't drain samples if we were reset. */
797   if (!GST_CLOCK_TIME_IS_VALID (resample->t0))
798     return;
799
800   resample->funcs->get_ratio (resample->state, &num, &den);
801
802   history_len = resample->funcs->get_input_latency (resample->state);
803   out_len = out_processed =
804       gst_util_uint64_scale_int_ceil (history_len, den, num);
805   outsize = out_len * resample->channels * (resample->width / 8);
806
807   res =
808       gst_pad_alloc_buffer_and_set_caps (GST_BASE_TRANSFORM_SRC_PAD (resample),
809       GST_BUFFER_OFFSET_NONE, outsize,
810       GST_PAD_CAPS (GST_BASE_TRANSFORM_SRC_PAD (resample)), &outbuf);
811   if (G_UNLIKELY (res != GST_FLOW_OK)) {
812     GST_WARNING_OBJECT (resample, "failed allocating buffer of %d bytes",
813         outsize);
814     return;
815   }
816
817   if (resample->funcs->width != resample->width) {
818     /* need to convert data format;  allocate workspace */
819     if (!gst_audio_resample_workspace_realloc (&resample->tmp_out,
820             &resample->tmp_out_size, (resample->funcs->width / 8) * out_len *
821             resample->channels)) {
822       GST_ERROR_OBJECT (resample, "failed to allocate workspace");
823       return;
824     }
825
826     /* process */
827     err = resample->funcs->process (resample->state, NULL, &out_len,
828         resample->tmp_out, &out_processed);
829
830     /* convert output format */
831     gst_audio_resample_convert_buffer (resample, resample->tmp_out,
832         GST_BUFFER_DATA (outbuf), out_processed, TRUE);
833   } else {
834     /* don't need to convert data format;  process */
835     err = resample->funcs->process (resample->state, NULL, &history_len,
836         GST_BUFFER_DATA (outbuf), &out_processed);
837   }
838
839   /* If we wrote more than allocated something is really wrong now
840    * and we should better abort immediately */
841   g_assert (out_len >= out_processed);
842
843   if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) {
844     GST_WARNING_OBJECT (resample, "Failed to process drain: %s",
845         resample->funcs->strerror (err));
846     gst_buffer_unref (outbuf);
847     return;
848   }
849
850   if (G_UNLIKELY (out_processed == 0)) {
851     GST_WARNING_OBJECT (resample, "Failed to get drain, dropping buffer");
852     gst_buffer_unref (outbuf);
853     return;
854   }
855
856   if (GST_CLOCK_TIME_IS_VALID (resample->t0)) {
857     GST_BUFFER_OFFSET (outbuf) = resample->next_out_offset;
858     GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET (outbuf) + out_processed;
859     GST_BUFFER_TIMESTAMP (outbuf) = resample->t0 +
860         gst_util_uint64_scale_int_round (GST_BUFFER_OFFSET (outbuf) -
861         resample->out_offset0, GST_SECOND, resample->outrate);
862     GST_BUFFER_DURATION (outbuf) = resample->t0 +
863         gst_util_uint64_scale_int_round (GST_BUFFER_OFFSET_END (outbuf) -
864         resample->out_offset0, GST_SECOND, resample->outrate) -
865         GST_BUFFER_TIMESTAMP (outbuf);
866     resample->next_out_offset += out_processed;
867     resample->next_in_offset += 0;
868   } else {
869     GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE;
870     GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;
871     GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
872     GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
873   }
874
875   GST_BUFFER_SIZE (outbuf) =
876       out_processed * resample->channels * (resample->width / 8);
877
878   GST_LOG_OBJECT (resample,
879       "Pushing drain buffer of %u bytes with timestamp %" GST_TIME_FORMAT
880       " duration %" GST_TIME_FORMAT " offset %" G_GUINT64_FORMAT " offset_end %"
881       G_GUINT64_FORMAT, GST_BUFFER_SIZE (outbuf),
882       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
883       GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)), GST_BUFFER_OFFSET (outbuf),
884       GST_BUFFER_OFFSET_END (outbuf));
885
886   res = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (resample), outbuf);
887
888   if (G_UNLIKELY (res != GST_FLOW_OK))
889     GST_WARNING_OBJECT (resample, "Failed to push drain: %s",
890         gst_flow_get_name (res));
891
892   return;
893 }
894
895 static gboolean
896 gst_audio_resample_event (GstBaseTransform * base, GstEvent * event)
897 {
898   GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
899
900   switch (GST_EVENT_TYPE (event)) {
901     case GST_EVENT_FLUSH_STOP:
902       gst_audio_resample_reset_state (resample);
903       resample->t0 = GST_CLOCK_TIME_NONE;
904       resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
905       resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
906       resample->next_in_offset = GST_BUFFER_OFFSET_NONE;
907       resample->next_out_offset = GST_BUFFER_OFFSET_NONE;
908       resample->need_discont = TRUE;
909       break;
910     case GST_EVENT_NEWSEGMENT:
911       gst_audio_resample_push_drain (resample);
912       gst_audio_resample_reset_state (resample);
913       resample->t0 = GST_CLOCK_TIME_NONE;
914       resample->in_offset0 = GST_BUFFER_OFFSET_NONE;
915       resample->out_offset0 = GST_BUFFER_OFFSET_NONE;
916       resample->next_in_offset = GST_BUFFER_OFFSET_NONE;
917       resample->next_out_offset = GST_BUFFER_OFFSET_NONE;
918       resample->need_discont = TRUE;
919       break;
920     case GST_EVENT_EOS:
921       gst_audio_resample_push_drain (resample);
922       gst_audio_resample_reset_state (resample);
923       break;
924     default:
925       break;
926   }
927
928   return parent_class->event (base, event);
929 }
930
931 static gboolean
932 gst_audio_resample_check_discont (GstAudioResample * resample, GstBuffer * buf)
933 {
934   guint64 offset;
935   guint64 delta;
936
937   /* is the incoming buffer a discontinuity? */
938   if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (buf)))
939     return TRUE;
940
941   /* no valid timestamps or offsets to compare --> no discontinuity */
942   if (G_UNLIKELY (!(GST_BUFFER_TIMESTAMP_IS_VALID (buf) &&
943               GST_CLOCK_TIME_IS_VALID (resample->t0) &&
944               resample->in_offset0 != GST_BUFFER_OFFSET_NONE &&
945               resample->next_in_offset != GST_BUFFER_OFFSET_NONE)))
946     return FALSE;
947
948   /* convert the inbound timestamp to an offset. */
949   offset =
950       resample->in_offset0 +
951       gst_util_uint64_scale_int_round (GST_BUFFER_TIMESTAMP (buf) -
952       resample->t0, resample->inrate, GST_SECOND);
953
954   /* many elements generate imperfect streams due to rounding errors, so we
955    * permit a small error (up to one sample) without triggering a filter
956    * flush/restart (if triggered incorrectly, this will be audible) */
957   delta = ABS ((gint64) (offset - resample->next_in_offset));
958   if (delta <= 1)
959     return FALSE;
960
961   GST_WARNING_OBJECT (resample,
962       "encountered timestamp discontinuity of %lu samples = %" GST_TIME_FORMAT,
963       delta, 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   OilProfile a, b;
1394   gdouble av, bv;
1395   SpeexResamplerState *sta, *stb;
1396   int i;
1397
1398   oil_profile_init (&a);
1399   oil_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     oil_profile_start (&a);
1417     if (!_benchmark_int_float (sta))
1418       goto error;
1419     oil_profile_stop (&a);
1420   }
1421
1422   /* Benchmark */
1423   for (i = 0; i < 10; i++) {
1424     oil_profile_start (&b);
1425     if (!_benchmark_int_int (stb))
1426       goto error;
1427     oil_profile_stop (&b);
1428   }
1429
1430   /* Handle results */
1431   oil_profile_get_ave_std (&a, &av, NULL);
1432   oil_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   oil_init ();
1461
1462   if (!_benchmark_integer_resampling ())
1463     return FALSE;
1464 #endif
1465
1466   if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY,
1467           GST_TYPE_AUDIO_RESAMPLE)) {
1468     return FALSE;
1469   }
1470
1471   return TRUE;
1472 }
1473
1474 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1475     GST_VERSION_MINOR,
1476     "audioresample",
1477     "Resamples audio", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
1478     GST_PACKAGE_ORIGIN);