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