diff --git a/configure.ac b/configure.ac
index fc43831..bc7f13f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -274,6 +274,11 @@ AC_DEFINE_UNQUOTED(LIBDIR, "$LIBDIR", [library dir])
 dnl set location of plugin directory
 AG_GST_SET_PLUGINDIR
 
+dnl command-line options if libtapencoder's and libtapdecoder's headers and libs are in nonstandard places
+AC_ARG_WITH(libtap-includes,
+              AS_HELP_STRING([--with-libtap-includes],[Where the header files for libtap are located]))
+AC_ARG_WITH(libtap-libs, AS_HELP_STRING([--with-libtap-libs],[Where the libtap library is located]))
+
 dnl set release date/time
 AG_GST_SET_PACKAGE_RELEASE_DATETIME_WITH_NANO([$PACKAGE_VERSION_NANO],
   ["${srcdir}/gst-plugins-bad.doap"],
@@ -410,6 +415,7 @@ AG_GST_CHECK_PLUGIN(smooth)
 AG_GST_CHECK_PLUGIN(speed)
 AG_GST_CHECK_PLUGIN(subenc)
 AG_GST_CHECK_PLUGIN(stereo)
+AG_GST_CHECK_PLUGIN(tap)
 AG_GST_CHECK_PLUGIN(tta)
 AG_GST_CHECK_PLUGIN(videofilters)
 AG_GST_CHECK_PLUGIN(videomeasure)
@@ -2184,6 +2190,32 @@ AG_GST_CHECK_FEATURE(HLS, [http live streaming plugin], hls, [
   PKG_CHECK_MODULES(GNUTLS, gnutls >= 2.11.3, [ HAVE_HLS="yes" ], [ HAVE_HLS="no" ])
 ])
 
+dnl *** tapcodec ***
+translit(dnm, m, l) AM_CONDITIONAL(USE_TAPCODEC, true)
+AG_GST_CHECK_FEATURE(TAPCODEC, [TAP audio codec], tapcodec, [
+  if test "x$with_libtap_includes" != x; then
+    TAPCODEC_CPPFLAGS="-I$with_libtap_includes"
+  fi
+  if test "x$with_libtap_libs" != x; then
+    TAPCODEC_LDFLAGS="-L$with_libtap_libs"
+  fi
+  CPPFLAGS="$CPPFLAGS $TAPCODEC_CPPFLAGS"
+  LDFLAGS="$LDFLAGS $TAPCODEC_LDFLAGS"
+  AC_CHECK_HEADER(tapencoder.h, [
+    AC_CHECK_HEADER(tapdecoder.h, [
+      AC_CHECK_LIB(tapencoder,tapenc_get_pulse, [
+        AC_CHECK_LIB(tapdecoder,tapdec_set_pulse, [
+          HAVE_TAPCODEC=yes
+          TAPCODEC_LIBS="-ltapencoder -ltapdecoder"
+          AC_SUBST(TAPCODEC_LIBS)
+          AC_SUBST(TAPCODEC_CPPFLAGS)
+          AC_SUBST(TAPCODEC_LDFLAGS)
+        ])
+      ])
+    ])
+  ])
+])
+
 else
 
 dnl not building plugins with external dependencies,
@@ -2256,6 +2288,7 @@ AM_CONDITIONAL(USE_VDPAU, false)
 AM_CONDITIONAL(USE_SCHRO, false)
 AM_CONDITIONAL(USE_ZBAR, false)
 AM_CONDITIONAL(USE_RTMP, false)
+AM_CONDITIONAL(USE_TAPCODEC, false)
 AM_CONDITIONAL(USE_TELETEXTDEC, false)
 AM_CONDITIONAL(USE_SNDIO, false)
 AM_CONDITIONAL(USE_UVCH264, false)
@@ -2403,6 +2436,7 @@ gst/smooth/Makefile
 gst/speed/Makefile
 gst/subenc/Makefile
 gst/stereo/Makefile
+gst/tap/Makefile
 gst/tta/Makefile
 gst/videofilters/Makefile
 gst/videomeasure/Makefile
@@ -2513,6 +2547,7 @@ ext/soundtouch/Makefile
 ext/spandsp/Makefile
 ext/sndio/Makefile
 ext/srtp/Makefile
+ext/tap/Makefile
 ext/teletextdec/Makefile
 ext/gme/Makefile
 ext/gsettings/Makefile
diff --git a/ext/Makefile.am b/ext/Makefile.am
index c06d481..b67e556 100644
--- a/ext/Makefile.am
+++ b/ext/Makefile.am
@@ -244,6 +244,12 @@ else
 RSVG_DIR=
 endif
 
+if USE_TAPCODEC
+TAPCODEC_DIR = tap
+else
+TAPCODEC_DIR =
+endif
+
 if USE_TIMIDITY
 TIMIDITY_DIR=timidity
 endif
@@ -432,6 +438,7 @@ SUBDIRS=\
 	$(GME_DIR) \
 	$(SPC_DIR) \
 	$(SRTP_DIR) \
+	$(TAPCODEC_DIR) \
 	$(TELETEXTDEC_DIR) \
 	$(TIMIDITY_DIR) \
 	$(XVID_DIR) \
@@ -491,6 +498,7 @@ DIST_SUBDIRS = \
 	spc \
 	srtp \
 	gme \
+	tap \
 	teletextdec \
 	timidity \
 	voaacenc \
diff --git a/ext/tap/Makefile.am b/ext/tap/Makefile.am
new file mode 100644
index 0000000..35ac3a3
--- /dev/null
+++ b/ext/tap/Makefile.am
@@ -0,0 +1,14 @@
+# Note: plugindir is set in configure
+
+plugin_LTLIBRARIES = libgsttapcodec.la
+
+# sources used to compile this plug-in
+libgsttapcodec_la_SOURCES = gsttapenc.c gsttapdec.c plugin.c
+
+# compiler and linker flags used to compile this plugin, set in configure.ac
+libgsttapcodec_la_CFLAGS = $(GST_CFLAGS)
+libgsttapcodec_la_CPPFLAGS = $(TAPCODEC_CPPFLAGS)
+libgsttapcodec_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) -lgstaudio-$(GST_API_VERSION) $(TAPCODEC_LIBS)
+libgsttapcodec_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(TAPCODEC_LDFLAGS)
+libgsttapcodec_la_LIBTOOLFLAGS = --tag=disable-static
+
diff --git a/ext/tap/gsttapdec.c b/ext/tap/gsttapdec.c
new file mode 100644
index 0000000..44d0e0e
--- /dev/null
+++ b/ext/tap/gsttapdec.c
@@ -0,0 +1,398 @@
+/*
+ * GStreamer
+ * Copyright (C) 2011-2013 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-tapdec
+ *
+ * Convert a Commodore TAP stream to audio format.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v -m fakesrc ! tapefiledec ! tapdec ! fakesink silent=TRUE
+ * ]|
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudiodecoder.h>
+
+#include "gsttapdec.h"
+#include "tapdecoder.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_tapdec_debug);
+#define GST_CAT_DEFAULT gst_tapdec_debug
+
+#define GST_TYPE_TAPDEC \
+  (gst_tapdec_get_type())
+#define gst_tapdec(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TAPDEC,GstTapDec))
+#define gst_tapdec_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TAPDEC,GstTapDecClass))
+#define GST_IS_TAPDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TAPDEC))
+#define GST_IS_TAPDEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TAPDEC))
+
+typedef struct _GstTapDec GstTapDec;
+typedef struct _GstTapDecClass GstTapDecClass;
+
+struct _GstTapDec
+{
+  GstAudioDecoder element;
+
+  gboolean inverted;
+  guint volume;
+  gint waveform;
+
+  struct tap_dec_t *tap;
+};
+
+struct _GstTapDecClass
+{
+  GstAudioDecoderClass parent_class;
+};
+
+enum
+{
+  PROP_0,
+  PROP_VOLUME,
+  PROP_TRIGGER_ON_RISING_EDGE,
+  PROP_WAVEFORM,
+  N_PROPERTIES
+};
+
+static GType
+gst_waveforms_get_type (void)
+{
+  static GType waveforms_type = 0;
+
+  if (waveforms_type == 0) {
+    static const GEnumValue waveforms_profiles[] = {
+      {TAPDEC_SQUARE, "square", "Square wave"},
+      {TAPDEC_TRIANGLE, "triangle", "Triangular wave"},
+      {TAPDEC_SINE, "sine", "Sinusoidal wave"},
+      {0, NULL, NULL},
+    };
+    waveforms_type =
+        g_enum_register_static ("GstTapEncWaveforms", waveforms_profiles);
+  }
+
+  return waveforms_type;
+}
+
+/* the capabilities of the inputs and outputs.
+ *
+ * describe the real formats here.
+ */
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-tap")
+    );
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw, "
+        "format=" GST_AUDIO_NE (S32) ", "
+        "channels = (int) 1," "layout = (string) interleaved")
+    );
+
+G_DEFINE_TYPE (GstTapDec, gst_tapdec, GST_TYPE_AUDIO_DECODER);
+
+/* GObject vmethod implementations */
+
+static void
+gst_tapdec_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstTapDec *filter = gst_tapdec (object);
+
+  switch (prop_id) {
+    case PROP_VOLUME:
+      filter->volume = g_value_get_uint (value);
+      break;
+    case PROP_TRIGGER_ON_RISING_EDGE:
+      filter->inverted = g_value_get_boolean (value);
+      break;
+    case PROP_WAVEFORM:
+      filter->waveform = g_value_get_enum (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_tapdec_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstTapDec *filter = gst_tapdec (object);
+
+  switch (prop_id) {
+    case PROP_VOLUME:
+      g_value_set_uint (value, filter->volume);
+      break;
+    case PROP_TRIGGER_ON_RISING_EDGE:
+      g_value_set_boolean (value, filter->inverted);
+      break;
+    case PROP_WAVEFORM:
+      g_value_set_enum (value, filter->waveform);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+/* GstAudioDecoder vmethod implementations */
+
+static gboolean
+gst_tapdec_start (GstAudioDecoder * parent)
+{
+  GstTapDec *filter = gst_tapdec (parent);
+  filter->tap = tapdec_init2 (filter->volume,
+      filter->inverted, filter->waveform);
+  gst_audio_decoder_set_estimate_rate (parent, TRUE);
+  return TRUE;
+}
+
+static gboolean
+gst_tapdec_set_format (GstAudioDecoder * parent, GstCaps * caps)
+{
+  GstTapDec *filter = gst_tapdec (parent);
+  GstCaps *out_caps;
+  GstAudioInfo info;
+  gboolean ret;
+  gboolean halfwaves;
+  GstStructure *structure;
+  const GValue *rate;
+
+  GST_DEBUG_OBJECT (filter, "Getting input caps");
+
+  structure = gst_caps_get_structure (caps, 0);
+  if (!(rate = gst_structure_get_value (structure, "rate"))) {
+    GST_ERROR_OBJECT (filter, "input caps have no sample rate field");
+    return FALSE;
+  }
+  if (!gst_structure_get_boolean (structure, "halfwaves", &halfwaves)) {
+    GST_ERROR_OBJECT (filter, "input caps have no halfwaves field");
+    return FALSE;
+  }
+  GST_DEBUG_OBJECT (filter, "Setting output caps, rate %d, %s waves",
+      g_value_get_int (rate), halfwaves ? "half" : "full");
+
+  tapdec_enable_halfwaves (filter->tap, halfwaves);
+  out_caps =
+      gst_caps_make_writable (gst_static_pad_template_get_caps (&src_factory));
+  gst_caps_set_value (out_caps, "rate", rate);
+  ret = gst_audio_info_from_caps (&info, out_caps);
+  gst_caps_unref (out_caps);
+  if (!ret)
+    return FALSE;
+  gst_audio_decoder_set_output_format (parent, &info);
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_tapdec_parse (GstAudioDecoder * dec,
+    GstAdapter * adapter, gint * offset, gint * length)
+{
+  guint size = gst_adapter_available (adapter);
+  g_return_val_if_fail (size > 0, GST_FLOW_ERROR);
+  *offset = 0;
+  *length = size - (size % 4);
+
+  return GST_FLOW_OK;
+}
+
+static gboolean
+gst_tapdec_stop (GstAudioDecoder * dec)
+{
+  GstTapDec *filter = gst_tapdec (dec);
+  tapdec_exit (filter->tap);
+  filter->tap = NULL;
+  return TRUE;
+}
+
+static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
+
+/* GstElement vmethod implementations */
+
+
+/* chain function
+ * this function does the actual processing
+ */
+
+#define TAPDEC_OUTBUF_SIZE 1024
+
+static GstFlowReturn
+gst_tapdec_handle_frame (GstAudioDecoder * parent, GstBuffer * buf)
+{
+  GstTapDec *filter = gst_tapdec (parent);
+  int32_t *data;
+  int32_t *outdata;
+  uint32_t buflen;
+  uint32_t bufsofar;
+  uint32_t total_pulses;
+  GstMapInfo map;
+  GstBuffer *outbuf;
+  GstMapInfo outmap;
+  GstMemory *outmemory = NULL;
+
+  if (filter->tap == NULL) {
+    GST_ERROR_OBJECT (filter, "not initialised: input not a tape?");
+    return GST_FLOW_ERROR;
+  }
+
+  if (buf == NULL) {
+    GST_LOG_OBJECT (filter, "No input data");
+    return GST_FLOW_OK;
+  }
+
+  gst_buffer_map (buf, &map, GST_MAP_READ);
+  data = (int32_t *) map.data;
+  buflen = map.size / sizeof (int32_t);
+
+  outbuf = gst_buffer_new ();
+
+  for (bufsofar = 0; bufsofar < buflen; bufsofar++) {
+    uint32_t npulses;
+    tapdec_set_pulse (filter->tap, data[bufsofar]);
+    do {
+      if (outmemory == NULL) {
+        outmemory =
+            gst_allocator_alloc (NULL,
+            TAPDEC_OUTBUF_SIZE * sizeof (int32_t), NULL);
+        total_pulses = 0;
+        gst_memory_map (outmemory, &outmap, GST_MAP_WRITE);
+        outdata = (int32_t *) outmap.data;
+      }
+      npulses =
+          tapdec_get_buffer (filter->tap, outdata + total_pulses,
+          TAPDEC_OUTBUF_SIZE - total_pulses);
+      total_pulses += npulses;
+      if (total_pulses >= TAPDEC_OUTBUF_SIZE) {
+        gst_memory_unmap (outmemory, &outmap);
+        gst_memory_resize (outmemory, 0, total_pulses * sizeof (int32_t));
+        gst_buffer_append_memory (outbuf, outmemory);
+        outmemory = NULL;
+      }
+    } while (npulses > 0);
+  }
+
+  if (outmemory != NULL) {
+    gst_memory_unmap (outmemory, &outmap);
+    gst_memory_resize (outmemory, 0, total_pulses * sizeof (int32_t));
+    gst_buffer_append_memory (outbuf, outmemory);
+  }
+  return gst_audio_decoder_finish_frame (parent, outbuf, 1);
+}
+
+
+/* initialize the tapdecoder's class */
+static void
+gst_tapdec_class_init (GstTapDecClass * klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstElementClass *gstelement_class = (GstElementClass *) klass;
+  GstAudioDecoderClass *gstaudiodecoder_class = (GstAudioDecoderClass *) klass;
+
+  gst_element_class_set_details_simple (gstelement_class,
+      "Commodore 64 TAP format decoder",
+      "Decoder/Audio",
+      "Decodes Commodore TAP data to audio",
+      "Fabrizio Gennari <fabrizio.ge@tiscali.it>");
+
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&src_factory));
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&sink_factory));
+  gobject_class->set_property = gst_tapdec_set_property;
+  gobject_class->get_property = gst_tapdec_get_property;
+
+  obj_properties[PROP_VOLUME] =
+      g_param_spec_uint ("volume", "Volume", "Volume", 0, 255,
+      254, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
+  obj_properties[PROP_TRIGGER_ON_RISING_EDGE] =
+      g_param_spec_boolean ("inverted", "Inverted waveform",
+      "If true, the output waveform will be inverted: a positive signal will become negative and vice versa",
+      TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
+  obj_properties[PROP_WAVEFORM] =
+      g_param_spec_enum ("waveform", "Waveform",
+      "Waveform to be used in output", gst_waveforms_get_type (), TAPDEC_SQUARE,
+      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
+  g_object_class_install_properties (gobject_class, N_PROPERTIES,
+      obj_properties);
+
+  gstaudiodecoder_class->start = gst_tapdec_start;
+  gstaudiodecoder_class->stop = gst_tapdec_stop;
+  gstaudiodecoder_class->set_format = gst_tapdec_set_format;
+  gstaudiodecoder_class->parse = gst_tapdec_parse;
+  gstaudiodecoder_class->handle_frame = gst_tapdec_handle_frame;
+}
+
+/* initialize the new element
+ * instantiate pads and add them to element
+ * set pad calback functions
+ * initialize instance structure
+ */
+static void
+gst_tapdec_init (GstTapDec * filter)
+{
+}
+
+gboolean
+gst_tapdec_register (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_tapdec_debug, "tapdec",
+      0, "Commodore TAP format decoder");
+  return gst_element_register (plugin, "tapdec", GST_RANK_SECONDARY,
+      GST_TYPE_TAPDEC);
+}
diff --git a/ext/tap/gsttapdec.h b/ext/tap/gsttapdec.h
new file mode 100644
index 0000000..c48d861
--- /dev/null
+++ b/ext/tap/gsttapdec.h
@@ -0,0 +1,55 @@
+/*
+ * GStreamer
+ * Copyright (C) 2011-2013 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_TAPDEC_H__
+#define __GST_TAPDEC_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS gboolean gst_tapdec_register (GstPlugin * plugin);
+GType
+gst_tapdec_get_type (void)
+    G_GNUC_CONST;
+
+G_END_DECLS
+#endif /* __GST_TAPDEC_H__ */
diff --git a/ext/tap/gsttapenc.c b/ext/tap/gsttapenc.c
new file mode 100644
index 0000000..f7375dd
--- /dev/null
+++ b/ext/tap/gsttapenc.c
@@ -0,0 +1,367 @@
+/*
+ * GStreamer
+ * Copyright (C) 2011-2013 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-tapenc
+ *
+ * Convert an audio stream to the Commodore TAP format.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v -m fakesrc ! tapenc ! tapefileenc ! fakesink silent=TRUE
+ * ]|
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <gst/gst.h>
+#include <gst/base/gstbytewriter.h>
+#include <gst/audio/gstaudioencoder.h>
+
+#include "gsttapenc.h"
+#include "tapencoder.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_tapenc_debug);
+#define GST_CAT_DEFAULT gst_tapenc_debug
+
+#define GST_TYPE_TAPENC \
+  (gst_tapenc_get_type())
+#define GST_TAPENC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TAPENC,GstTapEnc))
+#define GST_TAPENC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TAPENC,GstTapEncClass))
+#define GST_IS_TAPENC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TAPENC))
+#define GST_IS_TAPENC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TAPENC))
+
+typedef struct _GstTapEnc GstTapEnc;
+typedef struct _GstTapEncClass GstTapEncClass;
+
+struct _GstTapEnc
+{
+  GstAudioEncoder element;
+
+  gboolean inverted;
+
+  gboolean halfwaves;
+
+  guchar sensitivity;
+
+  guint min_duration;
+
+  guint unconsumed_samples;
+
+  guchar initial_threshold;
+
+  struct tap_enc_t *tap;
+};
+
+struct _GstTapEncClass
+{
+  GstAudioEncoderClass parent_class;
+};
+
+/* Filter signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0,
+  PROP_MIN_DURATION,
+  PROP_SENSITIVITY,
+  PROP_INVERTED,
+  PROP_HALFWAVES,
+  PROP_INITIAL_THRESHOLD
+};
+
+/* the capabilities of the inputs and outputs.
+ *
+ * describe the real formats here.
+ */
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw,"
+        "format=" GST_AUDIO_NE (S32) "," "channels=(int)1")
+    );
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-tap")
+    );
+
+G_DEFINE_TYPE (GstTapEnc, gst_tapenc, GST_TYPE_AUDIO_ENCODER);
+
+/* GObject vmethod implementations */
+
+static void
+gst_tapenc_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstTapEnc *filter = GST_TAPENC (object);
+
+  switch (prop_id) {
+    case PROP_MIN_DURATION:
+      filter->min_duration = g_value_get_uint (value);
+      break;
+    case PROP_SENSITIVITY:
+      filter->sensitivity = (guchar) g_value_get_uint (value);
+      break;
+    case PROP_INITIAL_THRESHOLD:
+      filter->initial_threshold = (guchar) g_value_get_uint (value);
+      break;
+    case PROP_INVERTED:
+      filter->inverted = g_value_get_boolean (value);
+      break;
+    case PROP_HALFWAVES:
+      filter->halfwaves = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_tapenc_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstTapEnc *filter = GST_TAPENC (object);
+
+  switch (prop_id) {
+    case PROP_MIN_DURATION:
+      g_value_set_uint (value, filter->min_duration);
+      break;
+    case PROP_SENSITIVITY:
+      g_value_set_uint (value, filter->sensitivity);
+      break;
+    case PROP_INITIAL_THRESHOLD:
+      g_value_set_uint (value, filter->initial_threshold);
+      break;
+    case PROP_INVERTED:
+      g_value_set_boolean (value, filter->inverted);
+      break;
+    case PROP_HALFWAVES:
+      g_value_set_boolean (value, filter->halfwaves);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+/* GstAudioEncoder vmethod implementations */
+
+static gboolean
+gst_tapenc_stop (GstAudioEncoder * enc)
+{
+  GstTapEnc *filter = GST_TAPENC (enc);
+
+  tapenc_exit (filter->tap);
+
+  return TRUE;
+}
+
+static gboolean
+gst_tapenc_set_format (GstAudioEncoder * enc, GstAudioInfo * info)
+{
+  GstTapEnc *filter = GST_TAPENC (enc);
+
+  GstCaps *othercaps = gst_caps_new_simple ("audio/x-tap",
+      "rate", G_TYPE_INT, info->rate,
+      "halfwaves", G_TYPE_BOOLEAN, filter->halfwaves,
+      NULL);
+  gst_audio_encoder_set_output_format (enc, othercaps);
+  return TRUE;
+}
+
+static gboolean
+gst_tapenc_start (GstAudioEncoder * enc)
+{
+  GstTapEnc *filter = GST_TAPENC (enc);
+
+  filter->tap = tapenc_init2 (filter->min_duration,
+      filter->sensitivity, filter->initial_threshold, filter->inverted);
+
+  if (filter->tap == NULL)
+    return FALSE;
+
+  tapenc_toggle_trigger_on_both_edges (filter->tap, filter->halfwaves);
+  filter->unconsumed_samples = 0;
+
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_tapenc_handle_frame (GstAudioEncoder * enc, GstBuffer * buf)
+{
+  GstTapEnc *filter = GST_TAPENC (enc);
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstByteWriter *writer = gst_byte_writer_new ();
+  guint size;
+  uint32_t pulse, samples = 0, buflen = 0;
+
+  if (buf != NULL) {
+    int32_t *data;
+    uint32_t bufsofar = 0;
+    GstMapInfo map;
+
+    gst_buffer_map (buf, &map, GST_MAP_READ);
+    data = (int32_t *) map.data;
+    buflen = map.size / sizeof (int32_t);
+
+    GST_DEBUG_OBJECT (filter, "Received %u samples", buflen);
+    while (bufsofar < buflen) {
+      bufsofar +=
+          tapenc_get_pulse (filter->tap, data + bufsofar, buflen - bufsofar,
+          &pulse);
+      if (pulse > 0) {
+        gst_byte_writer_put_data (writer, (const guint8 *) &pulse,
+            sizeof (pulse));
+        samples = filter->unconsumed_samples + bufsofar;
+      }
+    }
+
+    gst_buffer_unmap (buf, &map);
+  } else {
+    GST_DEBUG_OBJECT (filter, "Flushing");
+    pulse = tapenc_flush (filter->tap);
+    if (pulse > 0) {
+      gst_byte_writer_put_data (writer, (const guint8 *) &pulse,
+          sizeof (pulse));
+      samples = filter->unconsumed_samples;
+    }
+  }
+
+  filter->unconsumed_samples += buflen;
+  size = gst_byte_writer_get_size (writer);
+  if (size > 0) {
+    GstBuffer *newbuf = gst_byte_writer_free_and_get_buffer (writer);
+    GST_DEBUG_OBJECT (filter, "Outputting %lu bytes, flushing %u samples",
+        gst_buffer_get_size (newbuf), samples);
+    ret = gst_audio_encoder_finish_frame (enc, newbuf, samples);
+    filter->unconsumed_samples -= samples;
+  } else
+    gst_byte_writer_free (writer);
+
+  return ret;
+}
+
+/* initialize the tapencoder's class */
+static void
+gst_tapenc_class_init (GstTapEncClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+  GstAudioEncoderClass *gstaudioencoder_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstaudioencoder_class = (GstAudioEncoderClass *) klass;
+
+  gst_element_class_set_details_simple (gstelement_class,
+      "Commodore 64 TAP format encoder",
+      "Encoder/Audio",
+      "Encodes audio as Commodore 64 TAP data",
+      "Fabrizio Gennari <fabrizio.ge@tiscali.it>");
+
+  gstaudioencoder_class->start = gst_tapenc_start;
+  gstaudioencoder_class->set_format = gst_tapenc_set_format;
+  gstaudioencoder_class->stop = gst_tapenc_stop;
+  gstaudioencoder_class->handle_frame = gst_tapenc_handle_frame;
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&src_factory));
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&sink_factory));
+  gobject_class->set_property = gst_tapenc_set_property;
+  gobject_class->get_property = gst_tapenc_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_MIN_DURATION,
+      g_param_spec_uint ("min-duration", "Minimum duration",
+          "Minimum duration of a pulse, in samples", 0, UINT_MAX, 0,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (gobject_class, PROP_SENSITIVITY,
+      g_param_spec_uint ("sensitivity", "Sensitivity",
+          "How much the detector should be sensitive to a wave much smaller than the previous one. 100 = detect all waves. 0 = all waves less than 1/2 high than the previous one are ignored",
+          0, 100, 12,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (gobject_class, PROP_INVERTED,
+      g_param_spec_boolean ("inverted", "Inverted waveform",
+          "If true, the input waveform will be treated as inverted (upside down). A positive signal will be interpreted as negative and vice versa",
+          FALSE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (gobject_class, PROP_HALFWAVES,
+      g_param_spec_boolean ("halfwaves", "Use halfwaves",
+          "If true, both rising edges and falling edges are boundaries between pulses. Some C16/+4 tapes need it",
+          FALSE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (gobject_class, PROP_INITIAL_THRESHOLD,
+      g_param_spec_uint ("initial-threshold", "Initial threshold",
+          "Level the signal needs to reach to overcome initial noise", 0, 127,
+          20, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+
+  GST_DEBUG_CATEGORY_INIT (gst_tapenc_debug, "tapenc",
+      0, "Commodore TAP format encoder");
+}
+
+static void
+gst_tapenc_init (GstTapEnc * filter)
+{
+}
+
+gboolean
+gst_tapenc_register (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "tapenc", GST_RANK_NONE,
+      GST_TYPE_TAPENC);
+}
diff --git a/ext/tap/gsttapenc.h b/ext/tap/gsttapenc.h
new file mode 100644
index 0000000..d7cb44e
--- /dev/null
+++ b/ext/tap/gsttapenc.h
@@ -0,0 +1,55 @@
+/*
+ * GStreamer
+ * Copyright (C) 2013 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_TAPENC_H__
+#define __GST_TAPENC_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS gboolean gst_tapenc_register (GstPlugin * plugin);
+GType
+gst_tapenc_get_type (void)
+    G_GNUC_CONST;
+
+G_END_DECLS
+#endif /* __GST_TAPENC_H__ */
diff --git a/ext/tap/plugin.c b/ext/tap/plugin.c
new file mode 100644
index 0000000..44aaaa6
--- /dev/null
+++ b/ext/tap/plugin.c
@@ -0,0 +1,73 @@
+/*
+ * GStreamer
+ * Copyright (C) 2011 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* entry point to initialize the plug-in
+ * initialize the plug-in itself
+ * register the element factories and other features
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <gst/gst.h>
+
+#include "gsttapenc.h"
+#include "gsttapdec.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  return gst_tapenc_register (plugin)
+      && gst_tapdec_register (plugin);
+}
+
+/* gstreamer looks for this structure to register tapcodec
+ *
+ *
+ */
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    tapcodec,
+    "Commodore tape audio encoder and decoder",
+    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
diff --git a/gst/tap/Makefile.am b/gst/tap/Makefile.am
new file mode 100644
index 0000000..204bdf0
--- /dev/null
+++ b/gst/tap/Makefile.am
@@ -0,0 +1,21 @@
+# Note: plugindir is set in configure
+
+plugin_LTLIBRARIES = libgsttap.la
+
+# sources used to compile this plug-in
+libgsttap_la_SOURCES = \
+gstdmpdec.c gstdmpdec.h \
+gsttapfileenc.c gsttapfileenc.h \
+gsttapfiledec.c gsttapfiledec.h \
+gsttapconvert.c gsttapconvert.h \
+plugin.c
+
+# compiler and linker flags used to compile this plugin, set in configure.ac
+libgsttap_la_CFLAGS = $(GST_CFLAGS)
+libgsttap_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS)
+libgsttap_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgsttap_la_LIBTOOLFLAGS = --tag=disable-static
+
+# headers we need but don't want installed
+noinst_HEADERS = gstdmpdec.h gsttapfileenc.h gsttapfiledec.h gsttapconvert.h
+
diff --git a/gst/tap/gstdmpdec.c b/gst/tap/gstdmpdec.c
new file mode 100644
index 0000000..b51cb64
--- /dev/null
+++ b/gst/tap/gstdmpdec.c
@@ -0,0 +1,351 @@
+/*
+ * GStreamer
+ * Copyright (C) 2011-2013 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-dmpdec
+ *
+ * Reads a DC2N DMP file and extract its Commodore TAP stream.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v -m fakesrc ! dmpdec ! tapdec ! wavenc ! fakesink silent=TRUE
+ * ]|
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+#include <string.h>
+
+#include "gstdmpdec.h"
+
+/* #defines don't like whitespacey bits */
+#define GST_TYPE_DMPDEC \
+  (gst_dmpdec_get_type())
+#define GST_DMPDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DMPDEC,GstDmpDec))
+#define GST_DMPDEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DMPDEC,GstDmpDecClass))
+#define GST_IS_DMPDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DMPDEC))
+#define GST_IS_DMPDEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DMPDEC))
+
+typedef struct _GstDmpDec GstDmpDec;
+typedef struct _GstDmpDecClass GstDmpDecClass;
+
+struct _GstDmpDec
+{
+  GstElement element;
+
+  GstPad *sinkpad, *srcpad;
+
+  gboolean read_header;
+
+  guchar bytes_per_sample;
+  guint overflow;
+  guint rate;
+
+  GstAdapter *adapter;
+  GstClockTime timestamp;
+};
+
+struct _GstDmpDecClass
+{
+  GstElementClass parent_class;
+};
+
+GST_DEBUG_CATEGORY_STATIC (gst_dmpdec_debug);
+#define GST_CAT_DEFAULT gst_dmpdec_debug
+
+/* the capabilities of the inputs and outputs.
+ *
+ * describe the real formats here.
+ */
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-tap-dmp")
+    );
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-tap, " "halfwaves = (boolean) FALSE ")
+    );
+
+G_DEFINE_TYPE (GstDmpDec, gst_dmpdec, GST_TYPE_ELEMENT);
+
+/* GObject vmethod implementations */
+static void
+gst_dmpdec_finalize (GObject * object)
+{
+  GstDmpDec *dec = GST_DMPDEC (object);
+  g_object_unref (dec->adapter);
+}
+
+/* GstElement vmethod implementations */
+static GstStateChangeReturn
+gst_dmpdec_change_state (GstElement * element, GstStateChange transition)
+{
+  GstDmpDec *dec = GST_DMPDEC (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      dec->read_header = 0;
+      dec->timestamp = 0;
+      gst_adapter_clear (dec->adapter);
+      break;
+    default:
+      break;
+  }
+
+  return GST_ELEMENT_CLASS (gst_dmpdec_parent_class)->change_state (element,
+      transition);
+}
+
+/* initialize the dmpdec's class */
+static void
+gst_dmpdec_class_init (GstDmpDecClass * gclass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+  GObjectClass *object_class = G_OBJECT_CLASS (gclass);
+
+  gst_element_class_set_details_simple (element_class,
+      "Commodore DMP (format generated by DC2N devices) file reader",
+      "Decoder/Audio",
+      "Reads TAP data from DMP files",
+      "Fabrizio Gennari <fabrizio.ge@tiscali.it>");
+
+  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));
+
+  element_class->change_state = gst_dmpdec_change_state;
+  object_class->finalize = gst_dmpdec_finalize;
+}
+
+/* chain function
+ * this function does the actual processing
+ */
+
+#define DMPDEC_HEADER_SIZE 20
+
+static GstFlowReturn
+gst_dmpdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
+{
+  GstDmpDec *filter = GST_DMPDEC (parent);
+  GstFlowReturn ret = GST_FLOW_OK;
+  guint num_inpulses;
+  gpointer header;
+
+  gst_adapter_push (filter->adapter, buf);
+  if (!filter->read_header
+      && (header =
+          gst_adapter_take (filter->adapter, DMPDEC_HEADER_SIZE)) != NULL) {
+    const char expected_signature[] = "DC2N-TAP-RAW";
+    guint8 *header_data = (guint8 *) header;
+    gboolean header_valid;
+    GstCaps *srccaps, *templatecaps;
+    GstStructure *srcstructure;
+    GValue rate = { 0 };
+    guchar bits_per_sample;
+    GstEvent *new_caps_event;
+    GstEvent *new_segment_event;
+    GstSegment new_segment;
+
+    GST_DEBUG_OBJECT (filter, "Reading header");
+    header_valid =
+        !memcmp (header_data, expected_signature, strlen (expected_signature));
+    header_data += strlen (expected_signature);
+    header_valid = header_valid && ((*header_data++) == 0);     /* only version 0 supported */
+    header_data += 2;           /* skip the 2 useless bytes in header */
+    bits_per_sample = *header_data++;
+    filter->bytes_per_sample = (bits_per_sample + 7) / 8;
+    filter->overflow = (1 << bits_per_sample) - 1;
+    g_value_init (&rate, G_TYPE_INT);
+    filter->rate = GST_READ_UINT32_LE (header_data);
+    g_value_set_int (&rate, filter->rate);
+    g_free (header);
+
+    if (!header_valid)
+      return GST_FLOW_ERROR;
+
+    templatecaps = gst_static_pad_template_get_caps (&src_factory);
+    srccaps = gst_caps_copy (templatecaps);
+    gst_caps_unref (templatecaps);
+    srcstructure = gst_caps_get_structure (srccaps, 0);
+    gst_structure_set_value (srcstructure, "rate", &rate);
+    new_caps_event = gst_event_new_caps (srccaps);
+    GST_DEBUG_OBJECT (filter, "Read header, set out caps %" GST_PTR_FORMAT,
+        srccaps);
+
+    gst_pad_push_event (filter->srcpad, new_caps_event);
+    gst_caps_unref (srccaps);
+    gst_segment_init (&new_segment, GST_FORMAT_TIME);
+    new_segment_event = gst_event_new_segment (&new_segment);
+    gst_pad_push_event (filter->srcpad, new_segment_event);
+    filter->read_header = TRUE;
+  }
+
+  /* Don't go past this pojnt before finishing with the header */
+  if (!filter->read_header)
+    return GST_FLOW_OK;
+
+  if ((num_inpulses =
+          gst_adapter_available (filter->adapter) / filter->bytes_per_sample) >
+      0) {
+    guint num_inbytes = num_inpulses * filter->bytes_per_sample;
+    guint num_outbytes;
+    const guint8 *inbytes = gst_adapter_map (filter->adapter, num_inbytes);
+    GstBuffer *newbuf =
+        gst_buffer_new_allocate (NULL, num_inpulses * sizeof (guint), NULL);
+    guint pos_inbyte, used_inbytes = 0, out_pulse = 0, pos_outpulse = 0;
+    GstMapInfo map;
+    guint *out_pulses;
+    GstClockTime duration = 0;
+
+    gst_buffer_map (newbuf, &map, GST_MAP_WRITE);
+    out_pulses = (guint *) map.data;
+    for (pos_inbyte = 0; pos_inbyte < num_inbytes;
+        pos_inbyte += filter->bytes_per_sample) {
+      guint inpulse;
+
+      switch (filter->bytes_per_sample) {
+        case 1:
+          inpulse = inbytes[pos_inbyte];
+          break;
+        case 2:
+          inpulse = GST_READ_UINT16_LE (inbytes + pos_inbyte);
+          break;
+        case 3:
+          inpulse = GST_READ_UINT24_LE (inbytes + pos_inbyte);
+          break;
+        case 4:
+        default:
+          inpulse = GST_READ_UINT32_LE (inbytes + pos_inbyte);
+          break;
+      }
+      out_pulse += inpulse;
+      if (inpulse > filter->overflow)
+        ret = GST_FLOW_ERROR;
+      else if (inpulse < filter->overflow) {
+        out_pulses[pos_outpulse++] = out_pulse;
+        duration += out_pulse;
+        out_pulse = 0;
+        used_inbytes = pos_inbyte + filter->bytes_per_sample;
+      }
+    }
+    gst_adapter_flush (filter->adapter, used_inbytes);
+    gst_buffer_unmap (newbuf, &map);
+    gst_adapter_unmap (filter->adapter);
+    if ((num_outbytes = pos_outpulse * sizeof (guint)) > 0) {
+      gst_buffer_resize (newbuf, 0, num_outbytes);
+      GST_BUFFER_DTS (newbuf) = filter->timestamp;
+      duration = gst_util_uint64_scale (duration, GST_SECOND, filter->rate);
+      GST_BUFFER_DURATION (newbuf) = duration;
+      filter->timestamp += duration;
+      GST_LOG_OBJECT (filter, "pushing %" GST_PTR_FORMAT, newbuf);
+      ret = gst_pad_push (filter->srcpad, newbuf);
+    } else
+      gst_buffer_unref (newbuf);
+  }
+
+  return ret;
+}
+
+static gboolean
+gst_dmpdec_event (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+  GST_LOG ("handling %s event", GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_CAPS:
+    case GST_EVENT_SEGMENT:
+      GST_DEBUG ("eating event");
+      gst_event_unref (event);
+      return TRUE;
+    default:
+      GST_DEBUG ("forwarding event");
+      return gst_pad_event_default (pad, parent, event);
+  }
+}
+
+/* initialize the new element
+ * instantiate pads and add them to element
+ * set pad calback functions
+ * initialize instance structure
+ */
+
+static void
+gst_dmpdec_init (GstDmpDec * filter)
+{
+  filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
+  gst_pad_set_chain_function (filter->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_dmpdec_chain));
+
+  filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
+  gst_pad_set_event_function (filter->sinkpad, gst_dmpdec_event);
+  gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
+  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
+
+  filter->adapter = gst_adapter_new ();
+  filter->read_header = FALSE;
+}
+
+gboolean
+gst_dmpdec_register (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_dmpdec_debug, "dmpdec",
+      0, "Commodore 64 DMP encoder");
+
+  return gst_element_register (plugin, "dmpdec", GST_RANK_MARGINAL,
+      GST_TYPE_DMPDEC);
+}
diff --git a/gst/tap/gstdmpdec.h b/gst/tap/gstdmpdec.h
new file mode 100644
index 0000000..578d3c5
--- /dev/null
+++ b/gst/tap/gstdmpdec.h
@@ -0,0 +1,57 @@
+/*
+ * GStreamer
+ * Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) 2011 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_DMPDEC_H__
+#define __GST_DMPDEC_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS gboolean gst_dmpdec_register (GstPlugin * plugin);
+GType
+gst_dmpdec_get_type (void)
+    G_GNUC_CONST;
+
+G_END_DECLS
+#endif /* __GST_DMPDEC_H__ */
diff --git a/gst/tap/gsttapconvert.c b/gst/tap/gsttapconvert.c
new file mode 100644
index 0000000..b215a39
--- /dev/null
+++ b/gst/tap/gsttapconvert.c
@@ -0,0 +1,226 @@
+/*
+ * GStreamer
+ * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
+ * Copyright (C) 2011 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-tapconvert
+ *
+ * Change frequency of an audio stream in the Commodore TAP format.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v -m fakesrc ! tapenc ! tapconvert ! tapefileenc ! fakesink silent=TRUE
+ * ]|
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+
+#include "gsttapconvert.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_tapconvert_debug);
+#define GST_CAT_DEFAULT gst_tapconvert_debug
+
+#define GST_TYPE_TAP_CONVERT \
+  (gst_tapconvert_get_type())
+#define GST_TAP_CONVERT(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TAP_CONVERT,GstTapConvert))
+#define GST_TAP_CONVERT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TAP_CONVERT,GstTapConvertClass))
+#define GST_IS_TAP_CONVERT(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TAP_CONVERT))
+#define GST_IS_TAP_CONVERT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TAP_CONVERT))
+
+typedef struct _GstTapConvert GstTapConvert;
+typedef struct _GstTapConvertClass GstTapConvertClass;
+
+struct _GstTapConvert
+{
+  GstBaseTransform element;
+
+  gint inrate;
+  gint outrate;
+};
+
+struct _GstTapConvertClass
+{
+  GstBaseTransformClass parent_class;
+};
+
+/* the capabilities of the inputs and outputs.
+ *
+ */
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-tap")
+    );
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-tap")
+    );
+
+/* debug category for fltering log messages
+ *
+ */
+
+G_DEFINE_TYPE (GstTapConvert, gst_tapconvert, GST_TYPE_BASE_TRANSFORM);
+
+static gboolean gst_tapconvert_set_caps (GstBaseTransform * trans,
+    GstCaps * incaps, GstCaps * outcaps);
+static GstFlowReturn gst_tapconvert_transform_ip (GstBaseTransform * base,
+    GstBuffer * outbuf);
+static GstCaps *gst_tapconvert_transform_caps (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * caps, GstCaps * filter);
+
+/* GObject vmethod implementations */
+
+/* initialize the plugin's class */
+static void
+gst_tapconvert_class_init (GstTapConvertClass * klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
+      GST_DEBUG_FUNCPTR (gst_tapconvert_transform_ip);
+  GST_BASE_TRANSFORM_CLASS (klass)->set_caps =
+      GST_DEBUG_FUNCPTR (gst_tapconvert_set_caps);
+  GST_BASE_TRANSFORM_CLASS (klass)->transform_caps =
+      GST_DEBUG_FUNCPTR (gst_tapconvert_transform_caps);
+
+  gst_element_class_set_details_simple (element_class,
+      "Commodore 64 TAP rate converter",
+      "Filter/Converter/Audio",
+      "Adapts rate of a Commodore TAP stream",
+      "Fabrizio Gennari <fabrizio.ge@tiscali.it>");
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sink_template));
+
+  GST_DEBUG_CATEGORY_INIT (gst_tapconvert_debug, "tapconvert", 0,
+      "TAP rate convert");
+}
+
+/* initialize the new element
+ * initialize instance structure
+ */
+static void
+gst_tapconvert_init (GstTapConvert * filter)
+{
+}
+
+/* GstBaseTransform vmethod implementations */
+
+/* this function does the actual processing
+ */
+static GstFlowReturn
+gst_tapconvert_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
+{
+  GstTapConvert *filter = GST_TAP_CONVERT (base);
+  guint buflen;
+  guint bufsofar;
+  guint *data;
+  GstMapInfo map;
+
+  if (!gst_buffer_map (outbuf, &map, GST_MAP_READWRITE))
+    return GST_FLOW_ERROR;
+  data = (guint32 *) map.data;
+  buflen = map.size / sizeof (guint32);
+  if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (outbuf)))
+    gst_object_sync_values (GST_OBJECT (filter), GST_BUFFER_TIMESTAMP (outbuf));
+
+  for (bufsofar = 0; bufsofar < buflen; bufsofar++) {
+    guint64 pulse = (guint64) data[bufsofar] * filter->outrate;
+    data[bufsofar] = (guint32) (pulse / filter->inrate);
+  }
+
+  return GST_FLOW_OK;
+}
+
+static gboolean
+gst_tapconvert_set_caps (GstBaseTransform * trans, GstCaps * incaps,
+    GstCaps * outcaps)
+{
+  GstTapConvert *filter = GST_TAP_CONVERT (trans);
+  GstStructure *instructure = gst_caps_get_structure (incaps, 0);
+  GstStructure *outstructure = gst_caps_get_structure (outcaps, 0);
+  gboolean ret1 = gst_structure_get_int (instructure, "rate", &filter->inrate);
+  gboolean ret2 =
+      gst_structure_get_int (outstructure, "rate", &filter->outrate);
+  gboolean ret = ret1 && ret2;
+
+  GST_DEBUG_OBJECT (trans, "from: %" GST_PTR_FORMAT, instructure);
+  GST_DEBUG_OBJECT (trans, "to: %" GST_PTR_FORMAT, outstructure);
+
+  if (!ret)
+    GST_ERROR_OBJECT (filter, "incomplete caps");
+
+  return ret;
+}
+
+static GstCaps *
+gst_tapconvert_transform_caps (GstBaseTransform * trans,
+    GstPadDirection direction, GstCaps * caps, GstCaps * filter)
+{
+  GstPad *otherpad = direction == GST_PAD_SRC ? trans->sinkpad : trans->srcpad;
+  GstCaps *newcaps = gst_caps_copy (caps);
+  GstStructure *newstructure = gst_caps_get_structure (newcaps, 0);
+  gboolean rate_changed = FALSE;
+  GstCaps *othercaps = gst_pad_peer_query_caps (otherpad, NULL);
+
+  GST_DEBUG_OBJECT (trans, "direction %s from: %" GST_PTR_FORMAT,
+      direction == GST_PAD_SRC ? "src" : "sink", newstructure);
+
+  if (othercaps && gst_caps_get_size (othercaps) > 0) {
+    GstStructure *structure = gst_caps_get_structure (othercaps, 0);
+    const GValue *rate = gst_structure_get_value (structure, "rate");
+
+    if (G_VALUE_HOLDS (rate, G_TYPE_INT)) {
+      rate_changed = TRUE;
+      gst_structure_set_value (newstructure, "rate", rate);
+    }
+  }
+
+  if (!rate_changed)
+    gst_structure_remove_field (newstructure, "rate");
+
+  GST_DEBUG_OBJECT (trans, "to: %" GST_PTR_FORMAT, newstructure);
+
+  return newcaps;
+}
+
+gboolean
+gst_tapconvert_register (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "tapconvert", GST_RANK_NONE,
+      GST_TYPE_TAP_CONVERT);
+}
diff --git a/gst/tap/gsttapconvert.h b/gst/tap/gsttapconvert.h
new file mode 100644
index 0000000..0a3c784
--- /dev/null
+++ b/gst/tap/gsttapconvert.h
@@ -0,0 +1,34 @@
+/*
+ * GStreamer
+ * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
+ * Copyright (C) 2011 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_PLUGIN_TEMPLATE_H__
+#define __GST_PLUGIN_TEMPLATE_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+
+G_BEGIN_DECLS gboolean gst_tapconvert_register (GstPlugin * plugin);
+GType
+gst_tapconvert_get_type (void)
+    G_GNUC_CONST;
+
+G_END_DECLS
+#endif /* __GST_PLUGIN_TEMPLATE_H__ */
diff --git a/gst/tap/gsttapfiledec.c b/gst/tap/gsttapfiledec.c
new file mode 100644
index 0000000..65890f3
--- /dev/null
+++ b/gst/tap/gsttapfiledec.c
@@ -0,0 +1,366 @@
+/*
+ * GStreamer
+ * Copyright (C) 2011-2013 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-tapfiledec
+ *
+ * Reads a Commodore TAP file and extract its Commodore TAP stream.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v -m fakesrc ! tapfiledec ! tapdec ! wavenc ! fakesink silent=TRUE
+ * ]|
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+#include <gst/base/gstbytewriter.h>
+#include <string.h>
+
+#include "gsttapfiledec.h"
+
+/* #defines don't like whitespacey bits */
+#define GST_TYPE_TAPFILEDEC \
+  (gst_tapfiledec_get_type())
+#define GST_TAPFILEDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TAPFILEDEC,GstTapFileDec))
+#define GST_TAPFILEDEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TAPFILEDEC,GstTapFileDecClass))
+#define GST_IS_TAPFILEDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TAPFILEDEC))
+#define GST_IS_TAPFILEDEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TAPFILEDEC))
+
+typedef struct _GstTapFileDec GstTapFileDec;
+typedef struct _GstTapFileDecClass GstTapFileDecClass;
+
+struct _GstTapFileDec
+{
+  GstElement element;
+
+  GstPad *sinkpad, *srcpad;
+
+  gboolean read_header;
+
+  guchar version;
+
+  GstAdapter *adapter;
+  GstClockTime timestamp;
+  gboolean last_was_0;
+  guint rate;
+};
+
+struct _GstTapFileDecClass
+{
+  GstElementClass parent_class;
+};
+
+GST_DEBUG_CATEGORY_STATIC (gst_tapfiledec_debug);
+#define GST_CAT_DEFAULT gst_tapfiledec_debug
+
+/* the capabilities of the inputs and outputs.
+ *
+ * describe the real formats here.
+ */
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-tap-tap")
+    );
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-tap, "
+        "rate = (int) { 110840, 111860, 123156, 127840, 138550 }")
+    );
+
+/* Who cares about having 8x these resolutions for pauses anyway? */
+static const guint tap_clocks[][2] = {
+  {123156, 127840},             /* C64 */
+  {138550, 127840},             /* VIC */
+  {110840, 111860}              /* C16 */
+};
+
+G_DEFINE_TYPE (GstTapFileDec, gst_tapfiledec, GST_TYPE_ELEMENT);
+
+/* GObject vmethod implementations */
+static void
+gst_tapfiledec_finalize (GObject * object)
+{
+  GstTapFileDec *dec = GST_TAPFILEDEC (object);
+  g_object_unref (dec->adapter);
+}
+
+/* GstElement vmethod implementations */
+static GstStateChangeReturn
+gst_tapfiledec_change_state (GstElement * element, GstStateChange transition)
+{
+  GstTapFileDec *dec = GST_TAPFILEDEC (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      dec->read_header = FALSE;
+      dec->last_was_0 = FALSE;
+      dec->timestamp = 0;
+      gst_adapter_clear (dec->adapter);
+      break;
+    default:
+      break;
+  }
+
+  return GST_ELEMENT_CLASS (gst_tapfiledec_parent_class)->change_state (element,
+      transition);
+}
+
+/* initialize the tapfiledec's class */
+static void
+gst_tapfiledec_class_init (GstTapFileDecClass * klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  gst_element_class_set_details_simple (element_class,
+      "Commodore 64 TAP file reader",
+      "Codec/Parser/Audio",
+      "Reads TAP data from TAP files",
+      "Fabrizio Gennari <fabrizio.ge@tiscali.it>");
+
+  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));
+
+  element_class->change_state = gst_tapfiledec_change_state;
+  object_class->finalize = gst_tapfiledec_finalize;
+}
+
+/* chain function
+ * this function does the actual processing
+ */
+
+#define TAPFILEDEC_HEADER_SIZE 20
+#define VALUE_OF_0_IN_TAP_V0 25000
+#define THREE_BYTE_OVERFLOW 0xFFFFFF
+
+static GstFlowReturn
+gst_tapfiledec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
+{
+  GstTapFileDec *filter = GST_TAPFILEDEC (parent);
+  GstFlowReturn ret = GST_FLOW_OK;
+  guint32 pulse = 0;
+  guint peeked_bytes = 0, bytes_to_flush = 0;
+  GstByteWriter *writer = gst_byte_writer_new ();
+  guint size;
+  const guint8 *inbytes;
+  guint num_inpulses;
+  gpointer header;
+  GstClockTime duration = 0;
+
+  gst_adapter_push (filter->adapter, buf);
+  if (!filter->read_header
+      && (header =
+          gst_adapter_take (filter->adapter, TAPFILEDEC_HEADER_SIZE)) != NULL) {
+    const char expected_signature1[] = "C64-TAPE-RAW";
+    const char expected_signature2[] = "C16-TAPE-RAW";
+    guint8 *header_data = (guint8 *) header;
+    gboolean header_valid;
+    GstCaps *srccaps;
+    GstEvent *new_caps_event;
+    guchar machine, video_standard;
+    GstEvent *new_segment_event;
+    GstSegment new_segment;
+
+    header_valid =
+        !memcmp (header_data, expected_signature1, strlen (expected_signature1))
+        || !memcmp (header_data, expected_signature2,
+        strlen (expected_signature2));
+    header_data += strlen (expected_signature1);
+    filter->version = *header_data++;
+    header_valid = header_valid &&
+        (filter->version == 0 || filter->version == 1 || filter->version == 2);
+    machine = *header_data++;
+    header_valid = header_valid && (machine == 0        /* C64 */
+        || machine == 1         /* VIC20 */
+        || machine == 2         /* C16 */
+        );
+    video_standard = *header_data++;
+    header_valid = header_valid && (video_standard == 0 /* PAL */
+        || video_standard == 1  /* NTSC */
+        );
+
+    g_free (header);
+
+    if (!header_valid)
+      return GST_FLOW_ERROR;
+
+    srccaps = gst_pad_query_caps (filter->srcpad, NULL);
+    srccaps = gst_caps_make_writable (srccaps);
+
+    filter->read_header = TRUE;
+    filter->rate = tap_clocks[machine][video_standard];
+
+    gst_caps_set_simple (srccaps,
+        "rate", G_TYPE_INT, filter->rate,
+        "halfwaves", G_TYPE_BOOLEAN, filter->version == 2, NULL);
+    new_caps_event = gst_event_new_caps (srccaps);
+
+    gst_pad_push_event (filter->srcpad, new_caps_event);
+    gst_segment_init (&new_segment, GST_FORMAT_TIME);
+    new_segment_event = gst_event_new_segment (&new_segment);
+    gst_pad_push_event (filter->srcpad, new_segment_event);
+  }
+
+  /* Don't go past this point before finishing with the header */
+  if (!filter->read_header)
+    return GST_FLOW_OK;
+
+  num_inpulses = gst_adapter_available (filter->adapter);
+  inbytes = gst_adapter_map (filter->adapter, num_inpulses);
+
+  while (1) {
+    guint inpulse;
+    gboolean overflow_occurred = FALSE;
+
+    if (num_inpulses < peeked_bytes + 1)
+      break;
+    inpulse = inbytes[peeked_bytes];
+    peeked_bytes++;
+    if (inpulse == 0) {
+      if (filter->version == 0) {
+        if (!filter->last_was_0) {
+          filter->last_was_0 = TRUE;
+          inpulse = VALUE_OF_0_IN_TAP_V0;
+        } else
+          overflow_occurred = TRUE;
+      } else {
+        if (num_inpulses < peeked_bytes + 3)
+          break;
+        inpulse = GST_READ_UINT24_LE (inbytes + peeked_bytes);
+        peeked_bytes += 3;
+        if (inpulse == THREE_BYTE_OVERFLOW)
+          overflow_occurred = TRUE;
+        inpulse /= 8;
+      }
+    } else
+      filter->last_was_0 = FALSE;
+    pulse += inpulse;
+    if (!overflow_occurred) {
+      gst_byte_writer_put_data (writer, (const guint8 *) &pulse,
+          sizeof (pulse));
+      duration += pulse;
+      pulse = 0;
+      bytes_to_flush = peeked_bytes;
+    }
+  }
+  gst_adapter_flush (filter->adapter, bytes_to_flush);
+  size = gst_byte_writer_get_size (writer);
+  if (size > 0) {
+    GstBuffer *newbuf = gst_byte_writer_free_and_get_buffer (writer);
+    GST_BUFFER_DTS (newbuf) = filter->timestamp;
+    duration = gst_util_uint64_scale (duration, GST_SECOND, filter->rate);
+    GST_BUFFER_DURATION (newbuf) = duration;
+    ret = gst_pad_push (filter->srcpad, newbuf);
+  } else
+    gst_byte_writer_free (writer);
+
+  return ret;
+}
+
+static gboolean
+gst_tapfiledec_event (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+  GST_LOG ("handling %s event", GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_CAPS:
+    case GST_EVENT_SEGMENT:
+      GST_DEBUG ("eating event");
+      gst_event_unref (event);
+      return TRUE;
+    default:
+      GST_DEBUG ("forwarding event");
+      return gst_pad_event_default (pad, parent, event);
+  }
+}
+
+/* initialize the new element
+ * instantiate pads and add them to element
+ * set pad callback functions
+ * initialize instance structure
+ */
+
+static void
+gst_tapfiledec_init (GstTapFileDec * filter)
+{
+  filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
+  gst_pad_set_chain_function (filter->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_tapfiledec_chain));
+  gst_pad_set_event_function (filter->sinkpad, gst_tapfiledec_event);
+
+  filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
+
+  gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
+  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
+
+  filter->adapter = gst_adapter_new ();
+  filter->read_header = FALSE;
+  filter->last_was_0 = FALSE;
+}
+
+gboolean
+gst_tapfiledec_register (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_tapfiledec_debug, "tapfiledec",
+      0, "Commodore TAP file decoder");
+
+  return gst_element_register (plugin, "tapfiledec", GST_RANK_MARGINAL,
+      GST_TYPE_TAPFILEDEC);
+}
diff --git a/gst/tap/gsttapfiledec.h b/gst/tap/gsttapfiledec.h
new file mode 100644
index 0000000..3b9594f
--- /dev/null
+++ b/gst/tap/gsttapfiledec.h
@@ -0,0 +1,57 @@
+/*
+ * GStreamer
+ * Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) 2011 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_TAPFILEDEC_H__
+#define __GST_TAPFILEDEC_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS gboolean gst_tapfiledec_register (GstPlugin * plugin);
+GType
+gst_tapfiledec_get_type (void)
+    G_GNUC_CONST;
+
+G_END_DECLS
+#endif /* __GST_TAPFILEDEC_H__ */
diff --git a/gst/tap/gsttapfileenc.c b/gst/tap/gsttapfileenc.c
new file mode 100644
index 0000000..e395a57
--- /dev/null
+++ b/gst/tap/gsttapfileenc.c
@@ -0,0 +1,486 @@
+/*
+ * GStreamer
+ * Copyright (C) 2011-2013 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-tapfileenc
+ *
+ * Dumps a Commodore TAP stream into the TAP file format.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v -m fakesrc ! tapenc ! tapfileenc ! fakesink silent=TRUE
+ * ]|
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <gst/gst.h>
+#include <gst/base/gstbytewriter.h>
+#include <string.h>
+
+#include "gsttapfileenc.h"
+
+/* #defines don't like whitespacey bits */
+#define GST_TYPE_TAPFILEENC \
+  (gst_tapfileenc_get_type())
+#define GST_TAPFILEENC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TAPFILEENC,GstTapFileEnc))
+#define GST_TAPFILEENC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TAPFILEENC,GstTapFileEncClass))
+#define GST_IS_TAPFILEENC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TAPFILEENC))
+#define GST_IS_TAPFILEENC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TAPFILEENC))
+
+typedef struct _GstTapFileEnc GstTapFileEnc;
+typedef struct _GstTapFileEncClass GstTapFileEncClass;
+
+struct _GstTapFileEnc
+{
+  GstElement element;
+
+  GstPad *sinkpad, *srcpad;
+
+  gboolean sent_header;
+  gboolean force_version_0;
+  guchar machine_byte;
+  guchar video_byte;
+  guchar version;
+  gboolean last_was_overflow;
+
+  guint length;
+};
+
+struct _GstTapFileEncClass
+{
+  GstElementClass parent_class;
+};
+
+GST_DEBUG_CATEGORY_STATIC (gst_tapfileenc_debug);
+#define GST_CAT_DEFAULT gst_tapfileenc_debug
+
+/* Filter signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0,
+  PROP_MACHINE_BYTE,
+  PROP_VIDEO_BYTE,
+  PROP_FORCE_VERSION_0
+};
+
+static const guint tap_clocks[][2] = {
+  {985248, 1022727},            /* C64 */
+  {1108405, 1022727},           /* VIC */
+  {886724, 894886}              /* C16 */
+};
+
+/* the capabilities of the inputs and outputs.
+ *
+ * describe the real formats here.
+ */
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-tap, "
+        "rate = (int) { 886724 , 894886 , 985248 , 1022727 , 1108405 }")
+    );
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-tap-tap")
+    );
+
+G_DEFINE_TYPE (GstTapFileEnc, gst_tapfileenc, GST_TYPE_ELEMENT);
+
+/* GObject vmethod implementations */
+
+static void
+gst_tapfileenc_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstTapFileEnc *filter = GST_TAPFILEENC (object);
+
+  switch (prop_id) {
+    case PROP_MACHINE_BYTE:
+      filter->machine_byte = (guchar) g_value_get_enum (value);
+      break;
+    case PROP_VIDEO_BYTE:
+      filter->video_byte = (guchar) g_value_get_enum (value);
+      break;
+    case PROP_FORCE_VERSION_0:
+      filter->force_version_0 = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_tapfileenc_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstTapFileEnc *filter = GST_TAPFILEENC (object);
+
+  switch (prop_id) {
+    case PROP_MACHINE_BYTE:
+      g_value_set_enum (value, filter->machine_byte);
+      break;
+    case PROP_VIDEO_BYTE:
+      g_value_set_enum (value, filter->video_byte);
+      break;
+    case PROP_FORCE_VERSION_0:
+      g_value_set_boolean (value, filter->force_version_0);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+/* Machine entry used in extended TAP header */
+#define TAP_MACHINE_C64 0
+#define TAP_MACHINE_VIC 1
+#define TAP_MACHINE_C16 2
+#define TAP_MACHINE_MAX 2
+
+/* Video standards */
+#define TAP_VIDEOTYPE_PAL  0
+#define TAP_VIDEOTYPE_NTSC 1
+#define TAP_VIDEOTYPE_MAX  1
+
+static GType
+gst_machines_get_type (void)
+{
+  static GType machines_type = 0;
+
+  if (machines_type == 0) {
+    static const GEnumValue machines_profiles[] = {
+      {TAP_MACHINE_C64, "C64", "Commodore 64"},
+      {TAP_MACHINE_C16, "C16", "Commodore 16/Plus-4"},
+      {TAP_MACHINE_VIC, "VIC20", "Commodore VIC-20"},
+      {0, NULL, NULL},
+    };
+    machines_type =
+        g_enum_register_static ("GstTapFileEncMachines", machines_profiles);
+  }
+
+  return machines_type;
+}
+
+static GType
+gst_videotypes_get_type (void)
+{
+  static GType videotypes_type = 0;
+
+  if (videotypes_type == 0) {
+    static const GEnumValue videotypes_profiles[] = {
+      {TAP_VIDEOTYPE_PAL, "PAL", "PAL"},
+      {TAP_VIDEOTYPE_NTSC, "NTSC", "NTSC"},
+      {0, NULL, NULL},
+    };
+    videotypes_type =
+        g_enum_register_static ("GstTapFileEncVideotypes", videotypes_profiles);
+  }
+
+  return videotypes_type;
+}
+
+/* initialize the tapfileenc's class */
+static void
+gst_tapfileenc_class_init (GstTapFileEncClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  gobject_class = (GObjectClass *) klass;
+
+  gobject_class->set_property = gst_tapfileenc_set_property;
+  gobject_class->get_property = gst_tapfileenc_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_MACHINE_BYTE,
+      g_param_spec_enum ("machine", "Machine",
+          "Machine for which this dump is intended", gst_machines_get_type (),
+          TAP_MACHINE_C64,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (gobject_class, PROP_VIDEO_BYTE,
+      g_param_spec_enum ("videotype", "Video type",
+          "Video type of machine for which this dump is intended",
+          gst_videotypes_get_type (), TAP_VIDEOTYPE_PAL,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+  g_object_class_install_property (gobject_class, PROP_FORCE_VERSION_0,
+      g_param_spec_boolean ("version-0", "Force version 0",
+          "If true, and incoming stream is not halfwaves, a version 0 TAP file will be created. Otherwise the version will be 1 for full waves and 2 for halfwaves.",
+          FALSE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
+
+  gst_element_class_set_details_simple (element_class,
+      "Commodore 64 TAP file writer",
+      "Codec/Encoder/Audio",
+      "Writes TAP data as TAP files",
+      "Fabrizio Gennari <fabrizio.ge@tiscali.it>");
+
+  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));
+}
+
+/* GstElement vmethod implementations */
+
+#define OVERFLOW_HI 0xFFFFFF
+#define OVERFLOW_LO 0x800
+
+static GstFlowReturn
+write_header (GstPad * pad, guchar version, guchar machine_byte,
+    guchar video_byte, guint len)
+{
+  GstBuffer *buf = gst_buffer_new_and_alloc (20);
+  guint8 *header;
+  const char signature_c64[] = "C64-TAPE-RAW";
+  const char signature_c16[] = "C16-TAPE-RAW";
+  GstMapInfo map;
+
+  gst_buffer_map (buf, &map, GST_MAP_WRITE);
+  header = map.data;
+  memcpy (header, machine_byte != 2 ? signature_c64 : signature_c16,
+      strlen (signature_c64));
+  header += strlen (signature_c64);
+  *header++ = version;
+  *header++ = machine_byte;
+  *header++ = video_byte;
+  *header++ = 0;
+  GST_WRITE_UINT32_LE (header, len);
+  gst_buffer_unmap (buf, &map);
+  return gst_pad_push (pad, buf);
+}
+
+/* chain function
+ * this function does the actual processing
+ */
+
+static GstFlowReturn
+gst_tapfileenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
+{
+  GstTapFileEnc *filter = GST_TAPFILEENC (parent);
+  GstMapInfo map;
+  guint *data;
+  guint buflen = gst_buffer_get_size (buf) / sizeof (guint32);
+  guint bufsofar;
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstByteWriter *writer = gst_byte_writer_new ();
+  guint size;
+
+  if (!filter->sent_header) {
+    ret = write_header (filter->srcpad, filter->version, filter->machine_byte, filter->video_byte, 0    /* real length will be written later */
+        );
+
+    if (ret != GST_FLOW_OK) {
+      GST_WARNING_OBJECT (filter, "push header failed: flow = %s",
+          gst_flow_get_name (ret));
+    }
+    filter->sent_header = TRUE;
+  }
+
+  gst_buffer_map (buf, &map, GST_MAP_READ);
+  data = (guint *) map.data;
+  for (bufsofar = 0; bufsofar < buflen; bufsofar++) {
+    guint pulse = data[bufsofar];
+    if (filter->version == 0) {
+      if (pulse >= OVERFLOW_LO && !filter->last_was_overflow) {
+        gst_byte_writer_put_uint8 (writer, 0);
+        filter->last_was_overflow = TRUE;
+      } else {
+        guint8 pulse8 = pulse / 8;
+        gst_byte_writer_put_uint8 (writer, pulse8);
+        filter->last_was_overflow = FALSE;
+      }
+    } else {
+      while (pulse >= OVERFLOW_HI) {
+        gst_byte_writer_put_uint8 (writer, 0);
+        gst_byte_writer_put_uint24_le (writer, OVERFLOW_HI);
+        pulse -= OVERFLOW_HI;
+      }
+      if (pulse >= OVERFLOW_LO) {
+        gst_byte_writer_put_uint8 (writer, 0);
+        gst_byte_writer_put_uint24_le (writer, pulse);
+      } else {
+        guint8 pulse8 = pulse / 8;
+        gst_byte_writer_put_uint8 (writer, pulse8);
+      }
+    }
+  }
+  gst_buffer_unmap (buf, &map);
+
+  size = gst_byte_writer_get_size (writer);
+  if (size > 0) {
+    GstBuffer *newbuf = gst_byte_writer_free_and_get_buffer (writer);
+    ret = gst_pad_push (filter->srcpad, newbuf);
+    filter->length += size;
+  } else
+    gst_byte_writer_free (writer);
+
+  return ret;
+}
+
+static gboolean
+gst_tapfileenc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+  GstTapFileEnc *filter = GST_TAPFILEENC (parent);
+  GstSegment segment;
+  GstStructure *structure;
+  gint samplerate;
+  GstCaps *caps;
+  gboolean halfwaves;
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_EOS:
+      /* seek to beginning of file */
+      gst_segment_init (&segment, GST_FORMAT_BYTES);
+      if (!gst_pad_push_event (filter->srcpad,
+              gst_event_new_segment (&segment)))
+        return FALSE;
+      write_header (filter->srcpad, filter->version, filter->machine_byte,
+          filter->video_byte, filter->length);
+      break;
+    case GST_EVENT_CAPS:
+      gst_event_parse_caps (event, &caps);
+      structure = gst_caps_get_structure (caps, 0);
+      if (!gst_structure_get_int (structure, "rate", &samplerate)) {
+        GST_ERROR_OBJECT (filter, "input caps have no sample rate field");
+        return FALSE;
+      } else if (samplerate !=
+          tap_clocks[filter->machine_byte][filter->video_byte]) {
+        GST_ERROR_OBJECT (filter, "wrong sample rate");
+        return FALSE;
+      } else if (!gst_structure_get_boolean (structure, "halfwaves",
+              &halfwaves)) {
+        GST_ERROR_OBJECT (filter, "input caps have no halfwaves field");
+        return FALSE;
+      } else if (halfwaves)
+        filter->version = 2;
+      else
+        filter->version = filter->force_version_0 ? 0 : 1;
+
+      break;
+    default:
+      break;
+  }
+
+  return gst_pad_event_default (pad, parent, event);
+}
+
+static gboolean
+gsttapfileenc_query (GstPad * pad, GstObject * parent, GstQuery * query)
+{
+  gboolean res;
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_CAPS:
+    {
+      GstTapFileEnc *filter = GST_TAPFILEENC (parent);
+
+      GstCaps *ret = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
+      GstStructure *structure = gst_caps_get_structure (ret, 0);
+      GValue value = { 0 };
+
+      g_value_init (&value, G_TYPE_INT);
+      g_value_set_int (&value,
+          tap_clocks[filter->machine_byte][filter->video_byte]);
+      gst_structure_set_value (structure, "rate", &value);
+      gst_query_set_caps_result (query, ret);
+      res = TRUE;
+
+      break;
+    }
+    default:
+      res = gst_pad_query_default (pad, parent, query);
+      break;
+  }
+  return res;
+}
+
+/* initialize the new element
+ * instantiate pads and add them to element
+ * set pad calback functions
+ * initialize instance structure
+ */
+
+static void
+gst_tapfileenc_init (GstTapFileEnc * filter)
+{
+  filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
+  gst_pad_set_chain_function (filter->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_tapfileenc_chain));
+  gst_pad_set_event_function (filter->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_tapfileenc_sink_event));
+  gst_pad_set_query_function (filter->sinkpad,
+      GST_DEBUG_FUNCPTR (gsttapfileenc_query));
+  filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
+
+  gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
+  gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
+  filter->sent_header = FALSE;
+  filter->length = 0;
+}
+
+gboolean
+gst_tapfileenc_register (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_tapfileenc_debug, "tapfileenc",
+      0, "Commodore 64 DMP encoder");
+
+  return gst_element_register (plugin, "tapfileenc", GST_RANK_NONE,
+      GST_TYPE_TAPFILEENC);
+}
diff --git a/gst/tap/gsttapfileenc.h b/gst/tap/gsttapfileenc.h
new file mode 100644
index 0000000..399d05d
--- /dev/null
+++ b/gst/tap/gsttapfileenc.h
@@ -0,0 +1,58 @@
+/*
+ * GStreamer
+ * Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) 2011 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_TAPFILEENC_H__
+#define __GST_TAPFILEENC_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS gboolean gst_tapfileenc_register (GstPlugin * plugin);
+GType
+gst_tapfileenc_get_type (void)
+    G_GNUC_CONST;
+
+
+G_END_DECLS
+#endif /* __GST_TAPENC_H__ */
diff --git a/gst/tap/plugin.c b/gst/tap/plugin.c
new file mode 100644
index 0000000..6dc21a0
--- /dev/null
+++ b/gst/tap/plugin.c
@@ -0,0 +1,77 @@
+/*
+ * GStreamer
+ * Copyright (C) 2011 Fabrizio Gennari <fabrizio.ge@tiscali.it>
+ *
+ * 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.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
+ * which case the following provisions apply instead of the ones
+ * mentioned above:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* entry point to initialize the plug-in
+ * initialize the plug-in itself
+ * register the element factories and other features
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <gst/gst.h>
+
+#include "gstdmpdec.h"
+#include "gsttapfileenc.h"
+#include "gsttapfiledec.h"
+#include "gsttapconvert.h"
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  return gst_dmpdec_register (plugin)
+      && gst_tapfileenc_register (plugin)
+      && gst_tapfiledec_register (plugin)
+      && gst_tapconvert_register (plugin);
+}
+
+/* gstreamer looks for this structure to register tapencoders
+ *
+ *
+ */
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    tap,
+    "Commodore tape file formats and frequency conversion support",
+    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
