gst/audioresample/gstaudioresample.c: Add support for other formats audioresample...
[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  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 /* Element-Checklist-Version: 5 */
21
22 /**
23  * SECTION:element-audioresample
24  *
25  * <refsect2>
26  * Audioresample resamples raw audio buffers to different sample rates using
27  * a configurable windowing function to enhance quality.
28  * <title>Example launch line</title>
29  * <para>
30  * <programlisting>
31  * gst-launch -v filesrc location=sine.ogg ! oggdemux ! vorbisdec ! audioconvert ! audioresample ! audio/x-raw-int, rate=8000 ! alsasink
32  * </programlisting>
33  * Decode an Ogg/Vorbis downsample to 8Khz and play sound through alsa. 
34  * To create the Ogg/Vorbis file refer to the documentation of vorbisenc.
35  * </para>
36  * </refsect2>
37  *
38  * Last reviewed on 2006-03-02 (0.10.4)
39  */
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #include <string.h>
46 #include <math.h>
47
48 /*#define DEBUG_ENABLED */
49 #include "gstaudioresample.h"
50 #include <gst/audio/audio.h>
51 #include <gst/base/gstbasetransform.h>
52
53 GST_DEBUG_CATEGORY (audioresample_debug);
54 #define GST_CAT_DEFAULT audioresample_debug
55
56 /* elementfactory information */
57 static GstElementDetails gst_audioresample_details =
58 GST_ELEMENT_DETAILS ("Audio scaler",
59     "Filter/Converter/Audio",
60     "Resample audio",
61     "David Schleef <ds@schleef.org>");
62
63 /* GstAudioresample signals and args */
64 enum
65 {
66   /* FILL ME */
67   LAST_SIGNAL
68 };
69
70 #define DEFAULT_FILTERLEN       16
71
72 enum
73 {
74   PROP_0,
75   PROP_FILTERLEN
76 };
77
78 #define SUPPORTED_CAPS \
79 GST_STATIC_CAPS ( \
80     "audio/x-raw-int, " \
81       "rate = (int) [ 1, MAX ], " \
82       "channels = (int) [ 1, MAX ], " \
83       "endianness = (int) BYTE_ORDER, " \
84       "width = (int) 16, " \
85       "depth = (int) 16, " \
86       "signed = (boolean) true;" \
87     "audio/x-raw-int, " \
88       "rate = (int) [ 1, MAX ], " \
89       "channels = (int) [ 1, MAX ], " \
90       "endianness = (int) BYTE_ORDER, " \
91       "width = (int) 32, " \
92       "depth = (int) 32, " \
93       "signed = (boolean) true;" \
94     "audio/x-raw-float, " \
95       "rate = (int) [ 1, MAX ], "       \
96       "channels = (int) [ 1, MAX ], " \
97       "endianness = (int) BYTE_ORDER, " \
98       "width = (int) 32; " \
99     "audio/x-raw-float, " \
100       "rate = (int) [ 1, MAX ], "       \
101       "channels = (int) [ 1, MAX ], " \
102       "endianness = (int) BYTE_ORDER, " \
103       "width = (int) 64" \
104 )
105
106 static GstStaticPadTemplate gst_audioresample_sink_template =
107 GST_STATIC_PAD_TEMPLATE ("sink",
108     GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS);
109
110 static GstStaticPadTemplate gst_audioresample_src_template =
111 GST_STATIC_PAD_TEMPLATE ("src",
112     GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS);
113
114 static void gst_audioresample_dispose (GObject * object);
115
116 static void gst_audioresample_set_property (GObject * object,
117     guint prop_id, const GValue * value, GParamSpec * pspec);
118 static void gst_audioresample_get_property (GObject * object,
119     guint prop_id, GValue * value, GParamSpec * pspec);
120
121 /* vmethods */
122 gboolean audioresample_get_unit_size (GstBaseTransform * base,
123     GstCaps * caps, guint * size);
124 GstCaps *audioresample_transform_caps (GstBaseTransform * base,
125     GstPadDirection direction, GstCaps * caps);
126 gboolean audioresample_transform_size (GstBaseTransform * trans,
127     GstPadDirection direction, GstCaps * incaps, guint insize,
128     GstCaps * outcaps, guint * outsize);
129 gboolean audioresample_set_caps (GstBaseTransform * base, GstCaps * incaps,
130     GstCaps * outcaps);
131 static GstFlowReturn audioresample_pushthrough (GstAudioresample *
132     audioresample);
133 static GstFlowReturn audioresample_transform (GstBaseTransform * base,
134     GstBuffer * inbuf, GstBuffer * outbuf);
135 static gboolean audioresample_event (GstBaseTransform * base, GstEvent * event);
136
137 /*static guint gst_audioresample_signals[LAST_SIGNAL] = { 0 }; */
138
139 #define DEBUG_INIT(bla) \
140   GST_DEBUG_CATEGORY_INIT (audioresample_debug, "audioresample", 0, "audio resampling element");
141
142 GST_BOILERPLATE_FULL (GstAudioresample, gst_audioresample, GstBaseTransform,
143     GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
144
145 static void
146 gst_audioresample_base_init (gpointer g_class)
147 {
148   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
149
150   gst_element_class_add_pad_template (gstelement_class,
151       gst_static_pad_template_get (&gst_audioresample_src_template));
152   gst_element_class_add_pad_template (gstelement_class,
153       gst_static_pad_template_get (&gst_audioresample_sink_template));
154
155   gst_element_class_set_details (gstelement_class, &gst_audioresample_details);
156 }
157
158 static void
159 gst_audioresample_class_init (GstAudioresampleClass * klass)
160 {
161   GObjectClass *gobject_class;
162
163   gobject_class = (GObjectClass *) klass;
164
165   gobject_class->set_property = gst_audioresample_set_property;
166   gobject_class->get_property = gst_audioresample_get_property;
167   gobject_class->dispose = gst_audioresample_dispose;
168
169   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILTERLEN,
170       g_param_spec_int ("filter_length", "filter_length", "filter_length",
171           0, G_MAXINT, DEFAULT_FILTERLEN,
172           G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
173
174   GST_BASE_TRANSFORM_CLASS (klass)->transform_size =
175       GST_DEBUG_FUNCPTR (audioresample_transform_size);
176   GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size =
177       GST_DEBUG_FUNCPTR (audioresample_get_unit_size);
178   GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
179       GST_DEBUG_FUNCPTR (audioresample_transform_caps);
180   GST_BASE_TRANSFORM_CLASS (klass)->set_caps =
181       GST_DEBUG_FUNCPTR (audioresample_set_caps);
182   GST_BASE_TRANSFORM_CLASS (klass)->transform =
183       GST_DEBUG_FUNCPTR (audioresample_transform);
184   GST_BASE_TRANSFORM_CLASS (klass)->event =
185       GST_DEBUG_FUNCPTR (audioresample_event);
186
187   GST_BASE_TRANSFORM_CLASS (klass)->passthrough_on_same_caps = TRUE;
188 }
189
190 static void
191 gst_audioresample_init (GstAudioresample * audioresample,
192     GstAudioresampleClass * klass)
193 {
194   ResampleState *r;
195   GstBaseTransform *trans;
196
197   trans = GST_BASE_TRANSFORM (audioresample);
198
199   /* buffer alloc passthrough is too impossible. FIXME, it
200    * is trivial in the passtrough case. */
201   gst_pad_set_bufferalloc_function (trans->sinkpad, NULL);
202
203   r = resample_new ();
204   audioresample->resample = r;
205   audioresample->ts_offset = -1;
206   audioresample->offset = -1;
207   audioresample->next_ts = -1;
208
209   resample_set_filter_length (r, DEFAULT_FILTERLEN);
210 }
211
212 static void
213 gst_audioresample_dispose (GObject * object)
214 {
215   GstAudioresample *audioresample = GST_AUDIORESAMPLE (object);
216
217   if (audioresample->resample) {
218     resample_free (audioresample->resample);
219     audioresample->resample = NULL;
220   }
221
222   G_OBJECT_CLASS (parent_class)->dispose (object);
223 }
224
225 /* vmethods */
226 gboolean
227 audioresample_get_unit_size (GstBaseTransform * base, GstCaps * caps,
228     guint * size)
229 {
230   gint width, channels;
231   GstStructure *structure;
232   gboolean ret;
233
234   g_return_val_if_fail (size, FALSE);
235
236   /* this works for both float and int */
237   structure = gst_caps_get_structure (caps, 0);
238   ret = gst_structure_get_int (structure, "width", &width);
239   ret &= gst_structure_get_int (structure, "channels", &channels);
240   g_return_val_if_fail (ret, FALSE);
241
242   *size = width * channels / 8;
243
244   return TRUE;
245 }
246
247 GstCaps *
248 audioresample_transform_caps (GstBaseTransform * base,
249     GstPadDirection direction, GstCaps * caps)
250 {
251   GstCaps *res;
252   GstStructure *structure;
253
254   /* transform caps gives one single caps so we can just replace
255    * the rate property with our range. */
256   res = gst_caps_copy (caps);
257   structure = gst_caps_get_structure (res, 0);
258   gst_structure_set (structure, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
259
260   return res;
261 }
262
263 static gboolean
264 resample_set_state_from_caps (ResampleState * state, GstCaps * incaps,
265     GstCaps * outcaps, gint * channels, gint * inrate, gint * outrate)
266 {
267   GstStructure *structure;
268   gboolean ret;
269   gint myinrate, myoutrate;
270   int mychannels;
271   gint width, depth;
272   ResampleFormat format;
273
274   GST_DEBUG ("incaps %" GST_PTR_FORMAT ", outcaps %"
275       GST_PTR_FORMAT, incaps, outcaps);
276
277   structure = gst_caps_get_structure (incaps, 0);
278
279   /* get width */
280   ret = gst_structure_get_int (structure, "width", &width);
281   if (!ret)
282     goto no_width;
283
284   /* figure out the format */
285   if (g_str_equal (gst_structure_get_name (structure), "audio/x-raw-float")) {
286     if (width == 32)
287       format = RESAMPLE_FORMAT_F32;
288     else if (width == 64)
289       format = RESAMPLE_FORMAT_F64;
290     else
291       goto wrong_depth;
292   } else {
293     /* for int, depth and width must be the same */
294     ret = gst_structure_get_int (structure, "depth", &depth);
295     if (!ret || width != depth)
296       goto not_equal;
297
298     if (width == 16)
299       format = RESAMPLE_FORMAT_S16;
300     else if (width == 32)
301       format = RESAMPLE_FORMAT_S32;
302     else
303       goto wrong_depth;
304   }
305   ret = gst_structure_get_int (structure, "rate", &myinrate);
306   ret &= gst_structure_get_int (structure, "channels", &mychannels);
307   if (!ret)
308     goto no_in_rate_channels;
309
310   structure = gst_caps_get_structure (outcaps, 0);
311   ret = gst_structure_get_int (structure, "rate", &myoutrate);
312   if (!ret)
313     goto no_out_rate;
314
315   if (channels)
316     *channels = mychannels;
317   if (inrate)
318     *inrate = myinrate;
319   if (outrate)
320     *outrate = myoutrate;
321
322   resample_set_format (state, format);
323   resample_set_n_channels (state, mychannels);
324   resample_set_input_rate (state, myinrate);
325   resample_set_output_rate (state, myoutrate);
326
327   return TRUE;
328
329   /* ERRORS */
330 no_width:
331   {
332     GST_DEBUG ("failed to get width from caps");
333     return FALSE;
334   }
335 not_equal:
336   {
337     GST_DEBUG ("width %d and depth %d must be the same", width, depth);
338     return FALSE;
339   }
340 wrong_depth:
341   {
342     GST_DEBUG ("unknown depth %d found", depth);
343     return FALSE;
344   }
345 no_in_rate_channels:
346   {
347     GST_DEBUG ("could not get input rate and channels");
348     return FALSE;
349   }
350 no_out_rate:
351   {
352     GST_DEBUG ("could not get output rate");
353     return FALSE;
354   }
355 }
356
357 gboolean
358 audioresample_transform_size (GstBaseTransform * base,
359     GstPadDirection direction, GstCaps * caps, guint size, GstCaps * othercaps,
360     guint * othersize)
361 {
362   GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
363   ResampleState *state;
364   GstCaps *srccaps, *sinkcaps;
365   gboolean use_internal = FALSE;        /* whether we use the internal state */
366   gboolean ret = TRUE;
367
368   GST_DEBUG_OBJECT (base, "asked to transform size %d in direction %s",
369       size, direction == GST_PAD_SINK ? "SINK" : "SRC");
370   if (direction == GST_PAD_SINK) {
371     sinkcaps = caps;
372     srccaps = othercaps;
373   } else {
374     sinkcaps = othercaps;
375     srccaps = caps;
376   }
377
378   /* if the caps are the ones that _set_caps got called with; we can use
379    * our own state; otherwise we'll have to create a state */
380   if (gst_caps_is_equal (sinkcaps, audioresample->sinkcaps) &&
381       gst_caps_is_equal (srccaps, audioresample->srccaps)) {
382     use_internal = TRUE;
383     state = audioresample->resample;
384   } else {
385     GST_DEBUG_OBJECT (audioresample,
386         "caps are not the set caps, creating state");
387     state = resample_new ();
388     resample_set_filter_length (state, audioresample->filter_length);
389     resample_set_state_from_caps (state, sinkcaps, srccaps, NULL, NULL, NULL);
390   }
391
392   if (direction == GST_PAD_SINK) {
393     /* asked to convert size of an incoming buffer */
394     *othersize = resample_get_output_size_for_input (state, size);
395   } else {
396     /* asked to convert size of an outgoing buffer */
397     *othersize = resample_get_input_size_for_output (state, size);
398   }
399   g_assert (*othersize % state->sample_size == 0);
400
401   /* we make room for one extra sample, given that the resampling filter
402    * can output an extra one for non-integral i_rate/o_rate */
403   GST_DEBUG_OBJECT (base, "transformed size %d to %d", size, *othersize);
404
405   if (!use_internal) {
406     resample_free (state);
407   }
408
409   return ret;
410 }
411
412 gboolean
413 audioresample_set_caps (GstBaseTransform * base, GstCaps * incaps,
414     GstCaps * outcaps)
415 {
416   gboolean ret;
417   gint inrate, outrate;
418   int channels;
419   GstAudioresample *audioresample = GST_AUDIORESAMPLE (base);
420
421   GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %"
422       GST_PTR_FORMAT, incaps, outcaps);
423
424   ret = resample_set_state_from_caps (audioresample->resample, incaps, outcaps,
425       &channels, &inrate, &outrate);
426
427   g_return_val_if_fail (ret, FALSE);
428
429   audioresample->channels = channels;
430   GST_DEBUG_OBJECT (audioresample, "set channels to %d", channels);
431   audioresample->i_rate = inrate;
432   GST_DEBUG_OBJECT (audioresample, "set i_rate to %d", inrate);
433   audioresample->o_rate = outrate;
434   GST_DEBUG_OBJECT (audioresample, "set o_rate to %d", outrate);
435
436   /* save caps so we can short-circuit in the size_transform if the caps
437    * are the same */
438   /* FIXME: clean them up in state change ? */
439   gst_caps_ref (incaps);
440   gst_caps_replace (&audioresample->sinkcaps, incaps);
441   gst_caps_ref (outcaps);
442   gst_caps_replace (&audioresample->srccaps, outcaps);
443
444   return TRUE;
445 }
446
447 static gboolean
448 audioresample_event (GstBaseTransform * base, GstEvent * event)
449 {
450   GstAudioresample *audioresample;
451
452   audioresample = GST_AUDIORESAMPLE (base);
453
454   switch (GST_EVENT_TYPE (event)) {
455     case GST_EVENT_FLUSH_START:
456       break;
457     case GST_EVENT_FLUSH_STOP:
458       resample_input_flush (audioresample->resample);
459       audioresample->ts_offset = -1;
460       audioresample->next_ts = -1;
461       audioresample->offset = -1;
462       break;
463     case GST_EVENT_NEWSEGMENT:
464       resample_input_pushthrough (audioresample->resample);
465       audioresample_pushthrough (audioresample);
466       audioresample->ts_offset = -1;
467       audioresample->next_ts = -1;
468       audioresample->offset = -1;
469       break;
470     case GST_EVENT_EOS:
471       resample_input_eos (audioresample->resample);
472       audioresample_pushthrough (audioresample);
473       break;
474     default:
475       break;
476   }
477   parent_class->event (base, event);
478
479   return TRUE;
480 }
481
482 static GstFlowReturn
483 audioresample_do_output (GstAudioresample * audioresample, GstBuffer * outbuf)
484 {
485   int outsize;
486   int outsamples;
487   ResampleState *r;
488
489   r = audioresample->resample;
490
491   outsize = resample_get_output_size (r);
492   GST_DEBUG_OBJECT (audioresample, "audioresample can give me %d bytes",
493       outsize);
494
495   /* protect against mem corruption */
496   if (outsize > GST_BUFFER_SIZE (outbuf)) {
497     GST_WARNING_OBJECT (audioresample,
498         "overriding audioresample's outsize %d with outbuffer's size %d",
499         outsize, GST_BUFFER_SIZE (outbuf));
500     outsize = GST_BUFFER_SIZE (outbuf);
501   }
502   /* catch possibly wrong size differences */
503   if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) {
504     GST_WARNING_OBJECT (audioresample,
505         "audioresample's outsize %d too far from outbuffer's size %d",
506         outsize, GST_BUFFER_SIZE (outbuf));
507   }
508
509   outsize = resample_get_output_data (r, GST_BUFFER_DATA (outbuf), outsize);
510   outsamples = outsize / r->sample_size;
511   GST_LOG_OBJECT (audioresample, "resample gave me %d bytes or %d samples",
512       outsize, outsamples);
513
514   GST_BUFFER_OFFSET (outbuf) = audioresample->offset;
515   GST_BUFFER_TIMESTAMP (outbuf) = audioresample->next_ts;
516
517   if (audioresample->ts_offset != -1) {
518     audioresample->offset += outsamples;
519     audioresample->ts_offset += outsamples;
520     audioresample->next_ts =
521         gst_util_uint64_scale_int (audioresample->ts_offset, GST_SECOND,
522         audioresample->o_rate);
523     GST_BUFFER_OFFSET_END (outbuf) = audioresample->offset;
524
525     /* we calculate DURATION as the difference between "next" timestamp
526      * and current timestamp so we ensure a contiguous stream, instead of
527      * having rounding errors. */
528     GST_BUFFER_DURATION (outbuf) = audioresample->next_ts -
529         GST_BUFFER_TIMESTAMP (outbuf);
530   } else {
531     /* no valid offset know, we can still sortof calculate the duration though */
532     GST_BUFFER_DURATION (outbuf) =
533         gst_util_uint64_scale_int (outsamples, GST_SECOND,
534         audioresample->o_rate);
535   }
536
537   /* check for possible mem corruption */
538   if (outsize > GST_BUFFER_SIZE (outbuf)) {
539     /* this is an error that when it happens, would need fixing in the
540      * resample library; we told
541      * it we wanted only GST_BUFFER_SIZE (outbuf), and it gave us more ! */
542     GST_WARNING_OBJECT (audioresample,
543         "audioresample, you memory corrupting bastard. "
544         "you gave me outsize %d while my buffer was size %d",
545         outsize, GST_BUFFER_SIZE (outbuf));
546     return GST_FLOW_ERROR;
547   }
548   /* catch possibly wrong size differences */
549   if (GST_BUFFER_SIZE (outbuf) - outsize > r->sample_size) {
550     GST_WARNING_OBJECT (audioresample,
551         "audioresample's written outsize %d too far from outbuffer's size %d",
552         outsize, GST_BUFFER_SIZE (outbuf));
553   }
554   GST_BUFFER_SIZE (outbuf) = outsize;
555
556   return GST_FLOW_OK;
557 }
558
559 static GstFlowReturn
560 audioresample_transform (GstBaseTransform * base, GstBuffer * inbuf,
561     GstBuffer * outbuf)
562 {
563   GstAudioresample *audioresample;
564   ResampleState *r;
565   guchar *data, *datacopy;
566   gulong size;
567   GstClockTime timestamp;
568
569   audioresample = GST_AUDIORESAMPLE (base);
570   r = audioresample->resample;
571
572   data = GST_BUFFER_DATA (inbuf);
573   size = GST_BUFFER_SIZE (inbuf);
574   timestamp = GST_BUFFER_TIMESTAMP (inbuf);
575
576   GST_DEBUG_OBJECT (audioresample, "got buffer of %ld bytes", size);
577
578   if (audioresample->ts_offset == -1) {
579     /* if we don't know the initial offset yet, calculate it based on the 
580      * input timestamp. */
581     if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
582       GstClockTime stime;
583
584       /* offset used to calculate the timestamps. We use the sample offset for this
585        * to make it more accurate. We want the first buffer to have the same timestamp
586        * as the incomming timestamp. */
587       audioresample->next_ts = timestamp;
588       audioresample->ts_offset =
589           gst_util_uint64_scale_int (timestamp, r->o_rate, GST_SECOND);
590       /* offset used to set as the buffer offset, this offset is always relative
591        * to the stream time, note that timestamp is not... */
592       stime = (timestamp - base->segment.start) + base->segment.time;
593       audioresample->offset =
594           gst_util_uint64_scale_int (stime, r->o_rate, GST_SECOND);
595     }
596   }
597
598   /* need to memdup, resample takes ownership. */
599   datacopy = g_memdup (data, size);
600   resample_add_input_data (r, datacopy, size, g_free, datacopy);
601
602   return audioresample_do_output (audioresample, outbuf);
603 }
604
605 /* push remaining data in the buffers out */
606 static GstFlowReturn
607 audioresample_pushthrough (GstAudioresample * audioresample)
608 {
609   int outsize;
610   ResampleState *r;
611   GstBuffer *outbuf;
612   GstFlowReturn res = GST_FLOW_OK;
613   GstBaseTransform *trans;
614
615   r = audioresample->resample;
616
617   outsize = resample_get_output_size (r);
618   if (outsize == 0)
619     goto done;
620
621   outbuf = gst_buffer_new_and_alloc (outsize);
622
623   res = audioresample_do_output (audioresample, outbuf);
624   if (res != GST_FLOW_OK)
625     goto done;
626
627   trans = GST_BASE_TRANSFORM (audioresample);
628
629   res = gst_pad_push (trans->srcpad, outbuf);
630
631 done:
632   return res;
633 }
634
635
636 static void
637 gst_audioresample_set_property (GObject * object, guint prop_id,
638     const GValue * value, GParamSpec * pspec)
639 {
640   GstAudioresample *audioresample;
641
642   g_return_if_fail (GST_IS_AUDIORESAMPLE (object));
643   audioresample = GST_AUDIORESAMPLE (object);
644
645   switch (prop_id) {
646     case PROP_FILTERLEN:
647       audioresample->filter_length = g_value_get_int (value);
648       GST_DEBUG_OBJECT (GST_ELEMENT (audioresample), "new filter length %d",
649           audioresample->filter_length);
650       resample_set_filter_length (audioresample->resample,
651           audioresample->filter_length);
652       break;
653     default:
654       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
655       break;
656   }
657 }
658
659 static void
660 gst_audioresample_get_property (GObject * object, guint prop_id,
661     GValue * value, GParamSpec * pspec)
662 {
663   GstAudioresample *audioresample;
664
665   g_return_if_fail (GST_IS_AUDIORESAMPLE (object));
666   audioresample = GST_AUDIORESAMPLE (object);
667
668   switch (prop_id) {
669     case PROP_FILTERLEN:
670       g_value_set_int (value, audioresample->filter_length);
671       break;
672     default:
673       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
674       break;
675   }
676 }
677
678
679 static gboolean
680 plugin_init (GstPlugin * plugin)
681 {
682   resample_init ();
683
684   if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY,
685           GST_TYPE_AUDIORESAMPLE)) {
686     return FALSE;
687   }
688
689   return TRUE;
690 }
691
692 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
693     GST_VERSION_MINOR,
694     "audioresample",
695     "Resamples audio", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME,
696     GST_PACKAGE_ORIGIN);