/* * ============================================================================ * * Preamble * * ============================================================================ */ /* * stuff from the C library */ #include /* * stuff from gobject/gstreamer */ #include #include #include #include /* * stuff from gstlal */ #include /* * ============================================================================ * * Utilities * * ============================================================================ */ /* * the number of samples available in the adapter */ static guint64 get_available_samples(GSTLALMean *element) { return gst_adapter_available(element->adapter) / sizeof(double); } /* * construct a buffer of zeros and push into adapter */ static int push_zeros(GSTLALMean *element, unsigned samples) { GstBuffer *zerobuf = gst_buffer_new_and_alloc(samples * sizeof(double)); if(!zerobuf) { GST_DEBUG_OBJECT(element, "failure allocating zero-pad buffer"); return -1; } memset(GST_BUFFER_DATA(zerobuf), 0, GST_BUFFER_SIZE(zerobuf)); gst_adapter_push(element->adapter, zerobuf); return 0; } /* * set the metadata on an output buffer */ static void set_metadata(GSTLALMean *element, GstBuffer *buf, guint64 outsamples, gboolean gap) { GST_BUFFER_SIZE(buf) = outsamples * 1 * sizeof(double); /* 1 = channels */ GST_BUFFER_OFFSET(buf) = element->next_out_offset; element->next_out_offset += outsamples; GST_BUFFER_OFFSET_END(buf) = element->next_out_offset; GST_BUFFER_TIMESTAMP(buf) = element->t0 + gst_util_uint64_scale_int_round(GST_BUFFER_OFFSET(buf) - element->offset0, GST_SECOND, element->rate); GST_BUFFER_DURATION(buf) = element->t0 + gst_util_uint64_scale_int_round(GST_BUFFER_OFFSET_END(buf) - element->offset0, GST_SECOND, element->rate) - GST_BUFFER_TIMESTAMP(buf); if(element->need_discont) { GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_DISCONT); element->need_discont = FALSE; } if(gap) GST_BUFFER_FLAG_SET(buf, GST_BUFFER_FLAG_GAP); else GST_BUFFER_FLAG_UNSET(buf, GST_BUFFER_FLAG_GAP); } /* * flush adapter */ static void flush(GSTLALMean *element, guint64 available_length) { if(available_length > element->n - 1) gst_adapter_flush(element->adapter, (available_length - (element->n - 1)) * sizeof(double)); } /* * averaging algorithm front-end */ static GstFlowReturn filter(GSTLALMean *element, GstBuffer *outbuf, guint64 output_length) { double *in, *out; guint64 available_length; guint64 i; guint64 j; /* * how much data is available? */ available_length = get_available_samples(element); g_assert(available_length >= output_length); /* * compute output samples */ in = (double *) gst_adapter_peek(element->adapter, available_length * sizeof(double)); out = (double *) GST_BUFFER_DATA(outbuf); for(i = 0; i < output_length; i++) { double sum = 0; guint64 offset = available_length - output_length + i; for(j = 0; j < element->n; j++) { if(j > offset) break; sum += in[offset - j]; } out[i] = sum / j; } /* * output produced? */ if(!output_length) return GST_BASE_TRANSFORM_FLOW_DROPPED; /* * flush data from the adapter. we want n - 1 samples to remain */ flush(element, available_length); /* * set buffer metadata */ set_metadata(element, outbuf, output_length, FALSE); /* * done */ return GST_FLOW_OK; } /* * ============================================================================ * * GStreamer Boiler Plate * * ============================================================================ */ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE( GST_BASE_TRANSFORM_SINK_NAME, GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS( "audio/x-raw-float, " \ "rate = (int) [1, MAX], " \ "channels = (int) 1, " \ "endianness = (int) BYTE_ORDER, " \ "width = (int) 64" ) ); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE( GST_BASE_TRANSFORM_SRC_NAME, GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS( "audio/x-raw-float, " \ "rate = (int) [1, MAX], " \ "channels = (int) 1, " \ "endianness = (int) BYTE_ORDER, " \ "width = (int) 64" ) ); GST_BOILERPLATE( GSTLALMean, gstlal_mean, GstBaseTransform, GST_TYPE_BASE_TRANSFORM ); enum property { ARG_N = 1 }; #define DEFAULT_N 1 /* * ============================================================================ * * GstBaseTransform Method Overrides * * ============================================================================ */ /* * get_unit_size() */ static gboolean get_unit_size(GstBaseTransform *trans, GstCaps *caps, guint *size) { GstStructure *str; gint channels; str = gst_caps_get_structure(caps, 0); if(!gst_structure_get_int(str, "channels", &channels)) { GST_DEBUG_OBJECT(trans, "unable to parse channels from %" GST_PTR_FORMAT, caps); return FALSE; } *size = sizeof(double) * channels; return TRUE; } /* * set_caps() */ static gboolean set_caps(GstBaseTransform *trans, GstCaps *incaps, GstCaps *outcaps) { GSTLALMean *element = GSTLAL_MEAN(trans); GstStructure *s; gint rate; gboolean success = TRUE; s = gst_caps_get_structure(outcaps, 0); if(!gst_structure_get_int(s, "rate", &rate)) { GST_DEBUG_OBJECT(element, "unable to parse rate from %" GST_PTR_FORMAT, outcaps); success = FALSE; } if(success) { element->rate = rate; } return success; } /* * start() */ static gboolean start(GstBaseTransform *trans) { GSTLALMean *element = GSTLAL_MEAN(trans); element->adapter = gst_adapter_new(); element->t0 = GST_CLOCK_TIME_NONE; element->offset0 = GST_BUFFER_OFFSET_NONE; element->next_out_offset = GST_BUFFER_OFFSET_NONE; element->need_discont = TRUE; return TRUE; } /* * stop() */ static gboolean stop(GstBaseTransform *trans) { GSTLALMean *element = GSTLAL_MEAN(trans); g_object_unref(element->adapter); element->adapter = NULL; return TRUE; } /* * transform() */ static GstFlowReturn transform(GstBaseTransform *trans, GstBuffer *inbuf, GstBuffer *outbuf) { GSTLALMean *element = GSTLAL_MEAN(trans); guint64 in_length; GstFlowReturn result; /* * check for discontinuity */ if(GST_BUFFER_IS_DISCONT(inbuf)) { /* * flush adapter */ gst_adapter_clear(element->adapter); /* * (re)sync timestamp and offset book-keeping */ element->t0 = GST_BUFFER_TIMESTAMP(inbuf); element->offset0 = GST_BUFFER_OFFSET(inbuf); element->next_out_offset = element->offset0; /* * be sure to flag the next output buffer as a discontinuity */ element->need_discont = TRUE; } /* * gap logic */ in_length = GST_BUFFER_OFFSET_END(inbuf) - GST_BUFFER_OFFSET(inbuf); if(!GST_BUFFER_FLAG_IS_SET(inbuf, GST_BUFFER_FLAG_GAP)) { /* * input is not 0s. */ gst_buffer_ref(inbuf); /* don't let the adapter free it */ gst_adapter_push(element->adapter, inbuf); result = filter(element, outbuf, in_length); } else { /* * input is 0s. */ push_zeros(element, in_length); flush(element, get_available_samples(element)); memset(GST_BUFFER_DATA(outbuf), 0, GST_BUFFER_SIZE(outbuf)); set_metadata(element, outbuf, in_length, TRUE); result = GST_FLOW_OK; } /* * done */ return result; } /* * ============================================================================ * * GObject Method Overrides * * ============================================================================ */ /* * set_property() */ static void set_property(GObject *object, enum property prop_id, const GValue *value, GParamSpec *pspec) { GSTLALMean *element = GSTLAL_MEAN(object); GST_OBJECT_LOCK(element); switch (prop_id) { case ARG_N: { guint32 old_n = element->n; element->n = g_value_get_uint(value); if(element->n != old_n) g_object_notify(object, "n"); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } GST_OBJECT_UNLOCK(element); } /* * get_property() */ static void get_property(GObject *object, enum property prop_id, GValue *value, GParamSpec *pspec) { GSTLALMean *element = GSTLAL_MEAN(object); GST_OBJECT_LOCK(element); switch (prop_id) { case ARG_N: g_value_set_uint(value, element->n); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } GST_OBJECT_UNLOCK(element); } /* * finalize() */ static void finalize(GObject *object) { GSTLALMean *element = GSTLAL_MEAN(object); /* * free resources */ if(element->adapter) { g_object_unref(element->adapter); element->adapter = NULL; } /* * chain to parent class' finalize() method */ G_OBJECT_CLASS(parent_class)->finalize(object); } /* * base_init() */ static void gstlal_mean_base_init(gpointer gclass) { GstElementClass *element_class = GST_ELEMENT_CLASS(gclass); GstBaseTransformClass *transform_class = GST_BASE_TRANSFORM_CLASS(gclass); gst_element_class_set_details_simple(element_class, "Average of last N samples", "Filter/Audio", "Each output sample is the average of the N most recent samples", "Kipp Cannon "); gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&src_factory)); gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&sink_factory)); transform_class->get_unit_size = GST_DEBUG_FUNCPTR(get_unit_size); transform_class->set_caps = GST_DEBUG_FUNCPTR(set_caps); transform_class->transform = GST_DEBUG_FUNCPTR(transform); transform_class->start = GST_DEBUG_FUNCPTR(start); transform_class->stop = GST_DEBUG_FUNCPTR(stop); } /* * class_init() */ static void gstlal_mean_class_init(GSTLALMeanClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->set_property = GST_DEBUG_FUNCPTR(set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR(get_property); gobject_class->finalize = GST_DEBUG_FUNCPTR(finalize); g_object_class_install_property( gobject_class, ARG_N, g_param_spec_uint( "n", "n", "Number of samples to average.", 0, G_MAXUINT, DEFAULT_N, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS ) ); } /* * init() */ static void gstlal_mean_init(GSTLALMean *filter, GSTLALMeanClass *kclass) { filter->rate = 0; filter->adapter = NULL; filter->n = DEFAULT_N; gst_base_transform_set_gap_aware(GST_BASE_TRANSFORM(filter), TRUE); }