2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
4 * gstlevel.c: signals RMS, peak and decaying peak levels
5 * Copyright (C) 2000,2001,2002,2003
6 * Thomas Vander Stichele <thomas at apestaart dot org>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
28 #include <gst/audio/audio.h>
32 /* elementfactory information */
33 static GstElementDetails level_details = {
35 "Filter/Analyzer/Audio",
36 "RMS/Peak/Decaying Peak Level signaller for audio/raw",
37 "Thomas <thomas@apestaart.org>"
42 static GstStaticPadTemplate sink_template_factory =
43 GST_STATIC_PAD_TEMPLATE ("level_sink",
46 GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS)
49 static GstStaticPadTemplate src_template_factory =
50 GST_STATIC_PAD_TEMPLATE ("level_src",
53 GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS)
56 /* Filter signals and args */
73 static void gst_level_class_init (GstLevelClass * klass);
74 static void gst_level_base_init (GstLevelClass * klass);
75 static void gst_level_init (GstLevel * filter);
77 static GstElementStateReturn gst_level_change_state (GstElement * element);
78 static void gst_level_set_property (GObject * object, guint prop_id,
79 const GValue * value, GParamSpec * pspec);
80 static void gst_level_get_property (GObject * object, guint prop_id,
81 GValue * value, GParamSpec * pspec);
83 static void gst_level_chain (GstPad * pad, GstData * _data);
85 static GstElementClass *parent_class = NULL;
86 static guint gst_filter_signals[LAST_SIGNAL] = { 0 };
89 gst_level_get_type (void)
91 static GType level_type = 0;
94 static const GTypeInfo level_info = {
95 sizeof (GstLevelClass),
96 (GBaseInitFunc) gst_level_base_init, NULL,
97 (GClassInitFunc) gst_level_class_init, NULL, NULL,
99 (GInstanceInitFunc) gst_level_init
101 level_type = g_type_register_static (GST_TYPE_ELEMENT, "GstLevel",
107 static GstPadLinkReturn
108 gst_level_link (GstPad * pad, const GstCaps * caps)
112 GstPadLinkReturn res;
113 GstStructure *structure;
117 filter = GST_LEVEL (gst_pad_get_parent (pad));
118 g_return_val_if_fail (filter != NULL, GST_PAD_LINK_REFUSED);
119 g_return_val_if_fail (GST_IS_LEVEL (filter), GST_PAD_LINK_REFUSED);
120 otherpad = (pad == filter->srcpad ? filter->sinkpad : filter->srcpad);
122 res = gst_pad_try_set_caps (otherpad, caps);
123 /* if ok, set filter */
124 if (res != GST_PAD_LINK_OK && res != GST_PAD_LINK_DONE) {
128 filter->num_samples = 0;
130 structure = gst_caps_get_structure (caps, 0);
131 ret = gst_structure_get_int (structure, "rate", &filter->rate);
132 ret &= gst_structure_get_int (structure, "width", &filter->width);
133 ret &= gst_structure_get_int (structure, "channels", &filter->channels);
136 return GST_PAD_LINK_REFUSED;
138 /* allocate channel variable arrays */
142 g_free (filter->peak);
143 if (filter->last_peak)
144 g_free (filter->last_peak);
145 if (filter->decay_peak)
146 g_free (filter->decay_peak);
147 if (filter->decay_peak_age)
148 g_free (filter->decay_peak_age);
152 g_free (filter->RMS_dB);
153 filter->CS = g_new (double, filter->channels);
154 filter->peak = g_new (double, filter->channels);
155 filter->last_peak = g_new (double, filter->channels);
156 filter->decay_peak = g_new (double, filter->channels);
157 filter->decay_peak_age = g_new (double, filter->channels);
158 filter->MS = g_new (double, filter->channels);
159 filter->RMS_dB = g_new (double, filter->channels);
161 for (i = 0; i < filter->channels; ++i) {
162 filter->CS[i] = filter->peak[i] = filter->last_peak[i] =
163 filter->decay_peak[i] = filter->decay_peak_age[i] =
164 filter->MS[i] = filter->RMS_dB[i] = 0.0;
167 filter->inited = TRUE;
169 return GST_PAD_LINK_OK;
173 gst_level_fast_16bit_chain (gint16 * in, guint num, gint channels,
174 gint resolution, double *CS, double *peak)
175 #include "filter.func"
177 gst_level_fast_8bit_chain (gint8 * in, guint num, gint channels,
178 gint resolution, double *CS, double *peak)
179 #include "filter.func"
180 static void gst_level_chain (GstPad * pad, GstData * _data)
182 GstBuffer *buf = GST_BUFFER (_data);
187 gint num_samples = 0;
190 g_return_if_fail (pad != NULL);
191 g_return_if_fail (GST_IS_PAD (pad));
192 g_return_if_fail (buf != NULL);
194 filter = GST_LEVEL (GST_OBJECT_PARENT (pad));
195 g_return_if_fail (filter != NULL);
196 g_return_if_fail (GST_IS_LEVEL (filter));
198 for (i = 0; i < filter->channels; ++i)
199 filter->CS[i] = filter->peak[i] = filter->MS[i] = filter->RMS_dB[i] = 0.0;
201 in_data = (gint16 *) GST_BUFFER_DATA (buf);
203 num_samples = GST_BUFFER_SIZE (buf) / (filter->width / 8);
204 if (num_samples % filter->channels != 0)
206 ("WARNING: level: programming error, data not properly interleaved");
208 for (i = 0; i < filter->channels; ++i) {
209 switch (filter->width) {
211 gst_level_fast_16bit_chain (in_data + i, num_samples,
212 filter->channels, filter->width - 1, &CS, &filter->peak[i]);
215 gst_level_fast_8bit_chain (((gint8 *) in_data) + i, num_samples,
216 filter->channels, filter->width - 1, &CS, &filter->peak[i]);
219 /* g_print ("DEBUG: CS %f, peak %f\n", CS, filter->peak[i]); */
223 gst_pad_push (filter->srcpad, GST_DATA (buf));
225 filter->num_samples += num_samples;
227 for (i = 0; i < filter->channels; ++i) {
228 filter->decay_peak_age[i] += num_samples;
229 /* g_print ("filter peak info [%d]: peak %f, age %f\n", i,
230 filter->last_peak[i], filter->decay_peak_age[i]); */
231 /* update running peak */
232 if (filter->peak[i] > filter->last_peak[i])
233 filter->last_peak[i] = filter->peak[i];
235 /* update decay peak */
236 if (filter->peak[i] >= filter->decay_peak[i]) {
237 /* g_print ("new peak, %f\n", filter->peak[i]); */
238 filter->decay_peak[i] = filter->peak[i];
239 filter->decay_peak_age[i] = 0;
241 /* make decay peak fall off if too old */
242 if (filter->decay_peak_age[i] > filter->rate * filter->decay_peak_ttl) {
245 double length; /* length of buffer in seconds */
248 length = (double) num_samples / (filter->channels * filter->rate);
249 falloff_dB = filter->decay_peak_falloff * length;
250 falloff = pow (10, falloff_dB / -20.0);
252 /* g_print ("falloff: length %f, dB falloff %f, falloff factor %e\n",
253 length, falloff_dB, falloff); */
254 filter->decay_peak[i] *= falloff;
255 /* g_print ("peak is %f samples old, decayed with factor %e to %f\n",
256 filter->decay_peak_age[i], falloff, filter->decay_peak[i]); */
261 /* do we need to emit ? */
263 if (filter->num_samples >= filter->interval * (gdouble) filter->rate) {
264 if (filter->signal) {
265 gdouble RMS, peak, endtime;
267 for (i = 0; i < filter->channels; ++i) {
268 RMS = sqrt (filter->CS[i] / (filter->num_samples / filter->channels));
269 peak = filter->last_peak[i];
270 num_samples = GST_BUFFER_SIZE (buf) / (filter->width / 8);
271 endtime = (double) GST_BUFFER_TIMESTAMP (buf) / GST_SECOND
272 + (double) num_samples / (double) filter->rate;
274 g_signal_emit (G_OBJECT (filter), gst_filter_signals[SIGNAL_LEVEL], 0,
276 20 * log10 (RMS), 20 * log10 (filter->last_peak[i]),
277 20 * log10 (filter->decay_peak[i]));
278 /* we emitted, so reset cumulative and normal peak */
280 filter->last_peak[i] = 0.0;
283 filter->num_samples = 0;
287 static GstElementStateReturn
288 gst_level_change_state (GstElement * element)
290 GstLevel *filter = GST_LEVEL (element);
292 switch (GST_STATE_TRANSITION (element)) {
293 case GST_STATE_PAUSED_TO_PLAYING:
295 return GST_STATE_FAILURE;
301 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
305 gst_level_set_property (GObject * object, guint prop_id,
306 const GValue * value, GParamSpec * pspec)
310 /* it's not null if we got it, but it might not be ours */
311 g_return_if_fail (GST_IS_LEVEL (object));
312 filter = GST_LEVEL (object);
315 case ARG_SIGNAL_LEVEL:
316 filter->signal = g_value_get_boolean (value);
318 case ARG_SIGNAL_INTERVAL:
319 filter->interval = g_value_get_double (value);
322 filter->decay_peak_ttl = g_value_get_double (value);
324 case ARG_PEAK_FALLOFF:
325 filter->decay_peak_falloff = g_value_get_double (value);
333 gst_level_get_property (GObject * object, guint prop_id,
334 GValue * value, GParamSpec * pspec)
338 /* it's not null if we got it, but it might not be ours */
339 g_return_if_fail (GST_IS_LEVEL (object));
340 filter = GST_LEVEL (object);
343 case ARG_SIGNAL_LEVEL:
344 g_value_set_boolean (value, filter->signal);
346 case ARG_SIGNAL_INTERVAL:
347 g_value_set_double (value, filter->interval);
350 g_value_set_double (value, filter->decay_peak_ttl);
352 case ARG_PEAK_FALLOFF:
353 g_value_set_double (value, filter->decay_peak_falloff);
356 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
362 gst_level_base_init (GstLevelClass * klass)
364 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
366 gst_element_class_add_pad_template (element_class,
367 gst_static_pad_template_get (&sink_template_factory));
368 gst_element_class_add_pad_template (element_class,
369 gst_static_pad_template_get (&src_template_factory));
370 gst_element_class_set_details (element_class, &level_details);
372 element_class->change_state = gst_level_change_state;
376 gst_level_class_init (GstLevelClass * klass)
378 GObjectClass *gobject_class;
379 GstElementClass *gstelement_class;
381 gobject_class = (GObjectClass *) klass;
382 gstelement_class = (GstElementClass *) klass;
384 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
385 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SIGNAL_LEVEL,
386 g_param_spec_boolean ("signal", "Signal",
387 "Emit level signals for each interval", TRUE, G_PARAM_READWRITE));
388 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SIGNAL_INTERVAL,
389 g_param_spec_double ("interval", "Interval",
390 "Interval between emissions (in seconds)",
391 0.01, 100.0, 0.1, G_PARAM_READWRITE));
392 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PEAK_TTL,
393 g_param_spec_double ("peak_ttl", "Peak TTL",
394 "Time To Live of decay peak before it falls back",
395 0, 100.0, 0.3, G_PARAM_READWRITE));
396 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PEAK_FALLOFF,
397 g_param_spec_double ("peak_falloff", "Peak Falloff",
398 "Decay rate of decay peak after TTL (in dB/sec)",
399 0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE));
401 gobject_class->set_property = gst_level_set_property;
402 gobject_class->get_property = gst_level_get_property;
404 gst_filter_signals[SIGNAL_LEVEL] =
405 g_signal_new ("level", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
406 G_STRUCT_OFFSET (GstLevelClass, level), NULL, NULL,
407 gstlevel_cclosure_marshal_VOID__DOUBLE_INT_DOUBLE_DOUBLE_DOUBLE,
409 G_TYPE_DOUBLE, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE);
413 gst_level_init (GstLevel * filter)
416 gst_pad_new_from_template (gst_static_pad_template_get
417 (&sink_template_factory), "sink");
418 gst_pad_set_link_function (filter->sinkpad, gst_level_link);
420 gst_pad_new_from_template (gst_static_pad_template_get
421 (&src_template_factory), "src");
422 gst_pad_set_link_function (filter->srcpad, gst_level_link);
424 gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
425 gst_pad_set_chain_function (filter->sinkpad, gst_level_chain);
426 filter->srcpad = gst_pad_new ("src", GST_PAD_SRC);
427 gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
432 filter->RMS_dB = NULL;
436 filter->channels = 0;
438 filter->interval = 0.1;
439 filter->decay_peak_ttl = 0.4;
440 filter->decay_peak_falloff = 10.0; /* dB falloff (/sec) */
444 plugin_init (GstPlugin * plugin)
446 return gst_element_register (plugin, "level", GST_RANK_NONE, GST_TYPE_LEVEL);
449 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
452 "Audio level plugin",
453 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)