diff --git a/configure.ac b/configure.ac index f59aa32c28..4d08e4eea8 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 0000000000..08f5ed37d8 --- /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 0000000000..e9a054926f --- /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 0000000000..e15676d032 --- /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 0000000000..aefffa3ed9 --- /dev/null +++ b/gst/monoscope/convolve.c @@ -0,0 +1,316 @@ +/* Karatsuba convolution + * + * Copyright (C) 1999 Ralph Loader + * + * 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 +#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 0000000000..d9709edc5f --- /dev/null +++ b/gst/monoscope/convolve.h @@ -0,0 +1,47 @@ +/* convolve.h: Header for convolutions. + * + * Copyright (C) 1999 Ralph Loader + * + * 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 0000000000..da370a18da --- /dev/null +++ b/gst/monoscope/gstmonoscope.c @@ -0,0 +1,367 @@ +/* gstmonoscope.c: implementation of monoscope drawing element + * Copyright (C) <2002> Richard Boulton + * + * 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 +#include + +#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 ", + "(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 0000000000..bddaa6df9f --- /dev/null +++ b/gst/monoscope/monoscope.c @@ -0,0 +1,143 @@ +/* monoscope.cpp + * Copyright (C) 2002 Richard Boulton + * Copyright (C) 1998-2001 Andy Lo A Foe + * 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 0000000000..95ccf11ada --- /dev/null +++ b/gst/monoscope/monoscope.h @@ -0,0 +1,10 @@ +#ifndef _MONOSCOPE_H +#define _MONOSCOPE_H + +#include + +void monoscope_init (guint32 resx, guint32 resy); +guint32 * monoscope_update (gint16 data [2][512]); +void monoscope_close (); + +#endif