From 8c36d071c2699694c2dabb9591221b66665f87c7 Mon Sep 17 00:00:00 2001 From: Aaron Viets <aaron.viets@ligo.org> Date: Wed, 12 Dec 2018 22:05:35 -0800 Subject: [PATCH] gstlal-calibration: new element lal_property to convert input data to a GObject property for other elements to use. --- gstlal-calibration/gst/lal/Makefile.am | 3 +- gstlal-calibration/gst/lal/gstlal_property.c | 545 ++++++++++++++++++ gstlal-calibration/gst/lal/gstlal_property.h | 99 ++++ .../gst/lal/gstlalcalibration.c | 2 + 4 files changed, 648 insertions(+), 1 deletion(-) create mode 100644 gstlal-calibration/gst/lal/gstlal_property.c create mode 100644 gstlal-calibration/gst/lal/gstlal_property.h diff --git a/gstlal-calibration/gst/lal/Makefile.am b/gstlal-calibration/gst/lal/Makefile.am index 69e8bcedf5..6e1cb03fe6 100644 --- a/gstlal-calibration/gst/lal/Makefile.am +++ b/gstlal-calibration/gst/lal/Makefile.am @@ -17,7 +17,8 @@ lib@GSTPLUGINPREFIX@gstlalcalibration_la_SOURCES = \ gstlal_transferfunction.c gstlal_transferfunction.h \ gstlal_trackfrequency.c gstlal_trackfrequency.h \ gstlal_adaptivefirfilt.c gstlal_adaptivefirfilt.h \ - gstlal_dqtukey.c gstlal_dqtukey.h + gstlal_dqtukey.c gstlal_dqtukey.h \ + gstlal_property.c gstlal_property.h lib@GSTPLUGINPREFIX@gstlalcalibration_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_CPPFLAGS) lib@GSTPLUGINPREFIX@gstlalcalibration_la_CFLAGS = $(AM_CFLAGS) $(LAL_CFLAGS) $(GSTLAL_CFLAGS) $(gstreamer_CFLAGS) $(gstreamer_audio_CFLAGS) lib@GSTPLUGINPREFIX@gstlalcalibration_la_LDFLAGS = $(AM_LDFLAGS) $(LAL_LIBS) $(GSTLAL_LIBS) $(PYTHON_LIBS) $(gstreamer_LIBS) $(gstreamer_audio_LIBS) $(GSTLAL_PLUGIN_LDFLAGS) diff --git a/gstlal-calibration/gst/lal/gstlal_property.c b/gstlal-calibration/gst/lal/gstlal_property.c new file mode 100644 index 0000000000..19a64b47f1 --- /dev/null +++ b/gstlal-calibration/gst/lal/gstlal_property.c @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2018 Aaron Viets <aaron.viets@ligo.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +/* + * ======================================================================== + * + * Preamble + * + * ======================================================================== + */ + + +/* + * stuff from the C library + */ + + +#include <complex.h> +#include <math.h> +#include <string.h> + + +/* + * stuff from glib/gstreamer + */ + + +#include <glib.h> +#include <gst/gst.h> +#include <gst/base/gstbasesink.h> +#include <gst/audio/audio.h> +#include <gst/audio/audio-format.h> + + +/* + * our own stuff + */ + + +#include <gstlal/gstlal.h> +#include <gstlal/gstlal_audio_info.h> +#include <gstlal/gstlal_debug.h> +#include <gstlal_property.h> + + +/* + * ============================================================================ + * + * GStreamer Boilerplate + * + * ============================================================================ + */ + + +#define GST_CAT_DEFAULT gstlal_property_debug +GST_DEBUG_CATEGORY_STATIC(GST_CAT_DEFAULT); + + +G_DEFINE_TYPE_WITH_CODE( + GSTLALProperty, + gstlal_property, + GST_TYPE_BASE_SINK, + GST_DEBUG_CATEGORY_INIT(GST_CAT_DEFAULT, "lal_property", 0, "lal_property element") +); + + +enum property { + ARG_UPDATE_SAMPLES = 1, + ARG_AVERAGE_SAMPLES, + ARG_SHIFT_SAMPLES, + ARG_UPDATE_WHEN_CHANGE, + ARG_CURRENT_AVERAGE, + ARG_FAKE +}; + + +static GParamSpec *properties[ARG_FAKE]; + + +/* + * ============================================================================ + * + * Utilities + * + * ============================================================================ + */ + + +static void rebuild_workspace_and_reset(GObject *object) { + return; +} + + +#define DEFINE_AVERAGE_INPUT_DATA(DTYPE) \ +static void average_input_data_ ## DTYPE(GSTLALProperty *element, DTYPE *src, guint64 src_size, guint64 pts) { \ + \ + gint64 i; \ + if(element->update_when_change) { \ + for(i = 0; i < (gint64) src_size; i++) { \ + /* Check if the input value has changed */ \ + if((double) src[i] != element->current_average) { \ + element->current_average = (double) src[i]; \ + GST_LOG_OBJECT(element, "Just computed new property"); \ + /* Let other elements know about the update */ \ + g_object_notify_by_pspec(G_OBJECT(element), properties[ARG_CURRENT_AVERAGE]); \ + } \ + } \ + } else { \ + gint64 start_sample, initial_samples, samples_to_add; \ + /* Find the location in src of the first sample that will go into the average */ \ + if(element->num_in_avg) \ + start_sample = 0; \ + else \ + start_sample = (gint64) (element->update_samples - (gst_util_uint64_scale_int_round(pts, element->rate, GST_SECOND) + element->average_samples - element->shift_samples) % element->update_samples) % element->update_samples; \ + \ + /* How many samples from this buffer will we need to add into this average? */ \ + samples_to_add = element->average_samples - element->num_in_avg < (gint64) src_size - start_sample ? element->average_samples - element->num_in_avg : (gint64) src_size - start_sample; \ + while(samples_to_add > 0) { \ + initial_samples = element->num_in_avg; \ + for(i = start_sample; i < start_sample + samples_to_add; i++) { \ + element->current_average += (double) src[i]; \ + } \ + element->num_in_avg += samples_to_add; \ + if(element->num_in_avg >= element->average_samples) { \ + \ + /* Number of samples in average should not become greater than specified by the user */ \ + g_assert_cmpint(element->num_in_avg, ==, element->average_samples); \ + \ + /* We still need to divide by n to get the average */ \ + element->current_average /= element->num_in_avg; \ + \ + GST_LOG_OBJECT(element, "Just computed new property"); \ + \ + /* Let other elements know about the update */ \ + g_object_notify_by_pspec(G_OBJECT(element), properties[ARG_CURRENT_AVERAGE]); \ + \ + element->num_in_avg = 0; \ + element->current_average = 0.0; \ + } \ + start_sample += element->update_samples - initial_samples; \ + samples_to_add = element->average_samples - element->num_in_avg < (gint64) src_size - start_sample ? element->average_samples - element->num_in_avg : (gint64) src_size - start_sample; \ + } \ + } \ + \ + return; \ +} + + +DEFINE_AVERAGE_INPUT_DATA(gint8); +DEFINE_AVERAGE_INPUT_DATA(gint16); +DEFINE_AVERAGE_INPUT_DATA(gint32); +DEFINE_AVERAGE_INPUT_DATA(guint8); +DEFINE_AVERAGE_INPUT_DATA(guint16); +DEFINE_AVERAGE_INPUT_DATA(guint32); +DEFINE_AVERAGE_INPUT_DATA(float); +DEFINE_AVERAGE_INPUT_DATA(double); + + +/* + * ============================================================================ + * + * GstBaseSink Overrides + * + * ============================================================================ + */ + + +/* + * get_unit_size() + */ + + +static gboolean get_unit_size(GstBaseSink *sink, GstCaps *caps, gsize *size) { + + GstAudioInfo info; + gboolean success = gstlal_audio_info_from_caps(&info, caps); + if(success) + *size = GST_AUDIO_INFO_BPF(&info); + else + GST_WARNING_OBJECT(sink, "unable to parse caps %" GST_PTR_FORMAT, caps); + return success; +} + + +/* + * set_caps() + */ + + +static gboolean set_caps(GstBaseSink *sink, GstCaps *caps) { + + GSTLALProperty *element = GSTLAL_PROPERTY(sink); + + gboolean success = TRUE; + + gsize unit_size; + + /* Parse the caps to find the format, sample rate, and number of channels */ + GstStructure *str = gst_caps_get_structure(caps, 0); + const gchar *name = gst_structure_get_string(str, "format"); + success &= (name != NULL); + success &= gst_structure_get_int(str, "rate", &element->rate); + + /* Find unit size */ + success &= get_unit_size(sink, caps, &unit_size); + element->unit_size = unit_size; + + /* Record the data type */ + if(success) { + if(!strchr(name, 'S')) + element->data_type = GSTLAL_PROPERTY_SIGNED; + else if(!strchr(name, 'U')) + element->data_type = GSTLAL_PROPERTY_UNSIGNED; + else if(!strchr(name, 'F')) + element->data_type = GSTLAL_PROPERTY_FLOAT; + else + g_assert_not_reached(); + } + + return success; +} + + +/* + * render() + */ + + +static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer) { + + GSTLALProperty *element = GSTLAL_PROPERTY(sink); + GstMapInfo mapinfo; + GstFlowReturn result = GST_FLOW_OK; + + /* + * check for discontinuity + */ + + if(G_UNLIKELY(GST_BUFFER_IS_DISCONT(buffer) || GST_BUFFER_OFFSET(buffer) != element->next_in_offset || !GST_CLOCK_TIME_IS_VALID(element->t0))) { + element->t0 = GST_BUFFER_PTS(buffer); + element->offset0 = GST_BUFFER_OFFSET(buffer); + if(!element->update_when_change) + element->current_average = 0.0; + } + element->next_in_offset = GST_BUFFER_OFFSET_END(buffer); + GST_DEBUG_OBJECT(element, "have buffer spanning %" GST_BUFFER_BOUNDARIES_FORMAT, GST_BUFFER_BOUNDARIES_ARGS(buffer)); + + /* Check if the data on this buffer is usable and if we plan to use it */ + gint64 next_start_sample = (element->update_samples - (gst_util_uint64_scale_int_round(GST_BUFFER_PTS(buffer), element->rate, GST_SECOND) + element->average_samples - element->shift_samples) % element->update_samples) % element->update_samples; + if(!GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_GAP) && mapinfo.size && (element->num_in_avg || next_start_sample < (gint64) gst_util_uint64_scale_int_round(GST_BUFFER_DURATION(buffer), element->rate, GST_SECOND) || element->update_when_change)) { + /* Get the data from the buffer */ + gst_buffer_map(buffer, &mapinfo, GST_MAP_READ); + + switch(element->data_type) { + case GSTLAL_PROPERTY_SIGNED: + switch(element->unit_size) { + case 8: + average_input_data_gint8(element, (gint8 *) mapinfo.data, mapinfo.size / element->unit_size, GST_BUFFER_PTS(buffer)); + break; + case 16: + average_input_data_gint16(element, (gint16 *) mapinfo.data, mapinfo.size / element->unit_size, GST_BUFFER_PTS(buffer)); + break; + case 32: + average_input_data_gint32(element, (gint32 *) mapinfo.data, mapinfo.size / element->unit_size, GST_BUFFER_PTS(buffer)); + break; + default: + g_assert_not_reached(); + break; + } + case GSTLAL_PROPERTY_UNSIGNED: + switch(element->unit_size) { + case 8: + average_input_data_guint8(element, (guint8 *) mapinfo.data, mapinfo.size / element->unit_size, GST_BUFFER_PTS(buffer)); + break; + case 16: + average_input_data_guint16(element, (guint16 *) mapinfo.data, mapinfo.size / element->unit_size, GST_BUFFER_PTS(buffer)); + break; + case 32: + average_input_data_guint32(element, (guint32 *) mapinfo.data, mapinfo.size / element->unit_size, GST_BUFFER_PTS(buffer)); + break; + default: + g_assert_not_reached(); + break; + } + case GSTLAL_PROPERTY_FLOAT: + switch(element->unit_size) { + case 32: + average_input_data_float(element, (float *) mapinfo.data, mapinfo.size / element->unit_size, GST_BUFFER_PTS(buffer)); + break; + case 64: + average_input_data_double(element, (double *) mapinfo.data, mapinfo.size / element->unit_size, GST_BUFFER_PTS(buffer)); + break; + default: + g_assert_not_reached(); + break; + } + default: + g_assert_not_reached(); + break; + + } + gst_buffer_unmap(buffer, &mapinfo); + } + + return result; +} + + +/* + * ============================================================================ + * + * GObject Methods + * + * ============================================================================ + */ + + +/* + * properties + */ + + +static void set_property(GObject *object, enum property id, const GValue *value, GParamSpec *pspec) +{ + GSTLALProperty *element = GSTLAL_PROPERTY(object); + + GST_OBJECT_LOCK(element); + + switch(id) { + case ARG_UPDATE_SAMPLES: + element->update_samples = g_value_get_int64(value); + break; + + case ARG_AVERAGE_SAMPLES: + element->average_samples = g_value_get_int64(value); + break; + + case ARG_SHIFT_SAMPLES: + element->shift_samples = g_value_get_int64(value); + break; + + case ARG_UPDATE_WHEN_CHANGE: + element->update_when_change = g_value_get_boolean(value); + break; + + case ARG_CURRENT_AVERAGE: + element->current_average = g_value_get_double(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, id, pspec); + break; + } + + GST_OBJECT_UNLOCK(element); +} + + +static void get_property(GObject *object, enum property id, GValue *value, GParamSpec *pspec) +{ + GSTLALProperty *element = GSTLAL_PROPERTY(object); + + GST_OBJECT_LOCK(element); + + switch(id) { + case ARG_UPDATE_SAMPLES: + g_value_set_int64(value, element->update_samples); + break; + + case ARG_AVERAGE_SAMPLES: + g_value_set_int64(value, element->average_samples); + break; + + case ARG_SHIFT_SAMPLES: + g_value_set_int64(value, element->shift_samples); + break; + + case ARG_UPDATE_WHEN_CHANGE: + g_value_set_boolean(value, element->update_when_change); + break; + + case ARG_CURRENT_AVERAGE: + g_value_set_double(value, element->current_average); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, id, pspec); + break; + } + + GST_OBJECT_UNLOCK(element); +} + + +/* + * class_init() + */ + + +#define CAPS \ + "audio/x-raw, " \ + "rate = (int) [1, MAX], " \ + "channels = (int) 1, " \ + "format = (string) {"GST_AUDIO_NE(F32)", "GST_AUDIO_NE(F64)", "GST_AUDIO_NE(U8)", "GST_AUDIO_NE(U16)", "GST_AUDIO_NE(U32)", "GST_AUDIO_NE(S8)", "GST_AUDIO_NE(S16)", "GST_AUDIO_NE(S32)"}, " \ + "layout = (string) interleaved, " \ + "channel-mask = (bitmask) 0" + + +static void gstlal_property_class_init(GSTLALPropertyClass *klass) { + + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + GstElementClass *element_class = GST_ELEMENT_CLASS(klass); + GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS(klass); + + gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR(set_caps); + gstbasesink_class->render = GST_DEBUG_FUNCPTR(render); + + gobject_class->set_property = GST_DEBUG_FUNCPTR(set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR(get_property); + + gst_element_class_set_details_simple( + element_class, + "Convert input data to a GObject property", + "Sink", + "Convert single-channel input data into a GObject property that can\n\t\t\t " + "be passed to other elements. The timing and frequency of updates\n\t\t\t " + "can be controlled by the user, or updates can be made to happen\n\t\t\t " + "anytime the input values change.", + "Aaron Viets <aaron.viets@ligo.org>" + ); + + gst_element_class_add_pad_template( + element_class, + gst_pad_template_new( + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + gst_caps_from_string(CAPS) + ) + ); + + + properties[ARG_UPDATE_SAMPLES] = g_param_spec_int64( + "update-samples", + "Update Samples", + "Number of input samples after which to update the property", + 0, G_MAXINT64, 320, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT + ); + properties[ARG_AVERAGE_SAMPLES] = g_param_spec_int64( + "average-samples", + "Average Samples", + "Number of input samples to average before updating the property", + 0, G_MAXINT64, 1, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT + ); + properties[ARG_SHIFT_SAMPLES] = g_param_spec_int64( + "shift-samples", + "Shift Samples", + "Number of input samples to shift the time of an update from a multiple of\n\t\t\t" + "update-samples", + G_MININT64, G_MAXINT64, 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT + ); + properties[ARG_UPDATE_WHEN_CHANGE] = g_param_spec_boolean( + "update-when-change", + "Update When Change", + "If true, updates will happen anytime there is a change in the input values", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT + ); + properties[ARG_CURRENT_AVERAGE] = g_param_spec_double( + "current-average", + "Current Average", + "The current value of the property, averaged over average-samples samples", + -G_MAXDOUBLE, G_MAXDOUBLE, -G_MAXDOUBLE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS + ); + + + g_object_class_install_property( + gobject_class, + ARG_UPDATE_SAMPLES, + properties[ARG_UPDATE_SAMPLES] + ); + g_object_class_install_property( + gobject_class, + ARG_AVERAGE_SAMPLES, + properties[ARG_AVERAGE_SAMPLES] + ); + g_object_class_install_property( + gobject_class, + ARG_SHIFT_SAMPLES, + properties[ARG_SHIFT_SAMPLES] + ); + g_object_class_install_property( + gobject_class, + ARG_UPDATE_WHEN_CHANGE, + properties[ARG_UPDATE_WHEN_CHANGE] + ); + g_object_class_install_property( + gobject_class, + ARG_CURRENT_AVERAGE, + properties[ARG_CURRENT_AVERAGE] + ); +} + + +/* + * init() + */ + + +static void gstlal_property_init(GSTLALProperty *element) { + + g_signal_connect(G_OBJECT(element), "notify::current-average", G_CALLBACK(rebuild_workspace_and_reset), NULL); + element->rate = 0; + element->unit_size = 0; + element->current_average = -G_MAXDOUBLE; + element->num_in_avg = 0; + element->update_samples = 0; + element->average_samples = 0; + element->shift_samples = 0; + element->update_when_change = FALSE; + + gst_base_sink_set_sync(GST_BASE_SINK(element), FALSE); + gst_base_sink_set_async_enabled(GST_BASE_SINK(element), FALSE); +} + diff --git a/gstlal-calibration/gst/lal/gstlal_property.h b/gstlal-calibration/gst/lal/gstlal_property.h new file mode 100644 index 0000000000..c9c568ef1c --- /dev/null +++ b/gstlal-calibration/gst/lal/gstlal_property.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 Aaron Viets <aaron.viets@ligo.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __GSTLAL_PROPERTY_H__ +#define __GSTLAL_PROPERTY_H__ + + +#include <glib.h> +#include <gst/gst.h> +#include <gst/base/gstbasesink.h> + + +G_BEGIN_DECLS + + +#define GSTLAL_PROPERTY_TYPE \ + (gstlal_property_get_type()) +#define GSTLAL_PROPERTY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GSTLAL_PROPERTY_TYPE, GSTLALProperty)) +#define GSTLAL_PROPERTY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GSTLAL_PROPERTY_TYPE, GSTLALPropertyClass)) +#define GST_IS_GSTLAL_PROPERTY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GSTLAL_PROPERTY_TYPE)) +#define GST_IS_GSTLAL_PROPERTY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GSTLAL_PROPERTY_TYPE)) + + +typedef struct _GSTLALProperty GSTLALProperty; +typedef struct _GSTLALPropertyClass GSTLALPropertyClass; + + +/** + * GSTLALProperty: + */ + + +struct _GSTLALProperty { + GstBaseSink basesink; + + /* stream info */ + gint rate; + gint unit_size; + enum gstlal_property_data_type { + GSTLAL_PROPERTY_SIGNED = 0, + GSTLAL_PROPERTY_UNSIGNED, + GSTLAL_PROPERTY_FLOAT, + } data_type; + + /* timestamp bookkeeping */ + GstClockTime t0; + guint64 offset0; + guint64 next_in_offset; + + /* filter memory */ + gint64 num_in_avg; + + /* properties */ + gint64 update_samples; + gint64 shift_samples; + gint64 average_samples; + gboolean update_when_change; + double current_average; +}; + + +/** + * GSTLALPropertyClass: + * @parent_class: the parent class + */ + + +struct _GSTLALPropertyClass { + GstBaseSinkClass parent_class; +}; + + +GType gstlal_property_get_type(void); + + +G_END_DECLS + + +#endif /* __GSTLAL_PROPERTY_H__ */ diff --git a/gstlal-calibration/gst/lal/gstlalcalibration.c b/gstlal-calibration/gst/lal/gstlalcalibration.c index 5d92ae4a5c..3805679957 100644 --- a/gstlal-calibration/gst/lal/gstlalcalibration.c +++ b/gstlal-calibration/gst/lal/gstlalcalibration.c @@ -69,6 +69,7 @@ #include <gstlal_trackfrequency.h> #include <gstlal_adaptivefirfilt.h> #include <gstlal_dqtukey.h> +#include <gstlal_property.h> /* @@ -102,6 +103,7 @@ static gboolean plugin_init(GstPlugin *plugin) {"lal_trackfrequency", GSTLAL_TRACKFREQUENCY_TYPE}, {"lal_adaptivefirfilt", GSTLAL_ADAPTIVEFIRFILT_TYPE}, {"lal_dqtukey", GSTLAL_DQTUKEY_TYPE}, + {"lal_property", GSTLAL_PROPERTY_TYPE}, {NULL, 0}, }; -- GitLab