diff --git a/gstlal-calibration/bin/gstlal_compute_strain b/gstlal-calibration/bin/gstlal_compute_strain
index be1654d0371285934e83aeffb7c90212196c34fa..2b71b1985dd16f746b7b543dbc8c2f6e38291847 100755
--- a/gstlal-calibration/bin/gstlal_compute_strain
+++ b/gstlal-calibration/bin/gstlal_compute_strain
@@ -2367,7 +2367,7 @@ if remove_cal_lines:
 
 # Set up gating for the power mains and noise subtraction
 if compute_calib_statevector and (remove_power_lines or witness_channel_list is not None) and noisesub_gate_bitmask > 0:
-	noisesubgate = obsintentchanneltee if noisesub_gate_channel == obsintent_channel_name else lownoisetee if noisesub_gate_channel == lownoise_channel_name else hwinjtee if noisesub_gate_channel == hwinj_channel_name else calibration_parts.caps_and_progress(pipeline, head_dict["noisesubgatechannel"], "audio/x-raw, format=U32LE, channels=1, channel-mask=(bitmask)0x0", noisesub_gate_channel)
+	noisesubgate = obsintentchanneltee if noisesub_gate_channel == obsintent_channel_name else lownoisechanneltee if noisesub_gate_channel == lownoise_channel_name else hwinjtee if noisesub_gate_channel == hwinj_channel_name else calibration_parts.caps_and_progress(pipeline, head_dict["noisesubgatechannel"], "audio/x-raw, format=U32LE, channels=1, channel-mask=(bitmask)0x0", noisesub_gate_channel)
 	noisesubgate = pipeparts.mkgeneric(pipeline, noisesubgate, "lal_logicalundersample", required_on = noisesub_gate_bitmask, status_out = pow(2,28))
 	noisesubgate = pipeparts.mkcapsfilter(pipeline, noisesubgate, calibstate_caps)
 	noisesubgatetee = pipeparts.mktee(pipeline, noisesubgate)
diff --git a/gstlal-calibration/gst/lal/Makefile.am b/gstlal-calibration/gst/lal/Makefile.am
index 6e1cb03fe695603262e056d9022b7029a1fc4bfb..5ad09424f5e833fd6ccb7ebe8b7d1edb3e605d1d 100644
--- a/gstlal-calibration/gst/lal/Makefile.am
+++ b/gstlal-calibration/gst/lal/Makefile.am
@@ -18,7 +18,8 @@ lib@GSTPLUGINPREFIX@gstlalcalibration_la_SOURCES = \
 	gstlal_trackfrequency.c gstlal_trackfrequency.h \
 	gstlal_adaptivefirfilt.c gstlal_adaptivefirfilt.h \
 	gstlal_dqtukey.c gstlal_dqtukey.h \
-	gstlal_property.c gstlal_property.h
+	gstlal_property.c gstlal_property.h \
+	gstlal_typecast.c gstlal_typecast.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_dqtukey.c b/gstlal-calibration/gst/lal/gstlal_dqtukey.c
index 17b0e5f27650387193801b3601cb45e5892846f7..f35f5557a6668d1b8416567005603fe426cc9b25 100644
--- a/gstlal-calibration/gst/lal/gstlal_dqtukey.c
+++ b/gstlal-calibration/gst/lal/gstlal_dqtukey.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, 2016  Aaron Viets
+ * 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
diff --git a/gstlal-calibration/gst/lal/gstlal_typecast.c b/gstlal-calibration/gst/lal/gstlal_typecast.c
new file mode 100644
index 0000000000000000000000000000000000000000..f8c2e5de11b17521dd668f61b1da4d287f4f0a1c
--- /dev/null
+++ b/gstlal-calibration/gst/lal/gstlal_typecast.c
@@ -0,0 +1,1113 @@
+/*
+ * Copyright (C) 2019  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 C
+ */
+
+
+#include <string.h>
+#include <math.h>
+
+
+/*
+ * stuff from gobject/gstreamer
+ */
+
+
+#include <glib.h>
+#include <gst/gst.h>
+#include <gst/audio/audio.h>
+#include <gst/base/gstbasetransform.h>
+#include <gstlal/gstlal.h>
+#include <gstlal/gstlal_audio_info.h>
+#include <gstlal_typecast.h>
+
+
+/*
+ * ============================================================================
+ *
+ *			       Utilities
+ *
+ * ============================================================================
+ */
+
+
+#define DEFINE_TYPECAST_BUFFER(INTYPE, INCOMPLEX, OUTTYPE, OUTCOMPLEX) \
+static void typecast_buffer_ ## INCOMPLEX ## INTYPE ## _to_ ## OUTCOMPLEX ## OUTTYPE(const INCOMPLEX INTYPE *src, guint64 src_size, OUTCOMPLEX OUTTYPE *dst) { \
+ \
+	guint64 i; \
+	for(i = 0; i < src_size; i++) \
+		dst[i] = (OUTCOMPLEX OUTTYPE) src[i]; \
+ \
+	return; \
+}
+
+
+DEFINE_TYPECAST_BUFFER(gint8, , gint8, );
+DEFINE_TYPECAST_BUFFER(gint8, , gint16, );
+DEFINE_TYPECAST_BUFFER(gint8, , gint32, );
+DEFINE_TYPECAST_BUFFER(gint8, , guint8, );
+DEFINE_TYPECAST_BUFFER(gint8, , guint16, );
+DEFINE_TYPECAST_BUFFER(gint8, , guint32, );
+DEFINE_TYPECAST_BUFFER(gint8, , float, );
+DEFINE_TYPECAST_BUFFER(gint8, , double, );
+DEFINE_TYPECAST_BUFFER(gint8, , float, complex);
+DEFINE_TYPECAST_BUFFER(gint8, , double, complex);
+DEFINE_TYPECAST_BUFFER(gint16, , gint8, );
+DEFINE_TYPECAST_BUFFER(gint16, , gint16, );
+DEFINE_TYPECAST_BUFFER(gint16, , gint32, );
+DEFINE_TYPECAST_BUFFER(gint16, , guint8, );
+DEFINE_TYPECAST_BUFFER(gint16, , guint16, );
+DEFINE_TYPECAST_BUFFER(gint16, , guint32, );
+DEFINE_TYPECAST_BUFFER(gint16, , float, );
+DEFINE_TYPECAST_BUFFER(gint16, , double, );
+DEFINE_TYPECAST_BUFFER(gint16, , float, complex);
+DEFINE_TYPECAST_BUFFER(gint16, , double, complex);
+DEFINE_TYPECAST_BUFFER(gint32, , gint8, );
+DEFINE_TYPECAST_BUFFER(gint32, , gint16, );
+DEFINE_TYPECAST_BUFFER(gint32, , gint32, );
+DEFINE_TYPECAST_BUFFER(gint32, , guint8, );
+DEFINE_TYPECAST_BUFFER(gint32, , guint16, );
+DEFINE_TYPECAST_BUFFER(gint32, , guint32, );
+DEFINE_TYPECAST_BUFFER(gint32, , float, );
+DEFINE_TYPECAST_BUFFER(gint32, , double, );
+DEFINE_TYPECAST_BUFFER(gint32, , float, complex);
+DEFINE_TYPECAST_BUFFER(gint32, , double, complex);
+DEFINE_TYPECAST_BUFFER(guint8, , gint8, );
+DEFINE_TYPECAST_BUFFER(guint8, , gint16, );
+DEFINE_TYPECAST_BUFFER(guint8, , gint32, );
+DEFINE_TYPECAST_BUFFER(guint8, , guint8, );
+DEFINE_TYPECAST_BUFFER(guint8, , guint16, );
+DEFINE_TYPECAST_BUFFER(guint8, , guint32, );
+DEFINE_TYPECAST_BUFFER(guint8, , float, );
+DEFINE_TYPECAST_BUFFER(guint8, , double, );
+DEFINE_TYPECAST_BUFFER(guint8, , float, complex);
+DEFINE_TYPECAST_BUFFER(guint8, , double, complex);
+DEFINE_TYPECAST_BUFFER(guint16, , gint8, );
+DEFINE_TYPECAST_BUFFER(guint16, , gint16, );
+DEFINE_TYPECAST_BUFFER(guint16, , gint32, );
+DEFINE_TYPECAST_BUFFER(guint16, , guint8, );
+DEFINE_TYPECAST_BUFFER(guint16, , guint16, );
+DEFINE_TYPECAST_BUFFER(guint16, , guint32, );
+DEFINE_TYPECAST_BUFFER(guint16, , float, );
+DEFINE_TYPECAST_BUFFER(guint16, , double, );
+DEFINE_TYPECAST_BUFFER(guint16, , float, complex);
+DEFINE_TYPECAST_BUFFER(guint16, , double, complex);
+DEFINE_TYPECAST_BUFFER(guint32, , gint8, );
+DEFINE_TYPECAST_BUFFER(guint32, , gint16, );
+DEFINE_TYPECAST_BUFFER(guint32, , gint32, );
+DEFINE_TYPECAST_BUFFER(guint32, , guint8, );
+DEFINE_TYPECAST_BUFFER(guint32, , guint16, );
+DEFINE_TYPECAST_BUFFER(guint32, , guint32, );
+DEFINE_TYPECAST_BUFFER(guint32, , float, );
+DEFINE_TYPECAST_BUFFER(guint32, , double, );
+DEFINE_TYPECAST_BUFFER(guint32, , float, complex);
+DEFINE_TYPECAST_BUFFER(guint32, , double, complex);
+DEFINE_TYPECAST_BUFFER(float, , gint8, );
+DEFINE_TYPECAST_BUFFER(float, , gint16, );
+DEFINE_TYPECAST_BUFFER(float, , gint32, );
+DEFINE_TYPECAST_BUFFER(float, , guint8, );
+DEFINE_TYPECAST_BUFFER(float, , guint16, );
+DEFINE_TYPECAST_BUFFER(float, , guint32, );
+DEFINE_TYPECAST_BUFFER(float, , float, );
+DEFINE_TYPECAST_BUFFER(float, , double, );
+DEFINE_TYPECAST_BUFFER(float, , float, complex);
+DEFINE_TYPECAST_BUFFER(float, , double, complex);
+DEFINE_TYPECAST_BUFFER(double, , gint8, );
+DEFINE_TYPECAST_BUFFER(double, , gint16, );
+DEFINE_TYPECAST_BUFFER(double, , gint32, );
+DEFINE_TYPECAST_BUFFER(double, , guint8, );
+DEFINE_TYPECAST_BUFFER(double, , guint16, );
+DEFINE_TYPECAST_BUFFER(double, , guint32, );
+DEFINE_TYPECAST_BUFFER(double, , float, );
+DEFINE_TYPECAST_BUFFER(double, , double, );
+DEFINE_TYPECAST_BUFFER(double, , float, complex);
+DEFINE_TYPECAST_BUFFER(double, , double, complex);
+DEFINE_TYPECAST_BUFFER(float, complex, gint8, );
+DEFINE_TYPECAST_BUFFER(float, complex, gint16, );
+DEFINE_TYPECAST_BUFFER(float, complex, gint32, );
+DEFINE_TYPECAST_BUFFER(float, complex, guint8, );
+DEFINE_TYPECAST_BUFFER(float, complex, guint16, );
+DEFINE_TYPECAST_BUFFER(float, complex, guint32, );
+DEFINE_TYPECAST_BUFFER(float, complex, float, );
+DEFINE_TYPECAST_BUFFER(float, complex, double, );
+DEFINE_TYPECAST_BUFFER(float, complex, float, complex);
+DEFINE_TYPECAST_BUFFER(float, complex, double, complex);
+DEFINE_TYPECAST_BUFFER(double, complex, gint8, );
+DEFINE_TYPECAST_BUFFER(double, complex, gint16, );
+DEFINE_TYPECAST_BUFFER(double, complex, gint32, );
+DEFINE_TYPECAST_BUFFER(double, complex, guint8, );
+DEFINE_TYPECAST_BUFFER(double, complex, guint16, );
+DEFINE_TYPECAST_BUFFER(double, complex, guint32, );
+DEFINE_TYPECAST_BUFFER(double, complex, float, );
+DEFINE_TYPECAST_BUFFER(double, complex, double, );
+DEFINE_TYPECAST_BUFFER(double, complex, float, complex);
+DEFINE_TYPECAST_BUFFER(double, complex, double, complex);
+
+
+/*
+ * set the metadata on an output buffer
+ */
+
+
+static void set_metadata(GSTLALTypeCast *element, GstBuffer *buf, guint64 outsamples, gboolean gap) {
+
+	GST_BUFFER_OFFSET(buf) = element->next_out_offset;
+	element->next_out_offset += outsamples;
+	GST_BUFFER_OFFSET_END(buf) = element->next_out_offset;
+	GST_BUFFER_PTS(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_PTS(buf);
+	if(G_UNLIKELY(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);
+}
+
+
+/*
+ * ============================================================================
+ *
+ *			   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, " \
+		"rate = " GST_AUDIO_RATE_RANGE ", " \
+		"channels = (int) [1, MAX], " \
+		"format = (string) {" GST_AUDIO_NE(S8) ", " GST_AUDIO_NE(S16) ", " GST_AUDIO_NE(S32) ", " GST_AUDIO_NE(U8) ", " GST_AUDIO_NE(U16) ", " GST_AUDIO_NE(U32) ", " GST_AUDIO_NE(F32) ", " GST_AUDIO_NE(F64) ", " GST_AUDIO_NE(Z64) ", " GST_AUDIO_NE(Z128) "}, " \
+		"layout = (string) interleaved, " \
+		"channel-mask = (bitmask) 0"
+	)
+);
+
+
+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, " \
+		"rate = " GST_AUDIO_RATE_RANGE ", " \
+		"channels = (int) [1, MAX], " \
+		"format = (string) {" GST_AUDIO_NE(S8) ", " GST_AUDIO_NE(S16) ", " GST_AUDIO_NE(S32) ", " GST_AUDIO_NE(U8) ", " GST_AUDIO_NE(U16) ", " GST_AUDIO_NE(U32) ", " GST_AUDIO_NE(F32) ", " GST_AUDIO_NE(F64) ", " GST_AUDIO_NE(Z64) ", " GST_AUDIO_NE(Z128) "}, " \
+		"layout = (string) interleaved, " \
+		"channel-mask = (bitmask) 0"
+	)
+);
+
+
+G_DEFINE_TYPE(
+	GSTLALTypeCast,
+	gstlal_typecast,
+	GST_TYPE_BASE_TRANSFORM
+);
+
+
+/*
+ * ============================================================================
+ *
+ *		     GstBaseTransform Method Overrides
+ *
+ * ============================================================================
+ */
+
+
+/*
+ * get_unit_size()
+ */
+
+
+static gboolean get_unit_size(GstBaseTransform *trans, GstCaps *caps, gsize *size) {
+
+	GstAudioInfo info;
+	gboolean success = TRUE;
+
+	success &= gstlal_audio_info_from_caps(&info, caps);
+
+	if(success) {
+		*size = GST_AUDIO_INFO_BPF(&info);
+	} else
+		GST_WARNING_OBJECT(trans, "unable to parse caps %" GST_PTR_FORMAT, caps);
+
+	return success;
+}
+
+
+/*
+ * transform_caps()
+ */
+
+
+static GstCaps *transform_caps(GstBaseTransform *trans, GstPadDirection direction, GstCaps *caps, GstCaps *filter) {
+
+	guint n;
+	static char *formats[] = {GST_AUDIO_NE(S8), GST_AUDIO_NE(S16), GST_AUDIO_NE(S32), GST_AUDIO_NE(U8), GST_AUDIO_NE(U16), GST_AUDIO_NE(U32), GST_AUDIO_NE(F32), GST_AUDIO_NE(F64), GST_AUDIO_NE(Z64), GST_AUDIO_NE(Z128)};
+
+	caps = gst_caps_normalize(gst_caps_copy(caps));
+	GstCaps *othercaps = gst_caps_new_empty();
+
+	switch(direction) {
+	case GST_PAD_SRC:
+	case GST_PAD_SINK:
+		/*
+		 * There are 10 possible formats in othercaps for each format in caps, so the
+		 * othercaps has 10 times as many structures. Otherwise the sink pad and source
+		 * pad caps are the same.
+		 */
+		for(n = 0; n < 10 * gst_caps_get_size(caps); n++) {
+			gst_caps_append(othercaps, gst_caps_copy_nth(caps, n / 10));
+			GstStructure *str = gst_caps_get_structure(othercaps, n);
+			gst_structure_set(str, "format", G_TYPE_STRING, formats[n % 10], NULL);
+		}
+		break;
+
+	case GST_PAD_UNKNOWN:
+		GST_ELEMENT_ERROR(trans, CORE, NEGOTIATION, (NULL), ("invalid direction GST_PAD_UNKNOWN"));
+		gst_caps_unref(caps);
+		return GST_CAPS_NONE;
+	}
+
+	othercaps = gst_caps_simplify(othercaps);
+
+	if(filter) {
+		GstCaps *intersection = gst_caps_intersect(othercaps, filter);
+		gst_caps_unref(othercaps);
+		othercaps = intersection;
+	}
+
+	gst_caps_unref(caps);
+	return othercaps;
+}
+
+
+/*
+ * set_caps()
+ */
+
+
+static gboolean set_caps(GstBaseTransform *trans, GstCaps *incaps, GstCaps *outcaps) {
+
+	guint i, test;
+	GSTLALTypeCast *element = GSTLAL_TYPECAST(trans);
+	gsize unit_size_in, unit_size_out;
+	const gchar *format_in;
+	const gchar *format_out;
+	static char *formats[] = {GST_AUDIO_NE(S8), GST_AUDIO_NE(S16), GST_AUDIO_NE(S32), GST_AUDIO_NE(U8), GST_AUDIO_NE(U16), GST_AUDIO_NE(U32), GST_AUDIO_NE(F32), GST_AUDIO_NE(F64), GST_AUDIO_NE(Z64), GST_AUDIO_NE(Z128)};
+	gboolean complexes[] = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE};
+	gboolean floats[] = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE};
+	gboolean signs[] = {TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE};
+
+	/* Find unit sizes of input and output */
+	if(!get_unit_size(trans, incaps, &unit_size_in)) {
+		GST_DEBUG_OBJECT(element, "failed to get unit size from input caps %" GST_PTR_FORMAT, incaps);
+		return FALSE;
+	}
+	if(!get_unit_size(trans, outcaps, &unit_size_out)) {
+		GST_DEBUG_OBJECT(element, "failed to get unit size from output caps %" GST_PTR_FORMAT, outcaps);
+		return FALSE;
+	}
+
+	element->unit_size_in = unit_size_in;
+	element->unit_size_out = unit_size_out;
+
+	/* Get the caps structure from the incaps */
+	GstStructure *str_in = gst_caps_get_structure(incaps, 0);
+	g_assert(str_in);
+
+	/* Number of channels, which is the same for input and output */
+	if(!gst_structure_get_int(str_in, "channels", &element->channels)) {
+		GST_DEBUG_OBJECT(element, "failed to get channels from input caps %" GST_PTR_FORMAT, incaps);
+		return FALSE;
+	}
+
+	/* Rate, which is the same for input and output */
+	if(!gst_structure_get_int(str_in, "rate", &element->rate)) {
+		GST_DEBUG_OBJECT(element, "failed to get rate from input caps %" GST_PTR_FORMAT, incaps);
+		return FALSE;
+	}
+
+	/* Check the incaps to see if it contains S32, etc. */
+	if(gst_structure_has_field(str_in, "format")) {
+		format_in = gst_structure_get_string(str_in, "format");
+	} else {
+		GST_ERROR_OBJECT(element, "No incaps format! Cannot set element caps.\n");
+		return FALSE;
+	}
+	test = 0;
+	for(i = 0; i < sizeof(formats) / sizeof(*formats); i++) {
+		if(!strcmp(format_in, formats[i])) {
+			element->complex_in = complexes[i];
+			element->float_in = floats[i];
+			element->sign_in = signs[i];
+			test++;
+		}			
+	}
+	if(test != 1) {
+		GST_ERROR_OBJECT(element, "element caps not properly set");
+		return FALSE;
+	}
+
+	/* Check the outcaps to see if it contains S32, etc. */
+	GstStructure *str_out = gst_caps_get_structure(outcaps, 0);
+	g_assert(str_out);
+
+	if(gst_structure_has_field(str_out, "format")) {
+		format_out = gst_structure_get_string(str_out, "format");
+	} else {
+		GST_ERROR_OBJECT(element, "No outcaps format! Cannot set element caps.\n");
+		return FALSE;
+	}
+	test = 0;
+	for(i = 0; i < sizeof(formats) / sizeof(*formats); i++) {
+		if(!strcmp(format_out, formats[i])) {
+			element->complex_out = complexes[i];
+			element->float_out = floats[i];
+			element->sign_out = signs[i];
+			test++;
+		}
+	}
+	if(test != 1) {
+		GST_ERROR_OBJECT(element, "element->sign_out and/or element->float_out not properly set");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/*
+ * transform_size()
+ */
+
+
+static gboolean transform_size(GstBaseTransform *trans, GstPadDirection direction, GstCaps *caps, gsize size, GstCaps *othercaps, gsize *othersize) {
+
+	GSTLALTypeCast *element = GSTLAL_TYPECAST(trans);
+
+	switch(direction) {
+	case GST_PAD_SRC:
+		/*
+		 * We know the size of the output buffer, and we will compute the size of the
+		 * input buffer.  The only difference is the unit size.
+		 */
+		if(G_UNLIKELY(size % element->unit_size_out)) {
+			GST_DEBUG_OBJECT(element, "buffer size %" G_GSIZE_FORMAT " is not a multiple of unit_size %" G_GSIZE_FORMAT, size, (gsize) element->unit_size_out);
+			return FALSE;
+		}
+
+		*othersize = (gsize) (size * element->unit_size_in / element->unit_size_out);
+
+		break;
+
+	case GST_PAD_SINK:
+		/*
+		 * We know the size of the input buffer, and we will compute the size of the
+		 * output buffer.  The only difference is the unit size.
+		 */
+		if(G_UNLIKELY(size % element->unit_size_in)) {
+			GST_DEBUG_OBJECT(element, "buffer size %" G_GSIZE_FORMAT " is not a multiple of unit_size %" G_GSIZE_FORMAT, size, (gsize) element->unit_size_in);
+			return FALSE;
+		}
+
+		*othersize = (gsize) (size * element->unit_size_out / element->unit_size_in);
+
+		break;
+
+	case GST_PAD_UNKNOWN:
+		GST_ELEMENT_ERROR(trans, CORE, NEGOTIATION, (NULL), ("invalid direction GST_PAD_UNKNOWN"));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/*
+ * transform()
+ */
+
+
+static GstFlowReturn transform(GstBaseTransform *trans, GstBuffer *inbuf, GstBuffer *outbuf) {
+
+	GSTLALTypeCast *element = GSTLAL_TYPECAST(trans);
+	GstMapInfo inmap, outmap;
+	GstFlowReturn result = GST_FLOW_OK;
+
+	/*
+	 * check for discontinuity
+	 */
+
+	if(G_UNLIKELY(GST_BUFFER_IS_DISCONT(inbuf) || GST_BUFFER_OFFSET(inbuf) != element->next_in_offset || !GST_CLOCK_TIME_IS_VALID(element->t0))) {
+		GST_DEBUG_OBJECT(element, "pushing discontinuous buffer at input timestamp %lu", (long unsigned) GST_TIME_AS_SECONDS(GST_BUFFER_PTS(inbuf)));
+		element->t0 = GST_BUFFER_PTS(inbuf);
+		element->offset0 = GST_BUFFER_OFFSET(inbuf);
+		element->next_out_offset = GST_BUFFER_OFFSET(inbuf);
+		element->need_discont = TRUE;
+	}
+
+	element->next_in_offset = GST_BUFFER_OFFSET_END(inbuf);
+
+	/*
+	 * process buffer
+	 */
+
+	gst_buffer_map(inbuf, &inmap, GST_MAP_READ);
+	gst_buffer_map(outbuf, &outmap, GST_MAP_WRITE);
+
+	g_assert_cmpuint(inmap.size % element->unit_size_in, ==, 0);
+	g_assert_cmpuint(outmap.size % element->unit_size_out, ==, 0);
+
+	guint64 src_size = inmap.size * element->channels / element->unit_size_in;
+
+	if(element->complex_in) {
+		switch(element->unit_size_in) {
+		case 8:
+			if(element->complex_out) {
+				switch(element->unit_size_out) {
+				case 8:
+					typecast_buffer_complexfloat_to_complexfloat((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 16:
+					typecast_buffer_complexfloat_to_complexdouble((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->float_out) {
+				switch(element->unit_size_out) {
+				case 4:
+					typecast_buffer_complexfloat_to_float((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 8:
+					typecast_buffer_complexfloat_to_double((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->sign_out) {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_complexfloat_to_gint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_complexfloat_to_gint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_complexfloat_to_gint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_complexfloat_to_guint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_complexfloat_to_guint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_complexfloat_to_guint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			}
+			break;
+		case 16:
+			if(element->complex_out) {
+				switch(element->unit_size_out) {
+				case 8:
+					typecast_buffer_complexdouble_to_complexfloat((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 16:
+					typecast_buffer_complexdouble_to_complexdouble((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->float_out) {
+				switch(element->unit_size_out) {
+				case 4:
+					typecast_buffer_complexdouble_to_float((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 8:
+					typecast_buffer_complexdouble_to_double((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->sign_out) {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_complexdouble_to_gint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_complexdouble_to_gint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_complexdouble_to_gint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_complexdouble_to_guint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_complexdouble_to_guint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_complexdouble_to_guint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			}
+			break;
+		default:
+			g_assert_not_reached();
+		}
+	} else if(element->float_in) {
+		switch(element->unit_size_in) {
+		case 4:
+			if(element->complex_out) {
+				switch(element->unit_size_out) {
+				case 8:
+					typecast_buffer_float_to_complexfloat((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 16:
+					typecast_buffer_float_to_complexdouble((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->float_out) {
+				switch(element->unit_size_out) {
+				case 4:
+					typecast_buffer_float_to_float((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 8:
+					typecast_buffer_float_to_double((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->sign_out) {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_float_to_gint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_float_to_gint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_float_to_gint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_float_to_guint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_float_to_guint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_float_to_guint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			}
+			break;
+		case 8:
+			if(element->complex_out) {
+				switch(element->unit_size_out) {
+				case 8:
+					typecast_buffer_double_to_complexfloat((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 16:
+					typecast_buffer_double_to_complexdouble((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->float_out) {
+				switch(element->unit_size_out) {
+				case 4:
+					typecast_buffer_double_to_float((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 8:
+					typecast_buffer_double_to_double((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->sign_out) {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_double_to_gint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_double_to_gint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_double_to_gint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_double_to_guint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_double_to_guint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_double_to_guint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			}
+			break;
+		default:
+			g_assert_not_reached();
+		}
+	} else if(element->sign_in) {
+		switch(element->unit_size_in) {
+		case 1:
+			if(element->complex_out) {
+				switch(element->unit_size_out) {
+				case 8:
+					typecast_buffer_gint8_to_complexfloat((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 16:
+					typecast_buffer_gint8_to_complexdouble((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->float_out) {
+				switch(element->unit_size_out) {
+				case 4:
+					typecast_buffer_gint8_to_float((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 8:
+					typecast_buffer_gint8_to_double((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->sign_out) {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_gint8_to_gint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_gint8_to_gint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_gint8_to_gint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_gint8_to_guint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_gint8_to_guint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_gint8_to_guint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			}
+			break;
+		case 2:
+			if(element->complex_out) {
+				switch(element->unit_size_out) {
+				case 8:
+					typecast_buffer_gint16_to_complexfloat((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 16:
+					typecast_buffer_gint16_to_complexdouble((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->float_out) {
+				switch(element->unit_size_out) {
+				case 4:
+					typecast_buffer_gint16_to_float((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 8:
+					typecast_buffer_gint16_to_double((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->sign_out) {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_gint16_to_gint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_gint16_to_gint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_gint16_to_gint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_gint16_to_guint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_gint16_to_guint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_gint16_to_guint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			}
+			break;
+		case 4:
+			if(element->complex_out) {
+				switch(element->unit_size_out) {
+				case 8:
+					typecast_buffer_gint32_to_complexfloat((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 16:
+					typecast_buffer_gint32_to_complexdouble((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->float_out) {
+				switch(element->unit_size_out) {
+				case 4:
+					typecast_buffer_gint32_to_float((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 8:
+					typecast_buffer_gint32_to_double((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->sign_out) {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_gint32_to_gint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_gint32_to_gint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_gint32_to_gint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_gint32_to_guint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_gint32_to_guint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_gint32_to_guint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			}
+			break;
+		default:
+			g_assert_not_reached();
+		}
+	} else {
+		switch(element->unit_size_in) {
+		case 1:
+			if(element->complex_out) {
+				switch(element->unit_size_out) {
+				case 8:
+					typecast_buffer_guint8_to_complexfloat((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 16:
+					typecast_buffer_guint8_to_complexdouble((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->float_out) {
+				switch(element->unit_size_out) {
+				case 4:
+					typecast_buffer_guint8_to_float((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 8:
+					typecast_buffer_guint8_to_double((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->sign_out) {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_guint8_to_gint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_guint8_to_gint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_guint8_to_gint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_guint8_to_guint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_guint8_to_guint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_guint8_to_guint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			}
+			break;
+		case 2:
+			if(element->complex_out) {
+				switch(element->unit_size_out) {
+				case 8:
+					typecast_buffer_guint16_to_complexfloat((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 16:
+					typecast_buffer_guint16_to_complexdouble((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->float_out) {
+				switch(element->unit_size_out) {
+				case 4:
+					typecast_buffer_guint16_to_float((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 8:
+					typecast_buffer_guint16_to_double((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->sign_out) {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_guint16_to_gint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_guint16_to_gint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_guint16_to_gint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_guint16_to_guint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_guint16_to_guint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_guint16_to_guint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			}
+			break;
+		case 4:
+			if(element->complex_out) {
+				switch(element->unit_size_out) {
+				case 8:
+					typecast_buffer_guint32_to_complexfloat((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 16:
+					typecast_buffer_guint32_to_complexdouble((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->float_out) {
+				switch(element->unit_size_out) {
+				case 4:
+					typecast_buffer_guint32_to_float((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 8:
+					typecast_buffer_guint32_to_double((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else if(element->sign_out) {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_guint32_to_gint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_guint32_to_gint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_guint32_to_gint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			} else {
+				switch(element->unit_size_out) {
+				case 1:
+					typecast_buffer_guint32_to_guint8((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 2:
+					typecast_buffer_guint32_to_guint16((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				case 4:
+					typecast_buffer_guint32_to_guint32((const void *) inmap.data, src_size, (void *) outmap.data);
+					break;
+				default:
+					g_assert_not_reached();
+				}
+			}
+			break;
+		default:
+			g_assert_not_reached();
+		}
+	}
+
+	set_metadata(element, outbuf, outmap.size / element->unit_size_out, GST_BUFFER_FLAG_IS_SET(inbuf, GST_BUFFER_FLAG_GAP));
+	gst_buffer_unmap(outbuf, &outmap);
+	gst_buffer_unmap(inbuf, &inmap);
+
+	/*
+	 * done
+	 */
+
+	return result;
+}
+
+
+/*
+ * ============================================================================
+ *
+ *			  GObject Method Overrides
+ *
+ * ============================================================================
+ */
+
+
+/*
+ * class_init()
+ */
+
+
+static void gstlal_typecast_class_init(GSTLALTypeCastClass *klass) {
+
+	GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+	GstBaseTransformClass *transform_class = GST_BASE_TRANSFORM_CLASS(klass);
+
+	transform_class->transform_caps = GST_DEBUG_FUNCPTR(transform_caps);
+	transform_class->transform_size = GST_DEBUG_FUNCPTR(transform_size);
+	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->passthrough_on_same_caps = TRUE;
+
+	gst_element_class_set_details_simple(element_class,
+		"TypeCast",
+		"Filter/Audio",
+		"Convert the data type of a buffer using a simple type cast",
+		"Aaron Viets <aaron.viets@ligo.org>"
+	);
+
+	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));
+}
+
+
+/*
+ * init()
+ */
+
+
+static void gstlal_typecast_init(GSTLALTypeCast *element) {
+
+	element->unit_size_in = 0;
+	element->unit_size_out = 0;
+	element->channels = 0;
+	gst_base_transform_set_gap_aware(GST_BASE_TRANSFORM(element), TRUE);
+}
+
diff --git a/gstlal-calibration/gst/lal/gstlal_typecast.h b/gstlal-calibration/gst/lal/gstlal_typecast.h
new file mode 100644
index 0000000000000000000000000000000000000000..676b43d6526677895b16762d09f799c5e26e08a2
--- /dev/null
+++ b/gstlal-calibration/gst/lal/gstlal_typecast.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 Aaron Viets <aaron.viets@ligo.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * 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_TYPECAST_H__
+#define __GSTLAL_TYPECAST_H__
+
+
+#include <glib.h>
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+
+
+G_BEGIN_DECLS
+#define GSTLAL_TYPECAST_TYPE \
+	(gstlal_typecast_get_type())
+#define GSTLAL_TYPECAST(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST((obj), GSTLAL_TYPECAST_TYPE, GSTLALTypeCast))
+#define GSTLAL_TYPECAST_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_CAST((klass), GSTLAL_TYPECAST_TYPE, GSTLALTypeCastClass))
+#define GST_IS_GSTLAL_TYPECAST(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE((obj), GSTLAL_TYPECAST_TYPE))
+#define GST_IS_GSTLAL_TYPECAST_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_TYPE((klass), GSTLAL_TYPECAST_TYPE))
+
+
+typedef struct _GSTLALTypeCast GSTLALTypeCast;
+typedef struct _GSTLALTypeCastClass GSTLALTypeCastClass;
+
+
+/*
+ * GSTLALTypeCast:
+ */
+
+
+struct _GSTLALTypeCast {
+	GstBaseTransform element;
+
+	/* stream info */
+	int unit_size_in;
+	int unit_size_out;
+	int channels;
+	int rate;
+	gboolean complex_in;
+	gboolean complex_out;
+	gboolean float_in;
+	gboolean float_out;
+	gboolean sign_in;
+	gboolean sign_out;
+
+	/* timestamp book-keeping */
+	GstClockTime t0;
+	guint64 offset0;
+	guint64 next_in_offset;
+	guint64 next_out_offset;
+	gboolean need_discont;
+};
+
+
+/*
+ * GSTLALTypeCastClass:
+ * @parent_class:  the parent class
+ */
+
+
+struct _GSTLALTypeCastClass {
+	GstBaseTransformClass parent_class;
+};
+
+
+GType gstlal_typecast_get_type(void);
+
+
+G_END_DECLS
+
+
+#endif  /* __GSTLAL_TYPECAST_H__ */
diff --git a/gstlal-calibration/gst/lal/gstlalcalibration.c b/gstlal-calibration/gst/lal/gstlalcalibration.c
index 38056799572e089fc19fdc49387794ca55b1fae1..e20d82af8cf5f867bbabccd1139dd91a7f223fa1 100644
--- a/gstlal-calibration/gst/lal/gstlalcalibration.c
+++ b/gstlal-calibration/gst/lal/gstlalcalibration.c
@@ -70,6 +70,7 @@
 #include <gstlal_adaptivefirfilt.h>
 #include <gstlal_dqtukey.h>
 #include <gstlal_property.h>
+#include <gstlal_typecast.h>
 
 
 /*
@@ -104,6 +105,7 @@ static gboolean plugin_init(GstPlugin *plugin)
 		{"lal_adaptivefirfilt", GSTLAL_ADAPTIVEFIRFILT_TYPE},
 		{"lal_dqtukey", GSTLAL_DQTUKEY_TYPE},
 		{"lal_property", GSTLAL_PROPERTY_TYPE},
+		{"lal_typecast", GSTLAL_TYPECAST_TYPE},
 		{NULL, 0},
 	};