diff options
author | Stefan Kost <ensonic@users.sf.net> | 2011-06-04 14:25:57 +0300 |
---|---|---|
committer | Stefan Kost <ensonic@users.sf.net> | 2011-06-06 15:25:13 +0300 |
commit | 45464a038ec6fcc3e691b6ced8af7ce4b0a42186 (patch) | |
tree | 84c58fc759fcce5f1a236d49dee9c18264e611d5 /gst/audiovisualizers | |
parent | 92d36cdbfe1320ccb21e8e564c79c3302de1846d (diff) |
audiovisualizers: rename scopes plugin to audiovisualizers
Diffstat (limited to 'gst/audiovisualizers')
-rw-r--r-- | gst/audiovisualizers/Makefile.am | 35 | ||||
-rw-r--r-- | gst/audiovisualizers/gstbasescope.c | 796 | ||||
-rw-r--r-- | gst/audiovisualizers/gstbasescope.h | 108 | ||||
-rw-r--r-- | gst/audiovisualizers/gstspectrascope.c | 221 | ||||
-rw-r--r-- | gst/audiovisualizers/gstspectrascope.h | 54 | ||||
-rw-r--r-- | gst/audiovisualizers/gstsynaescope.c | 300 | ||||
-rw-r--r-- | gst/audiovisualizers/gstsynaescope.h | 58 | ||||
-rw-r--r-- | gst/audiovisualizers/gstwavescope.c | 138 | ||||
-rw-r--r-- | gst/audiovisualizers/gstwavescope.h | 50 | ||||
-rw-r--r-- | gst/audiovisualizers/plugin.c | 49 |
10 files changed, 1809 insertions, 0 deletions
diff --git a/gst/audiovisualizers/Makefile.am b/gst/audiovisualizers/Makefile.am new file mode 100644 index 000000000..ac999a582 --- /dev/null +++ b/gst/audiovisualizers/Makefile.am @@ -0,0 +1,35 @@ +plugin_LTLIBRARIES = libgstaudiovisualizers.la + +libgstaudiovisualizers_la_SOURCES = plugin.c \ + gstbaseaudiovisualizer.c gstbaseaudiovisualizer.h \ + gstspectrascope.c gstspectrascope.h \ + gstsynaescope.c gstsynaescope.h \ + gstwavescope.c gstwavescope.h + +libgstaudiovisualizers_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) \ + $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \ + $(GST_CONTROLLER_CFLAGS) $(GST_CFLAGS) +libgstaudiovisualizers_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) \ + -lgstvideo-$(GST_MAJORMINOR) -lgstfft-$(GST_MAJORMINOR) \ + $(GST_BASE_LIBS) $(GST_CONTROLLER_LIBS) $(GST_LIBS) $(LIBM) +libgstaudiovisualizers_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstaudiovisualizers_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstbasescope.h \ + gstspectrascope.h gstsynaescope.h gstwavescope.h + +Android.mk: Makefile.am $(BUILT_SOURCES) + androgenizer \ + -:PROJECT audiovisualizers -:SHARED audiovisualizers \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstaudiovisualizers_la_SOURCES) \ + -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstaudiovisualizers_la_CFLAGS) \ + -:LDFLAGS $(libgstaudiovisualizers_la_LDFLAGS) \ + $(libgstaudiovisualizers_la_LIBADD) \ + -ldl \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ + > $@ + diff --git a/gst/audiovisualizers/gstbasescope.c b/gst/audiovisualizers/gstbasescope.c new file mode 100644 index 000000000..30d5bda16 --- /dev/null +++ b/gst/audiovisualizers/gstbasescope.c @@ -0,0 +1,796 @@ +/* GStreamer + * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net> + * + * gstbasescope.h: base class for audio visualisation elements + * + * 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:gstbasescope + * + * A basclass for scopes. Takes care of re-fitting the audio-rate to video-rate. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <string.h> +#include <gst/controller/gstcontroller.h> + +#include "gstbasescope.h" + +GST_DEBUG_CATEGORY_STATIC (base_scope_debug); +#define GST_CAT_DEFAULT (base_scope_debug) + +#define DEFAULT_SHADER GST_BASE_SCOPE_SHADER_FADE +#define DEFAULT_SHADE_AMOUNT 0x000a0a0a + +enum +{ + PROP_0, + PROP_SHADER, + PROP_SHADE_AMOUNT +}; + +static GstBaseTransformClass *parent_class = NULL; + +static void gst_base_scope_class_init (GstBaseScopeClass * klass); +static void gst_base_scope_init (GstBaseScope * scope, + GstBaseScopeClass * g_class); +static void gst_base_scope_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_base_scope_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_base_scope_dispose (GObject * object); + +static gboolean gst_base_scope_src_negotiate (GstBaseScope * scope); +static gboolean gst_base_scope_src_setcaps (GstPad * pad, GstCaps * caps); +static gboolean gst_base_scope_sink_setcaps (GstPad * pad, GstCaps * caps); + +static GstFlowReturn gst_base_scope_chain (GstPad * pad, GstBuffer * buffer); +static GstStateChangeReturn gst_base_scope_change_state (GstElement * element, + GstStateChange transition); + +/* shading functions */ + +#define GST_TYPE_BASE_SCOPE_SHADER (gst_base_scope_shader_get_type()) +static GType +gst_base_scope_shader_get_type (void) +{ + static GType shader_type = 0; + static const GEnumValue shaders[] = { + {GST_BASE_SCOPE_SHADER_NONE, "None", "none"}, + {GST_BASE_SCOPE_SHADER_FADE, "Fade", "fade"}, + {GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_UP, "Fade and move up", + "fade-and-move-up"}, + {GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_DOWN, "Fade and move down", + "fade-and-move-down"}, + {GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_HORIZ_OUT, + "Fade and move horizontaly out", + "fade-and-move-horiz-out"}, + {0, NULL, NULL}, + }; + + if (G_UNLIKELY (shader_type == 0)) { + shader_type = g_enum_register_static ("GstBaseScopeShader", shaders); + } + return shader_type; +} + +static void +shader_fade (GstBaseScope * scope, const guint8 * s, guint8 * d) +{ + guint i, bpf = scope->bpf; + guint r = (scope->shade_amount >> 16) & 0xff; + guint g = (scope->shade_amount >> 8) & 0xff; + guint b = (scope->shade_amount >> 0) & 0xff; + + /* we're only supporting GST_VIDEO_FORMAT_xRGB right now) */ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + for (i = 0; i < bpf;) { + d[i] = (s[i] > b) ? s[i] - b : 0; + i++; + d[i] = (s[i] > g) ? s[i] - g : 0; + i++; + d[i] = (s[i] > r) ? s[i] - r : 0; + i++; + d[i++] = 0; + } +#else + for (i = 0; i < bpf;) { + d[i++] = 0; + d[i] = (s[i] > r) ? s[i] - r : 0; + i++; + d[i] = (s[i] > g) ? s[i] - g : 0; + i++; + d[i] = (s[i] > b) ? s[i] - b : 0; + i++; + } +#endif +} + +static void +shader_fade_and_move_up (GstBaseScope * scope, const guint8 * s, guint8 * d) +{ + guint i, j, bpf = scope->bpf; + guint bpl = 4 * scope->width; + guint r = (scope->shade_amount >> 16) & 0xff; + guint g = (scope->shade_amount >> 8) & 0xff; + guint b = (scope->shade_amount >> 0) & 0xff; + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + for (j = 0, i = bpl; i < bpf;) { + d[j++] = (s[i] > b) ? s[i] - b : 0; + i++; + d[j++] = (s[i] > g) ? s[i] - g : 0; + i++; + d[j++] = (s[i] > r) ? s[i] - r : 0; + i++; + d[j++] = 0; + i++; + } + for (i = 0; i < bpl; i += 4) { + d[j] = (s[j] > b) ? s[j] - b : 0; + j++; + d[j] = (s[j] > g) ? s[j] - g : 0; + j++; + d[j] = (s[j] > r) ? s[j] - r : 0; + j++; + d[j++] = 0; + } +#else + for (j = 0, i = bpl; i < bpf;) { + d[j++] = 0; + i++; + d[j++] = (s[i] > r) ? s[i] - r : 0; + i++; + d[j++] = (s[i] > g) ? s[i] - g : 0; + i++; + d[j++] = (s[i] > b) ? s[i] - b : 0; + i++; + } + for (i = 0; i < bpl; i += 4) { + d[j++] = 0; + d[j] = (s[j] > r) ? s[j] - r : 0; + j++; + d[j] = (s[j] > g) ? s[j] - g : 0; + j++; + d[j] = (s[j] > b) ? s[j] - b : 0; + j++; + } +#endif +} + +static void +shader_fade_and_move_down (GstBaseScope * scope, const guint8 * s, guint8 * d) +{ + guint i, j, bpf = scope->bpf; + guint bpl = 4 * scope->width; + guint r = (scope->shade_amount >> 16) & 0xff; + guint g = (scope->shade_amount >> 8) & 0xff; + guint b = (scope->shade_amount >> 0) & 0xff; + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + for (i = 0; i < bpl;) { + d[i] = (s[i] > b) ? s[i] - b : 0; + i++; + d[i] = (s[i] > g) ? s[i] - g : 0; + i++; + d[i] = (s[i] > r) ? s[i] - r : 0; + i++; + d[i++] = 0; + } + for (j = bpl, i = 0; j < bpf;) { + d[j++] = (s[i] > b) ? s[i] - b : 0; + i++; + d[j++] = (s[i] > g) ? s[i] - g : 0; + i++; + d[j++] = (s[i] > r) ? s[i] - r : 0; + i++; + d[j++] = 0; + i++; + } +#else + for (i = 0; i < bpl;) { + d[i++] = 0; + d[i] = (s[i] > r) ? s[i] - r : 0; + i++; + d[i] = (s[i] > g) ? s[i] - g : 0; + i++; + d[i] = (s[i] > b) ? s[i] - b : 0; + i++; + } + for (j = bpl, i = 0; j < bpf;) { + d[j++] = 0; + i++; + d[j++] = (s[i] > r) ? s[i] - r : 0; + i++; + d[j++] = (s[i] > g) ? s[i] - g : 0; + i++; + d[j++] = (s[i] > b) ? s[i] - b : 0; + i++; + } +#endif +} + +static void +shader_fade_and_move_horiz_out (GstBaseScope * scope, const guint8 * s, + guint8 * d) +{ + guint i, j, bpf = scope->bpf / 2; + guint bpl = 4 * scope->width; + guint r = (scope->shade_amount >> 16) & 0xff; + guint g = (scope->shade_amount >> 8) & 0xff; + guint b = (scope->shade_amount >> 0) & 0xff; + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + /* middle up */ + for (j = 0, i = bpl; i < bpf;) { + d[j++] = (s[i] > b) ? s[i] - b : 0; + i++; + d[j++] = (s[i] > g) ? s[i] - g : 0; + i++; + d[j++] = (s[i] > r) ? s[i] - r : 0; + i++; + d[j++] = 0; + i++; + } + for (i = 0; i < bpl; i += 4) { + d[j] = (s[j] > b) ? s[j] - b : 0; + j++; + d[j] = (s[j] > g) ? s[j] - g : 0; + j++; + d[j] = (s[j] > r) ? s[j] - r : 0; + j++; + d[j++] = 0; + } + /* middle down */ + for (i = bpf; i < bpf + bpl;) { + d[i] = (s[i] > b) ? s[i] - b : 0; + i++; + d[i] = (s[i] > g) ? s[i] - g : 0; + i++; + d[i] = (s[i] > r) ? s[i] - r : 0; + i++; + d[i++] = 0; + } + for (j = bpf + bpl, i = bpf; j < bpf + bpf;) { + d[j++] = (s[i] > b) ? s[i] - b : 0; + i++; + d[j++] = (s[i] > g) ? s[i] - g : 0; + i++; + d[j++] = (s[i] > r) ? s[i] - r : 0; + i++; + d[j++] = 0; + i++; + } +#else + /* middle up */ + for (j = 0, i = bpl; i < bpf;) { + d[j++] = 0; + i++; + d[j++] = (s[i] > r) ? s[i] - r : 0; + i++; + d[j++] = (s[i] > g) ? s[i] - g : 0; + i++; + d[j++] = (s[i] > b) ? s[i] - b : 0; + i++; + } + for (i = 0; i < bpl; i += 4) { + d[j++] = 0; + d[j] = (s[j] > r) ? s[j] - r : 0; + j++; + d[j] = (s[j] > g) ? s[j] - g : 0; + j++; + d[j] = (s[j] > b) ? s[j] - b : 0; + j++; + } + /* middle down */ + for (i = bpf; i < bpf + bpl;) { + d[i++] = 0; + d[i] = (s[i] > r) ? s[i] - r : 0; + i++; + d[i] = (s[i] > g) ? s[i] - g : 0; + i++; + d[i] = (s[i] > b) ? s[i] - b : 0; + i++; + } + for (j = bpf + bpl, i = bpf; j < bpf + bpf;) { + d[j++] = 0; + i++; + d[j++] = (s[i] > r) ? s[i] - r : 0; + i++; + d[j++] = (s[i] > g) ? s[i] - g : 0; + i++; + d[j++] = (s[i] > b) ? s[i] - b : 0; + i++; + } +#endif +} + + +static void +gst_base_scope_change_shader (GstBaseScope * scope) +{ + switch (scope->shader_type) { + case GST_BASE_SCOPE_SHADER_NONE: + scope->shader = NULL; + break; + case GST_BASE_SCOPE_SHADER_FADE: + scope->shader = shader_fade; + break; + case GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_UP: + scope->shader = shader_fade_and_move_up; + break; + case GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_DOWN: + scope->shader = shader_fade_and_move_down; + break; + case GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_HORIZ_OUT: + scope->shader = shader_fade_and_move_horiz_out; + break; + default: + GST_ERROR ("invalid shader function"); + scope->shader = NULL; + break; + } +} + +/* base class */ + +GType +gst_base_scope_get_type (void) +{ + static volatile gsize base_scope_type = 0; + + if (g_once_init_enter (&base_scope_type)) { + static const GTypeInfo base_scope_info = { + sizeof (GstBaseScopeClass), + NULL, + NULL, + (GClassInitFunc) gst_base_scope_class_init, + NULL, + NULL, + sizeof (GstBaseScope), + 0, + (GInstanceInitFunc) gst_base_scope_init, + }; + GType _type; + + _type = g_type_register_static (GST_TYPE_ELEMENT, + "GstBaseScope", &base_scope_info, G_TYPE_FLAG_ABSTRACT); + g_once_init_leave (&base_scope_type, _type); + } + return (GType) base_scope_type; +} + +static void +gst_base_scope_class_init (GstBaseScopeClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *element_class = (GstElementClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + GST_DEBUG_CATEGORY_INIT (base_scope_debug, "basescope", 0, + "scope audio visualisation base class"); + + gobject_class->set_property = gst_base_scope_set_property; + gobject_class->get_property = gst_base_scope_get_property; + gobject_class->dispose = gst_base_scope_dispose; + + element_class->change_state = GST_DEBUG_FUNCPTR (gst_base_scope_change_state); + + g_object_class_install_property (gobject_class, PROP_SHADER, + g_param_spec_enum ("shader", "shader type", + "Shader function to apply on each frame", GST_TYPE_BASE_SCOPE_SHADER, + DEFAULT_SHADER, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_SHADE_AMOUNT, + g_param_spec_uint ("shade-amount", "shade amount", + "Shading color to use (big-endian ARGB)", 0, G_MAXUINT32, + DEFAULT_SHADE_AMOUNT, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_base_scope_init (GstBaseScope * scope, GstBaseScopeClass * g_class) +{ + GstPadTemplate *pad_template; + + /* create the sink and src pads */ + pad_template = + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink"); + g_return_if_fail (pad_template != NULL); + scope->sinkpad = gst_pad_new_from_template (pad_template, "sink"); + gst_pad_set_chain_function (scope->sinkpad, + GST_DEBUG_FUNCPTR (gst_base_scope_chain)); + gst_pad_set_setcaps_function (scope->sinkpad, + GST_DEBUG_FUNCPTR (gst_base_scope_sink_setcaps)); + gst_element_add_pad (GST_ELEMENT (scope), scope->sinkpad); + + pad_template = + gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src"); + g_return_if_fail (pad_template != NULL); + scope->srcpad = gst_pad_new_from_template (pad_template, "src"); + gst_pad_set_setcaps_function (scope->srcpad, + GST_DEBUG_FUNCPTR (gst_base_scope_src_setcaps)); + gst_element_add_pad (GST_ELEMENT (scope), scope->srcpad); + + scope->adapter = gst_adapter_new (); + scope->inbuf = gst_buffer_new (); + + /* properties */ + scope->shader_type = DEFAULT_SHADER; + gst_base_scope_change_shader (scope); + scope->shade_amount = DEFAULT_SHADE_AMOUNT; + + /* reset the initial video state */ + scope->width = 320; + scope->height = 200; + scope->fps_n = 25; /* desired frame rate */ + scope->fps_d = 1; + scope->frame_duration = GST_CLOCK_TIME_NONE; + + /* reset the initial audio state */ + scope->rate = GST_AUDIO_DEF_RATE; + scope->channels = 2; + + scope->next_ts = GST_CLOCK_TIME_NONE; + +} + +static void +gst_base_scope_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstBaseScope *scope = GST_BASE_SCOPE (object); + + switch (prop_id) { + case PROP_SHADER: + scope->shader_type = g_value_get_enum (value); + gst_base_scope_change_shader (scope); + break; + case PROP_SHADE_AMOUNT: + scope->shade_amount = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_base_scope_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstBaseScope *scope = GST_BASE_SCOPE (object); + + switch (prop_id) { + case PROP_SHADER: + g_value_set_enum (value, scope->shader_type); + break; + case PROP_SHADE_AMOUNT: + g_value_set_uint (value, scope->shade_amount); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_base_scope_dispose (GObject * object) +{ + GstBaseScope *scope = GST_BASE_SCOPE (object); + + if (scope->adapter) { + g_object_unref (scope->adapter); + scope->adapter = NULL; + } + if (scope->inbuf) { + gst_buffer_unref (scope->inbuf); + scope->inbuf = NULL; + } + if (scope->pixelbuf) { + g_free (scope->pixelbuf); + scope->pixelbuf = NULL; + } + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static gboolean +gst_base_scope_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstBaseScope *scope; + GstStructure *structure; + gint channels; + gint rate; + gboolean res = TRUE; + + scope = GST_BASE_SCOPE (gst_pad_get_parent (pad)); + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "channels", &channels) || + !gst_structure_get_int (structure, "rate", &rate)) + goto missing_caps_details; + + if (channels != 2) + goto wrong_channels; + + if (rate <= 0) + goto wrong_rate; + + scope->channels = channels; + scope->rate = rate; + + GST_DEBUG_OBJECT (scope, "audio: channels %d, rate %d", + scope->channels, scope->rate); + +done: + gst_object_unref (scope); + return res; + + /* Errors */ +missing_caps_details: + { + GST_WARNING_OBJECT (scope, "missing channels or rate in the caps"); + res = FALSE; + goto done; + } +wrong_channels: + { + GST_WARNING_OBJECT (scope, "number of channels must be 2, but is %d", + channels); + res = FALSE; + goto done; + } +wrong_rate: + { + GST_WARNING_OBJECT (scope, "sample rate must be >0, but is %d", rate); + res = FALSE; + goto done; + } +} + +static gboolean +gst_base_scope_src_negotiate (GstBaseScope * scope) +{ + GstCaps *othercaps, *target, *intersect; + GstStructure *structure; + const GstCaps *templ; + + templ = gst_pad_get_pad_template_caps (scope->srcpad); + + GST_DEBUG_OBJECT (scope, "performing negotiation"); + + /* see what the peer can do */ + othercaps = gst_pad_peer_get_caps (scope->srcpad); + if (othercaps) { + intersect = gst_caps_intersect (othercaps, templ); + gst_caps_unref (othercaps); + + if (gst_caps_is_empty (intersect)) + goto no_format; + + target = gst_caps_copy_nth (intersect, 0); + gst_caps_unref (intersect); + } else { + target = gst_caps_ref ((GstCaps *) templ); + } + + structure = gst_caps_get_structure (target, 0); + gst_structure_fixate_field_nearest_int (structure, "width", scope->width); + gst_structure_fixate_field_nearest_int (structure, "height", scope->height); + gst_structure_fixate_field_nearest_fraction (structure, "framerate", + scope->fps_n, scope->fps_d); + + GST_DEBUG_OBJECT (scope, "final caps are %" GST_PTR_FORMAT, target); + + gst_pad_set_caps (scope->srcpad, target); + gst_caps_unref (target); + + return TRUE; + +no_format: + { + gst_caps_unref (intersect); + return FALSE; + } +} + +static gboolean +gst_base_scope_src_setcaps (GstPad * pad, GstCaps * caps) +{ + GstBaseScope *scope; + GstBaseScopeClass *klass; + gint w, h; + gint num, denom; + GstVideoFormat format; + gboolean res = TRUE; + + scope = GST_BASE_SCOPE (gst_pad_get_parent (pad)); + klass = GST_BASE_SCOPE_CLASS (G_OBJECT_GET_CLASS (scope)); + + if (!gst_video_format_parse_caps (caps, &format, &w, &h)) { + goto missing_caps_details; + } + if (!gst_video_parse_caps_framerate (caps, &num, &denom)) { + goto missing_caps_details; + } + + scope->width = w; + scope->height = h; + scope->fps_n = num; + scope->fps_d = denom; + scope->video_format = format; + + scope->frame_duration = gst_util_uint64_scale_int (GST_SECOND, + scope->fps_d, scope->fps_n); + scope->spf = gst_util_uint64_scale_int (scope->rate, + scope->fps_d, scope->fps_n); + scope->req_spf = scope->spf; + + scope->bpf = w * h * 4; + + if (scope->pixelbuf) + g_free (scope->pixelbuf); + scope->pixelbuf = g_malloc0 (scope->bpf); + + if (klass->setup) + res = klass->setup (scope); + + GST_DEBUG_OBJECT (scope, "video: dimension %dx%d, framerate %d/%d", + scope->width, scope->height, scope->fps_n, scope->fps_d); + GST_DEBUG_OBJECT (scope, "blocks: spf %u, req_spf %u", + scope->spf, scope->req_spf); +done: + gst_object_unref (scope); + return res; + + /* Errors */ +missing_caps_details: + { + GST_WARNING_OBJECT (scope, + "missing width, height or framerate in the caps"); + res = FALSE; + goto done; + } +} + +static GstFlowReturn +gst_base_scope_chain (GstPad * pad, GstBuffer * buffer) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstBaseScope *scope; + GstBaseScopeClass *klass; + GstBuffer *inbuf; + guint avail, sbpf; + gboolean (*render) (GstBaseScope * scope, GstBuffer * audio, + GstBuffer * video); + + scope = GST_BASE_SCOPE (gst_pad_get_parent (pad)); + klass = GST_BASE_SCOPE_CLASS (G_OBJECT_GET_CLASS (scope)); + + render = klass->render; + + GST_LOG_OBJECT (scope, "chainfunc called"); + + /* resync on DISCONT */ + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) { + scope->next_ts = GST_CLOCK_TIME_NONE; + gst_adapter_clear (scope->adapter); + } + + if (GST_PAD_CAPS (scope->srcpad) == NULL) { + if (!gst_base_scope_src_negotiate (scope)) + return GST_FLOW_NOT_NEGOTIATED; + } + + /* Match timestamps from the incoming audio */ + if (GST_BUFFER_TIMESTAMP (buffer) != GST_CLOCK_TIME_NONE) + scope->next_ts = GST_BUFFER_TIMESTAMP (buffer); + + gst_adapter_push (scope->adapter, buffer); + + /* this is what we want */ + sbpf = scope->req_spf * scope->channels * sizeof (gint16); + + inbuf = scope->inbuf; + /* FIXME: the timestamp in the adapter would be different */ + gst_buffer_copy_metadata (inbuf, buffer, GST_BUFFER_COPY_ALL); + + /* this is what we have */ + avail = gst_adapter_available (scope->adapter); + while (avail > sbpf) { + GstBuffer *outbuf; + + ret = gst_pad_alloc_buffer_and_set_caps (scope->srcpad, + GST_BUFFER_OFFSET_NONE, + scope->bpf, GST_PAD_CAPS (scope->srcpad), &outbuf); + + /* no buffer allocated, we don't care why. */ + if (ret != GST_FLOW_OK) + break; + + /* sync controlled properties */ + gst_object_sync_values (G_OBJECT (scope), scope->next_ts); + + GST_BUFFER_TIMESTAMP (outbuf) = scope->next_ts; + GST_BUFFER_DURATION (outbuf) = scope->frame_duration; + if (scope->shader) { + memcpy (GST_BUFFER_DATA (outbuf), scope->pixelbuf, scope->bpf); + } else { + memset (GST_BUFFER_DATA (outbuf), 0, scope->bpf); + } + + GST_BUFFER_DATA (inbuf) = + (guint8 *) gst_adapter_peek (scope->adapter, sbpf); + GST_BUFFER_SIZE (inbuf) = sbpf; + + /* call class->render() vmethod */ + if (render) { + if (!render (scope, inbuf, outbuf)) { + ret = GST_FLOW_ERROR; + } else { + /* run various post processing (shading and geometri transformation */ + if (scope->shader) { + scope->shader (scope, GST_BUFFER_DATA (outbuf), scope->pixelbuf); + } + } + } + + ret = gst_pad_push (scope->srcpad, outbuf); + outbuf = NULL; + + GST_LOG_OBJECT (scope, "avail: %u, bpf: %u", avail, sbpf); + /* we want to take less or more, depending on spf : req_spf */ + if (avail - sbpf > sbpf) + gst_adapter_flush (scope->adapter, sbpf); + else if (avail - sbpf > 0) + gst_adapter_flush (scope->adapter, (avail - sbpf)); + avail = gst_adapter_available (scope->adapter); + + if (ret != GST_FLOW_OK) + break; + + if (scope->next_ts != GST_CLOCK_TIME_NONE) + scope->next_ts += scope->frame_duration; + + avail = gst_adapter_available (scope->adapter); + } + + gst_object_unref (scope); + + return ret; +} + +static GstStateChangeReturn +gst_base_scope_change_state (GstElement * element, GstStateChange transition) +{ + GstBaseScope *scope; + + scope = GST_BASE_SCOPE (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + scope->next_ts = GST_CLOCK_TIME_NONE; + gst_adapter_clear (scope->adapter); + break; + default: + break; + } + + return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); +} diff --git a/gst/audiovisualizers/gstbasescope.h b/gst/audiovisualizers/gstbasescope.h new file mode 100644 index 000000000..b37c36184 --- /dev/null +++ b/gst/audiovisualizers/gstbasescope.h @@ -0,0 +1,108 @@ +/* GStreamer + * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net> + * + * gstbasescope.c: base class for audio visualisation elements + * + * 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_BASE_SCOPE_H__ +#define __GST_BASE_SCOPE_H__ + +#include <gst/gst.h> +#include <gst/base/gstbasetransform.h> + +#include <gst/video/video.h> +#include <gst/audio/audio.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS +#define GST_TYPE_BASE_SCOPE (gst_base_scope_get_type()) +#define GST_BASE_SCOPE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASE_SCOPE,GstBaseScope)) +#define GST_BASE_SCOPE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASE_SCOPE,GstBaseScopeClass)) +#define GST_IS_SYNAESTHESIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASE_SCOPE)) +#define GST_IS_SYNAESTHESIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASE_SCOPE)) +typedef struct _GstBaseScope GstBaseScope; +typedef struct _GstBaseScopeClass GstBaseScopeClass; + +typedef void (*GstBaseScopeShaderFunc)(GstBaseScope *scope, const guint8 *s, guint8 *d); + +/** + * GstBaseScopeShader: + * @GST_BASE_SCOPE_SHADER_NONE: no shading + * @GST_BASE_SCOPE_SHADER_FADE: plain fading + * @GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_UP: fade and move up + * @GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_DOWN: fade and move down + * @GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_HORIZ_OUT: fade and move horizontaly out + * + * Different types of supported background shading functions. + */ +typedef enum { + GST_BASE_SCOPE_SHADER_NONE, + GST_BASE_SCOPE_SHADER_FADE, + GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_UP, + GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_DOWN, + GST_BASE_SCOPE_SHADER_FADE_AND_MOVE_HORIZ_OUT +} GstBaseScopeShader; + +struct _GstBaseScope +{ + GstElement parent; + + /* pads */ + GstPad *srcpad, *sinkpad; + + GstAdapter *adapter; + GstBuffer *inbuf; + guint8 *pixelbuf; + + GstBaseScopeShader shader_type; + GstBaseScopeShaderFunc shader; + guint32 shade_amount; + + guint64 next_ts; /* the timestamp of the next frame */ + guint64 frame_duration; + guint bpf; /* bytes per frame */ + guint bps; /* bytes per sample */ + guint spf; /* samples per video frame */ + guint req_spf; /* min samples per frame wanted by the subclass */ + + /* video state */ + GstVideoFormat video_format; + gint fps_n, fps_d; + gint width; + gint height; + gint channels; + + /* audio state */ + gint sample_rate; + gint rate; +}; + +struct _GstBaseScopeClass +{ + GstElementClass parent_class; + + /* virtual function, called whenever the format changes */ + gboolean (*setup) (GstBaseScope * scope); + + /* virtual function for rendering a frame */ + gboolean (*render) (GstBaseScope * scope, GstBuffer * audio, GstBuffer * video); +}; + +GType gst_base_scope_get_type (void); + +G_END_DECLS +#endif /* __GST_BASE_SCOPE_H__ */ diff --git a/gst/audiovisualizers/gstspectrascope.c b/gst/audiovisualizers/gstspectrascope.c new file mode 100644 index 000000000..2335e9f6f --- /dev/null +++ b/gst/audiovisualizers/gstspectrascope.c @@ -0,0 +1,221 @@ +/* GStreamer + * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net> + * + * gstspectrascope.c: frequency spectrum scope + * + * 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-spectrascope + * @see_also: goom + * + * Wavescope is a simple audio visualisation element. It renders the waveforms + * like on an oscilloscope. + * + * <refsect2> + * <title>Example launch line</title> + * |[ + * gst-launch audiotestsrc ! audioconvert ! spectrascope ! ximagesink + * ]| + * </refsect2> + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <stdlib.h> + +#include "gstspectrascope.h" + +static GstStaticPadTemplate gst_spectra_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_spectra_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 (spectra_scope_debug); +#define GST_CAT_DEFAULT spectra_scope_debug + +static void gst_spectra_scope_finalize (GObject * object); + +static gboolean gst_spectra_scope_setup (GstBaseScope * scope); +static gboolean gst_spectra_scope_render (GstBaseScope * scope, + GstBuffer * audio, GstBuffer * video); + + +GST_BOILERPLATE (GstSpectraScope, gst_spectra_scope, GstBaseScope, + GST_TYPE_BASE_SCOPE); + +static void +gst_spectra_scope_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details_simple (element_class, + "Frequency spectrum scope", "Visualization", + "Simple frequency spectrum scope", "Stefan Kost <ensonic@users.sf.net>"); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_spectra_scope_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_spectra_scope_sink_template)); +} + +static void +gst_spectra_scope_class_init (GstSpectraScopeClass * g_class) +{ + GObjectClass *gobject_class = (GObjectClass *) g_class; + GstBaseScopeClass *scope_class = (GstBaseScopeClass *) g_class; + + gobject_class->finalize = gst_spectra_scope_finalize; + + scope_class->setup = GST_DEBUG_FUNCPTR (gst_spectra_scope_setup); + scope_class->render = GST_DEBUG_FUNCPTR (gst_spectra_scope_render); +} + +static void +gst_spectra_scope_init (GstSpectraScope * scope, GstSpectraScopeClass * g_class) +{ + /* do nothing */ +} + +static void +gst_spectra_scope_finalize (GObject * object) +{ + GstSpectraScope *scope = GST_SPECTRA_SCOPE (object); + + if (scope->fft_ctx) { + gst_fft_s16_free (scope->fft_ctx); + scope->fft_ctx = NULL; + } + if (scope->freq_data) { + g_free (scope->freq_data); + scope->freq_data = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_spectra_scope_setup (GstBaseScope * bscope) +{ + GstSpectraScope *scope = GST_SPECTRA_SCOPE (bscope); + guint num_freq = bscope->width + 1; + + if (scope->fft_ctx) + gst_fft_s16_free (scope->fft_ctx); + g_free (scope->freq_data); + + /* 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 = g_new (GstFFTS16Complex, num_freq); + + 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_spectra_scope_render (GstBaseScope * bscope, GstBuffer * audio, + GstBuffer * video) +{ + GstSpectraScope *scope = GST_SPECTRA_SCOPE (bscope); + guint32 *vdata = (guint32 *) GST_BUFFER_DATA (video); + gint16 *adata = (gint16 *) GST_BUFFER_DATA (audio); + GstFFTS16Complex *fdata = scope->freq_data; + guint x, y, off; + guint l, h = bscope->height - 1; + gfloat fr, fi; + guint w = bscope->width; + + if (bscope->channels > 1) { + guint ch = bscope->channels; + guint num_samples = GST_BUFFER_SIZE (audio) / (ch * sizeof (gint16)); + guint i, c, v, s = 0; + + /* deinterleave and mixdown adata */ + for (i = 0; i < num_samples; i++) { + v = 0; + for (c = 0; c < ch; c++) { + v += adata[s++]; + } + adata[i] = v / ch; + } + } + + /* run fft */ + gst_fft_s16_window (scope->fft_ctx, adata, GST_FFT_WINDOW_HAMMING); + gst_fft_s16_fft (scope->fft_ctx, adata, fdata); + + /* draw lines */ + for (x = 0; x < bscope->width; x++) { + /* figure out the range so that we don't need to clip, + * or even better do a log mapping? */ + fr = (gfloat) fdata[1 + x].r / 512.0; + fi = (gfloat) fdata[1 + x].i / 512.0; + y = (guint) (h * fabs (fr * fr + fi * fi)); + if (y > h) + y = h; + y = h - y; + off = (y * w) + x; + vdata[off] = 0x00FFFFFF; + for (l = y + 1; l <= h; l++) { + off += w; + add_pixel (&vdata[off], 0x007F7F7F); + } + } + return TRUE; +} + +gboolean +gst_spectra_scope_plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (spectra_scope_debug, "spectrascope", 0, + "spectrascope"); + + return gst_element_register (plugin, "spectrascope", GST_RANK_NONE, + GST_TYPE_SPECTRA_SCOPE); +} diff --git a/gst/audiovisualizers/gstspectrascope.h b/gst/audiovisualizers/gstspectrascope.h new file mode 100644 index 000000000..d3aafca76 --- /dev/null +++ b/gst/audiovisualizers/gstspectrascope.h @@ -0,0 +1,54 @@ +/* GStreamer + * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net> + * + * gstspectrascope.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_SPECTRA_SCOPE_H__ +#define __GST_SPECTRA_SCOPE_H__ + +#include "gstbasescope.h" +#include <gst/fft/gstffts16.h> + +G_BEGIN_DECLS +#define GST_TYPE_SPECTRA_SCOPE (gst_spectra_scope_get_type()) +#define GST_SPECTRA_SCOPE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPECTRA_SCOPE,GstSpectraScope)) +#define GST_SPECTRA_SCOPE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPECTRA_SCOPE,GstSpectraScopeClass)) +#define GST_IS_SPECTRA_SCOPE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPECTRA_SCOPE)) +#define GST_IS_SPECTRA_SCOPE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPECTRA_SCOPE)) +typedef struct _GstSpectraScope GstSpectraScope; +typedef struct _GstSpectraScopeClass GstSpectraScopeClass; + +struct _GstSpectraScope +{ + GstBaseScope parent; + + GstFFTS16 *fft_ctx; + GstFFTS16Complex *freq_data; +}; + +struct _GstSpectraScopeClass +{ + GstBaseScopeClass parent_class; +}; + +GType gst_spectra_scope_get_type (void); +gboolean gst_spectra_scope_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_SPECTRA_SCOPE_H__ */
\ No newline at end of file diff --git a/gst/audiovisualizers/gstsynaescope.c b/gst/audiovisualizers/gstsynaescope.c new file mode 100644 index 000000000..674b07e29 --- /dev/null +++ b/gst/audiovisualizers/gstsynaescope.c @@ -0,0 +1,300 @@ +/* GStreamer + * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net> + * + * gstsynaescope.c: frequency spectrum scope + * + * 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. + * + * <refsect2> + * <title>Example launch line</title> + * |[ + * gst-launch audiotestsrc ! audioconvert ! synaescope ! ximagesink + * ]| + * </refsect2> + */ +#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, "Synaescope", + "Visualization", + "Creates video visualizations of audio input, using stereo and pitch information", + "Stefan Kost <ensonic@users.sf.net>"); + + 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/audiovisualizers/gstsynaescope.h b/gst/audiovisualizers/gstsynaescope.h new file mode 100644 index 000000000..09c18c32b --- /dev/null +++ b/gst/audiovisualizers/gstsynaescope.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net> + * + * 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 <gst/fft/gstffts16.h> + +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/audiovisualizers/gstwavescope.c b/gst/audiovisualizers/gstwavescope.c new file mode 100644 index 000000000..b009e334d --- /dev/null +++ b/gst/audiovisualizers/gstwavescope.c @@ -0,0 +1,138 @@ +/* GStreamer + * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net> + * + * gstwavescope.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-wavescope + * @see_also: goom + * + * Wavescope is a simple audio visualisation element. It renders the waveforms + * like on an oscilloscope. + * + * <refsect2> + * <title>Example launch line</title> + * |[ + * gst-launch audiotestsrc ! audioconvert ! wavescope ! ximagesink + * ]| + * </refsect2> + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstwavescope.h" + +static GstStaticPadTemplate gst_wave_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_wave_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 (wave_scope_debug); +#define GST_CAT_DEFAULT wave_scope_debug + +static gboolean gst_wave_scope_setup (GstBaseScope * scope); +static gboolean gst_wave_scope_render (GstBaseScope * scope, GstBuffer * audio, + GstBuffer * video); + + +GST_BOILERPLATE (GstWaveScope, gst_wave_scope, GstBaseScope, + GST_TYPE_BASE_SCOPE); + +static void +gst_wave_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 <ensonic@users.sf.net>"); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_wave_scope_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_wave_scope_sink_template)); +} + +static void +gst_wave_scope_class_init (GstWaveScopeClass * g_class) +{ + /*GObjectClass *gobject_class = (GObjectClass *) g_class; */ + GstBaseScopeClass *scope_class = (GstBaseScopeClass *) g_class; + + scope_class->setup = GST_DEBUG_FUNCPTR (gst_wave_scope_setup); + scope_class->render = GST_DEBUG_FUNCPTR (gst_wave_scope_render); +} + +static void +gst_wave_scope_init (GstWaveScope * scope, GstWaveScopeClass * g_class) +{ + /* do nothing */ +} + +static gboolean +gst_wave_scope_setup (GstBaseScope * scope) +{ + return TRUE; +} + +static gboolean +gst_wave_scope_render (GstBaseScope * scope, GstBuffer * audio, + GstBuffer * video) +{ + guint32 *vdata = (guint32 *) GST_BUFFER_DATA (video); + gint16 *adata = (gint16 *) GST_BUFFER_DATA (audio); + guint i, c, s, x, y, off, oy; + guint num_samples; + gfloat dx, dy; + guint w = scope->width; + + /* draw dots */ + num_samples = GST_BUFFER_SIZE (audio) / (scope->channels * sizeof (gint16)); + dx = (gfloat) scope->width / (gfloat) num_samples; + dy = scope->height / 65536.0; + oy = scope->height / 2; + s = 0; + for (i = 0; i < num_samples; i++) { + x = (guint) ((gfloat) i * dx); + for (c = 0; c < scope->channels; c++) { + y = (guint) (oy + (gfloat) adata[s++] * dy); + off = (y * w) + x; + vdata[off] = 0x00FFFFFF; + } + } + return TRUE; +} + +gboolean +gst_wave_scope_plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (wave_scope_debug, "wavescope", 0, "wavescope"); + + return gst_element_register (plugin, "wavescope", GST_RANK_NONE, + GST_TYPE_WAVE_SCOPE); +} diff --git a/gst/audiovisualizers/gstwavescope.h b/gst/audiovisualizers/gstwavescope.h new file mode 100644 index 000000000..a4c734f41 --- /dev/null +++ b/gst/audiovisualizers/gstwavescope.h @@ -0,0 +1,50 @@ +/* GStreamer + * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net> + * + * gstwavescope.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_WAVE_SCOPE_H__ +#define __GST_WAVE_SCOPE_H__ + +#include "gstbasescope.h" + +G_BEGIN_DECLS +#define GST_TYPE_WAVE_SCOPE (gst_wave_scope_get_type()) +#define GST_WAVE_SCOPE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WAVE_SCOPE,GstWaveScope)) +#define GST_WAVE_SCOPE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_WAVE_SCOPE,GstWaveScopeClass)) +#define GST_IS_WAVE_SCOPE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WAVE_SCOPE)) +#define GST_IS_WAVE_SCOPE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_WAVE_SCOPE)) +typedef struct _GstWaveScope GstWaveScope; +typedef struct _GstWaveScopeClass GstWaveScopeClass; + +struct _GstWaveScope +{ + GstBaseScope parent; +}; + +struct _GstWaveScopeClass +{ + GstBaseScopeClass parent_class; +}; + +GType gst_wave_scope_get_type (void); +gboolean gst_wave_scope_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_WAVE_SCOPE_H__ */
\ No newline at end of file diff --git a/gst/audiovisualizers/plugin.c b/gst/audiovisualizers/plugin.c new file mode 100644 index 000000000..42ac8a1b7 --- /dev/null +++ b/gst/audiovisualizers/plugin.c @@ -0,0 +1,49 @@ +/* GStreamer + * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net> + * + * plugin.c: scopes plugin + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <gst/gst.h> +#include <gst/controller/gstcontroller.h> + +#include "gstspectrascope.h" +#include "gstsynaescope.h" +#include "gstwavescope.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gboolean res = TRUE; + + /* initialize gst controller library */ + 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; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "audiovisualizers", + "Creates video visualizations of audio input", + plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) |