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