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