Commit ff816a14 authored by Richard Boulton's avatar Richard Boulton

I'm too lazy to comment this

Original commit message from CVS:
*** empty log message ***
parent b968efd7
......@@ -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
......
Makefile
Makefile.in
*.o
*.lo
*.la
.deps
.libs
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@
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).
/* 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;
}
/* 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
/* 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;
}