summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Boulton <richard@tartarus.org>2002-03-02 12:36:07 +0000
committerRichard Boulton <richard@tartarus.org>2002-03-02 12:36:07 +0000
commitff816a14751310025804f4dc5c272f28d075bf6e (patch)
treecaa7e46102048c52e1c71365c8af1715d61aa2c4
parentb968efd71b2cb7d96fb090c44fba54b3a6543b4c (diff)
I'm too lazy to comment this
Original commit message from CVS: *** empty log message ***
-rw-r--r--configure.ac3
-rw-r--r--gst/monoscope/.gitignore7
-rw-r--r--gst/monoscope/Makefile.am12
-rw-r--r--gst/monoscope/README11
-rw-r--r--gst/monoscope/convolve.c316
-rw-r--r--gst/monoscope/convolve.h47
-rw-r--r--gst/monoscope/gstmonoscope.c367
-rw-r--r--gst/monoscope/monoscope.c143
-rw-r--r--gst/monoscope/monoscope.h10
9 files changed, 915 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index f59aa32c2..4d08e4eea 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
index 000000000..08f5ed37d
--- /dev/null
+++ b/gst/monoscope/.gitignore
@@ -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
index 000000000..e9a054926
--- /dev/null
+++ b/gst/monoscope/Makefile.am
@@ -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
index 000000000..e15676d03
--- /dev/null
+++ b/gst/monoscope/README
@@ -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
index 000000000..aefffa3ed
--- /dev/null
+++ b/gst/monoscope/convolve.c
@@ -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
index 000000000..d9709edc5
--- /dev/null
+++ b/gst/monoscope/convolve.h
@@ -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
index 000000000..da370a18d
--- /dev/null
+++ b/gst/monoscope/gstmonoscope.c
@@ -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
index 000000000..bddaa6df9
--- /dev/null
+++ b/gst/monoscope/monoscope.c
@@ -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
index 000000000..95ccf11ad
--- /dev/null
+++ b/gst/monoscope/monoscope.h
@@ -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