level: use more g-style types
[platform/upstream/gstreamer.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 /*#include <liboil/liboil.h>*/
112
113 #include "gstlevel.h"
114
115 GST_DEBUG_CATEGORY_STATIC (level_debug);
116 #define GST_CAT_DEFAULT level_debug
117
118 static const GstElementDetails level_details = GST_ELEMENT_DETAILS ("Level",
119     "Filter/Analyzer/Audio",
120     "RMS/Peak/Decaying Peak Level messager for audio/raw",
121     "Thomas Vander Stichele <thomas at apestaart dot org>");
122
123 static GstStaticPadTemplate sink_template_factory =
124     GST_STATIC_PAD_TEMPLATE ("sink",
125     GST_PAD_SINK,
126     GST_PAD_ALWAYS,
127     GST_STATIC_CAPS ("audio/x-raw-int, "
128         "rate = (int) [ 1, MAX ], "
129         "channels = (int) [ 1, MAX ], "
130         "endianness = (int) BYTE_ORDER, "
131         "width = (int) { 8, 16, 32 }, "
132         "depth = (int) { 8, 16, 32 }, "
133         "signed = (boolean) true; "
134         "audio/x-raw-float, "
135         "rate = (int) [ 1, MAX ], "
136         "channels = (int) [ 1, MAX ], "
137         "endianness = (int) BYTE_ORDER, " "width = (int) {32, 64} ")
138     );
139
140 static GstStaticPadTemplate src_template_factory =
141     GST_STATIC_PAD_TEMPLATE ("src",
142     GST_PAD_SRC,
143     GST_PAD_ALWAYS,
144     GST_STATIC_CAPS ("audio/x-raw-int, "
145         "rate = (int) [ 1, MAX ], "
146         "channels = (int) [ 1, MAX ], "
147         "endianness = (int) BYTE_ORDER, "
148         "width = (int) { 8, 16, 32 }, "
149         "depth = (int) { 8, 16, 32 }, "
150         "signed = (boolean) true; "
151         "audio/x-raw-float, "
152         "rate = (int) [ 1, MAX ], "
153         "channels = (int) [ 1, MAX ], "
154         "endianness = (int) BYTE_ORDER, " "width = (int) {32, 64} ")
155     );
156
157 enum
158 {
159   PROP_0,
160   PROP_SIGNAL_LEVEL,
161   PROP_SIGNAL_INTERVAL,
162   PROP_PEAK_TTL,
163   PROP_PEAK_FALLOFF
164 };
165
166 GST_BOILERPLATE (GstLevel, gst_level, GstBaseTransform,
167     GST_TYPE_BASE_TRANSFORM);
168
169 static void gst_level_set_property (GObject * object, guint prop_id,
170     const GValue * value, GParamSpec * pspec);
171 static void gst_level_get_property (GObject * object, guint prop_id,
172     GValue * value, GParamSpec * pspec);
173 static void gst_level_finalize (GObject * obj);
174
175 static gboolean gst_level_set_caps (GstBaseTransform * trans, GstCaps * in,
176     GstCaps * out);
177 static gboolean gst_level_start (GstBaseTransform * trans);
178 static GstFlowReturn gst_level_transform_ip (GstBaseTransform * trans,
179     GstBuffer * in);
180
181
182 static void
183 gst_level_base_init (gpointer g_class)
184 {
185   GstElementClass *element_class = g_class;
186
187   gst_element_class_add_pad_template (element_class,
188       gst_static_pad_template_get (&sink_template_factory));
189   gst_element_class_add_pad_template (element_class,
190       gst_static_pad_template_get (&src_template_factory));
191   gst_element_class_set_details (element_class, &level_details);
192 }
193
194 static void
195 gst_level_class_init (GstLevelClass * klass)
196 {
197   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
198   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
199
200   gobject_class->set_property = gst_level_set_property;
201   gobject_class->get_property = gst_level_get_property;
202   gobject_class->finalize = gst_level_finalize;
203
204   g_object_class_install_property (gobject_class, PROP_SIGNAL_LEVEL,
205       g_param_spec_boolean ("message", "mesage",
206           "Post a level message for each passed interval",
207           TRUE, G_PARAM_READWRITE));
208   g_object_class_install_property (gobject_class, PROP_SIGNAL_INTERVAL,
209       g_param_spec_uint64 ("interval", "Interval",
210           "Interval of time between message posts (in nanoseconds)",
211           1, G_MAXUINT64, GST_SECOND / 10, G_PARAM_READWRITE));
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, G_PARAM_READWRITE));
216   g_object_class_install_property (gobject_class, PROP_PEAK_FALLOFF,
217       g_param_spec_double ("peak-falloff", "Peak Falloff",
218           "Decay rate of decay peak after TTL (in dB/sec)",
219           0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE));
220
221   GST_DEBUG_CATEGORY_INIT (level_debug, "level", 0, "Level calculation");
222
223   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_level_set_caps);
224   trans_class->start = GST_DEBUG_FUNCPTR (gst_level_start);
225   trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_level_transform_ip);
226   trans_class->passthrough_on_same_caps = TRUE;
227 }
228
229 static void
230 gst_level_init (GstLevel * filter, GstLevelClass * g_class)
231 {
232   filter->CS = NULL;
233   filter->peak = NULL;
234
235   filter->rate = 0;
236   filter->width = 0;
237   filter->channels = 0;
238
239   filter->interval = GST_SECOND / 10;
240   filter->decay_peak_ttl = GST_SECOND / 10 * 3;
241   filter->decay_peak_falloff = 10.0;    /* dB falloff (/sec) */
242
243   filter->message = TRUE;
244
245   filter->process = NULL;
246
247   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
248 }
249
250 static void
251 gst_level_finalize (GObject * obj)
252 {
253   GstLevel *filter = GST_LEVEL (obj);
254
255   g_free (filter->CS);
256   g_free (filter->peak);
257   g_free (filter->last_peak);
258   g_free (filter->decay_peak);
259   g_free (filter->decay_peak_base);
260   g_free (filter->decay_peak_age);
261
262   filter->CS = NULL;
263   filter->peak = NULL;
264   filter->last_peak = NULL;
265   filter->decay_peak = NULL;
266   filter->decay_peak_base = NULL;
267   filter->decay_peak_age = NULL;
268
269   G_OBJECT_CLASS (parent_class)->finalize (obj);
270 }
271
272 static void
273 gst_level_set_property (GObject * object, guint prop_id,
274     const GValue * value, GParamSpec * pspec)
275 {
276   GstLevel *filter = GST_LEVEL (object);
277
278   switch (prop_id) {
279     case PROP_SIGNAL_LEVEL:
280       filter->message = g_value_get_boolean (value);
281       break;
282     case PROP_SIGNAL_INTERVAL:
283       filter->interval = g_value_get_uint64 (value);
284       break;
285     case PROP_PEAK_TTL:
286       filter->decay_peak_ttl =
287           gst_guint64_to_gdouble (g_value_get_uint64 (value));
288       break;
289     case PROP_PEAK_FALLOFF:
290       filter->decay_peak_falloff = g_value_get_double (value);
291       break;
292     default:
293       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
294       break;
295   }
296 }
297
298 static void
299 gst_level_get_property (GObject * object, guint prop_id,
300     GValue * value, GParamSpec * pspec)
301 {
302   GstLevel *filter = GST_LEVEL (object);
303
304   switch (prop_id) {
305     case PROP_SIGNAL_LEVEL:
306       g_value_set_boolean (value, filter->message);
307       break;
308     case PROP_SIGNAL_INTERVAL:
309       g_value_set_uint64 (value, filter->interval);
310       break;
311     case PROP_PEAK_TTL:
312       g_value_set_uint64 (value, filter->decay_peak_ttl);
313       break;
314     case PROP_PEAK_FALLOFF:
315       g_value_set_double (value, filter->decay_peak_falloff);
316       break;
317     default:
318       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
319       break;
320   }
321 }
322
323
324 /* process one (interleaved) channel of incoming samples
325  * calculate square sum of samples
326  * normalize and average over number of samples
327  * returns a normalized cumulative square value, which can be averaged
328  * to return the average power as a double between 0 and 1
329  * also returns the normalized peak power (square of the highest amplitude)
330  *
331  * caller must assure num is a multiple of channels
332  * samples for multiple channels are interleaved
333  * input sample data enters in *in_data as 8 or 16 bit data
334  * this filter only accepts signed audio data, so mid level is always 0
335  *
336  * for 16 bit, this code considers the non-existant 32768 value to be
337  * full-scale; so 32767 will not map to 1.0
338  */
339
340 #define DEFINE_INT_LEVEL_CALCULATOR(TYPE, RESOLUTION)                         \
341 static void inline                                                            \
342 gst_level_calculate_##TYPE (gpointer data, guint num, guint channels,         \
343                             gdouble *NCS, gdouble *NPS)                       \
344 {                                                                             \
345   TYPE * in = (TYPE *)data;                                                   \
346   register guint j;                                                           \
347   gdouble squaresum = 0.0;           /* square sum of the integer samples */  \
348   register gdouble square = 0.0;     /* Square */                             \
349   register gdouble peaksquare = 0.0; /* Peak Square Sample */                 \
350   gdouble normalizer;               /* divisor to get a [-1.0, 1.0] range */  \
351                                                                               \
352   /* *NCS = 0.0; Normalized Cumulative Square */                              \
353   /* *NPS = 0.0; Normalized Peask Square */                                   \
354                                                                               \
355   normalizer = (gdouble) (G_GINT64_CONSTANT(1) << (RESOLUTION * 2));          \
356                                                                               \
357   /* oil_squaresum_shifted_s16(&squaresum,in,num); */                         \
358   for (j = 0; j < num; j += channels)                                         \
359   {                                                                           \
360     square = ((gdouble) in[j]) * in[j];                                       \
361     if (square > peaksquare) peaksquare = square;                             \
362     squaresum += square;                                                      \
363   }                                                                           \
364                                                                               \
365   *NCS = squaresum / normalizer;                                              \
366   *NPS = peaksquare / normalizer;                                             \
367 }
368
369 DEFINE_INT_LEVEL_CALCULATOR (gint32, 31);
370 DEFINE_INT_LEVEL_CALCULATOR (gint16, 15);
371 DEFINE_INT_LEVEL_CALCULATOR (gint8, 7);
372
373 #define DEFINE_FLOAT_LEVEL_CALCULATOR(TYPE)                                   \
374 static void inline                                                            \
375 gst_level_calculate_##TYPE (gpointer data, guint num, guint channels,         \
376                             gdouble *NCS, gdouble *NPS)                       \
377 {                                                                             \
378   TYPE * in = (TYPE *)data;                                                   \
379   register guint j;                                                           \
380   gdouble squaresum = 0.0;           /* square sum of the integer samples */  \
381   register gdouble square = 0.0;     /* Square */                             \
382   register gdouble peaksquare = 0.0; /* Peak Square Sample */                 \
383                                                                               \
384   /* *NCS = 0.0; Normalized Cumulative Square */                              \
385   /* *NPS = 0.0; Normalized Peask Square */                                   \
386                                                                               \
387   /* oil_squaresum_f64(&squaresum,in,num); */                                 \
388   for (j = 0; j < num; j += channels)                                         \
389   {                                                                           \
390     square = ((gdouble) in[j]) * in[j];                                       \
391     if (square > peaksquare) peaksquare = square;                             \
392     squaresum += square;                                                      \
393   }                                                                           \
394                                                                               \
395   *NCS = squaresum;                                                           \
396   *NPS = peaksquare;                                                          \
397 }
398
399 DEFINE_FLOAT_LEVEL_CALCULATOR (gfloat);
400 DEFINE_FLOAT_LEVEL_CALCULATOR (gdouble);
401
402 /* we would need stride to deinterleave also
403 static void inline
404 gst_level_calculate_gdouble (gpointer data, guint num, guint channels,
405                             gdouble *NCS, gdouble *NPS)
406 {
407   oil_squaresum_f64(NCS,(gdouble *)data,num);
408   *NPS = 0.0;
409 }
410 */
411
412
413 static gint
414 structure_get_int (GstStructure * structure, const gchar * field)
415 {
416   gint ret;
417
418   if (!gst_structure_get_int (structure, field, &ret))
419     g_assert_not_reached ();
420
421   return ret;
422 }
423
424 static gboolean
425 gst_level_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
426 {
427   GstLevel *filter = GST_LEVEL (trans);
428   const gchar *mimetype;
429   GstStructure *structure;
430   gint i;
431
432   structure = gst_caps_get_structure (in, 0);
433   filter->rate = structure_get_int (structure, "rate");
434   filter->width = structure_get_int (structure, "width");
435   filter->channels = structure_get_int (structure, "channels");
436   mimetype = gst_structure_get_name (structure);
437
438   /* FIXME: set calculator func depending on caps */
439   filter->process = NULL;
440   if (strcmp (mimetype, "audio/x-raw-int") == 0) {
441     GST_DEBUG_OBJECT (filter, "use int: %u", filter->width);
442     switch (filter->width) {
443       case 8:
444         filter->process = gst_level_calculate_gint8;
445         break;
446       case 16:
447         filter->process = gst_level_calculate_gint16;
448         break;
449       case 32:
450         filter->process = gst_level_calculate_gint32;
451         break;
452     }
453   } else if (strcmp (mimetype, "audio/x-raw-float") == 0) {
454     GST_DEBUG_OBJECT (filter, "use float, %u", filter->width);
455     switch (filter->width) {
456       case 32:
457         filter->process = gst_level_calculate_gfloat;
458         break;
459       case 64:
460         filter->process = gst_level_calculate_gdouble;
461         break;
462     }
463   }
464
465   /* allocate channel variable arrays */
466   g_free (filter->CS);
467   g_free (filter->peak);
468   g_free (filter->last_peak);
469   g_free (filter->decay_peak);
470   g_free (filter->decay_peak_base);
471   g_free (filter->decay_peak_age);
472   filter->CS = g_new (gdouble, filter->channels);
473   filter->peak = g_new (gdouble, filter->channels);
474   filter->last_peak = g_new (gdouble, filter->channels);
475   filter->decay_peak = g_new (gdouble, filter->channels);
476   filter->decay_peak_base = g_new (gdouble, filter->channels);
477
478   filter->decay_peak_age = g_new (GstClockTime, filter->channels);
479
480   for (i = 0; i < filter->channels; ++i) {
481     filter->CS[i] = filter->peak[i] = filter->last_peak[i] =
482         filter->decay_peak[i] = filter->decay_peak_base[i] = 0.0;
483     filter->decay_peak_age[i] = G_GINT64_CONSTANT (0);
484   }
485
486   return TRUE;
487 }
488
489 static gboolean
490 gst_level_start (GstBaseTransform * trans)
491 {
492   GstLevel *filter = GST_LEVEL (trans);
493
494   filter->num_frames = 0;
495
496   return TRUE;
497 }
498
499 static GstMessage *
500 gst_level_message_new (GstLevel * level, GstClockTime timestamp,
501     GstClockTime duration)
502 {
503   GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (level);
504   GstStructure *s;
505   GValue v = { 0, };
506   GstClockTime endtime, running_time, stream_time;
507
508   g_value_init (&v, GST_TYPE_LIST);
509
510   running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
511       timestamp);
512   stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
513       timestamp);
514   /* endtime is for backwards compatibility */
515   endtime = stream_time + duration;
516
517   s = gst_structure_new ("level",
518       "endtime", GST_TYPE_CLOCK_TIME, endtime,
519       "timestamp", G_TYPE_UINT64, timestamp,
520       "stream-time", G_TYPE_UINT64, stream_time,
521       "running-time", G_TYPE_UINT64, running_time,
522       "duration", G_TYPE_UINT64, duration, NULL);
523   /* will copy-by-value */
524   gst_structure_set_value (s, "rms", &v);
525   gst_structure_set_value (s, "peak", &v);
526   gst_structure_set_value (s, "decay", &v);
527
528   g_value_unset (&v);
529
530   return gst_message_new_element (GST_OBJECT (level), s);
531 }
532
533 static void
534 gst_level_message_append_channel (GstMessage * m, gdouble rms, gdouble peak,
535     gdouble decay)
536 {
537   GstStructure *s;
538   GValue v = { 0, };
539   GValue *l;
540
541   g_value_init (&v, G_TYPE_DOUBLE);
542
543   s = (GstStructure *) gst_message_get_structure (m);
544
545   l = (GValue *) gst_structure_get_value (s, "rms");
546   g_value_set_double (&v, rms);
547   gst_value_list_append_value (l, &v);  /* copies by value */
548
549   l = (GValue *) gst_structure_get_value (s, "peak");
550   g_value_set_double (&v, peak);
551   gst_value_list_append_value (l, &v);  /* copies by value */
552
553   l = (GValue *) gst_structure_get_value (s, "decay");
554   g_value_set_double (&v, decay);
555   gst_value_list_append_value (l, &v);  /* copies by value */
556
557   g_value_unset (&v);
558 }
559
560 static GstFlowReturn
561 gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
562 {
563   GstLevel *filter;
564   guint8 *in_data;
565   gdouble CS;
566   guint i;
567   guint num_frames = 0;
568   guint num_int_samples = 0;    /* number of interleaved samples
569                                  * ie. total count for all channels combined */
570   GstClockTimeDiff falloff_time;
571
572   filter = GST_LEVEL (trans);
573
574   in_data = GST_BUFFER_DATA (in);
575   num_int_samples = GST_BUFFER_SIZE (in) / (filter->width / 8);
576
577   GST_LOG_OBJECT (filter, "analyzing %u sample frames at ts %" GST_TIME_FORMAT,
578       num_int_samples, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (in)));
579
580   g_return_val_if_fail (num_int_samples % filter->channels == 0,
581       GST_FLOW_ERROR);
582
583   num_frames = num_int_samples / filter->channels;
584
585   for (i = 0; i < filter->channels; ++i) {
586     if (!GST_BUFFER_FLAG_IS_SET (in, GST_BUFFER_FLAG_GAP)) {
587       filter->process (in_data, num_int_samples, filter->channels, &CS,
588           &filter->peak[i]);
589       GST_LOG_OBJECT (filter,
590           "channel %d, cumulative sum %f, peak %f, over %d samples/%d channels",
591           i, CS, filter->peak[i], num_int_samples, filter->channels);
592       filter->CS[i] += CS;
593     } else {
594       filter->peak[i] = 0.0;
595     }
596     in_data += (filter->width / 8);
597
598     filter->decay_peak_age[i] +=
599         GST_FRAMES_TO_CLOCK_TIME (num_frames, filter->rate);
600     GST_LOG_OBJECT (filter, "filter peak info [%d]: decay peak %f, age %"
601         GST_TIME_FORMAT, i,
602         filter->decay_peak[i], GST_TIME_ARGS (filter->decay_peak_age[i]));
603
604     /* update running peak */
605     if (filter->peak[i] > filter->last_peak[i])
606       filter->last_peak[i] = filter->peak[i];
607
608     /* make decay peak fall off if too old */
609     falloff_time =
610         GST_CLOCK_DIFF (gst_gdouble_to_guint64 (filter->decay_peak_ttl),
611         filter->decay_peak_age[i]);
612     if (falloff_time > 0) {
613       gdouble falloff_dB;
614       gdouble falloff;
615       gdouble length;           /* length of falloff time in seconds */
616
617       length = (gdouble) (falloff_time / GST_SECOND);
618       falloff_dB = filter->decay_peak_falloff * length;
619       falloff = pow (10, falloff_dB / -20.0);
620
621       GST_LOG_OBJECT (filter,
622           "falloff: current %f, base %f, interval %" GST_TIME_FORMAT
623           ", dB falloff %f, factor %e",
624           filter->decay_peak[i], filter->decay_peak_base[i],
625           GST_TIME_ARGS (falloff_time), falloff_dB, falloff);
626       filter->decay_peak[i] = filter->decay_peak_base[i] * falloff;
627       GST_LOG_OBJECT (filter,
628           "peak is %" GST_TIME_FORMAT " old, decayed with factor %e to %f",
629           GST_TIME_ARGS (filter->decay_peak_age[i]), falloff,
630           filter->decay_peak[i]);
631     } else {
632       GST_LOG_OBJECT (filter, "peak not old enough, not decaying");
633     }
634
635     /* if the peak of this run is higher, the decay peak gets reset */
636     if (filter->peak[i] >= filter->decay_peak[i]) {
637       GST_LOG_OBJECT (filter, "new peak, %f", filter->peak[i]);
638       filter->decay_peak[i] = filter->peak[i];
639       filter->decay_peak_base[i] = filter->peak[i];
640       filter->decay_peak_age[i] = G_GINT64_CONSTANT (0);
641     }
642   }
643
644   if (G_UNLIKELY (!filter->num_frames)) {
645     /* remember start timestamp for message */
646     filter->message_ts = GST_BUFFER_TIMESTAMP (in);
647   }
648   filter->num_frames += num_frames;
649
650   /* do we need to message ? */
651   if (filter->num_frames >=
652       GST_CLOCK_TIME_TO_FRAMES (filter->interval, filter->rate)) {
653     if (filter->message) {
654       GstMessage *m;
655       GstClockTime duration =
656           GST_FRAMES_TO_CLOCK_TIME (filter->num_frames, filter->rate);
657
658       m = gst_level_message_new (filter, filter->message_ts, duration);
659
660       GST_LOG_OBJECT (filter,
661           "message: ts %" GST_TIME_FORMAT ", num_frames %d",
662           GST_TIME_ARGS (filter->message_ts), filter->num_frames);
663
664       for (i = 0; i < filter->channels; ++i) {
665         gdouble RMS;
666         gdouble RMSdB, lastdB, decaydB;
667
668         RMS = sqrt (filter->CS[i] / filter->num_frames);
669         GST_LOG_OBJECT (filter,
670             "message: channel %d, CS %f, num_frames %d, RMS %f",
671             i, filter->CS[i], filter->num_frames, RMS);
672         GST_LOG_OBJECT (filter,
673             "message: last_peak: %f, decay_peak: %f",
674             filter->last_peak[i], filter->decay_peak[i]);
675         /* RMS values are calculated in amplitude, so 20 * log 10 */
676         RMSdB = 20 * log10 (RMS);
677         /* peak values are square sums, ie. power, so 10 * log 10 */
678         lastdB = 10 * log10 (filter->last_peak[i]);
679         decaydB = 10 * log10 (filter->decay_peak[i]);
680
681         if (filter->decay_peak[i] < filter->last_peak[i]) {
682           /* this can happen in certain cases, for example when
683            * the last peak is between decay_peak and decay_peak_base */
684           GST_DEBUG_OBJECT (filter,
685               "message: decay peak dB %f smaller than last peak dB %f, copying",
686               decaydB, lastdB);
687           filter->decay_peak[i] = filter->last_peak[i];
688         }
689         GST_LOG_OBJECT (filter,
690             "message: RMS %f dB, peak %f dB, decay %f dB",
691             RMSdB, lastdB, decaydB);
692
693         gst_level_message_append_channel (m, RMSdB, lastdB, decaydB);
694
695         /* reset cumulative and normal peak */
696         filter->CS[i] = 0.0;
697         filter->last_peak[i] = 0.0;
698       }
699
700       gst_element_post_message (GST_ELEMENT (filter), m);
701     }
702     filter->num_frames = 0;
703   }
704
705   return GST_FLOW_OK;
706 }
707
708 static gboolean
709 plugin_init (GstPlugin * plugin)
710 {
711   /*oil_init (); */
712
713   return gst_element_register (plugin, "level", GST_RANK_NONE, GST_TYPE_LEVEL);
714 }
715
716 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
717     GST_VERSION_MINOR,
718     "level",
719     "Audio level plugin",
720     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);