static gboolean gst_level_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out) { GstLevel *filter = GST_LEVEL (trans); GstAudioInfo info; gint i, channels; if (!gst_audio_info_from_caps (&info, in)) return FALSE; switch (GST_AUDIO_INFO_FORMAT (&info)) { case GST_AUDIO_FORMAT_S8: filter->process = gst_level_calculate_gint8; break; case GST_AUDIO_FORMAT_S16: filter->process = gst_level_calculate_gint16; break; case GST_AUDIO_FORMAT_S32: filter->process = gst_level_calculate_gint32; break; case GST_AUDIO_FORMAT_F32: filter->process = gst_level_calculate_gfloat; break; case GST_AUDIO_FORMAT_F64: filter->process = gst_level_calculate_gdouble; break; default: filter->process = NULL; break; } filter->info = info; channels = GST_AUDIO_INFO_CHANNELS (&info); /* allocate channel variable arrays */ g_free (filter->CS); g_free (filter->peak); g_free (filter->last_peak); g_free (filter->decay_peak); g_free (filter->decay_peak_base); g_free (filter->decay_peak_age); filter->CS = g_new (gdouble, channels); filter->peak = g_new (gdouble, channels); filter->last_peak = g_new (gdouble, channels); filter->decay_peak = g_new (gdouble, channels); filter->decay_peak_base = g_new (gdouble, channels); filter->decay_peak_age = g_new (GstClockTime, channels); for (i = 0; i < channels; ++i) { filter->CS[i] = filter->peak[i] = filter->last_peak[i] = filter->decay_peak[i] = filter->decay_peak_base[i] = 0.0; filter->decay_peak_age[i] = G_GUINT64_CONSTANT (0); } gst_level_recalc_interval_frames (filter); return TRUE; }
static void gst_level_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstLevel *filter = GST_LEVEL (object); switch (prop_id) { case PROP_POST_MESSAGES: /* fall-through */ case PROP_MESSAGE: g_value_set_boolean (value, filter->post_messages); break; case PROP_INTERVAL: g_value_set_uint64 (value, filter->interval); break; case PROP_PEAK_TTL: g_value_set_uint64 (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; } }
static void gst_level_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstLevel *filter = GST_LEVEL (object); switch (prop_id) { case PROP_POST_MESSAGES: /* fall-through */ case PROP_MESSAGE: filter->post_messages = g_value_get_boolean (value); break; case PROP_INTERVAL: filter->interval = g_value_get_uint64 (value); /* Not exactly thread-safe, but property does not advertise that it * can be changed at runtime anyway */ if (GST_AUDIO_INFO_RATE (&filter->info)) { gst_level_recalc_interval_frames (filter); } break; case PROP_PEAK_TTL: filter->decay_peak_ttl = gst_guint64_to_gdouble (g_value_get_uint64 (value)); break; case PROP_PEAK_FALLOFF: filter->decay_peak_falloff = g_value_get_double (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void gst_level_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstLevel *filter = GST_LEVEL (object); switch (prop_id) { case PROP_POST_MESSAGES: /* fall-through */ case PROP_MESSAGE: filter->post_messages = g_value_get_boolean (value); break; case PROP_INTERVAL: filter->interval = g_value_get_uint64 (value); if (GST_AUDIO_INFO_RATE (&filter->info)) { filter->interval_frames = GST_CLOCK_TIME_TO_FRAMES (filter->interval, GST_AUDIO_INFO_RATE (&filter->info)); } break; case PROP_PEAK_TTL: filter->decay_peak_ttl = gst_guint64_to_gdouble (g_value_get_uint64 (value)); break; case PROP_PEAK_FALLOFF: filter->decay_peak_falloff = g_value_get_double (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_level_start (GstBaseTransform * trans) { GstLevel *filter = GST_LEVEL (trans); filter->num_frames = 0; filter->message_ts = GST_CLOCK_TIME_NONE; return TRUE; }
static gboolean gst_level_sink_event (GstBaseTransform * trans, GstEvent * event) { if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) { GstLevel *filter = GST_LEVEL (trans); gst_level_post_message (filter); } return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event); }
static void gst_level_finalize (GObject * obj) { GstLevel *filter = GST_LEVEL (obj); g_free (filter->CS); g_free (filter->peak); g_free (filter->last_peak); g_free (filter->decay_peak); g_free (filter->decay_peak_base); g_free (filter->decay_peak_age); filter->CS = NULL; filter->peak = NULL; filter->last_peak = NULL; filter->decay_peak = NULL; filter->decay_peak_base = NULL; filter->decay_peak_age = NULL; G_OBJECT_CLASS (parent_class)->finalize (obj); }
static GstFlowReturn gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in) { GstLevel *filter; GstMapInfo map; guint8 *in_data; gsize in_size; gdouble CS; guint i; guint num_frames; guint num_int_samples = 0; /* number of interleaved samples * ie. total count for all channels combined */ guint block_size, block_int_size; /* we subdivide buffers to not skip message * intervals */ GstClockTimeDiff falloff_time; gint channels, rate, bps; filter = GST_LEVEL (trans); channels = GST_AUDIO_INFO_CHANNELS (&filter->info); bps = GST_AUDIO_INFO_BPS (&filter->info); rate = GST_AUDIO_INFO_RATE (&filter->info); gst_buffer_map (in, &map, GST_MAP_READ); in_data = map.data; in_size = map.size; num_int_samples = in_size / bps; GST_LOG_OBJECT (filter, "analyzing %u sample frames at ts %" GST_TIME_FORMAT, num_int_samples, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (in))); g_return_val_if_fail (num_int_samples % channels == 0, GST_FLOW_ERROR); if (GST_BUFFER_FLAG_IS_SET (in, GST_BUFFER_FLAG_DISCONT)) { filter->message_ts = GST_BUFFER_TIMESTAMP (in); filter->num_frames = 0; } if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (filter->message_ts))) { filter->message_ts = GST_BUFFER_TIMESTAMP (in); } num_frames = num_int_samples / channels; while (num_frames > 0) { block_size = filter->interval_frames - filter->num_frames; block_size = MIN (block_size, num_frames); block_int_size = block_size * channels; for (i = 0; i < channels; ++i) { if (!GST_BUFFER_FLAG_IS_SET (in, GST_BUFFER_FLAG_GAP)) { filter->process (in_data + (bps * i), block_int_size, channels, &CS, &filter->peak[i]); GST_LOG_OBJECT (filter, "[%d]: cumulative squares %lf, over %d samples/%d channels", i, CS, block_int_size, channels); filter->CS[i] += CS; } else { filter->peak[i] = 0.0; } filter->decay_peak_age[i] += GST_FRAMES_TO_CLOCK_TIME (num_frames, rate); GST_LOG_OBJECT (filter, "[%d]: peak %f, last peak %f, decay peak %f, age %" GST_TIME_FORMAT, i, filter->peak[i], filter->last_peak[i], filter->decay_peak[i], GST_TIME_ARGS (filter->decay_peak_age[i])); /* update running peak */ if (filter->peak[i] > filter->last_peak[i]) filter->last_peak[i] = filter->peak[i]; /* make decay peak fall off if too old */ falloff_time = GST_CLOCK_DIFF (gst_gdouble_to_guint64 (filter->decay_peak_ttl), filter->decay_peak_age[i]); if (falloff_time > 0) { gdouble falloff_dB; gdouble falloff; gdouble length; /* length of falloff time in seconds */ length = (gdouble) falloff_time / (gdouble) GST_SECOND; falloff_dB = filter->decay_peak_falloff * length; falloff = pow (10, falloff_dB / -20.0); GST_LOG_OBJECT (filter, "falloff: current %f, base %f, interval %" GST_TIME_FORMAT ", dB falloff %f, factor %e", filter->decay_peak[i], filter->decay_peak_base[i], GST_TIME_ARGS (falloff_time), falloff_dB, falloff); filter->decay_peak[i] = filter->decay_peak_base[i] * falloff; GST_LOG_OBJECT (filter, "peak is %" GST_TIME_FORMAT " old, decayed with factor %e to %f", GST_TIME_ARGS (filter->decay_peak_age[i]), falloff, filter->decay_peak[i]); } else { GST_LOG_OBJECT (filter, "peak not old enough, not decaying"); } /* if the peak of this run is higher, the decay peak gets reset */ if (filter->peak[i] >= filter->decay_peak[i]) { GST_LOG_OBJECT (filter, "new peak, %f", filter->peak[i]); filter->decay_peak[i] = filter->peak[i]; filter->decay_peak_base[i] = filter->peak[i]; filter->decay_peak_age[i] = G_GINT64_CONSTANT (0); } } in_data += block_size * bps * channels; filter->num_frames += block_size; num_frames -= block_size; /* do we need to message ? */ if (filter->num_frames >= filter->interval_frames) { gst_level_post_message (filter); } } gst_buffer_unmap (in, &map); return GST_FLOW_OK; }