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