fix caps
[platform/upstream/gst-plugins-good.git] / gst / level / gstlevel.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * gstlevel.c: signals RMS, peak and decaying peak levels
5  * Copyright (C) 2000,2001,2002,2003
6  *           Thomas Vander Stichele <thomas at apestaart dot org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include <gst/gst.h>
25 #include "gstlevel.h"
26 #include "math.h"
27
28 /* elementfactory information */
29 static GstElementDetails level_details = {
30   "Level",
31   "Filter/Audio/Analysis",
32   "LGPL",
33   "RMS/Peak/Decaying Peak Level signaller for audio/raw",
34   VERSION,
35   "Thomas <thomas@apestaart.org>",
36   "(C) 2001, 2003, 2003",
37 };
38
39 /* pad templates */
40
41 GST_PAD_TEMPLATE_FACTORY (sink_template_factory,
42   "level_sink",
43   GST_PAD_SINK,
44   GST_PAD_ALWAYS,
45   GST_CAPS_NEW (
46     "level_sink",
47     "audio/x-raw-int",
48       "signed", GST_PROPS_BOOLEAN (TRUE),
49       "width", GST_PROPS_LIST (
50                  GST_PROPS_INT (8),
51                  GST_PROPS_INT (16)
52                ),
53       "depth", GST_PROPS_LIST (
54                  GST_PROPS_INT (8),
55                  GST_PROPS_INT (16)
56                ),
57       "rate", GST_PROPS_INT_RANGE (1, G_MAXINT),
58       "channels", GST_PROPS_INT_RANGE (1, 2)
59   )
60 )
61
62 GST_PAD_TEMPLATE_FACTORY (src_template_factory,
63   "level_src",
64   GST_PAD_SRC,
65   GST_PAD_ALWAYS,
66   GST_CAPS_NEW (
67     "level_src",
68     "audio/x-raw-int",
69     "signed", GST_PROPS_BOOLEAN (TRUE),
70     "width", GST_PROPS_LIST (
71                GST_PROPS_INT (8),
72                GST_PROPS_INT (16)
73              ),
74     "depth", GST_PROPS_LIST (
75                GST_PROPS_INT (8),
76                GST_PROPS_INT (16)
77              ),
78     "rate", GST_PROPS_INT_RANGE (1, G_MAXINT),
79     "channels", GST_PROPS_INT_RANGE (1, 2)
80   )
81 )
82
83 /* Filter signals and args */
84 enum {
85   /* FILL ME */
86   SIGNAL_LEVEL,
87   LAST_SIGNAL
88 };
89
90 enum {
91   ARG_0,
92   ARG_SIGNAL_LEVEL,
93   ARG_SIGNAL_INTERVAL,
94   ARG_PEAK_TTL,
95   ARG_PEAK_FALLOFF
96 };
97
98 static void             gst_level_class_init            (GstLevelClass *klass);
99 static void             gst_level_init                  (GstLevel *filter);
100
101 static void             gst_level_set_property                  (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
102 static void             gst_level_get_property                  (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
103
104 static void             gst_level_chain                 (GstPad *pad, GstBuffer *buf);
105
106 static GstElementClass *parent_class = NULL;
107 static guint gst_filter_signals[LAST_SIGNAL] = { 0 };
108
109 GType
110 gst_level_get_type (void) 
111 {
112   static GType level_type = 0;
113
114   if (!level_type) 
115   {
116     static const GTypeInfo level_info = 
117     {
118       sizeof (GstLevelClass), NULL, NULL,
119       (GClassInitFunc) gst_level_class_init, NULL, NULL,
120       sizeof (GstLevel), 0,
121       (GInstanceInitFunc) gst_level_init
122     };
123     level_type = g_type_register_static (GST_TYPE_ELEMENT, "GstLevel", 
124                                          &level_info, 0);
125   }
126   return level_type;
127 }
128
129 static GstPadLinkReturn
130 gst_level_connect (GstPad *pad, GstCaps *caps)
131 {
132   GstLevel *filter;
133   GstPad *otherpad;
134   GstPadLinkReturn res;
135   int i;
136
137   filter = GST_LEVEL (gst_pad_get_parent (pad));
138   g_return_val_if_fail (filter != NULL, GST_PAD_LINK_REFUSED);
139   g_return_val_if_fail (GST_IS_LEVEL (filter), GST_PAD_LINK_REFUSED);
140   otherpad = (pad == filter->srcpad ? filter->sinkpad : filter->srcpad);
141           
142   if (GST_CAPS_IS_FIXED (caps)) 
143   {
144     /* yep, got them */
145     res = gst_pad_try_set_caps (otherpad, caps);
146     /* if ok, set filter */
147     if (res == GST_PAD_LINK_OK)
148     {
149       filter->num_samples = 0;
150       /* FIXME: error handling */
151       if (! gst_caps_get_int (caps, "rate", &(filter->rate)))
152         g_warning ("WARNING: level: Could not get rate from caps\n");
153       if (!gst_caps_get_int (caps, "width", &(filter->width)))
154         g_warning ("WARNING: level: Could not get width from caps\n");
155       if (!gst_caps_get_int (caps, "channels", &(filter->channels)))
156         g_warning ("WARNING: level: Could not get number of channels from caps\n");
157
158       /* allocate channel variable arrays */
159       if (filter->CS) g_free (filter->CS);
160       if (filter->peak) g_free (filter->peak);
161       if (filter->last_peak) g_free (filter->last_peak);
162       if (filter->decay_peak) g_free (filter->decay_peak);
163       if (filter->decay_peak_age) g_free (filter->decay_peak_age);
164       if (filter->MS) g_free (filter->MS);
165       if (filter->RMS_dB) g_free (filter->RMS_dB);
166       filter->CS = g_new (double, filter->channels);
167       filter->peak = g_new (double, filter->channels);
168       filter->last_peak = g_new (double, filter->channels);
169       filter->decay_peak = g_new (double, filter->channels);
170       filter->decay_peak_age = g_new (double, filter->channels);
171       filter->MS = g_new (double, filter->channels);
172       filter->RMS_dB = g_new (double, filter->channels);
173       for (i = 0; i < filter->channels; ++i)
174       {
175         filter->CS[i] = filter->peak[i] = filter->last_peak[i] =
176                         filter->decay_peak[i] = filter->decay_peak_age[i] = 
177                         filter->MS[i] = filter->RMS_dB[i] = 0.0;
178       }
179     }
180     return res;
181   }
182   return GST_PAD_LINK_DELAYED;
183 }
184
185 static void inline
186 gst_level_fast_16bit_chain (gint16* in, guint num, gint channels, 
187                             gint resolution, double *CS, double *peak)
188 #include "filter.func"
189
190 static void inline
191 gst_level_fast_8bit_chain (gint8* in, guint num, gint channels, 
192                            gint resolution, double *CS, double *peak)
193 #include "filter.func"
194
195 static void
196 gst_level_chain (GstPad *pad, GstBuffer *buf)
197 {
198   GstLevel *filter;
199   gint16 *in_data;
200
201   double CS = 0.0;
202   gint num_samples = 0;
203   gint i;
204
205   g_return_if_fail (pad != NULL);
206   g_return_if_fail (GST_IS_PAD (pad));
207   g_return_if_fail (buf != NULL);
208
209
210   filter = GST_LEVEL (GST_OBJECT_PARENT (pad));
211   g_return_if_fail (filter != NULL);
212   g_return_if_fail (GST_IS_LEVEL (filter));
213
214   for (i = 0; i < filter->channels; ++i)
215     filter->CS[i] = filter->peak[i] = filter->MS[i] = filter->RMS_dB[i] = 0.0;
216   
217   in_data = (gint16 *) GST_BUFFER_DATA(buf);
218   
219   num_samples = GST_BUFFER_SIZE (buf) / (filter->width / 8);
220   if (num_samples % filter->channels != 0)
221     g_warning ("WARNING: level: programming error, data not properly interleaved");
222
223   for (i = 0; i < filter->channels; ++i)
224   {
225     switch (filter->width)
226     {
227       case 16:
228           gst_level_fast_16bit_chain (in_data + i, num_samples,
229                                       filter->channels, filter->width - 1,
230                                       &CS, &filter->peak[i]);
231           break;
232       case 8:
233           gst_level_fast_8bit_chain (((gint8 *) in_data) + i, num_samples,
234                                      filter->channels, filter->width - 1,
235                                      &CS, &filter->peak[i]);
236           break;
237     }
238     /* g_print ("DEBUG: CS %f, peak %f\n", CS, filter->peak[i]); */
239     filter->CS[i] += CS;
240
241   }
242   gst_pad_push (filter->srcpad, buf);
243
244   filter->num_samples += num_samples;
245
246   for (i = 0; i < filter->channels; ++i)
247   {
248     filter->decay_peak_age[i] += num_samples;
249     /* g_print ("filter peak info [%d]: peak %f, age %f\n", i, 
250              filter->last_peak[i], filter->decay_peak_age[i]); */
251     /* update running peak */
252     if (filter->peak[i] > filter->last_peak[i])
253         filter->last_peak[i] = filter->peak[i];
254
255     /* update decay peak */
256     if (filter->peak[i] >= filter->decay_peak[i])
257     {
258        /* g_print ("new peak, %f\n", filter->peak[i]); */
259        filter->decay_peak[i] = filter->peak[i];
260        filter->decay_peak_age[i] = 0;
261     }
262     else
263     {
264       /* make decay peak fall off if too old */
265       if (filter->decay_peak_age[i] > filter->rate * filter->decay_peak_ttl)
266       {
267         double falloff_dB;
268         double falloff;
269         double length;          /* length of buffer in seconds */
270
271  
272         length = (double) num_samples / (filter->channels * filter->rate);
273         falloff_dB = filter->decay_peak_falloff * length;
274         falloff = pow (10, falloff_dB / -20.0);
275
276         /* g_print ("falloff: length %f, dB falloff %f, falloff factor %e\n",
277                  length, falloff_dB, falloff); */
278         filter->decay_peak[i] *= falloff;
279         /* g_print ("peak is %f samples old, decayed with factor %e to %f\n",
280                  filter->decay_peak_age[i], falloff, filter->decay_peak[i]); */
281       }
282     }
283   }
284
285   /* do we need to emit ? */
286   
287   if (filter->num_samples >= filter->interval * (gdouble) filter->rate)
288   {
289     if (filter->signal)
290     {
291       gdouble RMS, peak;
292       for (i = 0; i < filter->channels; ++i)
293       {
294         RMS = sqrt (filter->CS[i] / (filter->num_samples / filter->channels));
295         peak = filter->last_peak[i];
296
297         g_signal_emit (G_OBJECT (filter), gst_filter_signals[SIGNAL_LEVEL], 0,
298                        i, 20 * log10 (RMS), 20 * log10 (filter->last_peak[i]),
299                        20 * log10 (filter->decay_peak[i]));
300         /* we emitted, so reset cumulative and normal peak */
301         filter->CS[i] = 0.0;
302         filter->last_peak[i] = 0.0;
303       }
304     }
305     filter->num_samples = 0;
306   }
307 }
308
309
310 static void
311 gst_level_set_property (GObject *object, guint prop_id, 
312                         const GValue *value, GParamSpec *pspec)
313 {
314   GstLevel *filter;
315
316   /* it's not null if we got it, but it might not be ours */
317   g_return_if_fail (GST_IS_LEVEL (object));
318   filter = GST_LEVEL (object);
319
320   switch (prop_id) {
321     case ARG_SIGNAL_LEVEL:
322       filter->signal = g_value_get_boolean (value);
323       break;
324     case ARG_SIGNAL_INTERVAL:
325       filter->interval = g_value_get_double (value);
326       break;
327     case ARG_PEAK_TTL:
328       filter->decay_peak_ttl = g_value_get_double (value);
329       break;
330     case ARG_PEAK_FALLOFF:
331       filter->decay_peak_falloff = g_value_get_double (value);
332       break;
333     default:
334       break;
335   }
336 }
337
338 static void
339 gst_level_get_property (GObject *object, guint prop_id, 
340                         GValue *value, GParamSpec *pspec)
341 {
342   GstLevel *filter;
343
344   /* it's not null if we got it, but it might not be ours */
345   g_return_if_fail (GST_IS_LEVEL (object));
346   filter = GST_LEVEL (object);
347
348   switch (prop_id) {
349     case ARG_SIGNAL_LEVEL:
350       g_value_set_boolean (value, filter->signal);
351       break;
352       case ARG_SIGNAL_INTERVAL:
353       g_value_set_double (value, filter->interval);
354       break;
355     case ARG_PEAK_TTL:
356       g_value_set_double (value, filter->decay_peak_ttl);
357       break;
358     case ARG_PEAK_FALLOFF:
359       g_value_set_double (value, filter->decay_peak_falloff);
360       break;
361    default:
362       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
363       break;
364   }
365 }
366
367 static void
368 gst_level_class_init (GstLevelClass *klass)
369 {
370   GObjectClass *gobject_class;
371   GstElementClass *gstelement_class;
372
373   gobject_class = (GObjectClass*) klass;
374   gstelement_class = (GstElementClass*) klass;
375
376   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
377   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SIGNAL_LEVEL,
378     g_param_spec_boolean ("signal", "Signal",
379                           "Emit level signals for each interval",
380                           TRUE, G_PARAM_READWRITE));
381   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SIGNAL_INTERVAL,
382     g_param_spec_double ("interval", "Interval",
383                          "Interval between emissions (in seconds)",
384                          0.01, 100.0, 0.1, G_PARAM_READWRITE));
385   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PEAK_TTL,
386     g_param_spec_double ("peak_ttl", "Peak TTL",
387                          "Time To Live of decay peak before it falls back",
388                          0, 100.0, 0.3, G_PARAM_READWRITE));
389   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PEAK_FALLOFF,
390     g_param_spec_double ("peak_falloff", "Peak Falloff",
391                          "Decay rate of decay peak after TTL (in dB/sec)",
392                          0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE));
393
394   gobject_class->set_property = gst_level_set_property;
395   gobject_class->get_property = gst_level_get_property;
396
397   gst_filter_signals[SIGNAL_LEVEL] = 
398     g_signal_new ("level", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
399                   G_STRUCT_OFFSET (GstLevelClass, level), NULL, NULL,
400                   gstlevel_cclosure_marshal_VOID__INT_DOUBLE_DOUBLE_DOUBLE,
401                   G_TYPE_NONE, 4,
402                   G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
403 }
404
405 static void
406 gst_level_init (GstLevel *filter)
407 {
408   filter->sinkpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET (sink_template_factory), "sink");
409   gst_pad_set_link_function (filter->sinkpad, gst_level_connect);
410   filter->srcpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET (src_template_factory), "src");
411   gst_pad_set_link_function (filter->srcpad, gst_level_connect);
412
413   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
414   gst_pad_set_chain_function (filter->sinkpad, gst_level_chain);
415   filter->srcpad = gst_pad_new ("src", GST_PAD_SRC);
416   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
417
418   filter->CS = NULL;
419   filter->peak = NULL;
420   filter->MS = NULL;
421   filter->RMS_dB = NULL;
422
423   filter->rate = 0;
424   filter->width = 0;
425   filter->channels = 0;
426
427   filter->interval = 0.1;
428   filter->decay_peak_ttl = 0.4;
429   filter->decay_peak_falloff = 10.0;    /* dB falloff (/sec) */
430 }
431
432 static gboolean
433 plugin_init (GModule *module, GstPlugin *plugin)
434 {
435   GstElementFactory *factory;
436
437   factory = gst_element_factory_new ("level", GST_TYPE_LEVEL,
438                                      &level_details);
439   g_return_val_if_fail (factory != NULL, FALSE);
440   
441   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (sink_template_factory));
442   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (src_template_factory));
443
444   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
445
446   return TRUE;
447 }
448
449 GstPluginDesc plugin_desc = {
450   GST_VERSION_MAJOR,
451   GST_VERSION_MINOR,
452   "level",
453   plugin_init
454 };