diff options
author | Andy Wingo <wingo@pobox.com> | 2005-07-04 14:02:46 +0000 |
---|---|---|
committer | Andy Wingo <wingo@pobox.com> | 2005-07-04 14:02:46 +0000 |
commit | 77a7c4c8fbc7a9fb59039c0f9469923dd651c009 (patch) | |
tree | 243e22219ac7b5c902681a70a40f5d7d9704b781 /gst/level/gstlevel.c | |
parent | d0516df78a67006fca5cd20295e6237d81470196 (diff) |
examples/level/: Examples moved out of the source dir. Not updated tho.
Original commit message from CVS:
2005-07-04 Andy Wingo <wingo@pobox.com>
* examples/level/:
* examples/level/Makefile.am:
* examples/level/README:
* examples/level/demo.c:
* examples/level/plot.c: Examples moved out of the source dir. Not
updated tho.
* configure.ac: Add level to the build.
* gst/level/Makefile.am:
* gst/level/gstlevel.h:
* gst/level/gstlevel.c: Cleaned up, ported to 0.9.
Diffstat (limited to 'gst/level/gstlevel.c')
-rw-r--r-- | gst/level/gstlevel.c | 514 |
1 files changed, 265 insertions, 249 deletions
diff --git a/gst/level/gstlevel.c b/gst/level/gstlevel.c index e1a3bb50..12cc8208 100644 --- a/gst/level/gstlevel.c +++ b/gst/level/gstlevel.c @@ -25,14 +25,12 @@ #include "config.h" #endif #include <gst/gst.h> -#include <gst/audio/audio.h> #include "gstlevel.h" #include "math.h" GST_DEBUG_CATEGORY (level_debug); #define GST_CAT_DEFAULT level_debug -/* elementfactory information */ static GstElementDetails level_details = { "Level", "Filter/Analyzer/Audio", @@ -40,8 +38,6 @@ static GstElementDetails level_details = { "Thomas <thomas@apestaart.org>" }; -/* pad templates */ - static GstStaticPadTemplate sink_template_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, @@ -66,87 +62,170 @@ GST_STATIC_PAD_TEMPLATE ("src", "depth = (int) { 8, 16 }, " "signed = (boolean) true") ); -/* Filter signals and args */ -enum -{ - /* FILL ME */ - SIGNAL_LEVEL, - LAST_SIGNAL -}; enum { - ARG_0, - ARG_SIGNAL_LEVEL, - ARG_SIGNAL_INTERVAL, - ARG_PEAK_TTL, - ARG_PEAK_FALLOFF + PROP_0, + PROP_SIGNAL_LEVEL, + PROP_SIGNAL_INTERVAL, + PROP_PEAK_TTL, + PROP_PEAK_FALLOFF }; -static void gst_level_class_init (GstLevelClass * klass); -static void gst_level_base_init (GstLevelClass * klass); -static void gst_level_init (GstLevel * filter); + +GST_BOILERPLATE (GstLevel, gst_level, GstBaseTransform, + GST_TYPE_BASE_TRANSFORM); + static void gst_level_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_level_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_level_chain (GstPad * pad, GstData * _data); +static gboolean gst_level_set_caps (GstBaseTransform * trans, GstCaps * in, + GstCaps * out); +static GstFlowReturn gst_level_transform (GstBaseTransform * trans, + GstBuffer * in, GstBuffer ** out); + + +static void +gst_level_base_init (gpointer g_class) +{ + GstElementClass *element_class = g_class; + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_template_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_template_factory)); + gst_element_class_set_details (element_class, &level_details); +} + +static void +gst_level_class_init (GstLevelClass * klass) +{ + GObjectClass *gobject_class; + GstBaseTransformClass *trans_class; + + gobject_class = (GObjectClass *) klass; + trans_class = (GstBaseTransformClass *) klass; + + gobject_class->set_property = gst_level_set_property; + gobject_class->get_property = gst_level_get_property; + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SIGNAL_LEVEL, + g_param_spec_boolean ("signal", "Signal", + "Emit level signals for each interval", TRUE, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SIGNAL_INTERVAL, + g_param_spec_double ("interval", "Interval", + "Interval between emissions (in seconds)", + 0.01, 100.0, 0.1, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PEAK_TTL, + g_param_spec_double ("peak_ttl", "Peak TTL", + "Time To Live of decay peak before it falls back", + 0, 100.0, 0.3, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PEAK_FALLOFF, + g_param_spec_double ("peak_falloff", "Peak Falloff", + "Decay rate of decay peak after TTL (in dB/sec)", + 0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE)); + + GST_DEBUG_CATEGORY_INIT (level_debug, "level", 0, "Level calculation"); + + trans_class->set_caps = gst_level_set_caps; + trans_class->transform = gst_level_transform; +} + +static void +gst_level_init (GstLevel * filter) +{ + filter->CS = NULL; + filter->peak = NULL; + filter->MS = NULL; + filter->RMS_dB = NULL; + + filter->rate = 0; + filter->width = 0; + filter->channels = 0; + + filter->interval = 0.1; + filter->decay_peak_ttl = 0.4; + filter->decay_peak_falloff = 10.0; /* dB falloff (/sec) */ +} + +static void +gst_level_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstLevel *filter = GST_LEVEL (object); -static GstElementClass *parent_class = NULL; -static guint gst_filter_signals[LAST_SIGNAL] = { 0 }; + switch (prop_id) { + case PROP_SIGNAL_LEVEL: + filter->signal = g_value_get_boolean (value); + break; + case PROP_SIGNAL_INTERVAL: + filter->interval = g_value_get_double (value); + break; + case PROP_PEAK_TTL: + filter->decay_peak_ttl = g_value_get_double (value); + break; + case PROP_PEAK_FALLOFF: + filter->decay_peak_falloff = g_value_get_double (value); + break; + default: + break; + } +} -GType -gst_level_get_type (void) +static void +gst_level_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) { - static GType level_type = 0; - - if (!level_type) { - static const GTypeInfo level_info = { - sizeof (GstLevelClass), - (GBaseInitFunc) gst_level_base_init, NULL, - (GClassInitFunc) gst_level_class_init, NULL, NULL, - sizeof (GstLevel), 0, - (GInstanceInitFunc) gst_level_init - }; - - level_type = g_type_register_static (GST_TYPE_ELEMENT, "GstLevel", - &level_info, 0); + GstLevel *filter = GST_LEVEL (object); + + switch (prop_id) { + case PROP_SIGNAL_LEVEL: + g_value_set_boolean (value, filter->signal); + break; + case PROP_SIGNAL_INTERVAL: + g_value_set_double (value, filter->interval); + break; + case PROP_PEAK_TTL: + g_value_set_double (value, filter->decay_peak_ttl); + break; + case PROP_PEAK_FALLOFF: + g_value_set_double (value, filter->decay_peak_falloff); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; } - return level_type; } -static GstPadLinkReturn -gst_level_link (GstPad * pad, const GstCaps * caps) +static gint +structure_get_int (GstStructure * structure, const gchar * field) +{ + gint ret; + + if (!gst_structure_get_int (structure, field, &ret)) + g_assert_not_reached (); + + return ret; +} + +static gboolean +gst_level_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out) { GstLevel *filter; - GstPad *otherpad; - GstPadLinkReturn res; GstStructure *structure; int i; - gboolean ret; - filter = GST_LEVEL (gst_pad_get_parent (pad)); - g_return_val_if_fail (filter != NULL, GST_PAD_LINK_REFUSED); - g_return_val_if_fail (GST_IS_LEVEL (filter), GST_PAD_LINK_REFUSED); - otherpad = (pad == filter->srcpad ? filter->sinkpad : filter->srcpad); - - res = gst_pad_try_set_caps (otherpad, caps); - /* if ok, set filter */ - if (res != GST_PAD_LINK_OK && res != GST_PAD_LINK_DONE) { - return res; - } + filter = GST_LEVEL (trans); filter->num_samples = 0; - structure = gst_caps_get_structure (caps, 0); - ret = gst_structure_get_int (structure, "rate", &filter->rate); - ret &= gst_structure_get_int (structure, "width", &filter->width); - ret &= gst_structure_get_int (structure, "channels", &filter->channels); - - if (!ret) - return GST_PAD_LINK_REFUSED; + structure = gst_caps_get_structure (in, 0); + filter->rate = structure_get_int (structure, "rate"); + filter->width = structure_get_int (structure, "width"); + filter->channels = structure_get_int (structure, "channels"); /* allocate channel variable arrays */ g_free (filter->CS); @@ -170,61 +249,129 @@ gst_level_link (GstPad * pad, const GstCaps * caps) filter->MS[i] = filter->RMS_dB[i] = 0.0; } - return GST_PAD_LINK_OK; + return TRUE; +} + +#if 0 +#define DEBUG(str,...) g_print (str, ...) +#else +#define DEBUG(str,...) /*nop */ +#endif + +/* process one (interleaved) channel of incoming samples + * calculate square sum of samples + * normalize and return normalized Cumulative Square + * caller must assure num is a multiple of channels + * this filter only accepts signed audio data, so mid level is always 0 + */ +#define DEFINE_LEVEL_CALCULATOR(TYPE) \ +static void inline \ +gst_level_calculate_##TYPE (TYPE * in, guint num, gint channels, \ + gint resolution, double *CS, double *peak) \ +{ \ + register int j; \ + double squaresum = 0.0; /* square sum of the integer samples */ \ + register double square = 0.0; /* Square */ \ + register double PSS = 0.0; /* Peak Square Sample */ \ + gdouble normalizer; \ + \ + *CS = 0.0; /* Cumulative Square for this block */ \ + \ + normalizer = (double) (1 << resolution); \ + \ + /* \ + * process data here \ + * input sample data enters in *in_data as 8 or 16 bit data \ + * samples for left and right channel are interleaved \ + * returns the Mean Square of the samples as a double between 0 and 1 \ + */ \ + \ + for (j = 0; j < num; j += channels) \ + { \ + DEBUG ("ch %d -> smp %d\n", j, in[j]); \ + square = (double) (in[j] * in[j]); \ + if (square > PSS) PSS = square; \ + squaresum += square; \ + } \ + *peak = PSS / ((double) normalizer * (double) normalizer); \ + \ + /* return normalized cumulative square */ \ + *CS = squaresum / ((double) normalizer * (double) normalizer); \ } -static void inline -gst_level_fast_16bit_chain (gint16 * in, guint num, gint channels, - gint resolution, double *CS, double *peak) -#include "filter.func" - static void inline - gst_level_fast_8bit_chain (gint8 * in, guint num, gint channels, - gint resolution, double *CS, double *peak) -#include "filter.func" - static void gst_level_chain (GstPad * pad, GstData * _data) +DEFINE_LEVEL_CALCULATOR (gint16); +DEFINE_LEVEL_CALCULATOR (gint8); + +static GstMessage * +gst_level_message_new (GstLevel * l, gdouble endtime) { - GstBuffer *buf = GST_BUFFER (_data); - GstLevel *filter; - gint16 *in_data; + GstStructure *s; + GValue v = { 0, }; + + g_value_init (&v, GST_TYPE_LIST); + + s = gst_structure_new ("level", "endtime", G_TYPE_DOUBLE, endtime, NULL); + /* will copy-by-value */ + gst_structure_set_value (s, "rms", &v); + gst_structure_set_value (s, "peak", &v); + gst_structure_set_value (s, "decay", &v); + + return gst_message_new_application (GST_OBJECT (l), s); +} +static void +gst_level_message_append_channel (GstMessage * m, gdouble rms, gdouble peak, + gdouble decay) +{ + GstStructure *s; + GValue v = { 0, }; + GValue *l; + + g_value_init (&v, G_TYPE_DOUBLE); + + s = (GstStructure *) gst_message_get_structure (m); + + l = (GValue *) gst_structure_get_value (s, "rms"); + g_value_set_double (&v, rms); + gst_value_list_append_value (l, &v); /* copies by value */ + + l = (GValue *) gst_structure_get_value (s, "peak"); + g_value_set_double (&v, peak); + gst_value_list_append_value (l, &v); /* copies by value */ + + l = (GValue *) gst_structure_get_value (s, "decay"); + g_value_set_double (&v, decay); + gst_value_list_append_value (l, &v); /* copies by value */ +} + +static GstFlowReturn +gst_level_transform (GstBaseTransform * trans, GstBuffer * in, GstBuffer ** out) +{ + GstLevel *filter; + gpointer in_data; double CS = 0.0; gint num_samples = 0; gint i; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (buf != NULL); - - filter = GST_LEVEL (GST_OBJECT_PARENT (pad)); - g_return_if_fail (filter != NULL); - g_return_if_fail (GST_IS_LEVEL (filter)); - - if (!gst_pad_is_negotiated (pad)) { - GST_ELEMENT_ERROR (filter, CORE, NEGOTIATION, (NULL), - ("sinkpad not negotiated")); - gst_data_unref (_data); - return; - } + filter = GST_LEVEL (trans); for (i = 0; i < filter->channels; ++i) filter->CS[i] = filter->peak[i] = filter->MS[i] = filter->RMS_dB[i] = 0.0; - in_data = (gint16 *) GST_BUFFER_DATA (buf); + in_data = GST_BUFFER_DATA (in); + num_samples = GST_BUFFER_SIZE (in) / (filter->width / 8); - /* total number of interleaved samples */ - num_samples = GST_BUFFER_SIZE (buf) / (filter->width / 8); - if (num_samples % filter->channels != 0) - g_warning - ("WARNING: level: programming error, data not properly interleaved"); + g_return_val_if_fail (num_samples % filter->channels == 0, + GST_FLOW_UNEXPECTED); for (i = 0; i < filter->channels; ++i) { switch (filter->width) { case 16: - gst_level_fast_16bit_chain (in_data + i, num_samples, + gst_level_calculate_gint16 (in_data + i, num_samples, filter->channels, filter->width - 1, &CS, &filter->peak[i]); break; case 8: - gst_level_fast_8bit_chain (((gint8 *) in_data) + i, num_samples, + gst_level_calculate_gint8 (((gint8 *) in_data) + i, num_samples, filter->channels, filter->width - 1, &CS, &filter->peak[i]); break; } @@ -233,21 +380,20 @@ gst_level_fast_16bit_chain (gint16 * in, guint num, gint channels, filter->CS[i] += CS; } - gst_pad_push (filter->srcpad, GST_DATA (buf)); filter->num_samples += num_samples; for (i = 0; i < filter->channels; ++i) { filter->decay_peak_age[i] += num_samples; - /* g_print ("filter peak info [%d]: peak %f, age %f\n", i, - filter->last_peak[i], filter->decay_peak_age[i]); */ + DEBUG ("filter peak info [%d]: peak %f, age %f\n", i, + filter->last_peak[i], filter->decay_peak_age[i]); /* update running peak */ if (filter->peak[i] > filter->last_peak[i]) filter->last_peak[i] = filter->peak[i]; /* update decay peak */ if (filter->peak[i] >= filter->decay_peak[i]) { - /* g_print ("new peak, %f\n", filter->peak[i]); */ + DEBUG ("new peak, %f\n", filter->peak[i]); filter->decay_peak[i] = filter->peak[i]; filter->decay_peak_age[i] = 0; } else { @@ -262,11 +408,11 @@ gst_level_fast_16bit_chain (gint16 * in, guint num, gint channels, falloff_dB = filter->decay_peak_falloff * length; falloff = pow (10, falloff_dB / -20.0); - /* g_print ("falloff: length %f, dB falloff %f, falloff factor %e\n", - length, falloff_dB, falloff); */ + DEBUG ("falloff: length %f, dB falloff %f, falloff factor %e\n", + length, falloff_dB, falloff); filter->decay_peak[i] *= falloff; - /* g_print ("peak is %f samples old, decayed with factor %e to %f\n", - filter->decay_peak_age[i], falloff, filter->decay_peak[i]); */ + DEBUG ("peak is %f samples old, decayed with factor %e to %f\n", + filter->decay_peak_age[i], falloff, filter->decay_peak[i]); } } } @@ -275,164 +421,34 @@ gst_level_fast_16bit_chain (gint16 * in, guint num, gint channels, if (filter->num_samples >= filter->interval * (gdouble) filter->rate) { if (filter->signal) { - gdouble RMS, peak, endtime; + GstMessage *m; + double endtime, RMS; + + endtime = (double) GST_BUFFER_TIMESTAMP (in) / GST_SECOND + + (double) num_samples / (double) filter->rate; + + m = gst_level_message_new (filter, endtime); for (i = 0; i < filter->channels; ++i) { RMS = sqrt (filter->CS[i] / (filter->num_samples / filter->channels)); - peak = filter->last_peak[i]; - num_samples = GST_BUFFER_SIZE (buf) / (filter->width / 8); - endtime = (double) GST_BUFFER_TIMESTAMP (buf) / GST_SECOND - + (double) num_samples / (double) filter->rate; - - g_signal_emit (G_OBJECT (filter), gst_filter_signals[SIGNAL_LEVEL], 0, - endtime, i, - 20 * log10 (RMS), 20 * log10 (filter->last_peak[i]), + + gst_level_message_append_channel (m, 20 * log10 (RMS), + 20 * log10 (filter->last_peak[i]), 20 * log10 (filter->decay_peak[i])); - /* we emitted, so reset cumulative and normal peak */ + + /* reset cumulative and normal peak */ filter->CS[i] = 0.0; filter->last_peak[i] = 0.0; } + + gst_element_post_message (GST_ELEMENT (filter), m); } filter->num_samples = 0; } -} - -static void -gst_level_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstLevel *filter; - - g_return_if_fail (GST_IS_LEVEL (object)); - filter = GST_LEVEL (object); - - switch (prop_id) { - case ARG_SIGNAL_LEVEL: - filter->signal = g_value_get_boolean (value); - break; - case ARG_SIGNAL_INTERVAL: - filter->interval = g_value_get_double (value); - break; - case ARG_PEAK_TTL: - filter->decay_peak_ttl = g_value_get_double (value); - break; - case ARG_PEAK_FALLOFF: - filter->decay_peak_falloff = g_value_get_double (value); - break; - default: - break; - } -} - -static void -gst_level_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstLevel *filter; - - g_return_if_fail (GST_IS_LEVEL (object)); - filter = GST_LEVEL (object); - - switch (prop_id) { - case ARG_SIGNAL_LEVEL: - g_value_set_boolean (value, filter->signal); - break; - case ARG_SIGNAL_INTERVAL: - g_value_set_double (value, filter->interval); - break; - case ARG_PEAK_TTL: - g_value_set_double (value, filter->decay_peak_ttl); - break; - case ARG_PEAK_FALLOFF: - g_value_set_double (value, filter->decay_peak_falloff); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} -static void -gst_level_base_init (GstLevelClass * klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&sink_template_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&src_template_factory)); - gst_element_class_set_details (element_class, &level_details); -} + *out = in; -static void -gst_level_class_init (GstLevelClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - parent_class = g_type_class_ref (GST_TYPE_ELEMENT); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SIGNAL_LEVEL, - g_param_spec_boolean ("signal", "Signal", - "Emit level signals for each interval", TRUE, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SIGNAL_INTERVAL, - g_param_spec_double ("interval", "Interval", - "Interval between emissions (in seconds)", - 0.01, 100.0, 0.1, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PEAK_TTL, - g_param_spec_double ("peak_ttl", "Peak TTL", - "Time To Live of decay peak before it falls back", - 0, 100.0, 0.3, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PEAK_FALLOFF, - g_param_spec_double ("peak_falloff", "Peak Falloff", - "Decay rate of decay peak after TTL (in dB/sec)", - 0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE)); - - gobject_class->set_property = gst_level_set_property; - gobject_class->get_property = gst_level_get_property; - - gst_filter_signals[SIGNAL_LEVEL] = - g_signal_new ("level", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstLevelClass, level), NULL, NULL, - gst_level_marshal_VOID__DOUBLE_INT_DOUBLE_DOUBLE_DOUBLE, - G_TYPE_NONE, 5, - G_TYPE_DOUBLE, G_TYPE_INT, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE); - - GST_DEBUG_CATEGORY_INIT (level_debug, "level", 0, "Level calculation"); -} - -static void -gst_level_init (GstLevel * filter) -{ - filter->sinkpad = - gst_pad_new_from_template (gst_static_pad_template_get - (&sink_template_factory), "sink"); - gst_pad_set_link_function (filter->sinkpad, gst_level_link); - gst_pad_set_getcaps_function (filter->sinkpad, gst_pad_proxy_getcaps); - gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad); - gst_pad_set_chain_function (filter->sinkpad, gst_level_chain); - - filter->srcpad = - gst_pad_new_from_template (gst_static_pad_template_get - (&src_template_factory), "src"); - gst_pad_set_link_function (filter->srcpad, gst_level_link); - gst_pad_set_getcaps_function (filter->srcpad, gst_pad_proxy_getcaps); - gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad); - - filter->CS = NULL; - filter->peak = NULL; - filter->MS = NULL; - filter->RMS_dB = NULL; - - filter->rate = 0; - filter->width = 0; - filter->channels = 0; - - filter->interval = 0.1; - filter->decay_peak_ttl = 0.4; - filter->decay_peak_falloff = 10.0; /* dB falloff (/sec) */ + return GST_FLOW_OK; } static gboolean |