diff --git a/configure.ac b/configure.ac
index 533ff95..46fb90f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -425,6 +425,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"],
@@ -543,6 +548,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)
@@ -2023,6 +2029,44 @@ AG_GST_CHECK_FEATURE(SNDIO, [sndio audio], sndio, [
   fi
 ])
 
+dnl *** tapenc ***
+translit(dnm, m, l) AM_CONDITIONAL(USE_TAPENC, true)
+AG_GST_CHECK_FEATURE(TAPENC, [TAP audio encoder], tapenc, [
+    TAPENC_LIBS=-ltapencoder
+    TAPENC_CPPFLAGS="-I$with_libtap_includes"
+    TAPENC_LDFLAGS="-L$with_libtap_libs"
+    CPPFLAGS="$CPPFLAGS $TAPENC_CPPFLAGS"
+    LDFLAGS="$LDFLAGS $TAPENC_LDFLAGS"
+    AC_CHECK_HEADER(tapencoder.h,
+      [
+        AC_CHECK_LIB(tapencoder,tapenc_get_pulse,HAVE_TAPENC=yes)
+      ]
+    )
+    AC_SUBST(TAPENC_LIBS)
+    AC_SUBST(TAPENC_CPPFLAGS)
+    AC_SUBST(TAPENC_LDFLAGS)
+  ]
+)
+
+dnl *** tapdec ***
+translit(dnm, m, l) AM_CONDITIONAL(USE_TAPDEC, true)
+AG_GST_CHECK_FEATURE(TAPDEC, [TAP audio decoder], tapdec, [
+    TAPDEC_LIBS=-ltapdecoder
+    TAPDEC_CPPFLAGS="-I$with_libtap_includes"
+    TAPDEC_LDFLAGS="-L$with_libtap_libs"
+    CPPFLAGS="$CPPFLAGS $TAPDEC_CPPFLAGS"
+    LDFLAGS="$LDFLAGS $TAPDEC_LDFLAGS"
+    AC_CHECK_HEADER(tapdecoder.h,
+      [
+        AC_CHECK_LIB(tapdecoder,tapdec_set_pulse,HAVE_TAPDEC=yes)
+      ]
+    )
+    AC_SUBST(TAPDEC_LIBS)
+    AC_SUBST(TAPDEC_CPPFLAGS)
+    AC_SUBST(TAPDEC_LDFLAGS)
+  ]
+)
+
 else
 
 dnl not building plugins with external dependencies,
@@ -2093,6 +2137,8 @@ AM_CONDITIONAL(USE_VDPAU, false)
 AM_CONDITIONAL(USE_SCHRO, false)
 AM_CONDITIONAL(USE_ZBAR, false)
 AM_CONDITIONAL(USE_RTMP, false)
+AM_CONDITIONAL(USE_TAPENC, false)
+AM_CONDITIONAL(USE_TAPDEC, false)
 AM_CONDITIONAL(USE_TELETEXTDEC, false)
 AM_CONDITIONAL(USE_SNDIO, false)
 AM_CONDITIONAL(USE_UVCH264, false)
@@ -2238,6 +2284,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
@@ -2342,6 +2389,8 @@ ext/sndfile/Makefile
 ext/soundtouch/Makefile
 ext/spandsp/Makefile
 ext/sndio/Makefile
+ext/tapenc/Makefile
+ext/tapdec/Makefile
 ext/teletextdec/Makefile
 ext/gme/Makefile
 ext/gsettings/Makefile
diff --git a/ext/Makefile.am b/ext/Makefile.am
index 68c347e..269f742 100644
--- a/ext/Makefile.am
+++ b/ext/Makefile.am
@@ -262,6 +262,18 @@ else
 RSVG_DIR=
 endif
 
+if USE_TAPENC
+TAPENC_DIR = tapenc
+else
+TAPENC_DIR =
+endif
+
+if USE_TAPDEC
+TAPDEC_DIR = tapdec
+else
+TAPDEC_DIR =
+endif
+
 if USE_TIMIDITY
 TIMIDITY_DIR=timidity
 endif
@@ -423,6 +435,8 @@ SUBDIRS=\
 	$(GME_DIR) \
 	$(SPC_DIR) \
 	$(SWFDEC_DIR) \
+	$(TAPENC_DIR) \
+	$(TAPDEC_DIR) \
 	$(TELETEXTDEC_DIR) \
 	$(TIMIDITY_DIR) \
 	$(XVID_DIR) \
@@ -479,6 +493,8 @@ DIST_SUBDIRS = \
 	spc \
 	gme \
 	swfdec \
+	tapenc \
+	tapdec \
 	teletextdec \
 	timidity \
 	voaacenc \
diff --git a/ext/tapdec/Makefile.am b/ext/tapdec/Makefile.am
new file mode 100644
index 0000000..7db5a30
--- /dev/null
+++ b/ext/tapdec/Makefile.am
@@ -0,0 +1,14 @@
+# Note: plugindir is set in configure
+
+plugin_LTLIBRARIES = libgsttapdec.la
+
+# sources used to compile this plug-in
+libgsttapdec_la_SOURCES = gsttapdec.c
+
+# compiler and linker flags used to compile this plugin, set in configure.ac
+libgsttapdec_la_CFLAGS = $(GST_CFLAGS)
+libgsttapdec_la_CPPFLAGS = $(TAPDEC_CPPFLAGS)
+libgsttapdec_la_LIBADD = $(GST_LIBS) $(TAPDEC_LIBS)
+libgsttapdec_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(TAPDEC_LDFLAGS)
+libgsttapdec_la_LIBTOOLFLAGS = --tag=disable-static
+
diff --git a/ext/tapdec/gsttapdec.c b/ext/tapdec/gsttapdec.c
new file mode 100644
index 0000000..50cbca4
--- /dev/null
+++ b/ext/tapdec/gsttapdec.c
@@ -0,0 +1,408 @@
+/*
+ * 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.
+ */
+
+/**
+ * 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 "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
+{
+  GstElement element;
+  GstPad *sinkpad, *srcpad;
+
+  gboolean inverted;
+  guint volume;
+  guint waveform;
+
+  struct tap_dec_t *tap;
+};
+
+struct _GstTapDecClass
+{
+  GstElementClass parent_class;
+};
+
+enum
+{
+  PROP_0,
+  PROP_VOLUME,
+  PROP_TRIGGER_ON_RISING_EDGE,
+  PROP_WAVEFORM,
+  N_PROPERTIES
+};
+
+/* 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=S32LE, "
+      "channels = (int) 1,"
+      "layout = (string) interleaved")
+    );
+
+G_DEFINE_TYPE (GstTapDec, gst_tapdec,
+    GST_TYPE_ELEMENT);
+
+/* 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_uint (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_uint (value, filter->waveform);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static GstStateChangeReturn
+gst_tapdec_change_state (GstElement * object, GstStateChange transition)
+{
+  GstTapDec *filter = gst_tapdec (object);
+  GstStateChangeReturn result = GST_ELEMENT_CLASS(gst_tapdec_parent_class)->change_state (object, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      tapdec_exit (filter->tap);
+      filter->tap = NULL;
+      break;
+    default:
+      break;
+  }
+
+  return result;
+}
+
+static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
+
+/* initialize the tapdecoder's class */
+static void
+gst_tapdec_class_init (GstTapDecClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+
+  gst_element_class_set_details_simple(gstelement_class,
+    "Commodore 64 TAP format decoder",
+    "Decoder/Audio",
+    "Decodes Commodore 64 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;
+  gstelement_class->change_state = gst_tapdec_change_state;
+
+  obj_properties[PROP_VOLUME] =
+      g_param_spec_uint ("volume", "Volume", "Volume", 0, 255,
+          254, G_PARAM_READWRITE);
+  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);
+  obj_properties[PROP_WAVEFORM] =
+      g_param_spec_uint ("waveform", "Waveform", "0=square, 1=triangle, 2=sine (if tapdecoder does not support sine, will fall back to square)", 0, 2,
+          0, G_PARAM_READWRITE);
+  g_object_class_install_properties (gobject_class, N_PROPERTIES,
+                                     obj_properties);
+}
+
+/* GstElement vmethod implementations */
+
+/* this function handles the link with other elements */
+static gboolean
+gst_tapdec_sink_event (GstPad * pad, GstObject *parent, GstEvent * event)
+{
+  switch (GST_EVENT_TYPE (event)) {
+  case GST_EVENT_CAPS:
+    {
+      GstTapDec *filter = gst_tapdec (parent);
+      GstStructure *structure;
+      GstCaps *othercaps;
+      gint samplerate;
+      gboolean halfwaves;
+      GstCaps* caps;
+      GstEvent *new_caps_event;
+
+      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;
+      }
+      if (!gst_structure_get_boolean (structure, "halfwaves", &halfwaves))
+      {
+        GST_ERROR_OBJECT (filter, "input caps have no halfwaves field");
+        return FALSE;
+      }
+
+      filter->tap = tapdec_init2(filter->volume,
+                            filter->inverted,
+                            filter->waveform==2 ? TAPDEC_SINE :
+                            filter->waveform==1 ? TAPDEC_TRIANGLE :
+                            TAPDEC_SQUARE
+                            );
+      tapdec_enable_halfwaves(filter->tap, halfwaves);
+
+      othercaps = gst_pad_query_caps (filter->srcpad, NULL);
+      othercaps = gst_caps_make_writable(othercaps);
+      gst_caps_set_simple(othercaps, "rate", G_TYPE_INT, samplerate, NULL);
+      new_caps_event = gst_event_new_caps (othercaps);
+
+      gst_pad_push_event (filter->srcpad, new_caps_event);
+    }
+    break;
+  default:
+    break;
+  }
+
+  return gst_pad_event_default (pad, parent, event);
+}
+
+/* chain function
+ * this function does the actual processing
+ */
+
+#define TAPDEC_OUTBUF_SIZE 1024
+
+static GstFlowReturn
+gst_tapdec_chain (GstPad * pad, GstObject *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 = NULL;
+  GstMapInfo outmap;
+  GstFlowReturn ret = GST_FLOW_OK, ret2;
+
+  gst_buffer_map(buf, &map, GST_MAP_READ);
+  data = (int32_t*) map.data;
+  buflen = map.size / sizeof(int32_t);
+
+  if (filter->tap == NULL) {
+    GST_ERROR_OBJECT (filter, "not initialised: input not a tape?");
+    return GST_FLOW_ERROR;
+  }
+
+  for (bufsofar = 0; bufsofar < buflen; bufsofar++) {
+    uint32_t npulses;
+    tapdec_set_pulse (filter->tap, data[bufsofar]);
+    do {
+      if (outbuf == NULL){
+        outbuf = gst_buffer_new_allocate(NULL, TAPDEC_OUTBUF_SIZE * sizeof(int32_t), NULL);
+        total_pulses = 0;
+        gst_buffer_map(outbuf, &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_buffer_unmap(outbuf, &outmap);
+        gst_buffer_set_size(outbuf, total_pulses * sizeof(int32_t));
+        if ( (ret2 = gst_pad_push (filter->srcpad, outbuf)) != GST_FLOW_OK);
+          ret = ret2;
+        outbuf = NULL;
+      }
+    } while (npulses > 0);
+  }
+
+  if (outbuf != NULL) {
+    if (total_pulses == 0)
+      gst_buffer_unref(outbuf);
+    else {
+      gst_buffer_set_size(outbuf, total_pulses * sizeof(int32_t));
+      if ( (ret2 = gst_pad_push (filter->srcpad, outbuf)) != GST_FLOW_OK);
+        ret = ret2;
+    }
+  }
+
+  return ret;
+}
+
+
+/* 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)
+{
+  guint i;
+
+  filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
+  gst_pad_set_event_function (filter->sinkpad,
+                                GST_DEBUG_FUNCPTR(gst_tapdec_sink_event));
+  gst_pad_set_chain_function (filter->sinkpad,
+                              GST_DEBUG_FUNCPTR(gst_tapdec_chain));
+
+  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);
+
+  /* set defaults for properties */
+  for (i = 1; i < N_PROPERTIES; i++) {
+    GValue default_value = {0,};
+    g_value_init (&default_value, G_PARAM_SPEC_VALUE_TYPE(obj_properties[i]));
+    g_param_value_set_default (obj_properties[i], &default_value);
+    g_object_set_property (G_OBJECT(filter), g_param_spec_get_name (obj_properties[i]), &default_value);
+  }
+}
+
+static gboolean
+gst_tapdec_register (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_tapdec_debug, "tapdec",
+      0, "Commodore 64 TAP format decoder");
+  return gst_element_register (plugin, "tapdec", GST_RANK_SECONDARY, GST_TYPE_TAPDEC);
+}
+
+/* gstreamer looks for this structure to register tapencoders
+ *
+ *
+ */
+GST_PLUGIN_DEFINE (
+    GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    tapdec,
+    "Commodore tape decoder support",
+    gst_tapdec_register,
+    VERSION,
+    "LGPL",
+    PACKAGE,
+    "http://wav-prg.sourceforge.net/"
+);
+
diff --git a/ext/tapdec/gsttapdec.h b/ext/tapdec/gsttapdec.h
new file mode 100644
index 0000000..43aa36b
--- /dev/null
+++ b/ext/tapdec/gsttapdec.h
@@ -0,0 +1,60 @@
+/*
+ * 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_TAPDEC_H__
+#define __GST_TAPDEC_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+gboolean
+gst_dmpdec_register (GstPlugin * plugin);
+GType gst_tapdec_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GST_TAPDEC_H__ */
+
diff --git a/ext/tapenc/Makefile.am b/ext/tapenc/Makefile.am
new file mode 100644
index 0000000..582edeb
--- /dev/null
+++ b/ext/tapenc/Makefile.am
@@ -0,0 +1,14 @@
+# Note: plugindir is set in configure
+
+plugin_LTLIBRARIES = libgsttapenc.la
+
+# sources used to compile this plug-in
+libgsttapenc_la_SOURCES = gsttapenc.c
+
+# compiler and linker flags used to compile this plugin, set in configure.ac
+libgsttapenc_la_CFLAGS = $(GST_CFLAGS)
+libgsttapenc_la_CPPFLAGS = $(TAPENC_CPPFLAGS)
+libgsttapenc_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(TAPENC_LIBS)
+libgsttapenc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(TAPENC_LDFLAGS)
+libgsttapenc_la_LIBTOOLFLAGS = --tag=disable-static
+
diff --git a/ext/tapenc/gsttapenc.c b/ext/tapenc/gsttapenc.c
new file mode 100644
index 0000000..5312c72
--- /dev/null
+++ b/ext/tapenc/gsttapenc.c
@@ -0,0 +1,425 @@
+/*
+ * 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.
+ */
+
+/**
+ * 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 "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
+{
+  GstElement element;
+
+  GstPad *sinkpad, *srcpad;
+
+  gboolean inverted;
+
+  gboolean halfwaves;
+
+  guchar sensitivity;
+
+  guint min_duration;
+
+  guchar initial_threshold;
+
+  GstBuffer *outbuf;
+
+  struct tap_enc_t *tap;
+};
+
+struct _GstTapEncClass
+{
+  GstElementClass 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=S32LE,"
+      "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_ELEMENT);
+
+/* 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;
+  }
+}
+
+static GstStateChangeReturn
+gst_tapenc_change_state (GstElement * object, GstStateChange transition)
+{
+  GstTapEnc *filter = GST_TAPENC (object);
+  GstStateChangeReturn result = GST_ELEMENT_CLASS(gst_tapenc_parent_class)->change_state (object, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      tapenc_exit (filter->tap);
+      filter->tap = NULL;
+      break;
+    default:
+      break;
+  }
+
+  return result;
+}
+
+static gboolean
+gst_tapenc_sink_event (GstPad * pad, GstObject *parent, GstEvent * event)
+{
+  GstTapEnc *filter = GST_TAPENC (parent);
+
+  switch (GST_EVENT_TYPE (event)) {
+  case GST_EVENT_EOS:
+    if (filter->tap != NULL) {
+      uint32_t flushed_pulses = tapenc_flush(filter->tap);
+      if (flushed_pulses > 0) {
+        GstBuffer *buffer = gst_buffer_new_allocate (NULL, sizeof(flushed_pulses), NULL);
+        GstMapInfo info;
+        gst_buffer_map(buffer, &info, GST_MAP_WRITE);
+        *((uint32_t*)info.data) = flushed_pulses;
+        gst_pad_push (filter->srcpad, buffer);
+      }
+    }
+    break;
+  case GST_EVENT_FLUSH_STOP:
+    tapenc_flush(filter->tap);
+    break;
+  case GST_EVENT_CAPS:
+  {
+    GstCaps* caps;
+    GstStructure *structure;
+    GstCaps *othercaps;
+    gint samplerate;
+
+    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;
+    }
+
+    othercaps =
+      gst_caps_new_simple ("audio/x-tap",
+      "rate", G_TYPE_INT, samplerate,
+      "halfwaves", G_TYPE_BOOLEAN, filter->halfwaves,
+      NULL);
+
+    filter->tap = tapenc_init2(filter->min_duration,
+                            filter->sensitivity,
+                            filter->initial_threshold,
+                            filter->inverted);
+
+    tapenc_toggle_trigger_on_both_edges(filter->tap, filter->halfwaves);
+
+    gst_pad_set_caps (filter->srcpad, othercaps);
+    gst_caps_unref (othercaps);
+  }
+    break;
+  default:
+    break;
+  }
+
+  return gst_pad_event_default (pad, parent, event);
+}
+
+/* initialize the tapencoder's class */
+static void
+gst_tapenc_class_init (GstTapEncClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) 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>");
+
+  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;
+  gstelement_class->change_state = gst_tapenc_change_state;
+
+  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_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_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_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_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));
+}
+
+/* GstElement vmethod implementations */
+
+
+/* chain function
+ * this function does the actual processing
+ */
+static GstFlowReturn
+gst_tapenc_chain (GstPad * pad, GstObject *parent, GstBuffer * buf)
+{
+  GstTapEnc *filter = GST_TAPENC (parent);
+  int32_t *data;
+  uint32_t buflen;
+  uint32_t bufsofar = 0;
+  GstFlowReturn ret = GST_FLOW_OK;
+  GstByteWriter *writer = gst_byte_writer_new();
+  guint size;
+  GstMapInfo map;
+
+  gst_buffer_map(buf, &map, GST_MAP_READ);
+  data = (int32_t*) map.data;
+  buflen = map.size / sizeof(int32_t);
+
+  while(bufsofar < buflen) {
+    uint32_t pulse;
+    bufsofar += tapenc_get_pulse(filter->tap, data + bufsofar, buflen - bufsofar, &pulse);
+    if (pulse > 0)
+      gst_byte_writer_put_uint32_le (writer, pulse);
+  }
+
+  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);
+  }
+
+  gst_buffer_unmap(buf, &map);
+
+  return ret;
+}
+
+
+/* initialize the new element
+ * instantiate pads and add them to element
+ * set pad calback functions
+ * initialize instance structure
+ */
+static void
+gst_tapenc_init (GstTapEnc * filter)
+{
+  guint i, nproperties;
+  GParamSpec ** properties = g_object_class_list_properties (G_OBJECT_GET_CLASS(filter), &nproperties);
+
+  filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
+  gst_pad_set_chain_function (filter->sinkpad,
+                              GST_DEBUG_FUNCPTR(gst_tapenc_chain));
+  gst_pad_set_event_function (filter->sinkpad,
+                              GST_DEBUG_FUNCPTR(gst_tapenc_sink_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);
+
+  /* set defaults for properties */
+  for (i = 0;i < nproperties; i++) {
+    GValue default_value = {0,};
+    g_value_init (&default_value, G_PARAM_SPEC_VALUE_TYPE(properties[i]));
+    g_param_value_set_default (properties[i], &default_value);
+    g_object_set_property (G_OBJECT(filter), g_param_spec_get_name (properties[i]), &default_value);
+  }
+  g_free(properties);
+
+  filter->outbuf = NULL;
+}
+
+static gboolean
+gst_tapenc_register (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_tapenc_debug, "tapenc",
+      0, "Commodore 64 TAP format encoder");
+  return gst_element_register (plugin, "tapenc", GST_RANK_NONE, GST_TYPE_TAPENC);
+}
+
+/* gstreamer looks for this structure to register tapencoders
+ *
+ *
+ */
+GST_PLUGIN_DEFINE (
+    GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    tapenc,
+    "Commodore tape encoder support",
+    gst_tapenc_register,
+    VERSION,
+    "LGPL",
+    PACKAGE,
+    "http://wav-prg.sourceforge.net/"
+);
+
diff --git a/ext/tapenc/gsttapenc.h b/ext/tapenc/gsttapenc.h
new file mode 100644
index 0000000..de6f60b
--- /dev/null
+++ b/ext/tapenc/gsttapenc.h
@@ -0,0 +1,60 @@
+/*
+ * 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_TAPENC_H__
+#define __GST_TAPENC_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+gboolean
+gst_dmpdec_register (GstPlugin * plugin);
+GType gst_tapenc_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GST_TAPENC_H__ */
+
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..66555a7
--- /dev/null
+++ b/gst/tap/gstdmpdec.c
@@ -0,0 +1,285 @@
+/*
+ * 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.
+ */
+
+/**
+ * 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;
+
+  GstAdapter * adapter;
+};
+
+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 */
+
+
+/* initialize the dmpdec's class */
+static void
+gst_dmpdec_class_init (GstDmpDecClass * gclass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+
+  gst_element_class_set_details_simple(element_class,
+    "Commodore 64 DMP 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));
+}
+
+/* GstElement vmethod implementations */
+
+/* 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, ret2;
+  guint num_inpulses;
+
+  gst_adapter_push (filter->adapter, buf);
+  if (!filter->read_header && gst_adapter_available (filter->adapter) >= DMPDEC_HEADER_SIZE) {
+    const char expected_signature[] = "DC2N-TAP-RAW";
+    GstBuffer *header_buf = gst_adapter_take_buffer (filter->adapter, DMPDEC_HEADER_SIZE);
+    guint8 *header_data;
+    gboolean header_valid;
+    GstCaps * srccaps;
+    GstStructure * srcstructure;
+    GValue rate = {0};
+    guchar bits_per_sample;
+    GstMapInfo map;
+
+    if (!header_buf)
+      return GST_FLOW_ERROR;
+
+    gst_buffer_map (header_buf, &map, GST_MAP_WRITE);
+    header_data = map.data;
+    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);
+    g_value_set_int (&rate, GST_READ_UINT32_LE (header_data));
+    gst_buffer_unmap (header_buf, &map);
+    gst_buffer_unref (header_buf);
+
+    if (!header_valid)
+      return GST_FLOW_ERROR;
+
+    srccaps = gst_caps_copy (gst_pad_template_get_caps (gst_pad_get_pad_template (filter->srcpad)));
+    srcstructure = gst_caps_get_structure (srccaps, 0);
+    gst_structure_set_value (srcstructure, "rate", &rate);
+    gst_pad_set_caps (filter->srcpad, srccaps);
+    gst_caps_unref (srccaps);
+    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;
+    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;
+
+    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;
+        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);
+    gst_buffer_resize(newbuf, 0, pos_outpulse * sizeof(guint));
+    ret2 = gst_pad_push (filter->srcpad, newbuf);
+    if (ret2 != GST_FLOW_OK)
+      ret = ret2;
+  }
+
+  return ret;
+}
+
+
+/* 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_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..5eefb90
--- /dev/null
+++ b/gst/tap/gstdmpdec.h
@@ -0,0 +1,60 @@
+/*
+ * 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..b0e43b6
--- /dev/null
+++ b/gst/tap/gsttapconvert.c
@@ -0,0 +1,228 @@
+/*
+ * 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
+ *
+ */
+#define DEBUG_INIT(bla) \
+  GST_DEBUG_CATEGORY_INIT (gst_tapconvert_debug, "tapconvert", 0, "TAP rate convert");
+
+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));
+}
+
+/* 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 = gst_buffer_get_size (outbuf) / sizeof(guint);
+  guint bufsofar;
+  guint *data;
+  GstMapInfo map;
+
+  gst_buffer_map (outbuf, &map, GST_MAP_READ);
+  data = (guint*) map.data;
+  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] = (guint) (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..3ba4b10
--- /dev/null
+++ b/gst/tap/gsttapconvert.h
@@ -0,0 +1,36 @@
+/*
+ * 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..2e1f929
--- /dev/null
+++ b/gst/tap/gsttapfiledec.c
@@ -0,0 +1,333 @@
+/*
+ * 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.
+ */
+
+/**
+ * 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;
+};
+
+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 */
+
+/* initialize the tapfiledec's class */
+static void
+gst_tapfiledec_class_init (GstTapFileDecClass * klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  gst_element_class_set_details_simple(element_class,
+    "Commodore 64 TAP file reader",
+    "Decoder/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));
+}
+
+/* GstElement vmethod implementations */
+
+/*static gboolean gsttapfiledec_query (GstPad *pad,
+                                     GstObject *parent,
+                                     GstQuery *query)
+{
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_CAPS:
+    {
+      GstTapFileDec *filter = GST_TAPFILEDEC (parent);
+      GstCaps * result = filter->read_header ?
+        gst_pad_get_current_caps (pad) :
+        gst_pad_get_pad_template_caps (pad);
+
+      GST_DEBUG_OBJECT (filter, "Requested caps from source pad %" GST_PTR_FORMAT, result);
+      gst_query_set_caps_result (query, gst_caps_copy (result));
+      return TRUE;
+    }
+    default:
+      return gst_pad_query_default (pad, parent, query);
+  }
+}*/
+
+/* chain function
+ * this function does the actual processing
+ */
+
+#define TAPFILEDEC_HEADER_SIZE 20
+#define ONE_BYTE_OVERFLOW 0x100
+#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;
+  guint pulse = 0;
+  guint peeked_bytes = 0;
+  GstByteWriter *writer = gst_byte_writer_new();
+  guint size;
+  const guint8 * inbytes;
+  guint num_inpulses;
+
+  gst_adapter_push (filter->adapter, buf);
+  if (!filter->read_header && gst_adapter_available (filter->adapter) >= TAPFILEDEC_HEADER_SIZE) {
+    const char expected_signature1[] = "C64-TAPE-RAW";
+    const char expected_signature2[] = "C16-TAPE-RAW";
+    GstBuffer *header_buf = gst_adapter_take_buffer (filter->adapter, TAPFILEDEC_HEADER_SIZE);
+    guint8 *header_data;
+    gboolean header_valid;
+    GstCaps * srccaps;
+    GstEvent * new_caps_event;
+    guchar machine, video_standard;
+    GstMapInfo map;
+
+    if (!header_buf)
+      return GST_FLOW_ERROR;
+
+    gst_buffer_map(header_buf, &map, GST_MAP_READ);
+    header_data = map.data;
+    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 */
+     );
+
+    gst_buffer_unmap(header_buf, &map);
+    gst_buffer_unref (header_buf);
+
+    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;
+
+    gst_caps_set_simple(srccaps, "rate", G_TYPE_INT, tap_clocks[machine][video_standard], NULL);
+    gst_caps_set_simple(srccaps, "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);
+  }
+
+  /* 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) {
+        inpulse = ONE_BYTE_OVERFLOW;
+        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;
+      }
+    }
+    pulse += inpulse;
+    if (!overflow_occurred) {
+      gst_byte_writer_put_uint32_le (writer, pulse);
+      pulse = 0;
+    }
+  }
+  gst_adapter_flush (filter->adapter, peeked_bytes);
+  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);
+  }
+  else
+    gst_byte_writer_free (writer);
+
+  return ret;
+}
+
+
+/* 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));
+
+  filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
+/*  gst_pad_set_query_function (filter->srcpad,
+                                GST_DEBUG_FUNCPTR(gsttapfiledec_query));*/
+
+  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_tapfiledec_register (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_tapfiledec_debug, "tapfiledec",
+      0, "Commodore 64 DMP encoder");
+
+  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..5287da9
--- /dev/null
+++ b/gst/tap/gsttapfiledec.h
@@ -0,0 +1,60 @@
+/*
+ * 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..bd3e404
--- /dev/null
+++ b/gst/tap/gsttapfileenc.c
@@ -0,0 +1,469 @@
+/*
+ * 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.
+ */
+
+/**
+ * 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;
+
+  guint rate;
+
+  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,
+  N_PROPERTIES
+};
+
+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_uint (value);
+      break;
+    case PROP_VIDEO_BYTE:
+      filter->video_byte = (guchar) g_value_get_uint (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_uint (value, filter->machine_byte);
+      break;
+    case PROP_VIDEO_BYTE:
+      g_value_set_uint (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;
+  }
+}
+
+static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
+
+/* 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;
+
+  obj_properties[PROP_MACHINE_BYTE] =
+      g_param_spec_uint ("machine", "Machine", "Tag representing machine for which this dump is intended. 0=C64, 1=VIC20, 2=C16/+4.", 0, 2,
+          0, G_PARAM_READWRITE);
+  obj_properties[PROP_VIDEO_BYTE] =
+      g_param_spec_uint ("videotype", "Video type", "Tag representing video type of machine for which this dump is intended. 0=PAL, 1=NTSC.", 0, 100,
+          0, G_PARAM_READWRITE);
+  obj_properties[PROP_FORCE_VERSION_0] =
+      g_param_spec_boolean ("version_0", "Force version 0", "If true, and incoming stream is not semiwaves, a version 0 TAP file will be created. Otherwise the version will be 1 for full waves and 2 for semiwaves.",
+          FALSE, G_PARAM_READWRITE);
+
+  g_object_class_install_properties (gobject_class, N_PROPERTIES,
+                                     obj_properties);
+
+  gst_element_class_set_details_simple(element_class,
+    "Commodore 64 TAP file writer",
+    "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);
+    gst_pad_push_event (filter->srcpad,
+      gst_event_new_segment (&segment));
+    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)
+{
+  guint i;
+
+  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);
+
+  /* set defaults for properties */
+  for (i = 1; i < N_PROPERTIES; i++) {
+    GValue default_value = {0,};
+    g_value_init (&default_value, G_PARAM_SPEC_VALUE_TYPE(obj_properties[i]));
+    g_param_value_set_default (obj_properties[i], &default_value);
+    g_object_set_property (G_OBJECT(filter), g_param_spec_get_name (obj_properties[i]), &default_value);
+  }
+
+  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..275263a
--- /dev/null
+++ b/gst/tap/gsttapfileenc.h
@@ -0,0 +1,61 @@
+/*
+ * 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..e3cad92
--- /dev/null
+++ b/gst/tap/plugin.c
@@ -0,0 +1,85 @@
+/*
+ * 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",
+    PACKAGE,
+    "http://wav-prg.sourceforge.net/"
+);
+
