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