gst/: Port to new base class.
[platform/upstream/gstreamer.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 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 #include <gst/gst.h>
28 #include "gstlevel.h"
29 #include "math.h"
30
31 GST_DEBUG_CATEGORY (level_debug);
32 #define GST_CAT_DEFAULT level_debug
33
34 static GstElementDetails level_details = {
35   "Level",
36   "Filter/Analyzer/Audio",
37   "RMS/Peak/Decaying Peak Level signaller for audio/raw",
38   "Thomas <thomas@apestaart.org>"
39 };
40
41 static GstStaticPadTemplate sink_template_factory =
42 GST_STATIC_PAD_TEMPLATE ("sink",
43     GST_PAD_SINK,
44     GST_PAD_ALWAYS,
45     GST_STATIC_CAPS ("audio/x-raw-int, "
46         "rate = (int) [ 1, MAX ], "
47         "channels = (int) [ 1, 2 ], "
48         "endianness = (int) BYTE_ORDER, "
49         "width = (int) { 8, 16 }, "
50         "depth = (int) { 8, 16 }, " "signed = (boolean) true")
51     );
52
53 static GstStaticPadTemplate src_template_factory =
54 GST_STATIC_PAD_TEMPLATE ("src",
55     GST_PAD_SRC,
56     GST_PAD_ALWAYS,
57     GST_STATIC_CAPS ("audio/x-raw-int, "
58         "rate = (int) [ 1, MAX ], "
59         "channels = (int) [ 1, 2 ], "
60         "endianness = (int) BYTE_ORDER, "
61         "width = (int) { 8, 16 }, "
62         "depth = (int) { 8, 16 }, " "signed = (boolean) true")
63     );
64
65
66 enum
67 {
68   PROP_0,
69   PROP_SIGNAL_LEVEL,
70   PROP_SIGNAL_INTERVAL,
71   PROP_PEAK_TTL,
72   PROP_PEAK_FALLOFF
73 };
74
75
76 GST_BOILERPLATE (GstLevel, gst_level, GstBaseTransform,
77     GST_TYPE_BASE_TRANSFORM);
78
79
80 static void gst_level_set_property (GObject * object, guint prop_id,
81     const GValue * value, GParamSpec * pspec);
82 static void gst_level_get_property (GObject * object, guint prop_id,
83     GValue * value, GParamSpec * pspec);
84
85 static gboolean gst_level_set_caps (GstBaseTransform * trans, GstCaps * in,
86     GstCaps * out);
87 static GstFlowReturn gst_level_transform (GstBaseTransform * trans,
88     GstBuffer * in, GstBuffer * out);
89
90
91 static void
92 gst_level_base_init (gpointer g_class)
93 {
94   GstElementClass *element_class = g_class;
95
96   gst_element_class_add_pad_template (element_class,
97       gst_static_pad_template_get (&sink_template_factory));
98   gst_element_class_add_pad_template (element_class,
99       gst_static_pad_template_get (&src_template_factory));
100   gst_element_class_set_details (element_class, &level_details);
101 }
102
103 static void
104 gst_level_class_init (GstLevelClass * klass)
105 {
106   GObjectClass *gobject_class;
107   GstBaseTransformClass *trans_class;
108
109   gobject_class = (GObjectClass *) klass;
110   trans_class = (GstBaseTransformClass *) klass;
111
112   gobject_class->set_property = gst_level_set_property;
113   gobject_class->get_property = gst_level_get_property;
114
115   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SIGNAL_LEVEL,
116       g_param_spec_boolean ("signal", "Signal",
117           "Emit level signals for each interval", TRUE, G_PARAM_READWRITE));
118   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SIGNAL_INTERVAL,
119       g_param_spec_double ("interval", "Interval",
120           "Interval between emissions (in seconds)",
121           0.01, 100.0, 0.1, G_PARAM_READWRITE));
122   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PEAK_TTL,
123       g_param_spec_double ("peak_ttl", "Peak TTL",
124           "Time To Live of decay peak before it falls back",
125           0, 100.0, 0.3, G_PARAM_READWRITE));
126   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PEAK_FALLOFF,
127       g_param_spec_double ("peak_falloff", "Peak Falloff",
128           "Decay rate of decay peak after TTL (in dB/sec)",
129           0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE));
130
131   GST_DEBUG_CATEGORY_INIT (level_debug, "level", 0, "Level calculation");
132
133   trans_class->set_caps = gst_level_set_caps;
134   trans_class->transform = gst_level_transform;
135 }
136
137 static void
138 gst_level_init (GstLevel * filter)
139 {
140   filter->CS = NULL;
141   filter->peak = NULL;
142   filter->MS = NULL;
143   filter->RMS_dB = NULL;
144
145   filter->rate = 0;
146   filter->width = 0;
147   filter->channels = 0;
148
149   filter->interval = 0.1;
150   filter->decay_peak_ttl = 0.4;
151   filter->decay_peak_falloff = 10.0;    /* dB falloff (/sec) */
152 }
153
154 static void
155 gst_level_set_property (GObject * object, guint prop_id,
156     const GValue * value, GParamSpec * pspec)
157 {
158   GstLevel *filter = GST_LEVEL (object);
159
160   switch (prop_id) {
161     case PROP_SIGNAL_LEVEL:
162       filter->signal = g_value_get_boolean (value);
163       break;
164     case PROP_SIGNAL_INTERVAL:
165       filter->interval = g_value_get_double (value);
166       break;
167     case PROP_PEAK_TTL:
168       filter->decay_peak_ttl = g_value_get_double (value);
169       break;
170     case PROP_PEAK_FALLOFF:
171       filter->decay_peak_falloff = g_value_get_double (value);
172       break;
173     default:
174       break;
175   }
176 }
177
178 static void
179 gst_level_get_property (GObject * object, guint prop_id,
180     GValue * value, GParamSpec * pspec)
181 {
182   GstLevel *filter = GST_LEVEL (object);
183
184   switch (prop_id) {
185     case PROP_SIGNAL_LEVEL:
186       g_value_set_boolean (value, filter->signal);
187       break;
188     case PROP_SIGNAL_INTERVAL:
189       g_value_set_double (value, filter->interval);
190       break;
191     case PROP_PEAK_TTL:
192       g_value_set_double (value, filter->decay_peak_ttl);
193       break;
194     case PROP_PEAK_FALLOFF:
195       g_value_set_double (value, filter->decay_peak_falloff);
196       break;
197     default:
198       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
199       break;
200   }
201 }
202
203 static gint
204 structure_get_int (GstStructure * structure, const gchar * field)
205 {
206   gint ret;
207
208   if (!gst_structure_get_int (structure, field, &ret))
209     g_assert_not_reached ();
210
211   return ret;
212 }
213
214 static gboolean
215 gst_level_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
216 {
217   GstLevel *filter;
218   GstStructure *structure;
219   int i;
220
221   filter = GST_LEVEL (trans);
222
223   filter->num_samples = 0;
224
225   structure = gst_caps_get_structure (in, 0);
226   filter->rate = structure_get_int (structure, "rate");
227   filter->width = structure_get_int (structure, "width");
228   filter->channels = structure_get_int (structure, "channels");
229
230   /* allocate channel variable arrays */
231   g_free (filter->CS);
232   g_free (filter->peak);
233   g_free (filter->last_peak);
234   g_free (filter->decay_peak);
235   g_free (filter->decay_peak_age);
236   g_free (filter->MS);
237   g_free (filter->RMS_dB);
238   filter->CS = g_new (double, filter->channels);
239   filter->peak = g_new (double, filter->channels);
240   filter->last_peak = g_new (double, filter->channels);
241   filter->decay_peak = g_new (double, filter->channels);
242   filter->decay_peak_age = g_new (double, filter->channels);
243   filter->MS = g_new (double, filter->channels);
244   filter->RMS_dB = g_new (double, filter->channels);
245
246   for (i = 0; i < filter->channels; ++i) {
247     filter->CS[i] = filter->peak[i] = filter->last_peak[i] =
248         filter->decay_peak[i] = filter->decay_peak_age[i] =
249         filter->MS[i] = filter->RMS_dB[i] = 0.0;
250   }
251
252   return TRUE;
253 }
254
255 #if 0
256 #define DEBUG(str,...) g_print (str, ...)
257 #else
258 #define DEBUG(str,...)          /*nop */
259 #endif
260
261 /* process one (interleaved) channel of incoming samples
262  * calculate square sum of samples
263  * normalize and return normalized Cumulative Square
264  * caller must assure num is a multiple of channels
265  * this filter only accepts signed audio data, so mid level is always 0
266  */
267 #define DEFINE_LEVEL_CALCULATOR(TYPE)                                           \
268 static void inline                                                              \
269 gst_level_calculate_##TYPE (TYPE * in, guint num, gint channels,                \
270                             gint resolution, double *CS, double *peak)          \
271 {                                                                               \
272   register int j;                                                               \
273   double squaresum = 0.0;       /* square sum of the integer samples */         \
274   register double square = 0.0;         /* Square */                            \
275   register double PSS = 0.0;            /* Peak Square Sample */                \
276   gdouble normalizer;                                                           \
277                                                                                 \
278   *CS = 0.0;      /* Cumulative Square for this block */                        \
279                                                                                 \
280   normalizer = (double) (1 << resolution);                                      \
281                                                                                 \
282   /*                                                                            \
283    * process data here                                                          \
284    * input sample data enters in *in_data as 8 or 16 bit data                   \
285    * samples for left and right channel are interleaved                         \
286    * returns the Mean Square of the samples as a double between 0 and 1         \
287    */                                                                           \
288                                                                                 \
289   for (j = 0; j < num; j += channels)                                           \
290   {                                                                             \
291     DEBUG ("ch %d -> smp %d\n", j, in[j]);                                      \
292     square = (double) (in[j] * in[j]);                                          \
293     if (square > PSS) PSS = square;                                             \
294     squaresum += square;                                                        \
295   }                                                                             \
296   *peak = PSS / ((double) normalizer * (double) normalizer);                    \
297                                                                                 \
298   /* return normalized cumulative square */                                     \
299   *CS = squaresum / ((double) normalizer * (double) normalizer);                \
300 }
301
302 DEFINE_LEVEL_CALCULATOR (gint16);
303 DEFINE_LEVEL_CALCULATOR (gint8);
304
305 static GstMessage *
306 gst_level_message_new (GstLevel * l, gdouble endtime)
307 {
308   GstStructure *s;
309   GValue v = { 0, };
310
311   g_value_init (&v, GST_TYPE_LIST);
312
313   s = gst_structure_new ("level", "endtime", G_TYPE_DOUBLE, endtime, NULL);
314   /* will copy-by-value */
315   gst_structure_set_value (s, "rms", &v);
316   gst_structure_set_value (s, "peak", &v);
317   gst_structure_set_value (s, "decay", &v);
318
319   return gst_message_new_application (GST_OBJECT (l), s);
320 }
321
322 static void
323 gst_level_message_append_channel (GstMessage * m, gdouble rms, gdouble peak,
324     gdouble decay)
325 {
326   GstStructure *s;
327   GValue v = { 0, };
328   GValue *l;
329
330   g_value_init (&v, G_TYPE_DOUBLE);
331
332   s = (GstStructure *) gst_message_get_structure (m);
333
334   l = (GValue *) gst_structure_get_value (s, "rms");
335   g_value_set_double (&v, rms);
336   gst_value_list_append_value (l, &v);  /* copies by value */
337
338   l = (GValue *) gst_structure_get_value (s, "peak");
339   g_value_set_double (&v, peak);
340   gst_value_list_append_value (l, &v);  /* copies by value */
341
342   l = (GValue *) gst_structure_get_value (s, "decay");
343   g_value_set_double (&v, decay);
344   gst_value_list_append_value (l, &v);  /* copies by value */
345 }
346
347 static GstFlowReturn
348 gst_level_transform (GstBaseTransform * trans, GstBuffer * in, GstBuffer * out)
349 {
350   GstLevel *filter;
351   gpointer in_data;
352   double CS = 0.0;
353   gint num_samples = 0;
354   gint i;
355
356   filter = GST_LEVEL (trans);
357
358   for (i = 0; i < filter->channels; ++i)
359     filter->CS[i] = filter->peak[i] = filter->MS[i] = filter->RMS_dB[i] = 0.0;
360
361   in_data = GST_BUFFER_DATA (in);
362   num_samples = GST_BUFFER_SIZE (in) / (filter->width / 8);
363
364   g_return_val_if_fail (num_samples % filter->channels == 0, GST_FLOW_ERROR);
365
366   for (i = 0; i < filter->channels; ++i) {
367     switch (filter->width) {
368       case 16:
369         gst_level_calculate_gint16 (in_data + i, num_samples,
370             filter->channels, filter->width - 1, &CS, &filter->peak[i]);
371         break;
372       case 8:
373         gst_level_calculate_gint8 (((gint8 *) in_data) + i, num_samples,
374             filter->channels, filter->width - 1, &CS, &filter->peak[i]);
375         break;
376     }
377     GST_LOG_OBJECT (filter, "channel %d, cumulative sum %f, peak %f", i, CS,
378         filter->peak[i]);
379     filter->CS[i] += CS;
380
381   }
382
383   filter->num_samples += num_samples;
384
385   for (i = 0; i < filter->channels; ++i) {
386     filter->decay_peak_age[i] += num_samples;
387     DEBUG ("filter peak info [%d]: peak %f, age %f\n", i,
388         filter->last_peak[i], filter->decay_peak_age[i]);
389     /* update running peak */
390     if (filter->peak[i] > filter->last_peak[i])
391       filter->last_peak[i] = filter->peak[i];
392
393     /* update decay peak */
394     if (filter->peak[i] >= filter->decay_peak[i]) {
395       DEBUG ("new peak, %f\n", filter->peak[i]);
396       filter->decay_peak[i] = filter->peak[i];
397       filter->decay_peak_age[i] = 0;
398     } else {
399       /* make decay peak fall off if too old */
400       if (filter->decay_peak_age[i] > filter->rate * filter->decay_peak_ttl) {
401         double falloff_dB;
402         double falloff;
403         double length;          /* length of buffer in seconds */
404
405
406         length = (double) num_samples / (filter->channels * filter->rate);
407         falloff_dB = filter->decay_peak_falloff * length;
408         falloff = pow (10, falloff_dB / -20.0);
409
410         DEBUG ("falloff: length %f, dB falloff %f, falloff factor %e\n",
411             length, falloff_dB, falloff);
412         filter->decay_peak[i] *= falloff;
413         DEBUG ("peak is %f samples old, decayed with factor %e to %f\n",
414             filter->decay_peak_age[i], falloff, filter->decay_peak[i]);
415       }
416     }
417   }
418
419   /* do we need to emit ? */
420
421   if (filter->num_samples >= filter->interval * (gdouble) filter->rate) {
422     if (filter->signal) {
423       GstMessage *m;
424       double endtime, RMS;
425
426       endtime = (double) GST_BUFFER_TIMESTAMP (in) / GST_SECOND
427           + (double) num_samples / (double) filter->rate;
428
429       m = gst_level_message_new (filter, endtime);
430
431       for (i = 0; i < filter->channels; ++i) {
432         RMS = sqrt (filter->CS[i] / (filter->num_samples / filter->channels));
433
434         gst_level_message_append_channel (m, 20 * log10 (RMS),
435             20 * log10 (filter->last_peak[i]),
436             20 * log10 (filter->decay_peak[i]));
437
438         /* reset cumulative and normal peak */
439         filter->CS[i] = 0.0;
440         filter->last_peak[i] = 0.0;
441       }
442
443       gst_element_post_message (GST_ELEMENT (filter), m);
444     }
445     filter->num_samples = 0;
446   }
447
448   return GST_FLOW_OK;
449 }
450
451 static gboolean
452 plugin_init (GstPlugin * plugin)
453 {
454   return gst_element_register (plugin, "level", GST_RANK_NONE, GST_TYPE_LEVEL);
455 }
456
457 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
458     GST_VERSION_MINOR,
459     "level",
460     "Audio level plugin",
461     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)