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>
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.
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.
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.
23 * SECTION:element-level
25 * Level analyses incoming audio buffers and, if the #GstLevel:message property
26 * is %TRUE, generates an element message named
27 * <classname>"level"</classname>:
28 * after each interval of time given by the #GstLevel:interval property.
29 * The message's structure contains these fields:
34 * <classname>"timestamp"</classname>:
35 * the timestamp of the buffer that triggered the message.
41 * <classname>"stream-time"</classname>:
42 * the stream time of the buffer.
48 * <classname>"running-time"</classname>:
49 * the running_time of the buffer.
55 * <classname>"duration"</classname>:
56 * the duration of the buffer.
62 * <classname>"endtime"</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)
69 * #GValueArray of #gdouble
70 * <classname>"peak"</classname>:
71 * the peak power level in dB for each channel
76 * #GValueArray of #gdouble
77 * <classname>"decay"</classname>:
78 * the decaying peak power level in dB for each channel
79 * The decaying peak level follows the peak level, but starts dropping if no
80 * new peak is reached after the time given by the #GstLevel:peak-ttl.
81 * When the decaying peak level drops, it does so at the decay rate as
82 * specified by the #GstLevel:peak-falloff.
87 * #GValueArray of #gdouble
88 * <classname>"rms"</classname>:
89 * the Root Mean Square (or average power) level in dB for each channel
95 * <title>Example application</title>
96 * <informalexample><programlisting language="C">
97 * <xi:include xmlns:xi="http://www.w3.org/2003/XInclude" parse="text" href="../../../../tests/examples/level/level-example.c" />
98 * </programlisting></informalexample>
106 /* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
107 * with newer GLib versions (>= 2.31.0) */
108 #define GLIB_DISABLE_DEPRECATION_WARNINGS
113 #include <gst/audio/audio.h>
115 #include "gstlevel.h"
117 GST_DEBUG_CATEGORY_STATIC (level_debug);
118 #define GST_CAT_DEFAULT level_debug
120 #define EPSILON 1e-35f
122 static GstStaticPadTemplate sink_template_factory =
123 GST_STATIC_PAD_TEMPLATE ("sink",
126 GST_STATIC_CAPS ("audio/x-raw, "
127 "format = (string) { S8, " GST_AUDIO_NE (S16) ", " GST_AUDIO_NE (S32)
128 ", " GST_AUDIO_NE (F32) "," GST_AUDIO_NE (F64) " },"
129 "layout = (string) interleaved, "
130 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]")
133 static GstStaticPadTemplate src_template_factory =
134 GST_STATIC_PAD_TEMPLATE ("src",
137 GST_STATIC_CAPS ("audio/x-raw, "
138 "format = (string) { S8, " GST_AUDIO_NE (S16) ", " GST_AUDIO_NE (S32)
139 ", " GST_AUDIO_NE (F32) "," GST_AUDIO_NE (F64) " },"
140 "layout = (string) interleaved, "
141 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]")
154 #define gst_level_parent_class parent_class
155 G_DEFINE_TYPE (GstLevel, gst_level, GST_TYPE_BASE_TRANSFORM);
157 static void gst_level_set_property (GObject * object, guint prop_id,
158 const GValue * value, GParamSpec * pspec);
159 static void gst_level_get_property (GObject * object, guint prop_id,
160 GValue * value, GParamSpec * pspec);
161 static void gst_level_finalize (GObject * obj);
163 static gboolean gst_level_set_caps (GstBaseTransform * trans, GstCaps * in,
165 static gboolean gst_level_start (GstBaseTransform * trans);
166 static GstFlowReturn gst_level_transform_ip (GstBaseTransform * trans,
168 static void gst_level_post_message (GstLevel * filter);
169 static gboolean gst_level_sink_event (GstBaseTransform * trans,
171 static void gst_level_recalc_interval_frames (GstLevel * level);
174 gst_level_class_init (GstLevelClass * klass)
176 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
177 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
178 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
180 gobject_class->set_property = gst_level_set_property;
181 gobject_class->get_property = gst_level_get_property;
182 gobject_class->finalize = gst_level_finalize;
185 * GstLevel:post-messages
187 * Post messages on the bus with level information.
191 g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
192 g_param_spec_boolean ("post-messages", "Post Messages",
193 "Whether to post a 'level' element message on the bus for each "
194 "passed interval", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
195 /* FIXME(2.0): remove this property */
197 * GstLevel:post-messages
199 * Post messages on the bus with level information.
201 * Deprecated: use the #GstLevel:post-messages property
203 #ifndef GST_REMOVE_DEPRECATED
204 g_object_class_install_property (gobject_class, PROP_MESSAGE,
205 g_param_spec_boolean ("message", "message",
206 "Post a 'level' message for each passed interval "
207 "(deprecated, use the post-messages property instead)", TRUE,
208 G_PARAM_READWRITE | G_PARAM_DEPRECATED | G_PARAM_STATIC_STRINGS));
210 g_object_class_install_property (gobject_class, PROP_INTERVAL,
211 g_param_spec_uint64 ("interval", "Interval",
212 "Interval of time between message posts (in nanoseconds)",
213 1, G_MAXUINT64, GST_SECOND / 10,
214 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
215 g_object_class_install_property (gobject_class, PROP_PEAK_TTL,
216 g_param_spec_uint64 ("peak-ttl", "Peak TTL",
217 "Time To Live of decay peak before it falls back (in nanoseconds)",
218 0, G_MAXUINT64, GST_SECOND / 10 * 3,
219 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
220 g_object_class_install_property (gobject_class, PROP_PEAK_FALLOFF,
221 g_param_spec_double ("peak-falloff", "Peak Falloff",
222 "Decay rate of decay peak after TTL (in dB/sec)",
223 0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
225 GST_DEBUG_CATEGORY_INIT (level_debug, "level", 0, "Level calculation");
227 gst_element_class_add_static_pad_template (element_class,
228 &sink_template_factory);
229 gst_element_class_add_static_pad_template (element_class,
230 &src_template_factory);
231 gst_element_class_set_static_metadata (element_class, "Level",
232 "Filter/Analyzer/Audio",
233 "RMS/Peak/Decaying Peak Level messager for audio/raw",
234 "Thomas Vander Stichele <thomas at apestaart dot org>");
236 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_level_set_caps);
237 trans_class->start = GST_DEBUG_FUNCPTR (gst_level_start);
238 trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_level_transform_ip);
239 trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_level_sink_event);
240 trans_class->passthrough_on_same_caps = TRUE;
244 gst_level_init (GstLevel * filter)
248 filter->last_peak = NULL;
249 filter->decay_peak = NULL;
250 filter->decay_peak_base = NULL;
251 filter->decay_peak_age = NULL;
253 gst_audio_info_init (&filter->info);
255 filter->interval = GST_SECOND / 10;
256 filter->decay_peak_ttl = GST_SECOND / 10 * 3;
257 filter->decay_peak_falloff = 10.0; /* dB falloff (/sec) */
259 filter->post_messages = TRUE;
261 filter->process = NULL;
263 gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
267 gst_level_finalize (GObject * obj)
269 GstLevel *filter = GST_LEVEL (obj);
272 g_free (filter->peak);
273 g_free (filter->last_peak);
274 g_free (filter->decay_peak);
275 g_free (filter->decay_peak_base);
276 g_free (filter->decay_peak_age);
280 filter->last_peak = NULL;
281 filter->decay_peak = NULL;
282 filter->decay_peak_base = NULL;
283 filter->decay_peak_age = NULL;
285 G_OBJECT_CLASS (parent_class)->finalize (obj);
289 gst_level_set_property (GObject * object, guint prop_id,
290 const GValue * value, GParamSpec * pspec)
292 GstLevel *filter = GST_LEVEL (object);
295 case PROP_POST_MESSAGES:
298 filter->post_messages = g_value_get_boolean (value);
301 filter->interval = g_value_get_uint64 (value);
302 /* Not exactly thread-safe, but property does not advertise that it
303 * can be changed at runtime anyway */
304 if (GST_AUDIO_INFO_RATE (&filter->info)) {
305 gst_level_recalc_interval_frames (filter);
309 filter->decay_peak_ttl =
310 gst_guint64_to_gdouble (g_value_get_uint64 (value));
312 case PROP_PEAK_FALLOFF:
313 filter->decay_peak_falloff = g_value_get_double (value);
316 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
322 gst_level_get_property (GObject * object, guint prop_id,
323 GValue * value, GParamSpec * pspec)
325 GstLevel *filter = GST_LEVEL (object);
328 case PROP_POST_MESSAGES:
331 g_value_set_boolean (value, filter->post_messages);
334 g_value_set_uint64 (value, filter->interval);
337 g_value_set_uint64 (value, filter->decay_peak_ttl);
339 case PROP_PEAK_FALLOFF:
340 g_value_set_double (value, filter->decay_peak_falloff);
343 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
349 /* process one (interleaved) channel of incoming samples
350 * calculate square sum of samples
351 * normalize and average over number of samples
352 * returns a normalized cumulative square value, which can be averaged
353 * to return the average power as a double between 0 and 1
354 * also returns the normalized peak power (square of the highest amplitude)
356 * caller must assure num is a multiple of channels
357 * samples for multiple channels are interleaved
358 * input sample data enters in *in_data and is not modified
359 * this filter only accepts signed audio data, so mid level is always 0
361 * for integers, this code considers the non-existant positive max value to be
362 * full-scale; so max-1 will not map to 1.0
365 #define DEFINE_INT_LEVEL_CALCULATOR(TYPE, RESOLUTION) \
367 gst_level_calculate_##TYPE (gpointer data, guint num, guint channels, \
368 gdouble *NCS, gdouble *NPS) \
370 TYPE * in = (TYPE *)data; \
372 gdouble squaresum = 0.0; /* square sum of the input samples */ \
373 register gdouble square = 0.0; /* Square */ \
374 register gdouble peaksquare = 0.0; /* Peak Square Sample */ \
375 gdouble normalizer; /* divisor to get a [-1.0, 1.0] range */ \
377 /* *NCS = 0.0; Normalized Cumulative Square */ \
378 /* *NPS = 0.0; Normalized Peak Square */ \
380 for (j = 0; j < num; j += channels) { \
381 square = ((gdouble) in[j]) * in[j]; \
382 if (square > peaksquare) peaksquare = square; \
383 squaresum += square; \
386 normalizer = (gdouble) (G_GINT64_CONSTANT(1) << (RESOLUTION * 2)); \
387 *NCS = squaresum / normalizer; \
388 *NPS = peaksquare / normalizer; \
391 DEFINE_INT_LEVEL_CALCULATOR (gint32, 31);
392 DEFINE_INT_LEVEL_CALCULATOR (gint16, 15);
393 DEFINE_INT_LEVEL_CALCULATOR (gint8, 7);
395 /* FIXME: use orc to calculate squaresums? */
396 #define DEFINE_FLOAT_LEVEL_CALCULATOR(TYPE) \
398 gst_level_calculate_##TYPE (gpointer data, guint num, guint channels, \
399 gdouble *NCS, gdouble *NPS) \
401 TYPE * in = (TYPE *)data; \
403 gdouble squaresum = 0.0; /* square sum of the input samples */ \
404 register gdouble square = 0.0; /* Square */ \
405 register gdouble peaksquare = 0.0; /* Peak Square Sample */ \
407 /* *NCS = 0.0; Normalized Cumulative Square */ \
408 /* *NPS = 0.0; Normalized Peak Square */ \
410 /* orc_level_squaresum_f64(&squaresum,in,num); */ \
411 for (j = 0; j < num; j += channels) { \
412 square = ((gdouble) in[j]) * in[j]; \
413 if (square > peaksquare) peaksquare = square; \
414 squaresum += square; \
421 DEFINE_FLOAT_LEVEL_CALCULATOR (gfloat);
422 DEFINE_FLOAT_LEVEL_CALCULATOR (gdouble);
424 /* we would need stride to deinterleave also
426 gst_level_calculate_gdouble (gpointer data, guint num, guint channels,
427 gdouble *NCS, gdouble *NPS)
429 orc_level_squaresum_f64(NCS,(gdouble *)data,num);
435 gst_level_recalc_interval_frames (GstLevel * level)
437 GstClockTime interval = level->interval;
438 guint sample_rate = GST_AUDIO_INFO_RATE (&level->info);
439 guint interval_frames;
441 interval_frames = GST_CLOCK_TIME_TO_FRAMES (interval, sample_rate);
443 if (interval_frames == 0) {
444 GST_WARNING_OBJECT (level, "interval %" GST_TIME_FORMAT " is too small, "
445 "should be at least %" GST_TIME_FORMAT " for sample rate %u",
446 GST_TIME_ARGS (interval),
447 GST_TIME_ARGS (GST_FRAMES_TO_CLOCK_TIME (1, sample_rate)), sample_rate);
451 level->interval_frames = interval_frames;
453 GST_INFO_OBJECT (level, "interval_frames now %u for interval "
454 "%" GST_TIME_FORMAT " and sample rate %u", interval_frames,
455 GST_TIME_ARGS (interval), sample_rate);
459 gst_level_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
461 GstLevel *filter = GST_LEVEL (trans);
465 if (!gst_audio_info_from_caps (&info, in))
468 switch (GST_AUDIO_INFO_FORMAT (&info)) {
469 case GST_AUDIO_FORMAT_S8:
470 filter->process = gst_level_calculate_gint8;
472 case GST_AUDIO_FORMAT_S16:
473 filter->process = gst_level_calculate_gint16;
475 case GST_AUDIO_FORMAT_S32:
476 filter->process = gst_level_calculate_gint32;
478 case GST_AUDIO_FORMAT_F32:
479 filter->process = gst_level_calculate_gfloat;
481 case GST_AUDIO_FORMAT_F64:
482 filter->process = gst_level_calculate_gdouble;
485 filter->process = NULL;
491 channels = GST_AUDIO_INFO_CHANNELS (&info);
493 /* allocate channel variable arrays */
495 g_free (filter->peak);
496 g_free (filter->last_peak);
497 g_free (filter->decay_peak);
498 g_free (filter->decay_peak_base);
499 g_free (filter->decay_peak_age);
500 filter->CS = g_new (gdouble, channels);
501 filter->peak = g_new (gdouble, channels);
502 filter->last_peak = g_new (gdouble, channels);
503 filter->decay_peak = g_new (gdouble, channels);
504 filter->decay_peak_base = g_new (gdouble, channels);
506 filter->decay_peak_age = g_new (GstClockTime, channels);
508 for (i = 0; i < channels; ++i) {
509 filter->CS[i] = filter->peak[i] = filter->last_peak[i] =
510 filter->decay_peak[i] = filter->decay_peak_base[i] = 0.0;
511 filter->decay_peak_age[i] = G_GUINT64_CONSTANT (0);
514 gst_level_recalc_interval_frames (filter);
520 gst_level_start (GstBaseTransform * trans)
522 GstLevel *filter = GST_LEVEL (trans);
524 filter->num_frames = 0;
525 filter->message_ts = GST_CLOCK_TIME_NONE;
531 gst_level_message_new (GstLevel * level, GstClockTime timestamp,
532 GstClockTime duration)
534 GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (level);
537 GstClockTime endtime, running_time, stream_time;
539 running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
541 stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
543 /* endtime is for backwards compatibility */
544 endtime = stream_time + duration;
546 s = gst_structure_new ("level",
547 "endtime", GST_TYPE_CLOCK_TIME, endtime,
548 "timestamp", G_TYPE_UINT64, timestamp,
549 "stream-time", G_TYPE_UINT64, stream_time,
550 "running-time", G_TYPE_UINT64, running_time,
551 "duration", G_TYPE_UINT64, duration, NULL);
553 g_value_init (&v, G_TYPE_VALUE_ARRAY);
554 g_value_take_boxed (&v, g_value_array_new (0));
555 gst_structure_take_value (s, "rms", &v);
557 g_value_init (&v, G_TYPE_VALUE_ARRAY);
558 g_value_take_boxed (&v, g_value_array_new (0));
559 gst_structure_take_value (s, "peak", &v);
561 g_value_init (&v, G_TYPE_VALUE_ARRAY);
562 g_value_take_boxed (&v, g_value_array_new (0));
563 gst_structure_take_value (s, "decay", &v);
565 return gst_message_new_element (GST_OBJECT (level), s);
569 gst_level_message_append_channel (GstMessage * m, gdouble rms, gdouble peak,
572 const GValue *array_val;
577 g_value_init (&v, G_TYPE_DOUBLE);
579 s = (GstStructure *) gst_message_get_structure (m);
581 array_val = gst_structure_get_value (s, "rms");
582 arr = (GValueArray *) g_value_get_boxed (array_val);
583 g_value_set_double (&v, rms);
584 g_value_array_append (arr, &v); /* copies by value */
586 array_val = gst_structure_get_value (s, "peak");
587 arr = (GValueArray *) g_value_get_boxed (array_val);
588 g_value_set_double (&v, peak);
589 g_value_array_append (arr, &v); /* copies by value */
591 array_val = gst_structure_get_value (s, "decay");
592 arr = (GValueArray *) g_value_get_boxed (array_val);
593 g_value_set_double (&v, decay);
594 g_value_array_append (arr, &v); /* copies by value */
600 gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
609 guint num_int_samples = 0; /* number of interleaved samples
610 * ie. total count for all channels combined */
611 guint block_size, block_int_size; /* we subdivide buffers to not skip message
613 GstClockTimeDiff falloff_time;
614 gint channels, rate, bps;
616 filter = GST_LEVEL (trans);
618 channels = GST_AUDIO_INFO_CHANNELS (&filter->info);
619 bps = GST_AUDIO_INFO_BPS (&filter->info);
620 rate = GST_AUDIO_INFO_RATE (&filter->info);
622 gst_buffer_map (in, &map, GST_MAP_READ);
626 num_int_samples = in_size / bps;
628 GST_LOG_OBJECT (filter, "analyzing %u sample frames at ts %" GST_TIME_FORMAT,
629 num_int_samples, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (in)));
631 g_return_val_if_fail (num_int_samples % channels == 0, GST_FLOW_ERROR);
633 if (GST_BUFFER_FLAG_IS_SET (in, GST_BUFFER_FLAG_DISCONT)) {
634 filter->message_ts = GST_BUFFER_TIMESTAMP (in);
635 filter->num_frames = 0;
637 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (filter->message_ts))) {
638 filter->message_ts = GST_BUFFER_TIMESTAMP (in);
641 num_frames = num_int_samples / channels;
642 while (num_frames > 0) {
643 block_size = filter->interval_frames - filter->num_frames;
644 block_size = MIN (block_size, num_frames);
645 block_int_size = block_size * channels;
647 for (i = 0; i < channels; ++i) {
648 if (!GST_BUFFER_FLAG_IS_SET (in, GST_BUFFER_FLAG_GAP)) {
649 filter->process (in_data + (bps * i), block_int_size, channels, &CS,
651 GST_LOG_OBJECT (filter,
652 "[%d]: cumulative squares %lf, over %d samples/%d channels",
653 i, CS, block_int_size, channels);
656 filter->peak[i] = 0.0;
659 filter->decay_peak_age[i] += GST_FRAMES_TO_CLOCK_TIME (num_frames, rate);
660 GST_LOG_OBJECT (filter,
661 "[%d]: peak %f, last peak %f, decay peak %f, age %" GST_TIME_FORMAT,
662 i, filter->peak[i], filter->last_peak[i], filter->decay_peak[i],
663 GST_TIME_ARGS (filter->decay_peak_age[i]));
665 /* update running peak */
666 if (filter->peak[i] > filter->last_peak[i])
667 filter->last_peak[i] = filter->peak[i];
669 /* make decay peak fall off if too old */
671 GST_CLOCK_DIFF (gst_gdouble_to_guint64 (filter->decay_peak_ttl),
672 filter->decay_peak_age[i]);
673 if (falloff_time > 0) {
676 gdouble length; /* length of falloff time in seconds */
678 length = (gdouble) falloff_time / (gdouble) GST_SECOND;
679 falloff_dB = filter->decay_peak_falloff * length;
680 falloff = pow (10, falloff_dB / -20.0);
682 GST_LOG_OBJECT (filter,
683 "falloff: current %f, base %f, interval %" GST_TIME_FORMAT
684 ", dB falloff %f, factor %e",
685 filter->decay_peak[i], filter->decay_peak_base[i],
686 GST_TIME_ARGS (falloff_time), falloff_dB, falloff);
687 filter->decay_peak[i] = filter->decay_peak_base[i] * falloff;
688 GST_LOG_OBJECT (filter,
689 "peak is %" GST_TIME_FORMAT " old, decayed with factor %e to %f",
690 GST_TIME_ARGS (filter->decay_peak_age[i]), falloff,
691 filter->decay_peak[i]);
693 GST_LOG_OBJECT (filter, "peak not old enough, not decaying");
696 /* if the peak of this run is higher, the decay peak gets reset */
697 if (filter->peak[i] >= filter->decay_peak[i]) {
698 GST_LOG_OBJECT (filter, "new peak, %f", filter->peak[i]);
699 filter->decay_peak[i] = filter->peak[i];
700 filter->decay_peak_base[i] = filter->peak[i];
701 filter->decay_peak_age[i] = G_GINT64_CONSTANT (0);
704 in_data += block_size * bps * channels;
706 filter->num_frames += block_size;
707 num_frames -= block_size;
709 /* do we need to message ? */
710 if (filter->num_frames >= filter->interval_frames) {
711 gst_level_post_message (filter);
715 gst_buffer_unmap (in, &map);
721 gst_level_post_message (GstLevel * filter)
724 gint channels, rate, frames = filter->num_frames;
725 GstClockTime duration;
727 channels = GST_AUDIO_INFO_CHANNELS (&filter->info);
728 rate = GST_AUDIO_INFO_RATE (&filter->info);
729 duration = GST_FRAMES_TO_CLOCK_TIME (frames, rate);
731 if (filter->post_messages) {
733 gst_level_message_new (filter, filter->message_ts, duration);
735 GST_LOG_OBJECT (filter,
736 "message: ts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
737 ", num_frames %d", GST_TIME_ARGS (filter->message_ts),
738 GST_TIME_ARGS (duration), frames);
740 for (i = 0; i < channels; ++i) {
742 gdouble RMSdB, peakdB, decaydB;
744 RMS = sqrt (filter->CS[i] / frames);
745 GST_LOG_OBJECT (filter,
746 "message: channel %d, CS %f, RMS %f", i, filter->CS[i], RMS);
747 GST_LOG_OBJECT (filter,
748 "message: last_peak: %f, decay_peak: %f",
749 filter->last_peak[i], filter->decay_peak[i]);
750 /* RMS values are calculated in amplitude, so 20 * log 10 */
751 RMSdB = 20 * log10 (RMS + EPSILON);
752 /* peak values are square sums, ie. power, so 10 * log 10 */
753 peakdB = 10 * log10 (filter->last_peak[i] + EPSILON);
754 decaydB = 10 * log10 (filter->decay_peak[i] + EPSILON);
756 if (filter->decay_peak[i] < filter->last_peak[i]) {
757 /* this can happen in certain cases, for example when
758 * the last peak is between decay_peak and decay_peak_base */
759 GST_DEBUG_OBJECT (filter,
760 "message: decay peak dB %f smaller than last peak dB %f, copying",
762 filter->decay_peak[i] = filter->last_peak[i];
764 GST_LOG_OBJECT (filter,
765 "message: RMS %f dB, peak %f dB, decay %f dB",
766 RMSdB, peakdB, decaydB);
768 gst_level_message_append_channel (m, RMSdB, peakdB, decaydB);
770 /* reset cumulative and normal peak */
772 filter->last_peak[i] = 0.0;
775 gst_element_post_message (GST_ELEMENT (filter), m);
778 filter->num_frames -= frames;
779 filter->message_ts += duration;
784 gst_level_sink_event (GstBaseTransform * trans, GstEvent * event)
786 if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
787 GstLevel *filter = GST_LEVEL (trans);
789 gst_level_post_message (filter);
792 return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event);
796 plugin_init (GstPlugin * plugin)
798 return gst_element_register (plugin, "level", GST_RANK_NONE, GST_TYPE_LEVEL);
801 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
804 "Audio level plugin",
805 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);