Index: configure.ac
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-bad/configure.ac,v
retrieving revision 1.669
diff -u -p -r1.669 configure.ac
--- configure.ac	4 May 2006 15:32:10 -0000	1.669
+++ configure.ac	11 May 2006 15:09:07 -0000
@@ -76,6 +76,7 @@ GST_ARG_EXAMPLES
 
 dnl these are all the gst plug-ins, compilable without additional libs
 GST_PLUGINS_ALL="\
+  audiocompress \
   cdxaparse \
   freeze \
   modplug \
@@ -154,6 +155,7 @@ dnl uninstalled is selected preferential
 GST_CHECK_GST($GST_MAJORMINOR, [$GST_REQ])
 GST_CHECK_GST_BASE($GST_MAJORMINOR, [$GST_REQ])
 GST_CHECK_GST_CHECK($GST_MAJORMINOR, [$GST_REQ], no)
+GST_CHECK_GST_CONTROLLER($GST_MAJORMINOR, [$GST_REQ])
 
 dnl FIXME: get rid of this by making sure gstreamer-check brings it in
 dnl check for "check", unit testing library/header
@@ -628,6 +630,7 @@ AC_CONFIG_FILES(
 Makefile
 gst-plugins-bad.spec
 gst/Makefile
+gst/audiocompress/Makefile
 gst/cdxaparse/Makefile
 gst/freeze/Makefile
 gst/modplug/Makefile
--- /dev/null	2006-04-28 17:21:04.000000000 +0100
+++ gst/audiocompress/gsthardlimiter.c	2006-05-11 16:08:03.000000000 +0100
@@ -0,0 +1,381 @@
+/* GStreamer audio hard limiter
+ * Copyright (C) 2006 Artem Popov <artfwo@gmail.com>
+ *
+ * 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-hardlimiter
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * <para>
+ * <programlisting>
+ * gst-launch -v -m filesrc location=test.ogg ! decodebin ! audioconvert ! volume volume=2 ! hardlimiter limit=-12 ! alsasink
+ * </programlisting>
+ * </para>
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/controller/gstcontroller.h>
+#include <gst/audio/audio.h>
+#include <gst/floatcast/floatcast.h>
+#include <math.h>
+
+#include "gsthardlimiter.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_hard_limiter_debug);
+#define GST_CAT_DEFAULT gst_hard_limiter_debug
+
+#define LIMIT_FUNCTION(name,type,FROM_BYTEORDER,TO_BYTEORDER,what) \
+static void hardlimiter_limit_##name (GstHardLimiter *filter, gpointer src) \
+{                                                                           \
+  type value = FROM_BYTEORDER (*(type*)src);                                \
+  type positive_limit = (type) filter->positive_##what##_limit;				\
+  type negative_limit = (type) filter->negative_##what##_limit;				\
+  if (G_UNLIKELY (value > positive_limit)) {                                \
+    value = positive_limit;                                                 \
+  } else if (G_UNLIKELY (value < negative_limit)) {                         \
+    value = negative_limit;                                                 \
+  }                                                                         \
+  *(type*)src = TO_BYTEORDER (value);                                       \
+}
+
+/* float */
+LIMIT_FUNCTION (float32_le, gfloat, GFLOAT_FROM_LE, GFLOAT_TO_LE, double);
+LIMIT_FUNCTION (float32_be, gfloat,GFLOAT_FROM_BE, GFLOAT_TO_BE, double);
+LIMIT_FUNCTION (float64_be, gdouble, GDOUBLE_FROM_BE, GDOUBLE_TO_BE, double);
+LIMIT_FUNCTION (float64_le, gdouble, GDOUBLE_FROM_LE, GDOUBLE_TO_LE, double);
+
+/* 8-bit */
+LIMIT_FUNCTION (int8, gint8, /* no byteorder */, /* no byteorder */, integer);
+LIMIT_FUNCTION (uint8, guint8, /* no byteorder */, /* no byteorder */, integer);
+
+/* 16-bit */
+LIMIT_FUNCTION (int16_be, gint16, GINT16_FROM_BE, GINT16_TO_BE, integer);
+LIMIT_FUNCTION (int16_le, gint16, GINT16_FROM_LE, GINT16_TO_LE, integer);
+LIMIT_FUNCTION (uint16_be, guint16, GUINT16_FROM_BE, GUINT16_TO_BE, integer);
+LIMIT_FUNCTION (uint16_le, guint16, GUINT16_FROM_LE, GUINT16_TO_LE, integer);
+
+/* 32-bit */
+LIMIT_FUNCTION (int32_be, gint32, GINT32_FROM_BE, GINT32_TO_BE, integer);
+LIMIT_FUNCTION (int32_le, gint32, GINT32_FROM_LE, GINT32_TO_LE, integer);
+LIMIT_FUNCTION (uint32_be, guint32, GUINT32_FROM_BE, GUINT32_TO_BE, integer);
+LIMIT_FUNCTION (uint32_le, guint32, GUINT32_FROM_LE, GUINT32_TO_LE, integer);
+
+#define DEFAULT_ENABLED  TRUE
+#define DEFAULT_LIMIT    0.0
+enum
+{
+  PROP_0,
+  PROP_ENABLED,
+  PROP_LIMIT
+};
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS ";"
+        "audio/x-raw-int, "
+        "rate = (int) [1, MAX], "
+        "channels = (int) [1, MAX], "
+        "endianness = (int) {LITTLE_ENDIAN, BIG_ENDIAN}, "
+        "width = (int) {8, 16, 32}, "
+        "depth = (int) [1, 32], " "signed = (boolean) {true, false}")
+    );
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_AUDIO_FLOAT_PAD_TEMPLATE_CAPS ";"
+        "audio/x-raw-int, "
+        "rate = (int) [1, MAX], "
+        "channels = (int) [1, MAX], "
+        "endianness = (int) {LITTLE_ENDIAN, BIG_ENDIAN}, "
+        "width = (int) {8, 16, 32}, "
+        "depth = (int) [1, 32], " "signed = (boolean) {true, false}")
+    );
+
+static const GstElementDetails element_details =
+  GST_ELEMENT_DETAILS (
+    "Hard Limiter",
+    "Filter/Effects/Audio",
+    "Hardlimit raw audio signal",
+    "Artem Popov <artfwo@gmail.com>"
+  );
+
+GST_BOILERPLATE (GstHardLimiter, gst_hard_limiter, GstBaseTransform,
+    GST_TYPE_BASE_TRANSFORM);
+
+static void gst_hard_limiter_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_hard_limiter_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static GstFlowReturn gst_hard_limiter_transform_ip (GstBaseTransform * base,
+    GstBuffer * outbuf);
+static gboolean gst_hard_limiter_set_caps (GstBaseTransform * base,
+    GstCaps * incaps, GstCaps * outcaps);
+
+static void
+gst_hard_limiter_base_init (gpointer klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sink_template));
+  gst_element_class_set_details (element_class, &element_details);
+
+  GST_DEBUG_CATEGORY_INIT (gst_hard_limiter_debug, "hardlimiter", 0,
+      "hard limiter element");
+}
+
+static void
+gst_hard_limiter_class_init (GstHardLimiterClass * klass)
+{
+  GstBaseTransformClass *gstbasetrans_class;
+  GObjectClass *gobject_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstbasetrans_class = (GstBaseTransformClass *) klass;
+
+  gobject_class->set_property = gst_hard_limiter_set_property;
+  gobject_class->get_property = gst_hard_limiter_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_ENABLED,
+      g_param_spec_boolean ("enabled", "Enabled", "If true, limiting is enabled",
+          DEFAULT_ENABLED, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+  g_object_class_install_property (gobject_class, PROP_LIMIT,
+      g_param_spec_double ("limit", "Limit", "Limit in dB",
+          -G_MAXDOUBLE, 0.0, DEFAULT_LIMIT,
+          G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
+
+  gstbasetrans_class->transform_ip =
+      GST_DEBUG_FUNCPTR (gst_hard_limiter_transform_ip);
+  gstbasetrans_class->set_caps =
+      GST_DEBUG_FUNCPTR (gst_hard_limiter_set_caps);
+  gstbasetrans_class->passthrough_on_same_caps = TRUE;
+}
+
+static void
+gst_hard_limiter_init (GstHardLimiter * filter, GstHardLimiterClass * klass)
+{
+  gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
+  filter->enabled = DEFAULT_ENABLED;
+  filter->limit = DEFAULT_LIMIT;
+}
+
+static void
+gst_hard_limiter_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstHardLimiter *filter = GST_HARD_LIMITER (object);
+
+  switch (prop_id) {
+    case PROP_ENABLED:
+      g_value_set_boolean (value, filter->enabled);
+      break;
+    case PROP_LIMIT:
+      g_value_set_double (value, filter->limit);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_hard_limiter_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstHardLimiter *filter = GST_HARD_LIMITER (object);
+
+  switch (prop_id) {
+    case PROP_ENABLED:
+      filter->enabled = g_value_get_boolean (value);
+      break;
+    case PROP_LIMIT:
+      filter->limit = g_value_get_double (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static gboolean
+gst_hard_limiter_set_caps (GstBaseTransform * base, GstCaps * incaps,
+    GstCaps * outcaps)
+{
+  GstHardLimiter *filter = GST_HARD_LIMITER (base);
+  GstStructure *structure;
+  gboolean is_signed;
+  gint endianness;
+  gint width;
+  gint depth;
+
+  GST_DEBUG_OBJECT (base,
+      "incaps %" GST_PTR_FORMAT ", outcaps %" GST_PTR_FORMAT, incaps, outcaps);
+
+  structure = gst_caps_get_structure (incaps, 0);
+
+  if (!gst_structure_get_int (structure, "endianness", &endianness))
+    goto error;
+
+  if (!gst_structure_get_int (structure, "width", &width))
+    goto error;
+
+  filter->sample_size = width / 8;
+
+  filter->positive_double_limit = powf (10.0, filter->limit / 20.0);
+  filter->negative_double_limit = -filter->positive_double_limit;
+
+  if (gst_structure_has_name (structure, "audio/x-raw-float")) {
+    if (width == 32 && endianness == G_BIG_ENDIAN)
+      filter->do_limit = hardlimiter_limit_float32_be;
+    else if (width == 32 && endianness == G_LITTLE_ENDIAN)
+      filter->do_limit = hardlimiter_limit_float32_le;
+    else if (width == 64 && endianness == G_BIG_ENDIAN)
+      filter->do_limit = hardlimiter_limit_float64_be;
+    else if (width == 64 && endianness == G_LITTLE_ENDIAN)
+      filter->do_limit = hardlimiter_limit_float64_le;
+    else
+      goto error;
+
+    return TRUE;
+  }
+
+  if (!gst_structure_get_int (structure, "depth", &depth))
+    goto error;
+
+  if (!gst_structure_get_boolean (structure, "signed", &is_signed))
+    goto error;
+
+  /* ceil and floor swapped intentionally (values are updated below) */
+  filter->positive_integer_limit =
+      ceil (filter->positive_double_limit * pow (2, depth - 1));
+  filter->negative_integer_limit =
+      floor (filter->negative_double_limit * pow (2, depth - 1));
+
+  if (!is_signed) {
+    filter->positive_integer_limit += pow (2, depth - 1);
+    filter->negative_integer_limit += pow (2, depth - 1);
+  }
+
+  filter->positive_integer_limit--;
+  filter->negative_integer_limit++;
+
+  if (gst_structure_has_name (structure, "audio/x-raw-int")) {
+    gpointer funcs[4][2][2] = {
+          {
+            { GST_DEBUG_FUNCPTR (hardlimiter_limit_int8), NULL },
+            { GST_DEBUG_FUNCPTR (hardlimiter_limit_uint8), NULL }
+          }, {
+            { GST_DEBUG_FUNCPTR (hardlimiter_limit_int16_be),
+              GST_DEBUG_FUNCPTR (hardlimiter_limit_int16_le) },
+            { GST_DEBUG_FUNCPTR (hardlimiter_limit_uint16_be),
+              GST_DEBUG_FUNCPTR (hardlimiter_limit_uint16_le) }
+          }, {
+            { NULL, NULL },
+            { NULL, NULL }
+          }, {
+            { GST_DEBUG_FUNCPTR (hardlimiter_limit_int32_be),
+              GST_DEBUG_FUNCPTR (hardlimiter_limit_int32_le) },
+            { GST_DEBUG_FUNCPTR (hardlimiter_limit_uint32_be),
+              GST_DEBUG_FUNCPTR (hardlimiter_limit_uint32_le) }
+          }
+        };
+    gint w, s, e;
+
+    w = (width / 8) - 1;
+    g_assert (w >= 0 && w <= 3);
+    s = (width == 8 || is_signed) ? 0 : 1;
+    e = (endianness == G_BIG_ENDIAN) ? 0 : 1;
+
+    filter->do_limit = funcs[w][s][e];
+    g_assert (filter->do_limit != NULL);
+
+    GST_DEBUG_OBJECT (filter, "limit function: %s",
+        GST_DEBUG_FUNCPTR_NAME (filter->do_limit));
+
+    if (is_signed) {
+      GST_LOG_OBJECT (filter, "upper = %d", filter->positive_integer_limit);
+      GST_LOG_OBJECT (filter, "lower = %d", filter->negative_integer_limit);
+    } else {
+      GST_LOG_OBJECT (filter, "upper = %u", filter->positive_integer_limit);
+      GST_LOG_OBJECT (filter, "lower = %u", filter->negative_integer_limit);
+    }
+  }
+
+  return TRUE;
+
+error:
+  {
+    GST_DEBUG_OBJECT (base, "Invalid caps: %" GST_PTR_FORMAT, incaps);
+    return FALSE;
+  }
+}
+
+static GstFlowReturn
+gst_hard_limiter_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
+{
+  GstHardLimiter *filter = GST_HARD_LIMITER (base);
+  gint i;
+
+  if (!filter->enabled) {
+    GST_DEBUG_OBJECT (filter, "disabled");
+    return GST_FLOW_OK;
+  }
+
+  g_assert (gst_buffer_is_writable (outbuf));
+
+  if (GST_BUFFER_TIMESTAMP_IS_VALID (outbuf))
+    gst_object_sync_values (G_OBJECT (filter), GST_BUFFER_TIMESTAMP (outbuf));
+
+  /* FIXME: this is extremely inefficient */
+  for (i = 0; i < GST_BUFFER_SIZE (outbuf); i += filter->sample_size) {
+    filter->do_limit (filter, GST_BUFFER_DATA (outbuf) + i);
+  }
+
+  return GST_FLOW_OK;
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  gst_controller_init (NULL, NULL);
+
+  return gst_element_register (plugin, "hardlimiter", GST_RANK_NONE,
+      GST_TYPE_HARD_LIMITER);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "audiocompress",
+    "Audio Compression Elements",
+    plugin_init,
+    VERSION,
+    GST_LICENSE,
+    GST_PACKAGE_NAME,
+    GST_PACKAGE_ORIGIN
+    )
--- /dev/null	2006-04-28 17:21:04.000000000 +0100
+++ gst/audiocompress/gsthardlimiter.h	2006-05-11 12:12:06.000000000 +0100
@@ -0,0 +1,69 @@
+/* GStreamer audio hard limiter
+ * Copyright (C) 2006 Artem Popov <artfwo@gmail.com>
+ *
+ * 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_HARD_LIMITER_H__
+#define __GST_HARD_LIMITER_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_HARD_LIMITER \
+  (gst_hard_limiter_get_type())
+#define GST_HARD_LIMITER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_HARD_LIMITER,GstHardLimiter))
+#define GST_HARD_LIMITER_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_HARD_LIMITER,GstHardLimiterClass))
+#define GST_IS_PLUGIN_TEMPLATE(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_HARD_LIMITER))
+#define GST_IS_PLUGIN_TEMPLATE_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_HARD_LIMITER))
+
+typedef struct _GstHardLimiter GstHardLimiter;
+typedef struct _GstHardLimiterClass GstHardLimiterClass;
+
+struct _GstHardLimiter
+{
+  GstBaseTransform element;
+
+  gboolean   enabled;
+  gdouble    limit;
+
+  guint32    positive_integer_limit;
+  guint32    negative_integer_limit;
+
+  gdouble    positive_double_limit;
+  gdouble    negative_double_limit;
+
+  gint       sample_size;
+
+  void     (*do_limit) (GstHardLimiter * limiter, gpointer data);
+};
+
+struct _GstHardLimiterClass
+{
+  GstBaseTransformClass parent_class;
+};
+
+GType gst_hard_limiter_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_HARD_LIMITER_H__ */
--- /dev/null	2006-04-28 17:21:04.000000000 +0100
+++ gst/audiocompress/Makefile.am	2006-05-11 14:26:49.000000000 +0100
@@ -0,0 +1,18 @@
+# plugindir is set in configure
+
+# change libgstplugin.la to something more suitable
+plugin_LTLIBRARIES = libgstaudiocompress.la
+
+# for the next set of variables, rename the prefix if you renamed the .la
+
+# sources used to compile this plug-in
+libgstaudiocompress_la_SOURCES = gsthardlimiter.c gsthardlimiter.h
+
+# flags used to compile this plugin
+# add other _CFLAGS and _LIBS as needed
+libgstaudiocompress_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CONTROLLER_CFLAGS)
+libgstaudiocompress_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(GST_CONTROLLER_LIBS)
+libgstaudiocompress_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+# headers we need but don't want installed
+noinst_HEADERS = gsthardlimiter.h
