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>
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.
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.
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.
23 * SECTION:element-audioresample
25 * audioresample resamples raw audio buffers to different sample rates using
26 * a configurable windowing function to enhance quality.
29 * <title>Example launch line</title>
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.
38 * - Enable SSE/ARM optimizations and select at runtime
48 #include "gstaudioresample.h"
49 #include <gst/gstutils.h>
50 #include <gst/audio/audio.h>
51 #include <gst/base/gstbasetransform.h>
55 #include <orc-test/orctest.h>
56 #include <orc-test/orcprofile.h>
59 GST_DEBUG_CATEGORY (audio_resample_debug);
60 #define GST_CAT_DEFAULT audio_resample_debug
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 }"
73 #define SUPPORTED_CAPS \
74 GST_AUDIO_CAPS_MAKE ("{ F32BE, F64BE, S32BE, S24BE, S16BE, S8 }") \
75 ", layout = (string) { interleaved, non-interleaved }"
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;
84 static gboolean gst_audio_resample_use_int = FALSE;
87 static GstStaticPadTemplate gst_audio_resample_sink_template =
88 GST_STATIC_PAD_TEMPLATE ("sink",
91 GST_STATIC_CAPS (SUPPORTED_CAPS));
93 static GstStaticPadTemplate gst_audio_resample_src_template =
94 GST_STATIC_PAD_TEMPLATE ("src",
97 GST_STATIC_CAPS (SUPPORTED_CAPS));
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);
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,
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,
125 #define gst_audio_resample_parent_class parent_class
126 G_DEFINE_TYPE (GstAudioResample, gst_audio_resample, GST_TYPE_BASE_TRANSFORM);
129 gst_audio_resample_class_init (GstAudioResampleClass * klass)
131 GObjectClass *gobject_class = (GObjectClass *) klass;
132 GstElementClass *gstelement_class = (GstElementClass *) klass;
134 gobject_class->set_property = gst_audio_resample_set_property;
135 gobject_class->get_property = gst_audio_resample_get_property;
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));
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));
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>");
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);
172 GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE;
176 gst_audio_resample_init (GstAudioResample * resample)
178 GstBaseTransform *trans = GST_BASE_TRANSFORM (resample);
180 resample->quality = SPEEX_RESAMPLER_QUALITY_DEFAULT;
182 gst_base_transform_set_gap_aware (trans, TRUE);
183 gst_pad_set_query_function (trans->srcpad, gst_audio_resample_query);
188 gst_audio_resample_start (GstBaseTransform * base)
190 GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
192 resample->need_discont = TRUE;
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;
202 resample->tmp_in = NULL;
203 resample->tmp_in_size = 0;
204 resample->tmp_out = NULL;
205 resample->tmp_out_size = 0;
211 gst_audio_resample_stop (GstBaseTransform * base)
213 GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
215 if (resample->state) {
216 resample->funcs->destroy (resample->state);
217 resample->state = NULL;
220 resample->funcs = NULL;
222 g_free (resample->tmp_in);
223 resample->tmp_in = NULL;
224 resample->tmp_in_size = 0;
226 g_free (resample->tmp_out);
227 resample->tmp_out = NULL;
228 resample->tmp_out_size = 0;
234 gst_audio_resample_get_unit_size (GstBaseTransform * base, GstCaps * caps,
239 if (!gst_audio_info_from_caps (&info, caps))
242 *size = GST_AUDIO_INFO_BPF (&info);
249 GST_ERROR_OBJECT (base, "invalid caps");
255 gst_audio_resample_transform_caps (GstBaseTransform * base,
256 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
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);
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))
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);
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);
294 gst_caps_append_structure (res, s);
298 GstCaps *intersection;
301 gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST);
302 gst_caps_unref (res);
309 /* Fixate rate to the allowed rate that has the smallest difference */
311 gst_audio_resample_fixate_caps (GstBaseTransform * base,
312 GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
317 s = gst_caps_get_structure (caps, 0);
318 if (G_UNLIKELY (!gst_structure_get_int (s, "rate", &rate)))
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);
329 static const SpeexResampleFuncs *
330 gst_audio_resample_get_funcs (gint width, gboolean fp)
332 const SpeexResampleFuncs *funcs = NULL;
334 if (gst_audio_resample_use_int && (width == 8 || width == 16) && !fp)
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;
342 g_assert_not_reached ();
347 static SpeexResamplerState *
348 gst_audio_resample_init_state (GstAudioResample * resample, gint width,
349 gint channels, gint inrate, gint outrate, gint quality, gboolean fp)
351 SpeexResamplerState *ret = NULL;
352 gint err = RESAMPLER_ERR_SUCCESS;
353 const SpeexResampleFuncs *funcs = gst_audio_resample_get_funcs (width, fp);
355 ret = funcs->init (channels, inrate, outrate, quality, &err);
357 if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS)) {
358 GST_ERROR_OBJECT (resample, "Failed to create resampler state: %s",
359 funcs->strerror (err));
363 funcs->skip_zeros (ret);
369 gst_audio_resample_update_state (GstAudioResample * resample, gint width,
370 gint channels, gint inrate, gint outrate, gint quality, gboolean fp)
373 gboolean updated_latency = FALSE;
375 updated_latency = (resample->inrate != inrate
376 || quality != resample->quality) && resample->state != NULL;
378 if (resample->state == NULL) {
380 } else if (resample->channels != channels || fp != resample->fp
381 || width != resample->width) {
382 resample->funcs->destroy (resample->state);
384 gst_audio_resample_init_state (resample, width, channels, inrate,
385 outrate, quality, fp);
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;
392 err = resample->funcs->set_rate (resample->state, inrate, outrate);
394 if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS))
395 GST_ERROR_OBJECT (resample, "Failed to update rate: %s",
396 resample->funcs->strerror (err));
398 ret = (err == RESAMPLER_ERR_SUCCESS);
399 } else if (quality != resample->quality) {
400 gint err = RESAMPLER_ERR_SUCCESS;
402 err = resample->funcs->set_quality (resample->state, quality);
404 if (G_UNLIKELY (err != RESAMPLER_ERR_SUCCESS))
405 GST_ERROR_OBJECT (resample, "Failed to update quality: %s",
406 resample->funcs->strerror (err));
408 ret = (err == RESAMPLER_ERR_SUCCESS);
411 resample->width = width;
412 resample->channels = channels;
414 resample->quality = quality;
415 resample->inrate = inrate;
416 resample->outrate = outrate;
419 gst_element_post_message (GST_ELEMENT (resample),
420 gst_message_new_latency (GST_OBJECT (resample)));
426 gst_audio_resample_reset_state (GstAudioResample * resample)
429 resample->funcs->reset_mem (resample->state);
433 _gcd (gint a, gint b)
446 gst_audio_resample_transform_size (GstBaseTransform * base,
447 GstPadDirection direction, GstCaps * caps, gsize size, GstCaps * othercaps,
451 GstAudioInfo in, out;
452 guint32 ratio_den, ratio_num;
453 gint inrate, outrate, gcd;
456 GST_LOG_OBJECT (base, "asked to transform size %" G_GSIZE_FORMAT
457 " in direction %s", size, direction == GST_PAD_SINK ? "SINK" : "SRC");
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");
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);
472 /* Convert source buffer size to samples */
475 /* Simplify the conversion ratio factors */
476 gcd = _gcd (inrate, outrate);
477 ratio_num = inrate / gcd;
478 ratio_den = outrate / gcd;
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);
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);
490 GST_LOG_OBJECT (base,
491 "transformed size %" G_GSIZE_FORMAT " to %" G_GSIZE_FORMAT,
492 size * bpf, *othersize);
498 gst_audio_resample_set_caps (GstBaseTransform * base, GstCaps * incaps,
502 gint width, inrate, outrate, channels;
504 GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
505 GstAudioInfo in, out;
507 GST_LOG ("incaps %" GST_PTR_FORMAT ", outcaps %"
508 GST_PTR_FORMAT, incaps, outcaps);
510 if (!gst_audio_info_from_caps (&in, incaps))
512 if (!gst_audio_info_from_caps (&out, outcaps))
513 goto invalid_outcaps;
515 /* FIXME do some checks */
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);
525 gst_audio_resample_update_state (resample, width, channels, inrate,
526 outrate, resample->quality, fp);
528 if (G_UNLIKELY (!ret))
536 GST_ERROR_OBJECT (base, "invalid incaps");
541 GST_ERROR_OBJECT (base, "invalid outcaps");
546 #define GST_MAXINT24 (8388607)
547 #define GST_MININT24 (-8388608)
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
553 #define GST_READ_UINT24 GST_READ_UINT24_BE
554 #define GST_WRITE_UINT24 GST_WRITE_UINT24_BE
558 gst_audio_resample_convert_buffer (GstAudioResample * resample,
559 const guint8 * in, guint8 * out, guint len, gboolean inverse)
561 len *= resample->channels;
564 if (gst_audio_resample_use_int && resample->width == 8 && !resample->fp) {
565 gint8 *o = (gint8 *) out;
566 gint16 *i = (gint16 *) in;
570 tmp = *i + (G_MAXINT8 >> 1);
571 *o = CLAMP (tmp >> 8, G_MININT8, G_MAXINT8);
576 } else if (!gst_audio_resample_use_int && resample->width == 8
578 gint8 *o = (gint8 *) out;
579 gfloat *i = (gfloat *) in;
584 *o = (gint8) CLAMP (tmp * G_MAXINT8 + 0.5, G_MININT8, G_MAXINT8);
589 } else if (!gst_audio_resample_use_int && resample->width == 16
591 gint16 *o = (gint16 *) out;
592 gfloat *i = (gfloat *) in;
597 *o = (gint16) CLAMP (tmp * G_MAXINT16 + 0.5, G_MININT16, G_MAXINT16);
602 } else if (resample->width == 24 && !resample->fp) {
603 guint8 *o = (guint8 *) out;
604 gdouble *i = (gdouble *) in;
609 GST_WRITE_UINT24 (o, (gint32) CLAMP (tmp * GST_MAXINT24 + 0.5,
610 GST_MININT24, GST_MAXINT24));
615 } else if (resample->width == 32 && !resample->fp) {
616 gint32 *o = (gint32 *) out;
617 gdouble *i = (gdouble *) in;
622 *o = (gint32) CLAMP (tmp * G_MAXINT32 + 0.5, G_MININT32, G_MAXINT32);
628 g_assert_not_reached ();
631 if (gst_audio_resample_use_int && resample->width == 8 && !resample->fp) {
632 gint8 *i = (gint8 *) in;
633 gint16 *o = (gint16 *) out;
643 } else if (!gst_audio_resample_use_int && resample->width == 8
645 gint8 *i = (gint8 *) in;
646 gfloat *o = (gfloat *) out;
651 *o = tmp / G_MAXINT8;
656 } else if (!gst_audio_resample_use_int && resample->width == 16
658 gint16 *i = (gint16 *) in;
659 gfloat *o = (gfloat *) out;
664 *o = tmp / G_MAXINT16;
669 } else if (resample->width == 24 && !resample->fp) {
670 guint8 *i = (guint8 *) in;
671 gdouble *o = (gdouble *) out;
676 tmp2 = GST_READ_UINT24 (i);
677 if (tmp2 & 0x00800000)
680 *o = tmp / GST_MAXINT24;
685 } else if (resample->width == 32 && !resample->fp) {
686 gint32 *i = (gint32 *) in;
687 gdouble *o = (gdouble *) out;
692 *o = tmp / G_MAXINT32;
698 g_assert_not_reached ();
704 gst_audio_resample_workspace_realloc (guint8 ** workspace, guint * size,
708 if (new_size <= *size)
709 /* no need to resize */
711 new = g_realloc (*workspace, new_size);
713 /* failure (re)allocating memeory */
721 /* Push history_len zeros into the filter, but discard the output. */
723 gst_audio_resample_dump_drain (GstAudioResample * resample, guint history_len)
726 guint in_len, in_processed;
727 guint out_len, out_processed;
731 g_assert (resample->state != NULL);
733 resample->funcs->get_ratio (resample->state, &num, &den);
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);
743 buf = g_malloc (outsize);
744 resample->funcs->process (resample->state, NULL, &in_processed, buf,
748 g_assert (in_len == in_processed);
752 gst_audio_resample_push_drain (GstAudioResample * resample, guint history_len)
757 guint in_len, in_processed;
758 guint out_len, out_processed;
763 g_assert (resample->state != NULL);
765 /* Don't drain samples if we were reset. */
766 if (!GST_CLOCK_TIME_IS_VALID (resample->t0))
769 resample->funcs->get_ratio (resample->state, &num, &den);
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);
779 outbuf = gst_buffer_new_and_alloc (outsize);
781 gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
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");
793 err = resample->funcs->process (resample->state, NULL, &in_processed,
794 resample->tmp_out, &out_processed);
796 /* convert output format */
797 gst_audio_resample_convert_buffer (resample, resample->tmp_out,
798 map.data, out_processed, TRUE);
800 /* don't need to convert data format; process */
801 err = resample->funcs->process (resample->state, NULL, &in_processed,
802 map.data, &out_processed);
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);
809 outsize = out_processed * resample->channels * (resample->width / 8);
810 gst_buffer_unmap (outbuf, &map);
811 gst_buffer_resize (outbuf, 0, outsize);
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);
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,
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);
829 GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
830 GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
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;
837 GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE;
838 GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;
841 resample->samples_out += out_processed;
842 resample->samples_in += history_len;
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);
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));
858 res = gst_pad_push (GST_BASE_TRANSFORM_SRC_PAD (resample), outbuf);
860 if (G_UNLIKELY (res != GST_FLOW_OK))
861 GST_WARNING_OBJECT (resample, "Failed to push drain: %s",
862 gst_flow_get_name (res));
868 gst_audio_resample_sink_event (GstBaseTransform * base, GstEvent * event)
870 GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
872 switch (GST_EVENT_TYPE (event)) {
873 case GST_EVENT_FLUSH_STOP:
874 gst_audio_resample_reset_state (resample);
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;
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);
891 gst_audio_resample_reset_state (resample);
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;
904 if (resample->state) {
905 guint latency = resample->funcs->get_input_latency (resample->state);
906 gst_audio_resample_push_drain (resample, latency);
908 gst_audio_resample_reset_state (resample);
914 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (base, event);
918 gst_audio_resample_check_discont (GstAudioResample * resample, GstBuffer * buf)
923 /* is the incoming buffer a discontinuity? */
924 if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (buf)))
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))))
932 /* convert the inbound timestamp to an offset. */
934 gst_util_uint64_scale_int_round (GST_BUFFER_TIMESTAMP (buf) -
935 resample->t0, resample->inrate, GST_SECOND);
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))
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,
955 gst_audio_resample_process (GstAudioResample * resample, GstBuffer * inbuf,
958 GstMapInfo in_map, out_map;
960 guint32 in_len, in_processed;
961 guint32 out_len, out_processed;
962 guint filt_len = resample->funcs->get_filt_len (resample->state);
964 gst_buffer_map (inbuf, &in_map, GST_MAP_READ);
965 gst_buffer_map (outbuf, &out_map, GST_MAP_WRITE);
967 in_len = in_map.size / resample->channels;
968 out_len = out_map.size / resample->channels;
970 in_len /= (resample->width / 8);
971 out_len /= (resample->width / 8);
973 in_processed = in_len;
974 out_processed = out_len;
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) {
980 if (in_len >= filt_len - resample->num_gap_samples)
981 zeros_to_push = filt_len - resample->num_gap_samples;
983 zeros_to_push = in_len;
985 gst_audio_resample_push_drain (resample, zeros_to_push);
986 in_len -= zeros_to_push;
987 resample->num_gap_samples += zeros_to_push;
992 resample->funcs->get_ratio (resample->state, &num, &den);
993 if (resample->samples_in + in_len >= filt_len / 2)
995 gst_util_uint64_scale_int_ceil (resample->samples_in + in_len -
996 filt_len / 2, den, num) - resample->samples_out;
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;
1005 } else { /* not a gap */
1009 if (resample->num_gap_samples > filt_len) {
1010 /* push in enough zeros to restore the filter to the right offset */
1012 resample->funcs->get_ratio (resample->state, &num, &den);
1013 gst_audio_resample_dump_drain (resample,
1014 (resample->num_gap_samples - filt_len) % num);
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;
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;
1039 gst_audio_resample_convert_buffer (resample, in_map.data,
1040 resample->tmp_in, in_len, FALSE);
1043 err = resample->funcs->process (resample->state,
1044 resample->tmp_in, &in_processed, resample->tmp_out, &out_processed);
1046 /* convert output */
1047 gst_audio_resample_convert_buffer (resample, resample->tmp_out,
1048 out_map.data, out_processed, TRUE);
1050 /* no format conversion required; process */
1051 err = resample->funcs->process (resample->state,
1052 in_map.data, &in_processed, out_map.data, &out_processed);
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;
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);
1068 if (G_UNLIKELY (in_len != in_processed)) {
1069 GST_WARNING_OBJECT (resample, "converted %d of %d input samples",
1070 in_processed, in_len);
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,
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);
1082 GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
1083 GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
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;
1090 GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET_NONE;
1091 GST_BUFFER_OFFSET_END (outbuf) = GST_BUFFER_OFFSET_NONE;
1094 resample->samples_out += out_processed;
1095 resample->samples_in += in_len;
1097 gst_buffer_unmap (inbuf, &in_map);
1098 gst_buffer_unmap (outbuf, &out_map);
1100 outsize = out_processed * resample->channels * (resample->width / 8);
1101 gst_buffer_resize (outbuf, 0, outsize);
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));
1115 static GstFlowReturn
1116 gst_audio_resample_transform (GstBaseTransform * base, GstBuffer * inbuf,
1119 GstAudioResample *resample = GST_AUDIO_RESAMPLE (base);
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;
1130 gst_audio_resample_get_funcs (resample->width, resample->fp);
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));
1140 /* check for timestamp discontinuities; flush/reset if needed, and set
1141 * flag to resync timestamp and offset counters and send event
1143 if (G_UNLIKELY (gst_audio_resample_check_discont (resample, inbuf))) {
1144 gst_audio_resample_reset_state (resample);
1145 resample->need_discont = TRUE;
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;
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);
1161 GST_DEBUG_OBJECT (resample, "... but new timestamp is invalid");
1162 resample->t0 = GST_CLOCK_TIME_NONE;
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);
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;
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;
1180 ret = gst_audio_resample_process (resample, inbuf, outbuf);
1181 if (G_UNLIKELY (ret != GST_FLOW_OK))
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));
1198 gst_audio_resample_query (GstPad * pad, GstObject * parent, GstQuery * query)
1200 GstAudioResample *resample = GST_AUDIO_RESAMPLE (parent);
1201 GstBaseTransform *trans;
1202 gboolean res = TRUE;
1204 trans = GST_BASE_TRANSFORM (resample);
1206 switch (GST_QUERY_TYPE (query)) {
1207 case GST_QUERY_LATENCY:
1209 GstClockTime min, max;
1212 gint rate = resample->inrate;
1213 gint resampler_latency;
1215 if (resample->state)
1217 resample->funcs->get_input_latency (resample->state);
1219 resampler_latency = 0;
1221 if (gst_base_transform_is_passthrough (trans))
1222 resampler_latency = 0;
1225 gst_pad_peer_query (GST_BASE_TRANSFORM_SINK_PAD (trans),
1227 gst_query_parse_latency (query, &live, &min, &max);
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));
1233 /* add our own latency */
1234 if (rate != 0 && resampler_latency != 0)
1235 latency = gst_util_uint64_scale_round (resampler_latency,
1240 GST_DEBUG_OBJECT (resample, "Our latency: %" GST_TIME_FORMAT,
1241 GST_TIME_ARGS (latency));
1244 if (GST_CLOCK_TIME_IS_VALID (max))
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));
1251 gst_query_set_latency (query, live, min, max);
1256 res = gst_pad_query_default (pad, parent, query);
1263 gst_audio_resample_set_property (GObject * object, guint prop_id,
1264 const GValue * value, GParamSpec * pspec)
1266 GstAudioResample *resample;
1269 resample = GST_AUDIO_RESAMPLE (object);
1273 /* FIXME locking! */
1274 quality = g_value_get_int (value);
1275 GST_DEBUG_OBJECT (resample, "new quality %d", quality);
1277 gst_audio_resample_update_state (resample, resample->width,
1278 resample->channels, resample->inrate, resample->outrate,
1279 quality, resample->fp);
1282 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1288 gst_audio_resample_get_property (GObject * object, guint prop_id,
1289 GValue * value, GParamSpec * pspec)
1291 GstAudioResample *resample;
1293 resample = GST_AUDIO_RESAMPLE (object);
1297 g_value_set_int (value, resample->quality);
1300 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1305 /* FIXME: should have a benchmark fallback for the case where orc is disabled */
1306 #if defined(AUDIORESAMPLE_FORMAT_AUTO) && !defined(DISABLE_ORC)
1308 #define BENCHMARK_SIZE 512
1311 _benchmark_int_float (SpeexResamplerState * st)
1313 gint16 in[BENCHMARK_SIZE] = { 0, }, G_GNUC_UNUSED out[BENCHMARK_SIZE / 2];
1314 gfloat in_tmp[BENCHMARK_SIZE], out_tmp[BENCHMARK_SIZE / 2];
1316 guint32 inlen = BENCHMARK_SIZE, outlen = BENCHMARK_SIZE / 2;
1318 for (i = 0; i < BENCHMARK_SIZE; i++) {
1320 in_tmp[i] = tmp / G_MAXINT16;
1323 resample_float_resampler_process_interleaved_float (st,
1324 (const guint8 *) in_tmp, &inlen, (guint8 *) out_tmp, &outlen);
1327 GST_ERROR ("Failed to use float resampler");
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);
1340 _benchmark_int_int (SpeexResamplerState * st)
1342 gint16 in[BENCHMARK_SIZE] = { 0, }, out[BENCHMARK_SIZE / 2];
1343 guint32 inlen = BENCHMARK_SIZE, outlen = BENCHMARK_SIZE / 2;
1345 resample_int_resampler_process_interleaved_int (st, (const guint8 *) in,
1346 &inlen, (guint8 *) out, &outlen);
1349 GST_ERROR ("Failed to use int resampler");
1357 _benchmark_integer_resampling (void)
1361 SpeexResamplerState *sta, *stb;
1364 orc_profile_init (&a);
1365 orc_profile_init (&b);
1367 sta = resample_float_resampler_init (1, 48000, 24000, 4, NULL);
1369 GST_ERROR ("Failed to create float resampler state");
1373 stb = resample_int_resampler_init (1, 48000, 24000, 4, NULL);
1375 resample_float_resampler_destroy (sta);
1376 GST_ERROR ("Failed to create int resampler state");
1381 for (i = 0; i < 10; i++) {
1382 orc_profile_start (&a);
1383 if (!_benchmark_int_float (sta))
1385 orc_profile_stop (&a);
1389 for (i = 0; i < 10; i++) {
1390 orc_profile_start (&b);
1391 if (!_benchmark_int_int (stb))
1393 orc_profile_stop (&b);
1396 /* Handle results */
1397 orc_profile_get_ave_std (&a, &av, NULL);
1398 orc_profile_get_ave_std (&b, &bv, NULL);
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);
1406 GST_INFO ("Using integer resampler if appropriate: %lf < %lf", bv, av);
1408 GST_INFO ("Using float resampler for everything: %lf <= %lf", av, bv);
1413 resample_float_resampler_destroy (sta);
1414 resample_int_resampler_destroy (stb);
1418 #endif /* defined(AUDIORESAMPLE_FORMAT_AUTO) && !defined(DISABLE_ORC) */
1421 plugin_init (GstPlugin * plugin)
1423 GST_DEBUG_CATEGORY_INIT (audio_resample_debug, "audioresample", 0,
1424 "audio resampling element");
1426 #if defined(AUDIORESAMPLE_FORMAT_AUTO) && !defined(DISABLE_ORC)
1427 if (!_benchmark_integer_resampling ())
1430 GST_WARNING ("Orc disabled, can't benchmark int vs. float resampler");
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");
1438 if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY,
1439 GST_TYPE_AUDIO_RESAMPLE)) {
1446 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1449 "Resamples audio", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
1450 GST_PACKAGE_ORIGIN);