I'm too lazy to comment this
authorRichard Boulton <richard@tartarus.org>
Sat, 2 Mar 2002 12:36:07 +0000 (12:36 +0000)
committerRichard Boulton <richard@tartarus.org>
Sat, 2 Mar 2002 12:36:07 +0000 (12:36 +0000)
Original commit message from CVS:
*** empty log message ***

configure.ac
gst/monoscope/.gitignore [new file with mode: 0644]
gst/monoscope/Makefile.am [new file with mode: 0644]
gst/monoscope/README [new file with mode: 0644]
gst/monoscope/convolve.c [new file with mode: 0644]
gst/monoscope/convolve.h [new file with mode: 0644]
gst/monoscope/gstmonoscope.c [new file with mode: 0644]
gst/monoscope/monoscope.c [new file with mode: 0644]
gst/monoscope/monoscope.h [new file with mode: 0644]

index f59aa32c28dd2004cce9dbcd7dc7c2fe337aed8a..4d08e4eea884dbebc3384a7a72fa1131b1015ffc 100644 (file)
@@ -165,7 +165,7 @@ GST_PLUGINS_ALL="\
         cutter deinterlace flx goom intfloat law level\
         median mpeg1enc mpeg1sys mpeg1videoparse mpeg2enc mpeg2sub\
         mpegaudio mpegaudioparse mpegstream mpegtypes modplug\
-        passthrough playondemand rtjpeg silence sine\
+        monoscope passthrough playondemand rtjpeg silence sine\
         smooth spectrum speed stereo stereomono\
         synaesthesia udp videoscale volenv volume vumeter wavparse y4m"
 
@@ -734,6 +734,7 @@ gst/mpegstream/Makefile
 gst/mpegtypes/Makefile
 gst/modplug/Makefile
 gst/modplug/libmodplug/Makefile
+gst/monoscope/Makefile
 gst/passthrough/Makefile
 gst/playondemand/Makefile
 gst/rtjpeg/Makefile
diff --git a/gst/monoscope/.gitignore b/gst/monoscope/.gitignore
new file mode 100644 (file)
index 0000000..08f5ed3
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.in
+*.o
+*.lo
+*.la
+.deps
+.libs
diff --git a/gst/monoscope/Makefile.am b/gst/monoscope/Makefile.am
new file mode 100644 (file)
index 0000000..e9a0549
--- /dev/null
@@ -0,0 +1,12 @@
+plugindir = $(libdir)/gst
+
+plugin_LTLIBRARIES = libgstmonoscope.la
+
+libgstmonoscope_la_SOURCES = gstmonoscope.c monoscope.c convolve.c
+
+noinst_HEADERS = monoscope.h convolve.h
+
+libgstmonoscope_la_CFLAGS = -O2 -ffast-math $(GST_CFLAGS)
+libgstmonoscope_la_LIBADD = $(GST_LIBS)
+libgstmonoscope_la_LDFLAGS = @GST_PLUGIN_LDFLAGS@
+
diff --git a/gst/monoscope/README b/gst/monoscope/README
new file mode 100644 (file)
index 0000000..e15676d
--- /dev/null
@@ -0,0 +1,11 @@
+This is a visualization based on on the monoscope output plugin from
+alsaplayer.
+
+The monoscope output plugin was written primarily by Ralph Loader.
+
+This implementation is taken from alsaplayer version 0.99.54, at
+http://www.alsaplayer.org/
+
+Note: only one instance of this plugin may be created at a time: it has a
+lot of static data.  This should be fixed (and it shouldn't be hard to do
+so, either).
diff --git a/gst/monoscope/convolve.c b/gst/monoscope/convolve.c
new file mode 100644 (file)
index 0000000..aefffa3
--- /dev/null
@@ -0,0 +1,316 @@
+/* Karatsuba convolution
+ *
+ *  Copyright (C) 1999 Ralph Loader <suckfish@ihug.co.nz>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
+ *  
+ *
+ *  $Id$
+ *
+ */
+
+/* The algorithm is based on the following.  For the convolution of a pair
+ * of pairs, (a,b) * (c,d) = (0, a.c, a.d+b.c, b.d), we can reduce the four
+ * multiplications to three, by the formulae a.d+b.c = (a+b).(c+d) - a.c -
+ * b.d.  A similar relation enables us to compute a 2n by 2n convolution
+ * using 3 n by n convolutions, and thus a 2^n by 2^n convolution using 3^n
+ * multiplications (as opposed to the 4^n that the quadratic algorithm
+ * takes. */
+
+/* For large n, this is slower than the O(n log n) that the FFT method
+ * takes, but we avoid using complex numbers, and we only have to compute
+ * one convolution, as opposed to 3 FFTs.  We have good locality-of-
+ * reference as well, which will help on CPUs with tiny caches.  */
+
+/* E.g., for a 512 x 512 convolution, the FFT method takes 55 * 512 = 28160
+ * (real) multiplications, as opposed to 3^9 = 19683 for the Karatsuba
+ * algorithm.  We actually want 257 outputs of a 256 x 512 convolution;
+ * that doesn't appear to give an easy advantage for the FFT algorithm, but
+ * for the Karatsuba algorithm, it's easy to use two 256 x 256
+ * convolutions, taking 2 x 3^8 = 12312 multiplications.  [This difference
+ * is that the FFT method "wraps" the arrays, doing a 2^n x 2^n -> 2^n,
+ * while the Karatsuba algorithm pads with zeros, doing 2^n x 2^n -> 2.2^n
+ * - 1]. */
+
+/* There's a big lie above, actually... for a 4x4 convolution, it's quicker
+ * to do it using 16 multiplications than the more complex Karatsuba
+ * algorithm...  So the recursion bottoms out at 4x4s.  This increases the
+ * number of multiplications by a factor of 16/9, but reduces the overheads
+ * dramatically. */
+
+/* The convolution algorithm is implemented as a stack machine.  We have a
+ * stack of commands, each in one of the forms "do a 2^n x 2^n
+ * convolution", or "combine these three length 2^n outputs into one
+ * 2^{n+1} output." */
+
+#include <stdlib.h>
+#include "convolve.h"
+
+typedef union stack_entry_s {
+               struct {const double * left, * right; double * out;} v;
+               struct {double * main, * null;} b;
+
+} stack_entry;
+
+#define STACK_SIZE (CONVOLVE_DEPTH * 3)
+
+struct _struct_convolve_state {
+       double left [CONVOLVE_BIG];
+       double right [CONVOLVE_SMALL * 3];
+       double scratch [CONVOLVE_SMALL * 3];
+       stack_entry stack[STACK_SIZE];
+};
+
+/*
+ * Initialisation routine - sets up tables and space to work in.
+ * Returns a pointer to internal state, to be used when performing calls.
+ * On error, returns NULL.
+ * The pointer should be freed when it is finished with, by convolve_close().
+ */
+convolve_state *convolve_init(void)
+{
+       return (convolve_state *) malloc (sizeof(convolve_state));
+}
+
+/*
+ * Free the state allocated with convolve_init().
+ */
+void convolve_close(convolve_state *state)
+{
+       if (state)
+               free(state);
+}
+
+static void convolve_4 (double * out, const double * left, const double * right)
+/* This does a 4x4 -> 7 convolution.  For what it's worth, the slightly odd
+ * ordering gives about a 1% speed up on my Pentium II. */
+{
+       double l0, l1, l2, l3, r0, r1, r2, r3;
+       double a;
+       l0 = left[0];
+       r0 = right[0];
+       a = l0 * r0;
+       l1 = left[1];
+       r1 = right[1];
+       out[0] = a;
+       a = (l0 * r1) + (l1 * r0);
+       l2 = left[2];
+       r2 = right[2];
+       out[1] = a;
+       a = (l0 * r2) + (l1 * r1) + (l2 * r0);
+       l3 = left[3];
+       r3 = right[3];
+       out[2] = a;
+
+       out[3] = (l0 * r3) + (l1 * r2) + (l2 * r1) + (l3 * r0);
+       out[4] = (l1 * r3) + (l2 * r2) + (l3 * r1);
+       out[5] = (l2 * r3) + (l3 * r2);
+       out[6] = l3 * r3;
+}
+
+static void convolve_run (stack_entry * top, unsigned size, double * scratch)
+/* Interpret a stack of commands.  The stack starts with two entries; the
+ * convolution to do, and an illegal entry used to mark the stack top.  The
+ * size is the number of entries in each input, and must be a power of 2,
+ * and at least 8.  It is OK to have out equal to left and/or right.
+ * scratch must have length 3*size.  The number of stack entries needed is
+ * 3n-4 where size=2^n. */
+{
+       do {
+               const double * left;
+               const double * right;
+               double * out;
+
+               /* When we get here, the stack top is always a convolve,
+                * with size > 4.  So we will split it.  We repeatedly split
+                * the top entry until we get to size = 4. */
+                       
+               left = top->v.left;
+               right = top->v.right;
+               out = top->v.out;
+               top++;
+
+               do {
+                       double * s_left, * s_right;
+                       int i;
+
+                       /* Halve the size. */
+                       size >>= 1;
+
+                       /* Allocate the scratch areas. */
+                       s_left = scratch + size * 3;
+                       /* s_right is a length 2*size buffer also used for
+                        * intermediate output. */
+                       s_right = scratch + size * 4;
+
+                       /* Create the intermediate factors. */
+                       for (i = 0; i < size; i++) {
+                               double l = left[i] + left[i + size];
+                               double r = right[i] + right[i + size];
+                               s_left[i + size] = r;
+                               s_left[i] = l;
+                       }
+                       
+                       /* Push the combine entry onto the stack. */
+                       top -= 3;
+                       top[2].b.main = out;
+                       top[2].b.null = NULL;
+
+                       /* Push the low entry onto the stack.  This must be
+                        * the last of the three sub-convolutions, because
+                        * it may overwrite the arguments. */
+                       top[1].v.left = left;
+                       top[1].v.right = right;
+                       top[1].v.out = out;
+
+                       /* Push the mid entry onto the stack. */
+                       top[0].v.left = s_left;
+                       top[0].v.right = s_right;
+                       top[0].v.out = s_right;
+
+                       /* Leave the high entry in variables. */
+                       left += size;
+                       right += size;
+                       out += size * 2;
+
+               } while (size > 4);
+
+               /* When we get here, the stack top is a group of 3
+                * convolves, with size = 4, followed by some combines.  */
+               convolve_4 (out, left, right);
+               convolve_4 (top[0].v.out, top[0].v.left, top[0].v.right);
+               convolve_4 (top[1].v.out, top[1].v.left, top[1].v.right);
+               top += 2;
+
+               /* Now process combines. */
+               do {
+                       /* b.main is the output buffer, mid is the middle
+                        * part which needs to be adjusted in place, and
+                        * then folded back into the output.  We do this in
+                        * a slightly strange way, so as to avoid having
+                        * two loops. */
+                       double * out = top->b.main;
+                       double * mid = scratch + size * 4;
+                       unsigned int i;
+                       top++;
+                       out[size * 2 - 1] = 0;
+                       for (i = 0; i < size-1; i++) {
+                               double lo;
+                               double hi;
+                               lo = mid[0] - (out[0] + out[2 * size]) + out[size];
+                               hi = mid[size] - (out[size] + out[3 * size]) + out[2 * size];
+                               out[size] = lo;
+                               out[2 * size] = hi;
+                               out++;
+                               mid++;
+                       }
+                       size <<= 1;
+               } while (top->b.null == NULL);
+       } while (top->b.main != NULL);
+}
+
+int convolve_match (const int * lastchoice,
+                   const short * input,
+                   convolve_state * state)
+/* lastchoice is a 256 sized array.  input is a 512 array.  We find the
+ * contiguous length 256 sub-array of input that best matches lastchoice.
+ * A measure of how good a sub-array is compared with the lastchoice is
+ * given by the sum of the products of each pair of entries.  We maximise
+ * that, by taking an appropriate convolution, and then finding the maximum
+ * entry in the convolutions.  state is a (non-NULL) pointer returned by
+ * convolve_init.  */
+{
+       double avg;
+       double best;
+       int p = 0;
+       int i;
+       double * left = state->left;
+       double * right = state->right;
+       double * scratch = state->scratch;
+       stack_entry * top = state->stack + STACK_SIZE - 1;
+#if 1
+       for (i = 0; i < 512; i++)
+               left[i] = input[i];
+
+       avg = 0;
+       for (i = 0; i < 256; i++) {
+               double a = lastchoice[255 - i];
+               right[i] = a;
+               avg += a;
+       }
+#endif
+       /* We adjust the smaller of the two input arrays to have average
+        * value 0.  This makes the eventual result insensitive to both
+        * constant offsets and positive multipliers of the inputs. */
+       avg /= 256;
+       for (i = 0; i < 256; i++)
+               right[i] -= avg;
+       /* End-of-stack marker. */
+#if    0  /* The following line produces a CRASH, need to figure out why?!! */
+       top[1].b.null = scratch;
+#endif 
+       top[1].b.main = NULL;
+       /* The low 256x256, of which we want the high 256 outputs. */
+       top->v.left = left;
+       top->v.right = right;
+       top->v.out = right + 256;
+       convolve_run (top, 256, scratch);
+       
+       /* The high 256x256, of which we want the low 256 outputs. */
+       top->v.left = left + 256;
+       top->v.right = right;
+       top->v.out = right;
+       convolve_run (top, 256, scratch);
+
+       /* Now find the best position amoungs this.  Apart from the first
+        * and last, the required convolution outputs are formed by adding
+        * outputs from the two convolutions above. */
+       best = right[511];
+       right[767] = 0;
+       p = -1;
+       for (i = 0; i < 256; i++) {
+               double a = right[i] + right[i + 512];
+               if (a > best) {
+                       best = a;
+                       p = i;
+               }
+       }
+       p++;
+       
+#if 0
+       {
+               /* This is some debugging code... */
+               int bad = 0;
+               best = 0;
+               for (i = 0; i < 256; i++)
+                       best += ((double) input[i+p]) * ((double) lastchoice[i] - avg);
+               
+               for (i = 0; i < 257; i++) {
+                       double tot = 0;
+                       unsigned int j;
+                       for (j = 0; j < 256; j++)
+                               tot += ((double) input[i+j]) * ((double) lastchoice[j] - avg);
+                       if (tot > best)
+                               printf ("(%i)", i);
+                       if (tot != left[i + 255])
+                               printf ("!");
+               }
+
+               printf ("%i\n", p);
+       }
+#endif         
+
+       return p;
+}
diff --git a/gst/monoscope/convolve.h b/gst/monoscope/convolve.h
new file mode 100644 (file)
index 0000000..d9709ed
--- /dev/null
@@ -0,0 +1,47 @@
+/* convolve.h: Header for convolutions.
+ *
+ *  Copyright (C) 1999 Ralph Loader <suckfish@ihug.co.nz>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CONVOLVE_H
+#define CONVOLVE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* convolve_match takes two blocks, one twice the size of the other.  The
+ * sizes of these are CONVOLVE_BIG and CONVOLVE_SMALL respectively. */
+#define CONVOLVE_DEPTH 8
+#define CONVOLVE_SMALL (1 << CONVOLVE_DEPTH)
+#define CONVOLVE_BIG (CONVOLVE_SMALL * 2)
+
+/* Convolution stuff */
+typedef struct _struct_convolve_state convolve_state;
+
+convolve_state *convolve_init (void);
+void convolve_close (convolve_state * state);
+
+int convolve_match (const int * lastchoice,
+                   const short int * input,
+                   convolve_state * state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/gst/monoscope/gstmonoscope.c b/gst/monoscope/gstmonoscope.c
new file mode 100644 (file)
index 0000000..da370a1
--- /dev/null
@@ -0,0 +1,367 @@
+/* gstmonoscope.c: implementation of monoscope drawing element
+ * Copyright (C) <2002> Richard Boulton <richard@tartarus.org>
+ *
+ * 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.
+ */
+
+#include <config.h>
+#include <gst/gst.h>
+
+#include "monoscope.h"
+
+#define GST_TYPE_MONOSCOPE (gst_monoscope_get_type())
+#define GST_MONOSCOPE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MONOSCOPE,GstMonoscope))
+#define GST_MONOSCOPE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MONOSCOPE,GstMonoscope))
+#define GST_IS_MONOSCOPE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MONOSCOPE))
+#define GST_IS_MONOSCOPE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MONOSCOPE))
+
+typedef struct _GstMonoscope GstMonoscope;
+typedef struct _GstMonoscopeClass GstMonoscopeClass;
+
+struct _GstMonoscope {
+  GstElement element;
+
+  /* pads */
+  GstPad *sinkpad,*srcpad;
+  GstBufferPool *peerpool;
+
+  // the timestamp of the next frame
+  guint64 next_time;
+  gint16 datain[2][512];
+
+  // video state
+  gint fps;
+  gint width;
+  gint height;
+  gboolean first_buffer;
+};
+
+struct _GstMonoscopeClass {
+  GstElementClass parent_class;
+};
+
+GType gst_monoscope_get_type(void);
+
+
+/* elementfactory information */
+static GstElementDetails gst_monoscope_details = {
+  "Monoscope",
+  "Filter/Visualization",
+  "Displays a highly stabilised waveform of audio input",
+  VERSION,
+  "Richard Boulton <richard@tartarus.org>",
+  "(C) 2002",
+};
+
+/* signals and args */
+enum {
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum {
+  ARG_0,
+  ARG_WIDTH,
+  ARG_HEIGHT,
+  ARG_FPS,
+  /* FILL ME */
+};
+
+GST_PADTEMPLATE_FACTORY (src_template,
+  "src",
+  GST_PAD_SRC,
+  GST_PAD_ALWAYS,
+  GST_CAPS_NEW (
+    "monoscopesrc",
+    "video/raw",
+      "format",                GST_PROPS_FOURCC (GST_STR_FOURCC ("RGB ")),
+      "bpp",           GST_PROPS_INT (32),
+      "depth",         GST_PROPS_INT (32),
+      "endianness",    GST_PROPS_INT (G_BYTE_ORDER),
+      "red_mask",      GST_PROPS_INT (0xff0000),
+      "green_mask",    GST_PROPS_INT (0xff00),
+      "blue_mask",     GST_PROPS_INT (0xff),
+      "width",         GST_PROPS_INT_RANGE (16, 4096),
+      "height",                GST_PROPS_INT_RANGE (16, 4096)
+  )
+)
+
+GST_PADTEMPLATE_FACTORY (sink_template,
+  "sink",                                      /* the name of the pads */
+  GST_PAD_SINK,                                /* type of the pad */
+  GST_PAD_ALWAYS,                              /* ALWAYS/SOMETIMES */
+  GST_CAPS_NEW (
+    "monoscopesink",                           /* the name of the caps */
+    "audio/raw",                               /* the mime type of the caps */
+       /* Properties follow: */
+      "format",     GST_PROPS_STRING ("int"),
+      "law",        GST_PROPS_INT (0),
+      "endianness", GST_PROPS_INT (G_BYTE_ORDER),
+      "signed",     GST_PROPS_BOOLEAN (TRUE),
+      "width",      GST_PROPS_INT (16),
+      "depth",      GST_PROPS_INT (16),
+      "rate",       GST_PROPS_INT_RANGE (8000, 96000),
+      "channels",   GST_PROPS_INT (1)
+  )
+)
+
+
+static void            gst_monoscope_class_init        (GstMonoscopeClass *klass);
+static void            gst_monoscope_init              (GstMonoscope *monoscope);
+
+static void            gst_monoscope_set_property      (GObject *object, guint prop_id, 
+                                                const GValue *value, GParamSpec *pspec);
+static void            gst_monoscope_get_property      (GObject *object, guint prop_id, 
+                                                GValue *value, GParamSpec *pspec);
+
+static void            gst_monoscope_chain             (GstPad *pad, GstBuffer *buf);
+
+static GstPadConnectReturn 
+                       gst_monoscope_sinkconnect       (GstPad *pad, GstCaps *caps);
+
+static GstElementClass *parent_class = NULL;
+
+GType
+gst_monoscope_get_type (void)
+{
+  static GType type = 0;
+
+  if (!type) {
+    static const GTypeInfo info = {
+      sizeof (GstMonoscopeClass),      
+      NULL,      
+      NULL,      
+      (GClassInitFunc) gst_monoscope_class_init,
+      NULL,
+      NULL,
+      sizeof (GstMonoscope),
+      0,
+      (GInstanceInitFunc) gst_monoscope_init,
+    };
+    type = g_type_register_static (GST_TYPE_ELEMENT, "GstMonoscope", &info, 0);
+  }
+  return type;
+}
+
+static void
+gst_monoscope_class_init(GstMonoscopeClass *klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass*) klass;
+  gstelement_class = (GstElementClass*) klass;
+
+  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_WIDTH,
+    g_param_spec_int ("width","Width","The Width",
+                       1, 2048, 256, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HEIGHT,
+    g_param_spec_int ("height","Height","The height",
+                       1, 2048, 128, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FPS,
+    g_param_spec_int ("fps","FPS","Frames per second",
+                       1, 100, 25, G_PARAM_READWRITE));
+
+  gobject_class->set_property = gst_monoscope_set_property;
+  gobject_class->get_property = gst_monoscope_get_property;
+}
+
+static void
+gst_monoscope_init (GstMonoscope *monoscope)
+{
+  /* create the sink and src pads */
+  monoscope->sinkpad = gst_pad_new_from_template (
+                 GST_PADTEMPLATE_GET (sink_template ), "sink");
+  monoscope->srcpad = gst_pad_new_from_template (
+                 GST_PADTEMPLATE_GET (src_template ), "src");
+  gst_element_add_pad (GST_ELEMENT (monoscope), monoscope->sinkpad);
+  gst_element_add_pad (GST_ELEMENT (monoscope), monoscope->srcpad);
+
+  gst_pad_set_chain_function (monoscope->sinkpad, gst_monoscope_chain);
+  gst_pad_set_connect_function (monoscope->sinkpad, gst_monoscope_sinkconnect);
+
+  monoscope->next_time = 0;
+  monoscope->peerpool = NULL;
+
+  // reset the initial video state
+  monoscope->first_buffer = TRUE;
+  monoscope->width = 256;
+  monoscope->height = 128;
+  monoscope->fps = 25; // desired frame rate
+
+}
+
+static GstPadConnectReturn
+gst_monoscope_sinkconnect (GstPad *pad, GstCaps *caps)
+{
+  GstMonoscope *monoscope;
+  monoscope = GST_MONOSCOPE (gst_pad_get_parent (pad));
+
+  if (!GST_CAPS_IS_FIXED (caps)) {
+    return GST_PAD_CONNECT_DELAYED;
+  }
+
+  return GST_PAD_CONNECT_OK;
+}
+
+static void
+gst_monoscope_chain (GstPad *pad, GstBuffer *bufin)
+{
+  GstMonoscope *monoscope;
+  GstBuffer *bufout;
+  guint32 samples_in;
+  gint16 *data;
+  gint i;
+
+  monoscope = GST_MONOSCOPE (gst_pad_get_parent (pad));
+
+  GST_DEBUG (0, "Monoscope: chainfunc called\n");
+
+  samples_in = GST_BUFFER_SIZE (bufin) / sizeof (gint16);
+
+  GST_DEBUG (0, "input buffer has %d samples\n", samples_in);
+
+  /* FIXME: should really select the first 1024 samples after the timestamp. */
+  if (GST_BUFFER_TIMESTAMP (bufin) < monoscope->next_time || samples_in < 1024) {
+    GST_DEBUG (0, "timestamp is %llu: want >= %llu\n", GST_BUFFER_TIMESTAMP (bufin), monoscope->next_time);
+    gst_buffer_unref (bufin);
+    return;
+  }
+
+  data = (gint16 *) GST_BUFFER_DATA (bufin);
+  for (i=0; i < 512; i++) {
+    monoscope->datain[0][i] = *data++;
+    monoscope->datain[1][i] = *data++;
+  }
+
+  if (monoscope->first_buffer) {
+    GstCaps *caps;
+
+    monoscope_init (monoscope->width, monoscope->height);
+       
+    GST_DEBUG (0, "making new pad\n");
+
+    caps = GST_CAPS_NEW (
+                    "monoscopesrc",
+                    "video/raw",
+                      "format",        GST_PROPS_FOURCC (GST_STR_FOURCC ("RGB ")), 
+                      "bpp",           GST_PROPS_INT (32), 
+                      "depth",         GST_PROPS_INT (32), 
+                      "endianness",    GST_PROPS_INT (G_BYTE_ORDER), 
+                      "red_mask",      GST_PROPS_INT (0xff0000), 
+                      "green_mask",    GST_PROPS_INT (0x00ff00), 
+                      "blue_mask",     GST_PROPS_INT (0x0000ff), 
+                      "width",         GST_PROPS_INT (monoscope->width), 
+                      "height",        GST_PROPS_INT (monoscope->height)
+                  );
+
+    if (!gst_pad_try_set_caps (monoscope->srcpad, caps)) {
+      gst_element_error (GST_ELEMENT (monoscope), "could not set caps");
+      return;
+    }
+    monoscope->first_buffer = FALSE;
+  }
+
+  bufout = gst_buffer_new ();
+  GST_BUFFER_SIZE (bufout) = monoscope->width * monoscope->height * 4;
+  GST_BUFFER_DATA (bufout) = (guchar *) monoscope_update (monoscope->datain);
+  GST_BUFFER_TIMESTAMP (bufout) = monoscope->next_time;
+  GST_BUFFER_FLAG_SET (bufout, GST_BUFFER_DONTFREE);
+
+  monoscope->next_time += 1000000LL / monoscope->fps;
+
+  gst_pad_push (monoscope->srcpad, bufout);
+
+  gst_buffer_unref (bufin);
+
+  GST_DEBUG (0, "Monoscope: exiting chainfunc\n");
+
+}
+
+static void
+gst_monoscope_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+  GstMonoscope *monoscope;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail (GST_IS_MONOSCOPE (object));
+  monoscope = GST_MONOSCOPE (object);
+
+  switch (prop_id) {
+    case ARG_WIDTH:
+      monoscope->width = g_value_get_int (value);
+      break;
+    case ARG_HEIGHT:
+      monoscope->height = g_value_get_int (value);
+      break;
+    case ARG_FPS:
+      monoscope->fps = g_value_get_int (value);
+      break;
+    default:
+      break;
+  }
+}
+
+static void
+gst_monoscope_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+  GstMonoscope *monoscope;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail (GST_IS_MONOSCOPE (object));
+  monoscope = GST_MONOSCOPE (object);
+
+  switch (prop_id) {
+    case ARG_WIDTH:
+      g_value_set_int (value, monoscope->width);
+      break;
+    case ARG_HEIGHT:
+      g_value_set_int (value, monoscope->height);
+      break;
+    case ARG_FPS:
+      g_value_set_int (value, monoscope->fps);
+      break;
+    default:
+      break;
+  }
+}
+
+static gboolean
+plugin_init (GModule *module, GstPlugin *plugin)
+{
+  GstElementFactory *factory;
+
+  /* create an elementfactory for the monoscope element */
+  factory = gst_elementfactory_new("monoscope",GST_TYPE_MONOSCOPE,
+                                   &gst_monoscope_details);
+  g_return_val_if_fail(factory != NULL, FALSE);
+
+  gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (src_template));
+  gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (sink_template));
+
+  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
+
+  return TRUE;
+}
+
+GstPluginDesc plugin_desc = {
+  GST_VERSION_MAJOR,
+  GST_VERSION_MINOR,
+  "monoscope",
+  plugin_init
+};
diff --git a/gst/monoscope/monoscope.c b/gst/monoscope/monoscope.c
new file mode 100644 (file)
index 0000000..bddaa6d
--- /dev/null
@@ -0,0 +1,143 @@
+/*  monoscope.cpp
+ *  Copyright (C) 2002 Richard Boulton <richard@tartarus.org>
+ *  Copyright (C) 1998-2001 Andy Lo A Foe <andy@alsaplayer.org>
+ *  Original code by Tinic Uro
+ *
+ *  This code is copied from Alsaplayer.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include "monoscope.h"
+#include "convolve.h"
+
+#define scope_width 256
+#define scope_height 128
+
+static gint16 newEq[CONVOLVE_BIG];      // latest block of 512 samples.
+static gint16 copyEq[CONVOLVE_BIG];
+static int avgEq[CONVOLVE_SMALL];      // a running average of the last few.
+static int avgMax;                     // running average of max sample.
+static guint32 display[(scope_width + 1) * (scope_height + 1)];
+
+static convolve_state *state = NULL;
+static guint32 colors[64];
+
+static void colors_init(guint32 * colors)
+{
+    int i;
+    for (i = 0; i < 32; i++) {
+       colors[i] = (i*8 << 16) + (255 << 8);
+       colors[i+31] = (255 << 16) + (((31 - i) * 8) << 8);
+    }
+    colors[63] = (40 << 16) + (75 << 8);
+}
+
+void monoscope_init (guint32 resx, guint32 resy)
+{
+    state = convolve_init();
+    colors_init(colors);
+}
+
+guint32 * monoscope_update (gint16 data [2][512])
+{
+    /* Note that CONVOLVE_BIG must == data size here, ie 512. */
+    /* Really, we want samples evenly spread over the available data.
+     * Just taking a continuous chunk will do for now, though. */
+    int i;
+    for (i = 0; i < CONVOLVE_BIG; i++) {
+       /* Average the two channels. */
+       newEq[i] = (((int) data[0][i]) + (int) data[1][i]) >> 1;
+    }
+
+    int foo;
+    int bar;  
+    int h;
+    guchar bits[ 257 * 129];
+    guint32 *loc;
+
+       int factor;
+       int val;
+       int max = 1;
+       short * thisEq;
+       memcpy (copyEq, newEq, sizeof (short) * CONVOLVE_BIG);
+       thisEq = copyEq;
+#if 1                                  
+       val = convolve_match (avgEq, copyEq, state);
+       thisEq += val;
+#endif                                 
+       memset(display, 0, 256 * 128 * sizeof(guint32));
+       for (i=0; i < 256; i++) {
+           foo = thisEq[i] + (avgEq[i] >> 1);
+           avgEq[i] = foo;
+           if (foo < 0)
+               foo = -foo;
+           if (foo > max)
+               max = foo;
+       }
+       avgMax += max - (avgMax >> 8);
+       if (avgMax < max)
+           avgMax = max; /* Avoid overflow */
+       factor = 0x7fffffff / avgMax;
+       /* Keep the scaling sensible. */
+       if (factor > (1 << 18))
+           factor = 1 << 18;
+       if (factor < (1 << 8))
+           factor = 1 << 8;
+       for (i=0; i < 256; i++) {
+           foo = avgEq[i] * factor;
+           foo >>= 18;
+           if (foo > 63)
+               foo = 63;
+           if (foo < -64)
+               foo = -64;
+           val = (i + ((foo+64) << 8));
+           bar = val;
+           if ((bar > 0) && (bar < (256 * 128))) {
+               loc = display + bar;
+               if (foo < 0) {
+                   for (h = 0; h <= (-foo); h++) {
+                       *loc = colors[h];
+                       loc+=256; 
+                   }
+               } else {
+                   for (h = 0; h <= foo; h++) {
+                       *loc = colors[h];
+                       loc-=256;
+                   }
+               }
+           }
+       }
+
+       /* Draw grid. */
+       for (i=16;i < 128; i+=16) {
+           for (h = 0; h < 256; h+=2) {
+               display[(i << 8) + h] = colors[63];
+               if (i == 64)
+                   display[(i << 8) + h + 1] = colors[63];
+           }
+       }
+       for (i = 16; i < 256; i+=16) {
+           for (h = 0; h < 128; h+=2) {
+               display[i + (h << 8)] = colors[63];
+           }
+       }
+
+    return display;
+}
+
+void monoscope_close ()
+{
+}
+
diff --git a/gst/monoscope/monoscope.h b/gst/monoscope/monoscope.h
new file mode 100644 (file)
index 0000000..95ccf11
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _MONOSCOPE_H
+#define _MONOSCOPE_H
+
+#include <glib.h>
+
+void monoscope_init (guint32 resx, guint32 resy);
+guint32 * monoscope_update (gint16 data [2][512]);
+void monoscope_close ();
+
+#endif