Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.git] / gst / level / gstlevel.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2000,2001,2002,2003,2005
4  *           Thomas Vander Stichele <thomas at apestaart dot org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:element-level
24  *
25  * Level analyses incoming audio buffers and, if the #GstLevel:message property
26  * is #TRUE, generates an element message named
27  * <classname>&quot;level&quot;</classname>:
28  * after each interval of time given by the #GstLevel:interval property.
29  * The message's structure contains these fields:
30  * <itemizedlist>
31  * <listitem>
32  *   <para>
33  *   #GstClockTime
34  *   <classname>&quot;timestamp&quot;</classname>:
35  *   the timestamp of the buffer that triggered the message.
36  *   </para>
37  * </listitem>
38  * <listitem>
39  *   <para>
40  *   #GstClockTime
41  *   <classname>&quot;stream-time&quot;</classname>:
42  *   the stream time of the buffer.
43  *   </para>
44  * </listitem>
45  * <listitem>
46  *   <para>
47  *   #GstClockTime
48  *   <classname>&quot;running-time&quot;</classname>:
49  *   the running_time of the buffer.
50  *   </para>
51  * </listitem>
52  * <listitem>
53  *   <para>
54  *   #GstClockTime
55  *   <classname>&quot;duration&quot;</classname>:
56  *   the duration of the buffer.
57  *   </para>
58  * </listitem>
59  * <listitem>
60  *   <para>
61  *   #GstClockTime
62  *   <classname>&quot;endtime&quot;</classname>:
63  *   the end time of the buffer that triggered the message as stream time (this
64  *   is deprecated, as it can be calculated from stream-time + duration)
65  *   </para>
66  * </listitem>
67  * <listitem>
68  *   <para>
69  *   #GstValueList of #gdouble
70  *   <classname>&quot;peak&quot;</classname>:
71  *   the peak power level in dB for each channel
72  *   </para>
73  * </listitem>
74  * <listitem>
75  *   <para>
76  *   #GstValueList of #gdouble
77  *   <classname>&quot;decay&quot;</classname>:
78  *   the decaying peak power level in dB for each channel
79  *   the decaying peak level follows the peak level, but starts dropping
80  *   if no new peak is reached after the time given by
81  *   the <link linkend="GstLevel--peak-ttl">the time to live</link>.
82  *   When the decaying peak level drops, it does so at the decay rate
83  *   as specified by the
84  *   <link linkend="GstLevel--peak-falloff">the peak falloff rate</link>.
85  *   </para>
86  * </listitem>
87  * <listitem>
88  *   <para>
89  *   #GstValueList of #gdouble
90  *   <classname>&quot;rms&quot;</classname>:
91  *   the Root Mean Square (or average power) level in dB for each channel
92  *   </para>
93  * </listitem>
94  * </itemizedlist>
95  *
96  * <refsect2>
97  * <title>Example application</title>
98  * |[
99  * <xi:include xmlns:xi="http://www.w3.org/2003/XInclude" parse="text" href="../../../../tests/examples/level/level-example.c" />
100  * ]|
101  * </refsect2>
102  */
103
104 #ifdef HAVE_CONFIG_H
105 #include "config.h"
106 #endif
107 #include <string.h>
108 #include <math.h>
109 #include <gst/gst.h>
110 #include <gst/audio/audio.h>
111
112 #include "gstlevel.h"
113
114 GST_DEBUG_CATEGORY_STATIC (level_debug);
115 #define GST_CAT_DEFAULT level_debug
116
117 #define EPSILON 1e-35f
118
119 static GstStaticPadTemplate sink_template_factory =
120     GST_STATIC_PAD_TEMPLATE ("sink",
121     GST_PAD_SINK,
122     GST_PAD_ALWAYS,
123     GST_STATIC_CAPS ("audio/x-raw-int, "
124         "rate = (int) [ 1, MAX ], "
125         "channels = (int) [ 1, MAX ], "
126         "endianness = (int) BYTE_ORDER, "
127         "width = (int) { 8, 16, 32 }, "
128         "depth = (int) { 8, 16, 32 }, "
129         "signed = (boolean) true; "
130         "audio/x-raw-float, "
131         "rate = (int) [ 1, MAX ], "
132         "channels = (int) [ 1, MAX ], "
133         "endianness = (int) BYTE_ORDER, " "width = (int) {32, 64} ")
134     );
135
136 static GstStaticPadTemplate src_template_factory =
137     GST_STATIC_PAD_TEMPLATE ("src",
138     GST_PAD_SRC,
139     GST_PAD_ALWAYS,
140     GST_STATIC_CAPS ("audio/x-raw-int, "
141         "rate = (int) [ 1, MAX ], "
142         "channels = (int) [ 1, MAX ], "
143         "endianness = (int) BYTE_ORDER, "
144         "width = (int) { 8, 16, 32 }, "
145         "depth = (int) { 8, 16, 32 }, "
146         "signed = (boolean) true; "
147         "audio/x-raw-float, "
148         "rate = (int) [ 1, MAX ], "
149         "channels = (int) [ 1, MAX ], "
150         "endianness = (int) BYTE_ORDER, " "width = (int) {32, 64} ")
151     );
152
153 enum
154 {
155   PROP_0,
156   PROP_SIGNAL_LEVEL,
157   PROP_SIGNAL_INTERVAL,
158   PROP_PEAK_TTL,
159   PROP_PEAK_FALLOFF
160 };
161
162 GST_BOILERPLATE (GstLevel, gst_level, GstBaseTransform,
163     GST_TYPE_BASE_TRANSFORM);
164
165 static void gst_level_set_property (GObject * object, guint prop_id,
166     const GValue * value, GParamSpec * pspec);
167 static void gst_level_get_property (GObject * object, guint prop_id,
168     GValue * value, GParamSpec * pspec);
169 static void gst_level_finalize (GObject * obj);
170
171 static gboolean gst_level_set_caps (GstBaseTransform * trans, GstCaps * in,
172     GstCaps * out);
173 static gboolean gst_level_start (GstBaseTransform * trans);
174 static GstFlowReturn gst_level_transform_ip (GstBaseTransform * trans,
175     GstBuffer * in);
176
177
178 static void
179 gst_level_base_init (gpointer g_class)
180 {
181   GstElementClass *element_class = g_class;
182
183   gst_element_class_add_static_pad_template (element_class,
184       &sink_template_factory);
185   gst_element_class_add_static_pad_template (element_class,
186       &src_template_factory);
187   gst_element_class_set_details_simple (element_class, "Level",
188       "Filter/Analyzer/Audio",
189       "RMS/Peak/Decaying Peak Level messager for audio/raw",
190       "Thomas Vander Stichele <thomas at apestaart dot org>");
191 }
192
193 static void
194 gst_level_class_init (GstLevelClass * klass)
195 {
196   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
197   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
198
199   gobject_class->set_property = gst_level_set_property;
200   gobject_class->get_property = gst_level_get_property;
201   gobject_class->finalize = gst_level_finalize;
202
203   g_object_class_install_property (gobject_class, PROP_SIGNAL_LEVEL,
204       g_param_spec_boolean ("message", "message",
205           "Post a level message for each passed interval",
206           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
207   g_object_class_install_property (gobject_class, PROP_SIGNAL_INTERVAL,
208       g_param_spec_uint64 ("interval", "Interval",
209           "Interval of time between message posts (in nanoseconds)",
210           1, G_MAXUINT64, GST_SECOND / 10,
211           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
212   g_object_class_install_property (gobject_class, PROP_PEAK_TTL,
213       g_param_spec_uint64 ("peak-ttl", "Peak TTL",
214           "Time To Live of decay peak before it falls back (in nanoseconds)",
215           0, G_MAXUINT64, GST_SECOND / 10 * 3,
216           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
217   g_object_class_install_property (gobject_class, PROP_PEAK_FALLOFF,
218       g_param_spec_double ("peak-falloff", "Peak Falloff",
219           "Decay rate of decay peak after TTL (in dB/sec)",
220           0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
221
222   GST_DEBUG_CATEGORY_INIT (level_debug, "level", 0, "Level calculation");
223
224   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_level_set_caps);
225   trans_class->start = GST_DEBUG_FUNCPTR (gst_level_start);
226   trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_level_transform_ip);
227   trans_class->passthrough_on_same_caps = TRUE;
228 }
229
230 static void
231 gst_level_init (GstLevel * filter, GstLevelClass * g_class)
232 {
233   filter->CS = NULL;
234   filter->peak = NULL;
235
236   filter->rate = 0;
237   filter->width = 0;
238   filter->channels = 0;
239
240   filter->interval = GST_SECOND / 10;
241   filter->decay_peak_ttl = GST_SECOND / 10 * 3;
242   filter->decay_peak_falloff = 10.0;    /* dB falloff (/sec) */
243
244   filter->message = TRUE;
245
246   filter->process = NULL;
247
248   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
249 }
250
251 static void
252 gst_level_finalize (GObject * obj)
253 {
254   GstLevel *filter = GST_LEVEL (obj);
255
256   g_free (filter->CS);
257   g_free (filter->peak);
258   g_free (filter->last_peak);
259   g_free (filter->decay_peak);
260   g_free (filter->decay_peak_base);
261   g_free (filter->decay_peak_age);
262
263   filter->CS = NULL;
264   filter->peak = NULL;
265   filter->last_peak = NULL;
266   filter->decay_peak = NULL;
267   filter->decay_peak_base = NULL;
268   filter->decay_peak_age = NULL;
269
270   G_OBJECT_CLASS (parent_class)->finalize (obj);
271 }
272
273 static void
274 gst_level_set_property (GObject * object, guint prop_id,
275     const GValue * value, GParamSpec * pspec)
276 {
277   GstLevel *filter = GST_LEVEL (object);
278
279   switch (prop_id) {
280     case PROP_SIGNAL_LEVEL:
281       filter->message = g_value_get_boolean (value);
282       break;
283     case PROP_SIGNAL_INTERVAL:
284       filter->interval = g_value_get_uint64 (value);
285       if (filter->rate) {
286         filter->interval_frames =
287             GST_CLOCK_TIME_TO_FRAMES (filter->interval, filter->rate);
288       }
289       break;
290     case PROP_PEAK_TTL:
291       filter->decay_peak_ttl =
292           gst_guint64_to_gdouble (g_value_get_uint64 (value));
293       break;
294     case PROP_PEAK_FALLOFF:
295       filter->decay_peak_falloff = g_value_get_double (value);
296       break;
297     default:
298       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
299       break;
300   }
301 }
302
303 static void
304 gst_level_get_property (GObject * object, guint prop_id,
305     GValue * value, GParamSpec * pspec)
306 {
307   GstLevel *filter = GST_LEVEL (object);
308
309   switch (prop_id) {
310     case PROP_SIGNAL_LEVEL:
311       g_value_set_boolean (value, filter->message);
312       break;
313     case PROP_SIGNAL_INTERVAL:
314       g_value_set_uint64 (value, filter->interval);
315       break;
316     case PROP_PEAK_TTL:
317       g_value_set_uint64 (value, filter->decay_peak_ttl);
318       break;
319     case PROP_PEAK_FALLOFF:
320       g_value_set_double (value, filter->decay_peak_falloff);
321       break;
322     default:
323       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
324       break;
325   }
326 }
327
328
329 /* process one (interleaved) channel of incoming samples
330  * calculate square sum of samples
331  * normalize and average over number of samples
332  * returns a normalized cumulative square value, which can be averaged
333  * to return the average power as a double between 0 and 1
334  * also returns the normalized peak power (square of the highest amplitude)
335  *
336  * caller must assure num is a multiple of channels
337  * samples for multiple channels are interleaved
338  * input sample data enters in *in_data as 8 or 16 bit data
339  * this filter only accepts signed audio data, so mid level is always 0
340  *
341  * for 16 bit, this code considers the non-existant 32768 value to be
342  * full-scale; so 32767 will not map to 1.0
343  */
344
345 #define DEFINE_INT_LEVEL_CALCULATOR(TYPE, RESOLUTION)                         \
346 static void inline                                                            \
347 gst_level_calculate_##TYPE (gpointer data, guint num, guint channels,         \
348                             gdouble *NCS, gdouble *NPS)                       \
349 {                                                                             \
350   TYPE * in = (TYPE *)data;                                                   \
351   register guint j;                                                           \
352   gdouble squaresum = 0.0;           /* square sum of the integer samples */  \
353   register gdouble square = 0.0;     /* Square */                             \
354   register gdouble peaksquare = 0.0; /* Peak Square Sample */                 \
355   gdouble normalizer;               /* divisor to get a [-1.0, 1.0] range */  \
356                                                                               \
357   /* *NCS = 0.0; Normalized Cumulative Square */                              \
358   /* *NPS = 0.0; Normalized Peask Square */                                   \
359                                                                               \
360   normalizer = (gdouble) (G_GINT64_CONSTANT(1) << (RESOLUTION * 2));          \
361                                                                               \
362   /* oil_squaresum_shifted_s16(&squaresum,in,num); */                         \
363   for (j = 0; j < num; j += channels)                                         \
364   {                                                                           \
365     square = ((gdouble) in[j]) * in[j];                                       \
366     if (square > peaksquare) peaksquare = square;                             \
367     squaresum += square;                                                      \
368   }                                                                           \
369                                                                               \
370   *NCS = squaresum / normalizer;                                              \
371   *NPS = peaksquare / normalizer;                                             \
372 }
373
374 DEFINE_INT_LEVEL_CALCULATOR (gint32, 31);
375 DEFINE_INT_LEVEL_CALCULATOR (gint16, 15);
376 DEFINE_INT_LEVEL_CALCULATOR (gint8, 7);
377
378 #define DEFINE_FLOAT_LEVEL_CALCULATOR(TYPE)                                   \
379 static void inline                                                            \
380 gst_level_calculate_##TYPE (gpointer data, guint num, guint channels,         \
381                             gdouble *NCS, gdouble *NPS)                       \
382 {                                                                             \
383   TYPE * in = (TYPE *)data;                                                   \
384   register guint j;                                                           \
385   gdouble squaresum = 0.0;           /* square sum of the integer samples */  \
386   register gdouble square = 0.0;     /* Square */                             \
387   register gdouble peaksquare = 0.0; /* Peak Square Sample */                 \
388                                                                               \
389   /* *NCS = 0.0; Normalized Cumulative Square */                              \
390   /* *NPS = 0.0; Normalized Peask Square */                                   \
391                                                                               \
392   /* oil_squaresum_f64(&squaresum,in,num); */                                 \
393   for (j = 0; j < num; j += channels)                                         \
394   {                                                                           \
395     square = ((gdouble) in[j]) * in[j];                                       \
396     if (square > peaksquare) peaksquare = square;                             \
397     squaresum += square;                                                      \
398   }                                                                           \
399                                                                               \
400   *NCS = squaresum;                                                           \
401   *NPS = peaksquare;                                                          \
402 }
403
404 DEFINE_FLOAT_LEVEL_CALCULATOR (gfloat);
405 DEFINE_FLOAT_LEVEL_CALCULATOR (gdouble);
406
407 /* we would need stride to deinterleave also
408 static void inline
409 gst_level_calculate_gdouble (gpointer data, guint num, guint channels,
410                             gdouble *NCS, gdouble *NPS)
411 {
412   oil_squaresum_f64(NCS,(gdouble *)data,num);
413   *NPS = 0.0;
414 }
415 */
416
417
418 static gint
419 structure_get_int (GstStructure * structure, const gchar * field)
420 {
421   gint ret;
422
423   if (!gst_structure_get_int (structure, field, &ret))
424     g_assert_not_reached ();
425
426   return ret;
427 }
428
429 static gboolean
430 gst_level_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
431 {
432   GstLevel *filter = GST_LEVEL (trans);
433   const gchar *mimetype;
434   GstStructure *structure;
435   gint i;
436
437   structure = gst_caps_get_structure (in, 0);
438   filter->rate = structure_get_int (structure, "rate");
439   filter->width = structure_get_int (structure, "width");
440   filter->channels = structure_get_int (structure, "channels");
441   mimetype = gst_structure_get_name (structure);
442
443   /* FIXME: set calculator func depending on caps */
444   filter->process = NULL;
445   if (strcmp (mimetype, "audio/x-raw-int") == 0) {
446     GST_DEBUG_OBJECT (filter, "use int: %u", filter->width);
447     switch (filter->width) {
448       case 8:
449         filter->process = gst_level_calculate_gint8;
450         break;
451       case 16:
452         filter->process = gst_level_calculate_gint16;
453         break;
454       case 32:
455         filter->process = gst_level_calculate_gint32;
456         break;
457     }
458   } else if (strcmp (mimetype, "audio/x-raw-float") == 0) {
459     GST_DEBUG_OBJECT (filter, "use float, %u", filter->width);
460     switch (filter->width) {
461       case 32:
462         filter->process = gst_level_calculate_gfloat;
463         break;
464       case 64:
465         filter->process = gst_level_calculate_gdouble;
466         break;
467     }
468   }
469
470   /* allocate channel variable arrays */
471   g_free (filter->CS);
472   g_free (filter->peak);
473   g_free (filter->last_peak);
474   g_free (filter->decay_peak);
475   g_free (filter->decay_peak_base);
476   g_free (filter->decay_peak_age);
477   filter->CS = g_new (gdouble, filter->channels);
478   filter->peak = g_new (gdouble, filter->channels);
479   filter->last_peak = g_new (gdouble, filter->channels);
480   filter->decay_peak = g_new (gdouble, filter->channels);
481   filter->decay_peak_base = g_new (gdouble, filter->channels);
482
483   filter->decay_peak_age = g_new (GstClockTime, filter->channels);
484
485   for (i = 0; i < filter->channels; ++i) {
486     filter->CS[i] = filter->peak[i] = filter->last_peak[i] =
487         filter->decay_peak[i] = filter->decay_peak_base[i] = 0.0;
488     filter->decay_peak_age[i] = G_GUINT64_CONSTANT (0);
489   }
490
491   filter->interval_frames =
492       GST_CLOCK_TIME_TO_FRAMES (filter->interval, filter->rate);
493
494   return TRUE;
495 }
496
497 static gboolean
498 gst_level_start (GstBaseTransform * trans)
499 {
500   GstLevel *filter = GST_LEVEL (trans);
501
502   filter->num_frames = 0;
503
504   return TRUE;
505 }
506
507 static GstMessage *
508 gst_level_message_new (GstLevel * level, GstClockTime timestamp,
509     GstClockTime duration)
510 {
511   GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (level);
512   GstStructure *s;
513   GValue v = { 0, };
514   GstClockTime endtime, running_time, stream_time;
515
516   g_value_init (&v, GST_TYPE_LIST);
517
518   running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
519       timestamp);
520   stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
521       timestamp);
522   /* endtime is for backwards compatibility */
523   endtime = stream_time + duration;
524
525   s = gst_structure_new ("level",
526       "endtime", GST_TYPE_CLOCK_TIME, endtime,
527       "timestamp", G_TYPE_UINT64, timestamp,
528       "stream-time", G_TYPE_UINT64, stream_time,
529       "running-time", G_TYPE_UINT64, running_time,
530       "duration", G_TYPE_UINT64, duration, NULL);
531   /* will copy-by-value */
532   gst_structure_set_value (s, "rms", &v);
533   gst_structure_set_value (s, "peak", &v);
534   gst_structure_set_value (s, "decay", &v);
535
536   g_value_unset (&v);
537
538   return gst_message_new_element (GST_OBJECT (level), s);
539 }
540
541 static void
542 gst_level_message_append_channel (GstMessage * m, gdouble rms, gdouble peak,
543     gdouble decay)
544 {
545   GstStructure *s;
546   GValue v = { 0, };
547   GValue *l;
548
549   g_value_init (&v, G_TYPE_DOUBLE);
550
551   s = (GstStructure *) gst_message_get_structure (m);
552
553   l = (GValue *) gst_structure_get_value (s, "rms");
554   g_value_set_double (&v, rms);
555   gst_value_list_append_value (l, &v);  /* copies by value */
556
557   l = (GValue *) gst_structure_get_value (s, "peak");
558   g_value_set_double (&v, peak);
559   gst_value_list_append_value (l, &v);  /* copies by value */
560
561   l = (GValue *) gst_structure_get_value (s, "decay");
562   g_value_set_double (&v, decay);
563   gst_value_list_append_value (l, &v);  /* copies by value */
564
565   g_value_unset (&v);
566 }
567
568 static GstFlowReturn
569 gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
570 {
571   GstLevel *filter;
572   guint8 *in_data;
573   gdouble CS;
574   guint i;
575   guint num_frames = 0;
576   guint num_int_samples = 0;    /* number of interleaved samples
577                                  * ie. total count for all channels combined */
578   GstClockTimeDiff falloff_time;
579
580   filter = GST_LEVEL (trans);
581
582   in_data = GST_BUFFER_DATA (in);
583   num_int_samples = GST_BUFFER_SIZE (in) / (filter->width / 8);
584
585   GST_LOG_OBJECT (filter, "analyzing %u sample frames at ts %" GST_TIME_FORMAT,
586       num_int_samples, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (in)));
587
588   g_return_val_if_fail (num_int_samples % filter->channels == 0,
589       GST_FLOW_ERROR);
590
591   num_frames = num_int_samples / filter->channels;
592
593   for (i = 0; i < filter->channels; ++i) {
594     if (!GST_BUFFER_FLAG_IS_SET (in, GST_BUFFER_FLAG_GAP)) {
595       filter->process (in_data, num_int_samples, filter->channels, &CS,
596           &filter->peak[i]);
597       GST_LOG_OBJECT (filter,
598           "channel %d, cumulative sum %f, peak %f, over %d samples/%d channels",
599           i, CS, filter->peak[i], num_int_samples, filter->channels);
600       filter->CS[i] += CS;
601     } else {
602       filter->peak[i] = 0.0;
603     }
604     in_data += (filter->width / 8);
605
606     filter->decay_peak_age[i] +=
607         GST_FRAMES_TO_CLOCK_TIME (num_frames, filter->rate);
608     GST_LOG_OBJECT (filter, "filter peak info [%d]: decay peak %f, age %"
609         GST_TIME_FORMAT, i,
610         filter->decay_peak[i], GST_TIME_ARGS (filter->decay_peak_age[i]));
611
612     /* update running peak */
613     if (filter->peak[i] > filter->last_peak[i])
614       filter->last_peak[i] = filter->peak[i];
615
616     /* make decay peak fall off if too old */
617     falloff_time =
618         GST_CLOCK_DIFF (gst_gdouble_to_guint64 (filter->decay_peak_ttl),
619         filter->decay_peak_age[i]);
620     if (falloff_time > 0) {
621       gdouble falloff_dB;
622       gdouble falloff;
623       gdouble length;           /* length of falloff time in seconds */
624
625       length = (gdouble) falloff_time / (gdouble) GST_SECOND;
626       falloff_dB = filter->decay_peak_falloff * length;
627       falloff = pow (10, falloff_dB / -20.0);
628
629       GST_LOG_OBJECT (filter,
630           "falloff: current %f, base %f, interval %" GST_TIME_FORMAT
631           ", dB falloff %f, factor %e",
632           filter->decay_peak[i], filter->decay_peak_base[i],
633           GST_TIME_ARGS (falloff_time), falloff_dB, falloff);
634       filter->decay_peak[i] = filter->decay_peak_base[i] * falloff;
635       GST_LOG_OBJECT (filter,
636           "peak is %" GST_TIME_FORMAT " old, decayed with factor %e to %f",
637           GST_TIME_ARGS (filter->decay_peak_age[i]), falloff,
638           filter->decay_peak[i]);
639     } else {
640       GST_LOG_OBJECT (filter, "peak not old enough, not decaying");
641     }
642
643     /* if the peak of this run is higher, the decay peak gets reset */
644     if (filter->peak[i] >= filter->decay_peak[i]) {
645       GST_LOG_OBJECT (filter, "new peak, %f", filter->peak[i]);
646       filter->decay_peak[i] = filter->peak[i];
647       filter->decay_peak_base[i] = filter->peak[i];
648       filter->decay_peak_age[i] = G_GINT64_CONSTANT (0);
649     }
650   }
651
652   if (G_UNLIKELY (!filter->num_frames)) {
653     /* remember start timestamp for message */
654     filter->message_ts = GST_BUFFER_TIMESTAMP (in);
655   }
656   filter->num_frames += num_frames;
657
658   /* do we need to message ? */
659   if (filter->num_frames >= filter->interval_frames) {
660     if (filter->message) {
661       GstMessage *m;
662       GstClockTime duration =
663           GST_FRAMES_TO_CLOCK_TIME (filter->num_frames, filter->rate);
664
665       m = gst_level_message_new (filter, filter->message_ts, duration);
666
667       GST_LOG_OBJECT (filter,
668           "message: ts %" GST_TIME_FORMAT ", num_frames %d",
669           GST_TIME_ARGS (filter->message_ts), filter->num_frames);
670
671       for (i = 0; i < filter->channels; ++i) {
672         gdouble RMS;
673         gdouble RMSdB, lastdB, decaydB;
674
675         RMS = sqrt (filter->CS[i] / filter->num_frames);
676         GST_LOG_OBJECT (filter,
677             "message: channel %d, CS %f, num_frames %d, RMS %f",
678             i, filter->CS[i], filter->num_frames, RMS);
679         GST_LOG_OBJECT (filter,
680             "message: last_peak: %f, decay_peak: %f",
681             filter->last_peak[i], filter->decay_peak[i]);
682         /* RMS values are calculated in amplitude, so 20 * log 10 */
683         RMSdB = 20 * log10 (RMS + EPSILON);
684         /* peak values are square sums, ie. power, so 10 * log 10 */
685         lastdB = 10 * log10 (filter->last_peak[i] + EPSILON);
686         decaydB = 10 * log10 (filter->decay_peak[i] + EPSILON);
687
688         if (filter->decay_peak[i] < filter->last_peak[i]) {
689           /* this can happen in certain cases, for example when
690            * the last peak is between decay_peak and decay_peak_base */
691           GST_DEBUG_OBJECT (filter,
692               "message: decay peak dB %f smaller than last peak dB %f, copying",
693               decaydB, lastdB);
694           filter->decay_peak[i] = filter->last_peak[i];
695         }
696         GST_LOG_OBJECT (filter,
697             "message: RMS %f dB, peak %f dB, decay %f dB",
698             RMSdB, lastdB, decaydB);
699
700         gst_level_message_append_channel (m, RMSdB, lastdB, decaydB);
701
702         /* reset cumulative and normal peak */
703         filter->CS[i] = 0.0;
704         filter->last_peak[i] = 0.0;
705       }
706
707       gst_element_post_message (GST_ELEMENT (filter), m);
708     }
709     filter->num_frames = 0;
710   }
711
712   return GST_FLOW_OK;
713 }
714
715 static gboolean
716 plugin_init (GstPlugin * plugin)
717 {
718   /*oil_init (); */
719
720   return gst_element_register (plugin, "level", GST_RANK_NONE, GST_TYPE_LEVEL);
721 }
722
723 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
724     GST_VERSION_MINOR,
725     "level",
726     "Audio level plugin",
727     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);