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., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, 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
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
84 * <link linkend="GstLevel--peak-falloff">the peak falloff rate</link>.
89 * #GValueArray of #gdouble
90 * <classname>"rms"</classname>:
91 * the Root Mean Square (or average power) level in dB for each channel
97 * <title>Example application</title>
99 * <xi:include xmlns:xi="http://www.w3.org/2003/XInclude" parse="text" href="../../../../tests/examples/level/level-example.c" />
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
115 #include <gst/audio/audio.h>
117 #include "gstlevel.h"
119 GST_DEBUG_CATEGORY_STATIC (level_debug);
120 #define GST_CAT_DEFAULT level_debug
122 #define EPSILON 1e-35f
124 static GstStaticPadTemplate sink_template_factory =
125 GST_STATIC_PAD_TEMPLATE ("sink",
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 ]")
135 static GstStaticPadTemplate src_template_factory =
136 GST_STATIC_PAD_TEMPLATE ("src",
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 ]")
150 PROP_SIGNAL_INTERVAL,
155 #define gst_level_parent_class parent_class
156 G_DEFINE_TYPE (GstLevel, gst_level, GST_TYPE_BASE_TRANSFORM);
158 static void gst_level_set_property (GObject * object, guint prop_id,
159 const GValue * value, GParamSpec * pspec);
160 static void gst_level_get_property (GObject * object, guint prop_id,
161 GValue * value, GParamSpec * pspec);
162 static void gst_level_finalize (GObject * obj);
164 static gboolean gst_level_set_caps (GstBaseTransform * trans, GstCaps * in,
166 static gboolean gst_level_start (GstBaseTransform * trans);
167 static GstFlowReturn gst_level_transform_ip (GstBaseTransform * trans,
172 gst_level_class_init (GstLevelClass * klass)
174 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
175 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
176 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
178 gobject_class->set_property = gst_level_set_property;
179 gobject_class->get_property = gst_level_get_property;
180 gobject_class->finalize = gst_level_finalize;
182 g_object_class_install_property (gobject_class, PROP_SIGNAL_LEVEL,
183 g_param_spec_boolean ("message", "message",
184 "Post a level message for each passed interval",
185 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
186 g_object_class_install_property (gobject_class, PROP_SIGNAL_INTERVAL,
187 g_param_spec_uint64 ("interval", "Interval",
188 "Interval of time between message posts (in nanoseconds)",
189 1, G_MAXUINT64, GST_SECOND / 10,
190 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
191 g_object_class_install_property (gobject_class, PROP_PEAK_TTL,
192 g_param_spec_uint64 ("peak-ttl", "Peak TTL",
193 "Time To Live of decay peak before it falls back (in nanoseconds)",
194 0, G_MAXUINT64, GST_SECOND / 10 * 3,
195 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
196 g_object_class_install_property (gobject_class, PROP_PEAK_FALLOFF,
197 g_param_spec_double ("peak-falloff", "Peak Falloff",
198 "Decay rate of decay peak after TTL (in dB/sec)",
199 0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
201 GST_DEBUG_CATEGORY_INIT (level_debug, "level", 0, "Level calculation");
203 gst_element_class_add_pad_template (element_class,
204 gst_static_pad_template_get (&sink_template_factory));
205 gst_element_class_add_pad_template (element_class,
206 gst_static_pad_template_get (&src_template_factory));
207 gst_element_class_set_details_simple (element_class, "Level",
208 "Filter/Analyzer/Audio",
209 "RMS/Peak/Decaying Peak Level messager for audio/raw",
210 "Thomas Vander Stichele <thomas at apestaart dot org>");
212 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_level_set_caps);
213 trans_class->start = GST_DEBUG_FUNCPTR (gst_level_start);
214 trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_level_transform_ip);
215 trans_class->passthrough_on_same_caps = TRUE;
219 gst_level_init (GstLevel * filter)
224 gst_audio_info_init (&filter->info);
226 filter->interval = GST_SECOND / 10;
227 filter->decay_peak_ttl = GST_SECOND / 10 * 3;
228 filter->decay_peak_falloff = 10.0; /* dB falloff (/sec) */
230 filter->message = TRUE;
232 filter->process = NULL;
234 gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
238 gst_level_finalize (GObject * obj)
240 GstLevel *filter = GST_LEVEL (obj);
243 g_free (filter->peak);
244 g_free (filter->last_peak);
245 g_free (filter->decay_peak);
246 g_free (filter->decay_peak_base);
247 g_free (filter->decay_peak_age);
251 filter->last_peak = NULL;
252 filter->decay_peak = NULL;
253 filter->decay_peak_base = NULL;
254 filter->decay_peak_age = NULL;
256 G_OBJECT_CLASS (parent_class)->finalize (obj);
260 gst_level_set_property (GObject * object, guint prop_id,
261 const GValue * value, GParamSpec * pspec)
263 GstLevel *filter = GST_LEVEL (object);
266 case PROP_SIGNAL_LEVEL:
267 filter->message = g_value_get_boolean (value);
269 case PROP_SIGNAL_INTERVAL:
270 filter->interval = g_value_get_uint64 (value);
271 if (GST_AUDIO_INFO_RATE (&filter->info)) {
272 filter->interval_frames =
273 GST_CLOCK_TIME_TO_FRAMES (filter->interval,
274 GST_AUDIO_INFO_RATE (&filter->info));
278 filter->decay_peak_ttl =
279 gst_guint64_to_gdouble (g_value_get_uint64 (value));
281 case PROP_PEAK_FALLOFF:
282 filter->decay_peak_falloff = g_value_get_double (value);
285 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
291 gst_level_get_property (GObject * object, guint prop_id,
292 GValue * value, GParamSpec * pspec)
294 GstLevel *filter = GST_LEVEL (object);
297 case PROP_SIGNAL_LEVEL:
298 g_value_set_boolean (value, filter->message);
300 case PROP_SIGNAL_INTERVAL:
301 g_value_set_uint64 (value, filter->interval);
304 g_value_set_uint64 (value, filter->decay_peak_ttl);
306 case PROP_PEAK_FALLOFF:
307 g_value_set_double (value, filter->decay_peak_falloff);
310 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
316 /* process one (interleaved) channel of incoming samples
317 * calculate square sum of samples
318 * normalize and average over number of samples
319 * returns a normalized cumulative square value, which can be averaged
320 * to return the average power as a double between 0 and 1
321 * also returns the normalized peak power (square of the highest amplitude)
323 * caller must assure num is a multiple of channels
324 * samples for multiple channels are interleaved
325 * input sample data enters in *in_data as 8 or 16 bit data
326 * this filter only accepts signed audio data, so mid level is always 0
328 * for 16 bit, this code considers the non-existant 32768 value to be
329 * full-scale; so 32767 will not map to 1.0
332 #define DEFINE_INT_LEVEL_CALCULATOR(TYPE, RESOLUTION) \
334 gst_level_calculate_##TYPE (gpointer data, guint num, guint channels, \
335 gdouble *NCS, gdouble *NPS) \
337 TYPE * in = (TYPE *)data; \
339 gdouble squaresum = 0.0; /* square sum of the integer samples */ \
340 register gdouble square = 0.0; /* Square */ \
341 register gdouble peaksquare = 0.0; /* Peak Square Sample */ \
342 gdouble normalizer; /* divisor to get a [-1.0, 1.0] range */ \
344 /* *NCS = 0.0; Normalized Cumulative Square */ \
345 /* *NPS = 0.0; Normalized Peask Square */ \
347 normalizer = (gdouble) (G_GINT64_CONSTANT(1) << (RESOLUTION * 2)); \
349 /* oil_squaresum_shifted_s16(&squaresum,in,num); */ \
350 for (j = 0; j < num; j += channels) \
352 square = ((gdouble) in[j]) * in[j]; \
353 if (square > peaksquare) peaksquare = square; \
354 squaresum += square; \
357 *NCS = squaresum / normalizer; \
358 *NPS = peaksquare / normalizer; \
361 DEFINE_INT_LEVEL_CALCULATOR (gint32, 31);
362 DEFINE_INT_LEVEL_CALCULATOR (gint16, 15);
363 DEFINE_INT_LEVEL_CALCULATOR (gint8, 7);
365 #define DEFINE_FLOAT_LEVEL_CALCULATOR(TYPE) \
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 integer samples */ \
373 register gdouble square = 0.0; /* Square */ \
374 register gdouble peaksquare = 0.0; /* Peak Square Sample */ \
376 /* *NCS = 0.0; Normalized Cumulative Square */ \
377 /* *NPS = 0.0; Normalized Peask Square */ \
379 /* oil_squaresum_f64(&squaresum,in,num); */ \
380 for (j = 0; j < num; j += channels) \
382 square = ((gdouble) in[j]) * in[j]; \
383 if (square > peaksquare) peaksquare = square; \
384 squaresum += square; \
391 DEFINE_FLOAT_LEVEL_CALCULATOR (gfloat);
392 DEFINE_FLOAT_LEVEL_CALCULATOR (gdouble);
394 /* we would need stride to deinterleave also
396 gst_level_calculate_gdouble (gpointer data, guint num, guint channels,
397 gdouble *NCS, gdouble *NPS)
399 oil_squaresum_f64(NCS,(gdouble *)data,num);
406 gst_level_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
408 GstLevel *filter = GST_LEVEL (trans);
410 gint i, channels, rate;
412 if (!gst_audio_info_from_caps (&info, in))
415 switch (GST_AUDIO_INFO_FORMAT (&info)) {
416 case GST_AUDIO_FORMAT_S8:
417 filter->process = gst_level_calculate_gint8;
419 case GST_AUDIO_FORMAT_S16:
420 filter->process = gst_level_calculate_gint16;
422 case GST_AUDIO_FORMAT_S32:
423 filter->process = gst_level_calculate_gint32;
425 case GST_AUDIO_FORMAT_F32:
426 filter->process = gst_level_calculate_gfloat;
428 case GST_AUDIO_FORMAT_F64:
429 filter->process = gst_level_calculate_gdouble;
432 filter->process = NULL;
438 channels = GST_AUDIO_INFO_CHANNELS (&info);
439 rate = GST_AUDIO_INFO_RATE (&info);
441 /* allocate channel variable arrays */
443 g_free (filter->peak);
444 g_free (filter->last_peak);
445 g_free (filter->decay_peak);
446 g_free (filter->decay_peak_base);
447 g_free (filter->decay_peak_age);
448 filter->CS = g_new (gdouble, channels);
449 filter->peak = g_new (gdouble, channels);
450 filter->last_peak = g_new (gdouble, channels);
451 filter->decay_peak = g_new (gdouble, channels);
452 filter->decay_peak_base = g_new (gdouble, channels);
454 filter->decay_peak_age = g_new (GstClockTime, channels);
456 for (i = 0; i < channels; ++i) {
457 filter->CS[i] = filter->peak[i] = filter->last_peak[i] =
458 filter->decay_peak[i] = filter->decay_peak_base[i] = 0.0;
459 filter->decay_peak_age[i] = G_GUINT64_CONSTANT (0);
462 filter->interval_frames = GST_CLOCK_TIME_TO_FRAMES (filter->interval, rate);
468 gst_level_start (GstBaseTransform * trans)
470 GstLevel *filter = GST_LEVEL (trans);
472 filter->num_frames = 0;
478 gst_level_message_new (GstLevel * level, GstClockTime timestamp,
479 GstClockTime duration)
481 GstBaseTransform *trans = GST_BASE_TRANSFORM_CAST (level);
484 GstClockTime endtime, running_time, stream_time;
486 running_time = gst_segment_to_running_time (&trans->segment, GST_FORMAT_TIME,
488 stream_time = gst_segment_to_stream_time (&trans->segment, GST_FORMAT_TIME,
490 /* endtime is for backwards compatibility */
491 endtime = stream_time + duration;
493 s = gst_structure_new ("level",
494 "endtime", GST_TYPE_CLOCK_TIME, endtime,
495 "timestamp", G_TYPE_UINT64, timestamp,
496 "stream-time", G_TYPE_UINT64, stream_time,
497 "running-time", G_TYPE_UINT64, running_time,
498 "duration", G_TYPE_UINT64, duration, NULL);
500 g_value_init (&v, G_TYPE_VALUE_ARRAY);
501 g_value_take_boxed (&v, g_value_array_new (0));
502 gst_structure_take_value (s, "rms", &v);
504 g_value_init (&v, G_TYPE_VALUE_ARRAY);
505 g_value_take_boxed (&v, g_value_array_new (0));
506 gst_structure_take_value (s, "peak", &v);
508 g_value_init (&v, G_TYPE_VALUE_ARRAY);
509 g_value_take_boxed (&v, g_value_array_new (0));
510 gst_structure_take_value (s, "decay", &v);
512 return gst_message_new_element (GST_OBJECT (level), s);
516 gst_level_message_append_channel (GstMessage * m, gdouble rms, gdouble peak,
519 const GValue *array_val;
524 g_value_init (&v, G_TYPE_DOUBLE);
526 s = (GstStructure *) gst_message_get_structure (m);
528 array_val = gst_structure_get_value (s, "rms");
529 arr = (GValueArray *) g_value_get_boxed (array_val);
530 g_value_set_double (&v, rms);
531 g_value_array_append (arr, &v); /* copies by value */
533 array_val = gst_structure_get_value (s, "peak");
534 arr = (GValueArray *) g_value_get_boxed (array_val);
535 g_value_set_double (&v, peak);
536 g_value_array_append (arr, &v); /* copies by value */
538 array_val = gst_structure_get_value (s, "decay");
539 arr = (GValueArray *) g_value_get_boxed (array_val);
540 g_value_set_double (&v, decay);
541 g_value_array_append (arr, &v); /* copies by value */
547 gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
555 guint num_frames = 0;
556 guint num_int_samples = 0; /* number of interleaved samples
557 * ie. total count for all channels combined */
558 GstClockTimeDiff falloff_time;
559 gint channels, rate, bps;
561 filter = GST_LEVEL (trans);
563 channels = GST_AUDIO_INFO_CHANNELS (&filter->info);
564 bps = GST_AUDIO_INFO_BPS (&filter->info);
565 rate = GST_AUDIO_INFO_RATE (&filter->info);
567 gst_buffer_map (in, &map, GST_MAP_READ);
571 num_int_samples = in_size / bps;
573 GST_LOG_OBJECT (filter, "analyzing %u sample frames at ts %" GST_TIME_FORMAT,
574 num_int_samples, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (in)));
576 g_return_val_if_fail (num_int_samples % channels == 0, GST_FLOW_ERROR);
578 num_frames = num_int_samples / channels;
580 for (i = 0; i < channels; ++i) {
581 if (!GST_BUFFER_FLAG_IS_SET (in, GST_BUFFER_FLAG_GAP)) {
582 filter->process (in_data, num_int_samples, channels, &CS,
584 GST_LOG_OBJECT (filter,
585 "channel %d, cumulative sum %f, peak %f, over %d samples/%d channels",
586 i, CS, filter->peak[i], num_int_samples, channels);
589 filter->peak[i] = 0.0;
593 filter->decay_peak_age[i] += GST_FRAMES_TO_CLOCK_TIME (num_frames, rate);
594 GST_LOG_OBJECT (filter, "filter peak info [%d]: decay peak %f, age %"
596 filter->decay_peak[i], GST_TIME_ARGS (filter->decay_peak_age[i]));
598 /* update running peak */
599 if (filter->peak[i] > filter->last_peak[i])
600 filter->last_peak[i] = filter->peak[i];
602 /* make decay peak fall off if too old */
604 GST_CLOCK_DIFF (gst_gdouble_to_guint64 (filter->decay_peak_ttl),
605 filter->decay_peak_age[i]);
606 if (falloff_time > 0) {
609 gdouble length; /* length of falloff time in seconds */
611 length = (gdouble) falloff_time / (gdouble) GST_SECOND;
612 falloff_dB = filter->decay_peak_falloff * length;
613 falloff = pow (10, falloff_dB / -20.0);
615 GST_LOG_OBJECT (filter,
616 "falloff: current %f, base %f, interval %" GST_TIME_FORMAT
617 ", dB falloff %f, factor %e",
618 filter->decay_peak[i], filter->decay_peak_base[i],
619 GST_TIME_ARGS (falloff_time), falloff_dB, falloff);
620 filter->decay_peak[i] = filter->decay_peak_base[i] * falloff;
621 GST_LOG_OBJECT (filter,
622 "peak is %" GST_TIME_FORMAT " old, decayed with factor %e to %f",
623 GST_TIME_ARGS (filter->decay_peak_age[i]), falloff,
624 filter->decay_peak[i]);
626 GST_LOG_OBJECT (filter, "peak not old enough, not decaying");
629 /* if the peak of this run is higher, the decay peak gets reset */
630 if (filter->peak[i] >= filter->decay_peak[i]) {
631 GST_LOG_OBJECT (filter, "new peak, %f", filter->peak[i]);
632 filter->decay_peak[i] = filter->peak[i];
633 filter->decay_peak_base[i] = filter->peak[i];
634 filter->decay_peak_age[i] = G_GINT64_CONSTANT (0);
638 if (G_UNLIKELY (!filter->num_frames)) {
639 /* remember start timestamp for message */
640 filter->message_ts = GST_BUFFER_TIMESTAMP (in);
642 filter->num_frames += num_frames;
644 /* do we need to message ? */
645 if (filter->num_frames >= filter->interval_frames) {
646 if (filter->message) {
648 GstClockTime duration =
649 GST_FRAMES_TO_CLOCK_TIME (filter->num_frames, rate);
651 m = gst_level_message_new (filter, filter->message_ts, duration);
653 GST_LOG_OBJECT (filter,
654 "message: ts %" GST_TIME_FORMAT ", num_frames %d",
655 GST_TIME_ARGS (filter->message_ts), filter->num_frames);
657 for (i = 0; i < channels; ++i) {
659 gdouble RMSdB, lastdB, decaydB;
661 RMS = sqrt (filter->CS[i] / filter->num_frames);
662 GST_LOG_OBJECT (filter,
663 "message: channel %d, CS %f, num_frames %d, RMS %f",
664 i, filter->CS[i], filter->num_frames, RMS);
665 GST_LOG_OBJECT (filter,
666 "message: last_peak: %f, decay_peak: %f",
667 filter->last_peak[i], filter->decay_peak[i]);
668 /* RMS values are calculated in amplitude, so 20 * log 10 */
669 RMSdB = 20 * log10 (RMS + EPSILON);
670 /* peak values are square sums, ie. power, so 10 * log 10 */
671 lastdB = 10 * log10 (filter->last_peak[i] + EPSILON);
672 decaydB = 10 * log10 (filter->decay_peak[i] + EPSILON);
674 if (filter->decay_peak[i] < filter->last_peak[i]) {
675 /* this can happen in certain cases, for example when
676 * the last peak is between decay_peak and decay_peak_base */
677 GST_DEBUG_OBJECT (filter,
678 "message: decay peak dB %f smaller than last peak dB %f, copying",
680 filter->decay_peak[i] = filter->last_peak[i];
682 GST_LOG_OBJECT (filter,
683 "message: RMS %f dB, peak %f dB, decay %f dB",
684 RMSdB, lastdB, decaydB);
686 gst_level_message_append_channel (m, RMSdB, lastdB, decaydB);
688 /* reset cumulative and normal peak */
690 filter->last_peak[i] = 0.0;
693 gst_element_post_message (GST_ELEMENT (filter), m);
695 filter->num_frames = 0;
698 gst_buffer_unmap (in, &map);
704 plugin_init (GstPlugin * plugin)
708 return gst_element_register (plugin, "level", GST_RANK_NONE, GST_TYPE_LEVEL);
711 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
714 "Audio level plugin",
715 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);