363acd9b7dbae338e26cd4f5a2257a17bb2f656a
[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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <string.h>
27 #include <math.h>
28
29 /*#define DEBUG_ENABLED */
30 #include "gstaudioresample.h"
31 #include <gst/audio/audio.h>
32
33 GST_DEBUG_CATEGORY_STATIC (audioresample_debug);
34 #define GST_CAT_DEFAULT audioresample_debug
35
36 /* elementfactory information */
37 static GstElementDetails gst_audioresample_details =
38 GST_ELEMENT_DETAILS ("Audio scaler",
39     "Filter/Converter/Audio",
40     "Resample audio",
41     "David Schleef <ds@schleef.org>");
42
43 /* Audioresample signals and args */
44 enum
45 {
46   /* FILL ME */
47   LAST_SIGNAL
48 };
49
50 enum
51 {
52   ARG_0,
53   ARG_FILTERLEN
54 };
55
56 #define SUPPORTED_CAPS \
57   GST_STATIC_CAPS (\
58     "audio/x-raw-int, " \
59       "rate = (int) [ 1, MAX ], " \
60       "channels = (int) [ 1, MAX ], " \
61       "endianness = (int) BYTE_ORDER, " \
62       "width = (int) 16, " \
63       "depth = (int) 16, " \
64       "signed = (boolean) true")
65
66 #if 0
67   /* disabled because it segfaults */
68 "audio/x-raw-float, "
69     "rate = (int) [ 1, MAX ], "
70     "channels = (int) [ 1, MAX ], "
71     "endianness = (int) BYTE_ORDER, " "width = (int) 32")
72 #endif
73      static GstStaticPadTemplate gst_audioresample_sink_template =
74          GST_STATIC_PAD_TEMPLATE ("sink",
75          GST_PAD_SINK, GST_PAD_ALWAYS, SUPPORTED_CAPS);
76
77      static GstStaticPadTemplate gst_audioresample_src_template =
78          GST_STATIC_PAD_TEMPLATE ("src",
79          GST_PAD_SRC, GST_PAD_ALWAYS, SUPPORTED_CAPS);
80
81      static void gst_audioresample_base_init (gpointer g_class);
82      static void gst_audioresample_class_init (AudioresampleClass * klass);
83      static void gst_audioresample_init (Audioresample * audioresample);
84      static void gst_audioresample_dispose (GObject * object);
85
86      static void gst_audioresample_chain (GstPad * pad, GstData * _data);
87
88      static void gst_audioresample_set_property (GObject * object,
89          guint prop_id, const GValue * value, GParamSpec * pspec);
90      static void gst_audioresample_get_property (GObject * object,
91          guint prop_id, GValue * value, GParamSpec * pspec);
92
93      static GstElementClass *parent_class = NULL;
94
95 /*static guint gst_audioresample_signals[LAST_SIGNAL] = { 0 }; */
96
97      GType audioresample_get_type (void)
98      {
99        static GType audioresample_type = 0;
100
101        if (!audioresample_type)
102        {
103          static const GTypeInfo audioresample_info = {
104          sizeof (AudioresampleClass),
105                gst_audioresample_base_init,
106                NULL,
107                (GClassInitFunc) gst_audioresample_class_init,
108                NULL,
109                NULL,
110                sizeof (Audioresample), 0,
111                (GInstanceInitFunc) gst_audioresample_init,};
112
113          audioresample_type =
114              g_type_register_static (GST_TYPE_ELEMENT, "Audioresample",
115              &audioresample_info, 0);
116        }
117        return audioresample_type;
118      }
119
120 static void gst_audioresample_base_init (gpointer g_class)
121 {
122   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
123
124   gst_element_class_add_pad_template (gstelement_class,
125       gst_static_pad_template_get (&gst_audioresample_src_template));
126   gst_element_class_add_pad_template (gstelement_class,
127       gst_static_pad_template_get (&gst_audioresample_sink_template));
128
129   gst_element_class_set_details (gstelement_class, &gst_audioresample_details);
130 }
131
132 static void gst_audioresample_class_init (AudioresampleClass * klass)
133 {
134   GObjectClass *gobject_class;
135   GstElementClass *gstelement_class;
136
137   gobject_class = (GObjectClass *) klass;
138   gstelement_class = (GstElementClass *) klass;
139
140   gobject_class->set_property = gst_audioresample_set_property;
141   gobject_class->get_property = gst_audioresample_get_property;
142   gobject_class->dispose = gst_audioresample_dispose;
143
144   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FILTERLEN,
145       g_param_spec_int ("filter_length", "filter_length", "filter_length",
146           0, G_MAXINT, 16, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
147
148   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
149
150   GST_DEBUG_CATEGORY_INIT (audioresample_debug, "audioresample", 0,
151       "audioresample element");
152 }
153
154 static void gst_audioresample_expand_caps (GstCaps * caps)
155 {
156   gint i;
157
158   for (i = 0; i < gst_caps_get_size (caps); i++) {
159     GstStructure *structure = gst_caps_get_structure (caps, i);
160     const GValue *value;
161
162     value = gst_structure_get_value (structure, "rate");
163     if (value == NULL) {
164       GST_ERROR ("caps structure doesn't have required rate field");
165       return;
166     }
167
168     gst_structure_set (structure, "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT, 0);
169   }
170 }
171
172 static GstCaps *gst_audioresample_getcaps (GstPad * pad)
173 {
174   Audioresample *audioresample;
175   GstCaps *caps;
176   GstPad *otherpad;
177
178   audioresample = GST_AUDIORESAMPLE (gst_pad_get_parent (pad));
179
180   otherpad = (pad == audioresample->srcpad) ? audioresample->sinkpad :
181       audioresample->srcpad;
182   caps = gst_pad_get_allowed_caps (otherpad);
183
184   gst_audioresample_expand_caps (caps);
185
186   return caps;
187 }
188
189 static GstCaps *gst_audioresample_fixate (GstPad * pad, const GstCaps * caps)
190 {
191   Audioresample *audioresample;
192   GstPad *otherpad;
193   int rate;
194   GstCaps *copy;
195   GstStructure *structure;
196
197     audioresample = GST_AUDIORESAMPLE (gst_pad_get_parent (pad));
198
199   if (pad == audioresample->srcpad) {
200     otherpad = audioresample->sinkpad;
201     rate = audioresample->i_rate;
202   } else
203   {
204     otherpad = audioresample->srcpad;
205     rate = audioresample->o_rate;
206   }
207   if (!GST_PAD_IS_NEGOTIATING (otherpad))
208     return NULL;
209   if (gst_caps_get_size (caps) > 1)
210     return NULL;
211
212   copy = gst_caps_copy (caps);
213   structure = gst_caps_get_structure (copy, 0);
214   if (rate) {
215     if (gst_caps_structure_fixate_field_nearest_int (structure, "rate", rate)) {
216       return copy;
217     }
218   }
219   gst_caps_free (copy);
220   return NULL;
221 }
222
223 static GstPadLinkReturn gst_audioresample_link (GstPad * pad,
224     const GstCaps * caps)
225 {
226   Audioresample *audioresample;
227   GstStructure *structure;
228   int rate;
229   int channels;
230   gboolean ret;
231   GstPad *otherpad;
232
233     audioresample = GST_AUDIORESAMPLE (gst_pad_get_parent (pad));
234
235     otherpad = (pad == audioresample->srcpad) ? audioresample->sinkpad :
236       audioresample->srcpad;
237
238     structure = gst_caps_get_structure (caps, 0);
239     ret = gst_structure_get_int (structure, "rate", &rate);
240     ret &= gst_structure_get_int (structure, "channels", &channels);
241   if (!ret)
242   {
243     return GST_PAD_LINK_REFUSED;
244   }
245
246   if (gst_pad_is_negotiated (otherpad))
247   {
248     GstCaps *othercaps = gst_caps_copy (caps);
249     int otherrate;
250     GstPadLinkReturn linkret;
251
252     if (pad == audioresample->srcpad) {
253       otherrate = audioresample->i_rate;
254     } else {
255       otherrate = audioresample->o_rate;
256     }
257     gst_caps_set_simple (othercaps, "rate", G_TYPE_INT, otherrate, NULL);
258     linkret = gst_pad_try_set_caps (otherpad, othercaps);
259     if (GST_PAD_LINK_FAILED (linkret)) {
260       return GST_PAD_LINK_REFUSED;
261     }
262
263   }
264
265   audioresample->channels = channels;
266   resample_set_n_channels (audioresample->resample, audioresample->channels);
267   if (pad == audioresample->srcpad) {
268     audioresample->o_rate = rate;
269     resample_set_output_rate (audioresample->resample, audioresample->o_rate);
270     GST_DEBUG ("set o_rate to %d", rate);
271   } else {
272     audioresample->i_rate = rate;
273     resample_set_input_rate (audioresample->resample, audioresample->i_rate);
274     GST_DEBUG ("set i_rate to %d", rate);
275   }
276
277   return GST_PAD_LINK_OK;
278 }
279
280 static void gst_audioresample_init (Audioresample * audioresample)
281 {
282   ResampleState *r;
283
284   audioresample->sinkpad =
285       gst_pad_new_from_template (gst_static_pad_template_get
286       (&gst_audioresample_sink_template), "sink");
287   gst_element_add_pad (GST_ELEMENT (audioresample), audioresample->sinkpad);
288   gst_pad_set_chain_function (audioresample->sinkpad, gst_audioresample_chain);
289   gst_pad_set_link_function (audioresample->sinkpad, gst_audioresample_link);
290   gst_pad_set_getcaps_function (audioresample->sinkpad,
291       gst_audioresample_getcaps);
292   gst_pad_set_fixate_function (audioresample->sinkpad,
293       gst_audioresample_fixate);
294
295   audioresample->srcpad =
296       gst_pad_new_from_template (gst_static_pad_template_get
297       (&gst_audioresample_src_template), "src");
298
299   gst_element_add_pad (GST_ELEMENT (audioresample), audioresample->srcpad);
300   gst_pad_set_link_function (audioresample->srcpad, gst_audioresample_link);
301   gst_pad_set_getcaps_function (audioresample->srcpad,
302       gst_audioresample_getcaps);
303   gst_pad_set_fixate_function (audioresample->srcpad, gst_audioresample_fixate);
304
305   r = resample_new ();
306   audioresample->resample = r;
307
308   resample_set_filter_length (r, 64);
309   resample_set_format (r, RESAMPLE_FORMAT_S16);
310 }
311
312 static void gst_audioresample_dispose (GObject * object)
313 {
314   Audioresample *audioresample = GST_AUDIORESAMPLE (object);
315
316   if (audioresample->resample) {
317     resample_free (audioresample->resample);
318   }
319
320   G_OBJECT_CLASS (parent_class)->dispose (object);
321 }
322
323 static void gst_audioresample_chain (GstPad * pad, GstData * _data)
324 {
325   GstBuffer *buf = GST_BUFFER (_data);
326   Audioresample *audioresample;
327   ResampleState *r;
328   guchar *data;
329   gulong size;
330   int outsize;
331   GstBuffer *outbuf;
332
333   g_return_if_fail (pad != NULL);
334   g_return_if_fail (GST_IS_PAD (pad));
335   g_return_if_fail (buf != NULL);
336
337   audioresample = GST_AUDIORESAMPLE (gst_pad_get_parent (pad));
338
339   if (!GST_IS_BUFFER (_data)) {
340     gst_pad_push (audioresample->srcpad, _data);
341     return;
342   }
343
344   if (audioresample->passthru) {
345     gst_pad_push (audioresample->srcpad, GST_DATA (buf));
346     return;
347   }
348
349   r = audioresample->resample;
350
351   data = GST_BUFFER_DATA (buf);
352   size = GST_BUFFER_SIZE (buf);
353
354   GST_DEBUG ("got buffer of %ld bytes", size);
355
356   resample_add_input_data (r, data, size, (ResampleCallback) gst_data_unref,
357       buf);
358
359   outsize = resample_get_output_size (r);
360   /* FIXME this is audioresample being dumb.  dunno why */
361   if (outsize == 0) {
362     GST_ERROR ("overriding outbuf size");
363     outsize = size;
364   }
365   outbuf = gst_buffer_new_and_alloc (outsize);
366
367   outsize = resample_get_output_data (r, GST_BUFFER_DATA (outbuf), outsize);
368   GST_BUFFER_SIZE (outbuf) = outsize;
369
370   GST_BUFFER_TIMESTAMP (outbuf) =
371       audioresample->offset * GST_SECOND / audioresample->o_rate;
372   audioresample->offset += outsize / sizeof (gint16) / audioresample->channels;
373
374   gst_pad_push (audioresample->srcpad, GST_DATA (outbuf));
375 }
376
377 static void
378     gst_audioresample_set_property (GObject * object, guint prop_id,
379     const GValue * value, GParamSpec * pspec)
380 {
381   Audioresample *audioresample;
382
383     g_return_if_fail (GST_IS_AUDIORESAMPLE (object));
384     audioresample = GST_AUDIORESAMPLE (object);
385
386   switch (prop_id) {
387     case ARG_FILTERLEN:
388       audioresample->filter_length = g_value_get_int (value);
389       GST_DEBUG_OBJECT (GST_ELEMENT (audioresample), "new filter length %d\n",
390           audioresample->filter_length);
391       resample_set_filter_length (audioresample->resample,
392           audioresample->filter_length);
393       break;
394       default:G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
395       break;
396   }
397 }
398
399 static void
400     gst_audioresample_get_property (GObject * object, guint prop_id,
401     GValue * value, GParamSpec * pspec)
402 {
403   Audioresample *audioresample;
404
405   g_return_if_fail (GST_IS_AUDIORESAMPLE (object));
406   audioresample = GST_AUDIORESAMPLE (object);
407
408   switch (prop_id) {
409     case ARG_FILTERLEN:
410       g_value_set_int (value, audioresample->filter_length);
411       break;
412     default:
413       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
414       break;
415   }
416 }
417
418
419 static gboolean plugin_init (GstPlugin * plugin)
420 {
421   resample_init ();
422
423   if (!gst_element_register (plugin, "audioresample", GST_RANK_PRIMARY,
424           GST_TYPE_AUDIORESAMPLE)) {
425     return FALSE;
426   }
427
428   return TRUE;
429 }
430
431 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
432     GST_VERSION_MINOR,
433     "audioresample",
434     "Resamples audio", plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN)