59943d488e3e7506bd3273a72c43abb83217de3d
[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., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, 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  *   #GValueArray 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  *   #GValueArray 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  *   #GValueArray 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
108 /* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
109  * with newer GLib versions (>= 2.31.0) */
110 #define GLIB_DISABLE_DEPRECATION_WARNINGS
111
112 #include <string.h>
113 #include <math.h>
114 #include <gst/gst.h>
115 #include <gst/audio/audio.h>
116
117 #include "gstlevel.h"
118
119 GST_DEBUG_CATEGORY_STATIC (level_debug);
120 #define GST_CAT_DEFAULT level_debug
121
122 #define EPSILON 1e-35f
123
124 static GstStaticPadTemplate sink_template_factory =
125 GST_STATIC_PAD_TEMPLATE ("sink",
126     GST_PAD_SINK,
127     GST_PAD_ALWAYS,
128     GST_STATIC_CAPS ("audio/x-raw, "
129         "format = (string) { S8, " GST_AUDIO_NE (S16) ", " GST_AUDIO_NE (S32)
130         ", " GST_AUDIO_NE (F32) "," GST_AUDIO_NE (F64) " },"
131         "layout = (string) interleaved, "
132         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]")
133     );
134
135 static GstStaticPadTemplate src_template_factory =
136 GST_STATIC_PAD_TEMPLATE ("src",
137     GST_PAD_SRC,
138     GST_PAD_ALWAYS,
139     GST_STATIC_CAPS ("audio/x-raw, "
140         "format = (string) { S8, " GST_AUDIO_NE (S16) ", " GST_AUDIO_NE (S32)
141         ", " GST_AUDIO_NE (F32) "," GST_AUDIO_NE (F64) " },"
142         "layout = (string) interleaved, "
143         "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]")
144     );
145
146 enum
147 {
148   PROP_0,
149   PROP_POST_MESSAGES,
150   PROP_MESSAGE,
151   PROP_INTERVAL,
152   PROP_PEAK_TTL,
153   PROP_PEAK_FALLOFF
154 };
155
156 #define gst_level_parent_class parent_class
157 G_DEFINE_TYPE (GstLevel, gst_level, GST_TYPE_BASE_TRANSFORM);
158
159 static void gst_level_set_property (GObject * object, guint prop_id,
160     const GValue * value, GParamSpec * pspec);
161 static void gst_level_get_property (GObject * object, guint prop_id,
162     GValue * value, GParamSpec * pspec);
163 static void gst_level_finalize (GObject * obj);
164
165 static gboolean gst_level_set_caps (GstBaseTransform * trans, GstCaps * in,
166     GstCaps * out);
167 static gboolean gst_level_start (GstBaseTransform * trans);
168 static GstFlowReturn gst_level_transform_ip (GstBaseTransform * trans,
169     GstBuffer * in);
170 static void gst_level_post_message (GstLevel * filter);
171 static gboolean gst_level_sink_event (GstBaseTransform * trans,
172     GstEvent * event);
173
174
175 static void
176 gst_level_class_init (GstLevelClass * klass)
177 {
178   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
179   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
180   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
181
182   gobject_class->set_property = gst_level_set_property;
183   gobject_class->get_property = gst_level_get_property;
184   gobject_class->finalize = gst_level_finalize;
185
186   /**
187    * GstLevel:post-messages
188    *
189    * Post messages on the bus with level information.
190    *
191    * Since: 1.1.0
192    */
193   g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
194       g_param_spec_boolean ("post-messages", "Post Messages",
195           "Whether to post a 'level' element message on the bus for each "
196           "passed interval", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
197   /* FIXME(2.0): remove this property */
198   /**
199    * GstLevel:post-messages
200    *
201    * Post messages on the bus with level information.
202    *
203    * Deprecated: use the #GstLevel:post-messages property
204    */
205   g_object_class_install_property (gobject_class, PROP_MESSAGE,
206       g_param_spec_boolean ("message", "message",
207           "Post a 'level' message for each passed interval (deprecated)",
208           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
209   g_object_class_install_property (gobject_class, PROP_INTERVAL,
210       g_param_spec_uint64 ("interval", "Interval",
211           "Interval of time between message posts (in nanoseconds)",
212           1, G_MAXUINT64, GST_SECOND / 10,
213           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
214   g_object_class_install_property (gobject_class, PROP_PEAK_TTL,
215       g_param_spec_uint64 ("peak-ttl", "Peak TTL",
216           "Time To Live of decay peak before it falls back (in nanoseconds)",
217           0, G_MAXUINT64, GST_SECOND / 10 * 3,
218           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
219   g_object_class_install_property (gobject_class, PROP_PEAK_FALLOFF,
220       g_param_spec_double ("peak-falloff", "Peak Falloff",
221           "Decay rate of decay peak after TTL (in dB/sec)",
222           0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
223
224   GST_DEBUG_CATEGORY_INIT (level_debug, "level", 0, "Level calculation");
225
226   gst_element_class_add_pad_template (element_class,
227       gst_static_pad_template_get (&sink_template_factory));
228   gst_element_class_add_pad_template (element_class,
229       gst_static_pad_template_get (&src_template_factory));
230   gst_element_class_set_static_metadata (element_class, "Level",
231       "Filter/Analyzer/Audio",
232       "RMS/Peak/Decaying Peak Level messager for audio/raw",
233       "Thomas Vander Stichele <thomas at apestaart dot org>");
234
235   trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_level_set_caps);
236   trans_class->start = GST_DEBUG_FUNCPTR (gst_level_start);
237   trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_level_transform_ip);
238   trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_level_sink_event);
239   trans_class->passthrough_on_same_caps = TRUE;
240 }
241
242 static void
243 gst_level_init (GstLevel * filter)
244 {
245   filter->CS = NULL;
246   filter->peak = NULL;
247   filter->last_peak = NULL;
248   filter->decay_peak = NULL;
249   filter->decay_peak_base = NULL;
250   filter->decay_peak_age = NULL;
251
252   gst_audio_info_init (&filter->info);
253
254   filter->interval = GST_SECOND / 10;
255   filter->decay_peak_ttl = GST_SECOND / 10 * 3;
256   filter->decay_peak_falloff = 10.0;    /* dB falloff (/sec) */
257
258   filter->post_messages = TRUE;
259
260   filter->process = NULL;
261
262   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
263 }
264
265 static void
266 gst_level_finalize (GObject * obj)
267 {
268   GstLevel *filter = GST_LEVEL (obj);
269
270   g_free (filter->CS);
271   g_free (filter->peak);
272   g_free (filter->last_peak);
273   g_free (filter->decay_peak);
274   g_free (filter->decay_peak_base);
275   g_free (filter->decay_peak_age);
276
277   filter->CS = NULL;
278   filter->peak = NULL;
279   filter->last_peak = NULL;
280   filter->decay_peak = NULL;
281   filter->decay_peak_base = NULL;
282   filter->decay_peak_age = NULL;
283
284   G_OBJECT_CLASS (parent_class)->finalize (obj);
285 }
286
287 static void
288 gst_level_set_property (GObject * object, guint prop_id,
289     const GValue * value, GParamSpec * pspec)
290 {
291   GstLevel *filter = GST_LEVEL (object);
292
293   switch (prop_id) {
294     case PROP_POST_MESSAGES:
295       /* fall-through */
296     case PROP_MESSAGE:
297       filter->post_messages = g_value_get_boolean (value);
298       break;
299     case PROP_INTERVAL:
300       filter->interval = g_value_get_uint64 (value);
301       if (GST_AUDIO_INFO_RATE (&filter->info)) {
302         filter->interval_frames =
303             GST_CLOCK_TIME_TO_FRAMES (filter->interval,
304             GST_AUDIO_INFO_RATE (&filter->info));
305       }
306       break;
307     case PROP_PEAK_TTL:
308       filter->decay_peak_ttl =
309           gst_guint64_to_gdouble (g_value_get_uint64 (value));
310       break;
311     case PROP_PEAK_FALLOFF:
312       filter->decay_peak_falloff = g_value_get_double (value);
313       break;
314     default:
315       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
316       break;
317   }
318 }
319
320 static void
321 gst_level_get_property (GObject * object, guint prop_id,
322     GValue * value, GParamSpec * pspec)
323 {
324   GstLevel *filter = GST_LEVEL (object);
325
326   switch (prop_id) {
327     case PROP_POST_MESSAGES:
328       /* fall-through */
329     case PROP_MESSAGE:
330       g_value_set_boolean (value, filter->post_messages);
331       break;
332     case PROP_INTERVAL:
333       g_value_set_uint64 (value, filter->interval);
334       break;
335     case PROP_PEAK_TTL:
336       g_value_set_uint64 (value, filter->decay_peak_ttl);
337       break;
338     case PROP_PEAK_FALLOFF:
339       g_value_set_double (value, filter->decay_peak_falloff);
340       break;
341     default:
342       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
343       break;
344   }
345 }
346
347
348 /* process one (interleaved) channel of incoming samples
349  * calculate square sum of samples
350  * normalize and average over number of samples
351  * returns a normalized cumulative square value, which can be averaged
352  * to return the average power as a double between 0 and 1
353  * also returns the normalized peak power (square of the highest amplitude)
354  *
355  * caller must assure num is a multiple of channels
356  * samples for multiple channels are interleaved
357  * input sample data enters in *in_data and is not modified
358  * this filter only accepts signed audio data, so mid level is always 0
359  *
360  * for integers, this code considers the non-existant positive max value to be
361  * full-scale; so max-1 will not map to 1.0
362  */
363
364 #define DEFINE_INT_LEVEL_CALCULATOR(TYPE, RESOLUTION)                         \
365 static void inline                                                            \
366 gst_level_calculate_##TYPE (gpointer data, guint num, guint channels,         \
367                             gdouble *NCS, gdouble *NPS)                       \
368 {                                                                             \
369   TYPE * in = (TYPE *)data;                                                   \
370   register guint j;                                                           \
371   gdouble squaresum = 0.0;           /* square sum of the input samples */    \
372   register gdouble square = 0.0;     /* Square */                             \
373   register gdouble peaksquare = 0.0; /* Peak Square Sample */                 \
374   gdouble normalizer;                /* divisor to get a [-1.0, 1.0] range */ \
375                                                                               \
376   /* *NCS = 0.0; Normalized Cumulative Square */                              \
377   /* *NPS = 0.0; Normalized Peak Square */                                    \
378                                                                               \
379   for (j = 0; j < num; j += channels) {                                       \
380     square = ((gdouble) in[j]) * in[j];                                       \
381     if (square > peaksquare) peaksquare = square;                             \
382     squaresum += square;                                                      \
383   }                                                                           \
384                                                                               \
385   normalizer = (gdouble) (G_GINT64_CONSTANT(1) << (RESOLUTION * 2));          \
386   *NCS = squaresum / normalizer;                                              \
387   *NPS = peaksquare / normalizer;                                             \
388 }
389
390 DEFINE_INT_LEVEL_CALCULATOR (gint32, 31);
391 DEFINE_INT_LEVEL_CALCULATOR (gint16, 15);
392 DEFINE_INT_LEVEL_CALCULATOR (gint8, 7);
393
394 /* FIXME: use orc to calculate squaresums? */
395 #define DEFINE_FLOAT_LEVEL_CALCULATOR(TYPE)                                   \
396 static void inline                                                            \
397 gst_level_calculate_##TYPE (gpointer data, guint num, guint channels,         \
398                             gdouble *NCS, gdouble *NPS)                       \
399 {                                                                             \
400   TYPE * in = (TYPE *)data;                                                   \
401   register guint j;                                                           \
402   gdouble squaresum = 0.0;           /* square sum of the input samples */    \
403   register gdouble square = 0.0;     /* Square */                             \
404   register gdouble peaksquare = 0.0; /* Peak Square Sample */                 \
405                                                                               \
406   /* *NCS = 0.0; Normalized Cumulative Square */                              \
407   /* *NPS = 0.0; Normalized Peak Square */                                    \
408                                                                               \
409   /* orc_level_squaresum_f64(&squaresum,in,num); */                           \
410   for (j = 0; j < num; j += channels) {                                       \
411     square = ((gdouble) in[j]) * in[j];                                       \
412     if (square > peaksquare) peaksquare = square;                             \
413     squaresum += square;                                                      \
414   }                                                                           \
415                                                                               \
416   *NCS = squaresum;                                                           \
417   *NPS = peaksquare;                                                          \
418 }
419
420 DEFINE_FLOAT_LEVEL_CALCULATOR (gfloat);
421 DEFINE_FLOAT_LEVEL_CALCULATOR (gdouble);
422
423 /* we would need stride to deinterleave also
424 static void inline
425 gst_level_calculate_gdouble (gpointer data, guint num, guint channels,
426                             gdouble *NCS, gdouble *NPS)
427 {
428   orc_level_squaresum_f64(NCS,(gdouble *)data,num);
429   *NPS = 0.0;
430 }
431 */
432
433
434 static gboolean
435 gst_level_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
436 {
437   GstLevel *filter = GST_LEVEL (trans);
438   GstAudioInfo info;
439   gint i, channels, rate;
440
441   if (!gst_audio_info_from_caps (&info, in))
442     return FALSE;
443
444   switch (GST_AUDIO_INFO_FORMAT (&info)) {
445     case GST_AUDIO_FORMAT_S8:
446       filter->process = gst_level_calculate_gint8;
447       break;
448     case GST_AUDIO_FORMAT_S16:
449       filter->process = gst_level_calculate_gint16;
450       break;
451     case GST_AUDIO_FORMAT_S32:
452       filter->process = gst_level_calculate_gint32;
453       break;
454     case GST_AUDIO_FORMAT_F32:
455       filter->process = gst_level_calculate_gfloat;
456       break;
457     case GST_AUDIO_FORMAT_F64:
458       filter->process = gst_level_calculate_gdouble;
459       break;
460     default:
461       filter->process = NULL;
462       break;
463   }
464
465   filter->info = info;
466
467   channels = GST_AUDIO_INFO_CHANNELS (&info);
468   rate = GST_AUDIO_INFO_RATE (&info);
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, channels);
478   filter->peak = g_new (gdouble, channels);
479   filter->last_peak = g_new (gdouble, channels);
480   filter->decay_peak = g_new (gdouble, channels);
481   filter->decay_peak_base = g_new (gdouble, channels);
482
483   filter->decay_peak_age = g_new (GstClockTime, channels);
484
485   for (i = 0; i < 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 = GST_CLOCK_TIME_TO_FRAMES (filter->interval, rate);
492
493   return TRUE;
494 }
495
496 static gboolean
497 gst_level_start (GstBaseTransform * trans)
498 {
499   GstLevel *filter = GST_LEVEL (trans);
500
501   filter->num_frames = 0;
502   filter->message_ts = GST_CLOCK_TIME_NONE;
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   running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
517       timestamp);
518   stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
519       timestamp);
520   /* endtime is for backwards compatibility */
521   endtime = stream_time + duration;
522
523   s = gst_structure_new ("level",
524       "endtime", GST_TYPE_CLOCK_TIME, endtime,
525       "timestamp", G_TYPE_UINT64, timestamp,
526       "stream-time", G_TYPE_UINT64, stream_time,
527       "running-time", G_TYPE_UINT64, running_time,
528       "duration", G_TYPE_UINT64, duration, NULL);
529
530   g_value_init (&v, G_TYPE_VALUE_ARRAY);
531   g_value_take_boxed (&v, g_value_array_new (0));
532   gst_structure_take_value (s, "rms", &v);
533
534   g_value_init (&v, G_TYPE_VALUE_ARRAY);
535   g_value_take_boxed (&v, g_value_array_new (0));
536   gst_structure_take_value (s, "peak", &v);
537
538   g_value_init (&v, G_TYPE_VALUE_ARRAY);
539   g_value_take_boxed (&v, g_value_array_new (0));
540   gst_structure_take_value (s, "decay", &v);
541
542   return gst_message_new_element (GST_OBJECT (level), s);
543 }
544
545 static void
546 gst_level_message_append_channel (GstMessage * m, gdouble rms, gdouble peak,
547     gdouble decay)
548 {
549   const GValue *array_val;
550   GstStructure *s;
551   GValueArray *arr;
552   GValue v = { 0, };
553
554   g_value_init (&v, G_TYPE_DOUBLE);
555
556   s = (GstStructure *) gst_message_get_structure (m);
557
558   array_val = gst_structure_get_value (s, "rms");
559   arr = (GValueArray *) g_value_get_boxed (array_val);
560   g_value_set_double (&v, rms);
561   g_value_array_append (arr, &v);       /* copies by value */
562
563   array_val = gst_structure_get_value (s, "peak");
564   arr = (GValueArray *) g_value_get_boxed (array_val);
565   g_value_set_double (&v, peak);
566   g_value_array_append (arr, &v);       /* copies by value */
567
568   array_val = gst_structure_get_value (s, "decay");
569   arr = (GValueArray *) g_value_get_boxed (array_val);
570   g_value_set_double (&v, decay);
571   g_value_array_append (arr, &v);       /* copies by value */
572
573   g_value_unset (&v);
574 }
575
576 static GstFlowReturn
577 gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
578 {
579   GstLevel *filter;
580   GstMapInfo map;
581   guint8 *in_data;
582   gsize in_size;
583   gdouble CS;
584   guint i;
585   guint num_frames;
586   guint num_int_samples = 0;    /* number of interleaved samples
587                                  * ie. total count for all channels combined */
588   guint block_size, block_int_size;     /* we subdivide buffers to not skip message
589                                          * intervals */
590   GstClockTimeDiff falloff_time;
591   gint channels, rate, bps;
592
593   filter = GST_LEVEL (trans);
594
595   channels = GST_AUDIO_INFO_CHANNELS (&filter->info);
596   bps = GST_AUDIO_INFO_BPS (&filter->info);
597   rate = GST_AUDIO_INFO_RATE (&filter->info);
598
599   gst_buffer_map (in, &map, GST_MAP_READ);
600   in_data = map.data;
601   in_size = map.size;
602
603   num_int_samples = in_size / bps;
604
605   GST_LOG_OBJECT (filter, "analyzing %u sample frames at ts %" GST_TIME_FORMAT,
606       num_int_samples, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (in)));
607
608   g_return_val_if_fail (num_int_samples % channels == 0, GST_FLOW_ERROR);
609
610   if (GST_BUFFER_FLAG_IS_SET (in, GST_BUFFER_FLAG_DISCONT)) {
611     filter->message_ts = GST_BUFFER_TIMESTAMP (in);
612     filter->num_frames = 0;
613   }
614   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (filter->message_ts))) {
615     filter->message_ts = GST_BUFFER_TIMESTAMP (in);
616   }
617
618   num_frames = num_int_samples / channels;
619   while (num_frames > 0) {
620     block_size = filter->interval_frames - filter->num_frames;
621     block_size = MIN (block_size, num_frames);
622     block_int_size = block_size * channels;
623
624     for (i = 0; i < channels; ++i) {
625       if (!GST_BUFFER_FLAG_IS_SET (in, GST_BUFFER_FLAG_GAP)) {
626         filter->process (in_data, block_int_size, channels, &CS,
627             &filter->peak[i]);
628         GST_LOG_OBJECT (filter,
629             "[%d]: cumulative squares %lf, over %d samples/%d channels",
630             i, CS, block_int_size, channels);
631         filter->CS[i] += CS;
632       } else {
633         filter->peak[i] = 0.0;
634       }
635       in_data += bps;
636
637       filter->decay_peak_age[i] += GST_FRAMES_TO_CLOCK_TIME (num_frames, rate);
638       GST_LOG_OBJECT (filter,
639           "[%d]: peak %f, last peak %f, decay peak %f, age %" GST_TIME_FORMAT,
640           i, filter->peak[i], filter->last_peak[i], filter->decay_peak[i],
641           GST_TIME_ARGS (filter->decay_peak_age[i]));
642
643       /* update running peak */
644       if (filter->peak[i] > filter->last_peak[i])
645         filter->last_peak[i] = filter->peak[i];
646
647       /* make decay peak fall off if too old */
648       falloff_time =
649           GST_CLOCK_DIFF (gst_gdouble_to_guint64 (filter->decay_peak_ttl),
650           filter->decay_peak_age[i]);
651       if (falloff_time > 0) {
652         gdouble falloff_dB;
653         gdouble falloff;
654         gdouble length;         /* length of falloff time in seconds */
655
656         length = (gdouble) falloff_time / (gdouble) GST_SECOND;
657         falloff_dB = filter->decay_peak_falloff * length;
658         falloff = pow (10, falloff_dB / -20.0);
659
660         GST_LOG_OBJECT (filter,
661             "falloff: current %f, base %f, interval %" GST_TIME_FORMAT
662             ", dB falloff %f, factor %e",
663             filter->decay_peak[i], filter->decay_peak_base[i],
664             GST_TIME_ARGS (falloff_time), falloff_dB, falloff);
665         filter->decay_peak[i] = filter->decay_peak_base[i] * falloff;
666         GST_LOG_OBJECT (filter,
667             "peak is %" GST_TIME_FORMAT " old, decayed with factor %e to %f",
668             GST_TIME_ARGS (filter->decay_peak_age[i]), falloff,
669             filter->decay_peak[i]);
670       } else {
671         GST_LOG_OBJECT (filter, "peak not old enough, not decaying");
672       }
673
674       /* if the peak of this run is higher, the decay peak gets reset */
675       if (filter->peak[i] >= filter->decay_peak[i]) {
676         GST_LOG_OBJECT (filter, "new peak, %f", filter->peak[i]);
677         filter->decay_peak[i] = filter->peak[i];
678         filter->decay_peak_base[i] = filter->peak[i];
679         filter->decay_peak_age[i] = G_GINT64_CONSTANT (0);
680       }
681     }
682     in_data += ((block_int_size * bps) - bps);
683
684     filter->num_frames += block_size;
685     num_frames -= block_size;
686
687     /* do we need to message ? */
688     if (filter->num_frames >= filter->interval_frames) {
689       gst_level_post_message (filter);
690     }
691   }
692
693   gst_buffer_unmap (in, &map);
694
695   return GST_FLOW_OK;
696 }
697
698 static void
699 gst_level_post_message (GstLevel * filter)
700 {
701   guint i;
702   gint channels, rate, frames = filter->num_frames;
703   GstClockTime duration;
704
705   channels = GST_AUDIO_INFO_CHANNELS (&filter->info);
706   rate = GST_AUDIO_INFO_RATE (&filter->info);
707   duration = GST_FRAMES_TO_CLOCK_TIME (frames, rate);
708
709   if (filter->post_messages) {
710     GstMessage *m =
711         gst_level_message_new (filter, filter->message_ts, duration);
712
713     GST_LOG_OBJECT (filter,
714         "message: ts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
715         ", num_frames %d", GST_TIME_ARGS (filter->message_ts),
716         GST_TIME_ARGS (duration), frames);
717
718     for (i = 0; i < channels; ++i) {
719       gdouble RMS;
720       gdouble RMSdB, peakdB, decaydB;
721
722       RMS = sqrt (filter->CS[i] / frames);
723       GST_LOG_OBJECT (filter,
724           "message: channel %d, CS %f, RMS %f", i, filter->CS[i], RMS);
725       GST_LOG_OBJECT (filter,
726           "message: last_peak: %f, decay_peak: %f",
727           filter->last_peak[i], filter->decay_peak[i]);
728       /* RMS values are calculated in amplitude, so 20 * log 10 */
729       RMSdB = 20 * log10 (RMS + EPSILON);
730       /* peak values are square sums, ie. power, so 10 * log 10 */
731       peakdB = 10 * log10 (filter->last_peak[i] + EPSILON);
732       decaydB = 10 * log10 (filter->decay_peak[i] + EPSILON);
733
734       if (filter->decay_peak[i] < filter->last_peak[i]) {
735         /* this can happen in certain cases, for example when
736          * the last peak is between decay_peak and decay_peak_base */
737         GST_DEBUG_OBJECT (filter,
738             "message: decay peak dB %f smaller than last peak dB %f, copying",
739             decaydB, peakdB);
740         filter->decay_peak[i] = filter->last_peak[i];
741       }
742       GST_LOG_OBJECT (filter,
743           "message: RMS %f dB, peak %f dB, decay %f dB",
744           RMSdB, peakdB, decaydB);
745
746       gst_level_message_append_channel (m, RMSdB, peakdB, decaydB);
747
748       /* reset cumulative and normal peak */
749       filter->CS[i] = 0.0;
750       filter->last_peak[i] = 0.0;
751     }
752
753     gst_element_post_message (GST_ELEMENT (filter), m);
754
755   }
756   filter->num_frames -= frames;
757   filter->message_ts += duration;
758 }
759
760
761 static gboolean
762 gst_level_sink_event (GstBaseTransform * trans, GstEvent * event)
763 {
764   if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
765     GstLevel *filter = GST_LEVEL (trans);
766
767     gst_level_post_message (filter);
768   }
769
770   return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
771 }
772
773 static gboolean
774 plugin_init (GstPlugin * plugin)
775 {
776   return gst_element_register (plugin, "level", GST_RANK_NONE, GST_TYPE_LEVEL);
777 }
778
779 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
780     GST_VERSION_MINOR,
781     level,
782     "Audio level plugin",
783     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);