From b4afe9f69008527833991b09abee495cb82b34bf Mon Sep 17 00:00:00 2001 From: Stefan Kost Date: Mon, 30 May 2011 23:36:42 +0300 Subject: scopes: add a new element similar to synaesthesia https://bugzilla.gnome.org/show_bug.cgi?id=651536 --- gst/scopes/Makefile.am | 3 +- gst/scopes/gstsynaescope.c | 299 +++++++++++++++++++++++++++++++++++++++++++++ gst/scopes/gstsynaescope.h | 58 +++++++++ gst/scopes/plugin.c | 1 + 4 files changed, 360 insertions(+), 1 deletion(-) create mode 100644 gst/scopes/gstsynaescope.c create mode 100644 gst/scopes/gstsynaescope.h diff --git a/gst/scopes/Makefile.am b/gst/scopes/Makefile.am index 73467f40f..fa393d789 100644 --- a/gst/scopes/Makefile.am +++ b/gst/scopes/Makefile.am @@ -3,6 +3,7 @@ plugin_LTLIBRARIES = libgstscopes.la libgstscopes_la_SOURCES = \ gstbasescope.c plugin.c \ gstspectrascope.c gstspectrascope.h \ + gstsynaescope.c gstsynaescope.h \ gstwavescope.c gstwavescope.h libgstscopes_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) \ @@ -16,7 +17,7 @@ libgstscopes_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstscopes_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = gstbasescope.h \ - gstspectrascope.h gstwavescope.h + gstspectrascope.h gstsynaescope.h gstwavescope.h Android.mk: Makefile.am $(BUILT_SOURCES) androgenizer \ diff --git a/gst/scopes/gstsynaescope.c b/gst/scopes/gstsynaescope.c new file mode 100644 index 000000000..8dd823edf --- /dev/null +++ b/gst/scopes/gstsynaescope.c @@ -0,0 +1,299 @@ +/* GStreamer + * Copyright (C) <2011> Stefan Kost + * + * gstsynaescope.c: simple oscilloscope + * + * 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. + */ +/** + * SECTION:element-synaescope + * @see_also: goom + * + * Wavescope is a simple audio visualisation element. It renders the waveforms + * like on an oscilloscope. + * + * + * Example launch line + * |[ + * gst-launch audiotestsrc ! audioconvert ! synaescope ! ximagesink + * ]| + * + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstsynaescope.h" + +static GstStaticPadTemplate gst_synae_scope_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN) + ); + +static GstStaticPadTemplate gst_synae_scope_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_AUDIO_INT_STANDARD_PAD_TEMPLATE_CAPS) + ); + + +GST_DEBUG_CATEGORY_STATIC (synae_scope_debug); +#define GST_CAT_DEFAULT synae_scope_debug + +static void gst_synae_scope_finalize (GObject * object); + +static gboolean gst_synae_scope_setup (GstBaseScope * scope); +static gboolean gst_synae_scope_render (GstBaseScope * scope, GstBuffer * audio, + GstBuffer * video); + + +GST_BOILERPLATE (GstSynaeScope, gst_synae_scope, GstBaseScope, + GST_TYPE_BASE_SCOPE); + +static void +gst_synae_scope_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details_simple (element_class, "Waveform oscilloscope", + "Visualization", + "Simple waveform oscilloscope", "Stefan Kost "); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_synae_scope_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_synae_scope_sink_template)); +} + +static void +gst_synae_scope_class_init (GstSynaeScopeClass * g_class) +{ + GObjectClass *gobject_class = (GObjectClass *) g_class; + GstBaseScopeClass *scope_class = (GstBaseScopeClass *) g_class; + + gobject_class->finalize = gst_synae_scope_finalize; + + scope_class->setup = GST_DEBUG_FUNCPTR (gst_synae_scope_setup); + scope_class->render = GST_DEBUG_FUNCPTR (gst_synae_scope_render); +} + +static void +gst_synae_scope_init (GstSynaeScope * scope, GstSynaeScopeClass * g_class) +{ + guint32 *colors = scope->colors; + guint *shade = scope->shade; + guint i, r, g, b; + +#define BOUND(x) ((x) > 255 ? 255 : (x)) +#define PEAKIFY(x) BOUND((x) - (x)*(255-(x))/255/2) + + for (i = 0; i < 256; i++) { + r = PEAKIFY ((i & 15 * 16)); + g = PEAKIFY ((i & 15) * 16 + (i & 15 * 16) / 4); + b = PEAKIFY ((i & 15) * 16); + + colors[i] = (r << 16) | (g << 8) | b; + } +#undef BOUND +#undef PEAKIFY + + for (i = 0; i < 256; i++) + shade[i] = i * 200 >> 8; +} + +static void +gst_synae_scope_finalize (GObject * object) +{ + GstSynaeScope *scope = GST_SYNAE_SCOPE (object); + + if (scope->fft_ctx) { + gst_fft_s16_free (scope->fft_ctx); + scope->fft_ctx = NULL; + } + if (scope->freq_data_l) { + g_free (scope->freq_data_l); + scope->freq_data_l = NULL; + } + if (scope->freq_data_r) { + g_free (scope->freq_data_r); + scope->freq_data_r = NULL; + } + if (scope->adata_l) { + g_free (scope->adata_l); + scope->adata_l = NULL; + } + if (scope->adata_r) { + g_free (scope->adata_r); + scope->adata_r = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_synae_scope_setup (GstBaseScope * bscope) +{ + GstSynaeScope *scope = GST_SYNAE_SCOPE (bscope); + guint num_freq = bscope->height + 1; + + if (scope->fft_ctx) + gst_fft_s16_free (scope->fft_ctx); + g_free (scope->freq_data_l); + g_free (scope->freq_data_r); + g_free (scope->adata_l); + g_free (scope->adata_r); + + /* FIXME: we could have horizontal or vertical layout */ + + /* we'd need this amount of samples per render() call */ + bscope->req_spf = num_freq * 2 - 2; + scope->fft_ctx = gst_fft_s16_new (bscope->req_spf, FALSE); + scope->freq_data_l = g_new (GstFFTS16Complex, num_freq); + scope->freq_data_r = g_new (GstFFTS16Complex, num_freq); + + scope->adata_l = g_new (gint16, bscope->req_spf); + scope->adata_r = g_new (gint16, bscope->req_spf); + + return TRUE; +} + +static inline void +add_pixel (guint32 * _p, guint32 _c) +{ + guint8 *p = (guint8 *) _p; + guint8 *c = (guint8 *) & _c; + + if (p[0] < 255 - c[0]) + p[0] += c[0]; + else + p[0] = 255; + if (p[1] < 255 - c[1]) + p[1] += c[1]; + else + p[1] = 255; + if (p[2] < 255 - c[2]) + p[2] += c[2]; + else + p[2] = 255; + if (p[3] < 255 - c[3]) + p[3] += c[3]; + else + p[3] = 255; +} + +static gboolean +gst_synae_scope_render (GstBaseScope * bscope, GstBuffer * audio, + GstBuffer * video) +{ + GstSynaeScope *scope = GST_SYNAE_SCOPE (bscope); + guint32 *vdata = (guint32 *) GST_BUFFER_DATA (video); + gint16 *adata = (gint16 *) GST_BUFFER_DATA (audio); + gint16 *adata_l = scope->adata_l; + gint16 *adata_r = scope->adata_r; + GstFFTS16Complex *fdata_l = scope->freq_data_l; + GstFFTS16Complex *fdata_r = scope->freq_data_r; + gint x, y; + guint off; + gfloat frl, fil, frr, fir; + guint w = bscope->width; + guint h = bscope->height; + guint32 *colors = scope->colors, c; + guint *shade = scope->shade; + //guint w2 = w /2; + guint ch = bscope->channels; + guint num_samples = GST_BUFFER_SIZE (audio) / (ch * sizeof (gint16)); + gint i, j; + gint br, br1, br2; + gint clarity; + gfloat fc, r, l; + const guint sl = 30; + + /* deinterleave */ + for (i = 0, j = 0; i < num_samples; i++) { + adata_l[i] = adata[j++]; + adata_r[i] = adata[j++]; + } + + /* run fft */ + /* synaesthesia was using a signle fft with left -> real, right -> imag */ + //gst_fft_s16_window (scope->fft_ctx, adata_l, GST_FFT_WINDOW_HAMMING); + gst_fft_s16_fft (scope->fft_ctx, adata_l, fdata_l); + //gst_fft_s16_window (scope->fft_ctx, adata_r, GST_FFT_WINDOW_HAMMING); + gst_fft_s16_fft (scope->fft_ctx, adata_r, fdata_r); + + /* draw stars */ + for (y = 0; y < h; y++) { + frl = (gfloat) fdata_l[h - y].r / 512.0; + fil = (gfloat) fdata_l[h - y].i / 512.0; + l = sqrt (frl * frl + fil * fil); + frr = (gfloat) fdata_r[h - y].r / 512.0; + fir = (gfloat) fdata_r[h - y].i / 512.0; + r = sqrt (frr * frr + fir * fir); + fc = r + l; + + clarity = (gint) (((frl + frr) * (frl - frr) + (fil + fir) * (fil - fir)) / + (((frl + frr) * (frl + frr) + (fil - fir) * (fil - fir) + (frl - + frr) * (frl - frr) + (fil + fir) * (fil + fir)) * 256.0)); + + x = (guint) (r * w / fc); + br = y * fc * 100; + if (br > 0xFF) + br = 0xFF; + + br1 = br * (clarity + 128) >> 8; + br2 = br * (128 - clarity) >> 8; + br1 = CLAMP (br1, 0, 255); + br2 = CLAMP (br2, 0, 255); + + off = (y * w) + x; + c = colors[(br1 >> 4) + (br2 & 0xf0)]; + add_pixel (&vdata[off], c); + if ((x > (sl - 1)) && (x < (w - sl)) && (y > (sl - 1)) && (y < (h - sl))) { + for (i = 1; br1 || br2; i++, br1 = shade[br1], br2 = shade[br2]) { + c = colors[(br1 >> 4) + (br2 & 0xf0)]; + add_pixel (&vdata[off - i], c); + add_pixel (&vdata[off + i], c); + add_pixel (&vdata[off - i * w], c); + add_pixel (&vdata[off + i * w], c); + } + } else { + for (i = 1; br1 || br2; i++, br1 = shade[br1], br2 = shade[br2]) { + c = colors[(br1 >> 4) + (br2 & 0xf0)]; + if (x - i > 0) + add_pixel (&vdata[off - i], c); + if (x + i < (w - 1)) + add_pixel (&vdata[off + i], c); + if (y - i > 0) + add_pixel (&vdata[off - i * w], c); + if (y + i < (h - 1)) + add_pixel (&vdata[off + i * w], c); + } + } + } + + return TRUE; +} + +gboolean +gst_synae_scope_plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (synae_scope_debug, "synaescope", 0, "synaescope"); + + return gst_element_register (plugin, "synaescope", GST_RANK_NONE, + GST_TYPE_SYNAE_SCOPE); +} diff --git a/gst/scopes/gstsynaescope.h b/gst/scopes/gstsynaescope.h new file mode 100644 index 000000000..09c18c32b --- /dev/null +++ b/gst/scopes/gstsynaescope.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) <2011> Stefan Kost + * + * gstsynaescope.h: simple oscilloscope + * + * 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 __GST_SYNAE_SCOPE_H__ +#define __GST_SYNAE_SCOPE_H__ + +#include "gstbasescope.h" +#include + +G_BEGIN_DECLS +#define GST_TYPE_SYNAE_SCOPE (gst_synae_scope_get_type()) +#define GST_SYNAE_SCOPE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SYNAE_SCOPE,GstSynaeScope)) +#define GST_SYNAE_SCOPE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SYNAE_SCOPE,GstSynaeScopeClass)) +#define GST_IS_SYNAE_SCOPE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SYNAE_SCOPE)) +#define GST_IS_SYNAE_SCOPE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SYNAE_SCOPE)) +typedef struct _GstSynaeScope GstSynaeScope; +typedef struct _GstSynaeScopeClass GstSynaeScopeClass; + +struct _GstSynaeScope +{ + GstBaseScope parent; + + GstFFTS16 *fft_ctx; + GstFFTS16Complex *freq_data_l, *freq_data_r; + gint16 *adata_l, *adata_r; + + guint32 colors[256]; + guint shade[256]; +}; + +struct _GstSynaeScopeClass +{ + GstBaseScopeClass parent_class; +}; + +GType gst_synae_scope_get_type (void); +gboolean gst_synae_scope_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_SYNAE_SCOPE_H__ */ \ No newline at end of file diff --git a/gst/scopes/plugin.c b/gst/scopes/plugin.c index 1082541e2..de3291154 100644 --- a/gst/scopes/plugin.c +++ b/gst/scopes/plugin.c @@ -37,6 +37,7 @@ plugin_init (GstPlugin * plugin) gst_controller_init (NULL, NULL); res &= gst_spectra_scope_plugin_init (plugin); + res &= gst_synae_scope_plugin_init (plugin); res &= gst_wave_scope_plugin_init (plugin); return res; } -- cgit v1.2.1