Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / gst / audiovisualizers / gstwavescope.c
1 /* GStreamer
2  * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net>
3  *
4  * gstwavescope.c: simple oscilloscope
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 /**
21  * SECTION:element-wavescope
22  * @see_also: goom
23  *
24  * Wavescope is a simple audio visualisation element. It renders the waveforms
25  * like on an oscilloscope.
26  *
27  * <refsect2>
28  * <title>Example launch line</title>
29  * |[
30  * gst-launch audiotestsrc ! audioconvert ! wavescope ! ximagesink
31  * ]|
32  * </refsect2>
33  */
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #include <stdlib.h>
39 #include "gstwavescope.h"
40
41 static GstStaticPadTemplate gst_wave_scope_src_template =
42 GST_STATIC_PAD_TEMPLATE ("src",
43     GST_PAD_SRC,
44     GST_PAD_ALWAYS,
45     GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)
46     );
47
48 static GstStaticPadTemplate gst_wave_scope_sink_template =
49 GST_STATIC_PAD_TEMPLATE ("sink",
50     GST_PAD_SINK,
51     GST_PAD_ALWAYS,
52     GST_STATIC_CAPS (GST_AUDIO_INT_STANDARD_PAD_TEMPLATE_CAPS)
53     );
54
55
56 GST_DEBUG_CATEGORY_STATIC (wave_scope_debug);
57 #define GST_CAT_DEFAULT wave_scope_debug
58
59 enum
60 {
61   PROP_0,
62   PROP_STYLE
63 };
64
65 enum
66 {
67   STYLE_DOTS = 0,
68   STYLE_LINES,
69   STYLE_COLOR_DOTS,
70   STYLE_COLOR_LINES,
71   NUM_STYLES
72 };
73
74 #define GST_TYPE_WAVE_SCOPE_STYLE (gst_wave_scope_style_get_type ())
75 static GType
76 gst_wave_scope_style_get_type (void)
77 {
78   static GType gtype = 0;
79
80   if (gtype == 0) {
81     static const GEnumValue values[] = {
82       {STYLE_DOTS, "draw dots (default)", "dots"},
83       {STYLE_LINES, "draw lines", "lines"},
84       {STYLE_COLOR_DOTS, "draw color dots", "color-dots"},
85       {STYLE_COLOR_LINES, "draw color lines", "color-lines"},
86       {0, NULL, NULL}
87     };
88
89     gtype = g_enum_register_static ("GstWaveScopeStyle", values);
90   }
91   return gtype;
92 }
93
94 static void gst_wave_scope_set_property (GObject * object, guint prop_id,
95     const GValue * value, GParamSpec * pspec);
96 static void gst_wave_scope_get_property (GObject * object, guint prop_id,
97     GValue * value, GParamSpec * pspec);
98 static void gst_wave_scope_finalize (GObject * object);
99
100 static void render_dots (GstBaseAudioVisualizer * scope, guint32 * vdata,
101     gint16 * adata, guint num_samples);
102 static void render_lines (GstBaseAudioVisualizer * scope, guint32 * vdata,
103     gint16 * adata, guint num_samples);
104 static void render_color_dots (GstBaseAudioVisualizer * base, guint32 * vdata,
105     gint16 * adata, guint num_samples);
106 static void render_color_lines (GstBaseAudioVisualizer * base, guint32 * vdata,
107     gint16 * adata, guint num_samples);
108
109 static gboolean gst_wave_scope_setup (GstBaseAudioVisualizer * scope);
110 static gboolean gst_wave_scope_render (GstBaseAudioVisualizer * base,
111     GstBuffer * audio, GstBuffer * video);
112
113
114 GST_BOILERPLATE (GstWaveScope, gst_wave_scope, GstBaseAudioVisualizer,
115     GST_TYPE_BASE_AUDIO_VISUALIZER);
116
117 static void
118 gst_wave_scope_base_init (gpointer g_class)
119 {
120   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
121
122   gst_element_class_set_details_simple (element_class, "Waveform oscilloscope",
123       "Visualization",
124       "Simple waveform oscilloscope", "Stefan Kost <ensonic@users.sf.net>");
125
126   gst_element_class_add_static_pad_template (element_class,
127       &gst_wave_scope_src_template);
128   gst_element_class_add_static_pad_template (element_class,
129       &gst_wave_scope_sink_template);
130 }
131
132 static void
133 gst_wave_scope_class_init (GstWaveScopeClass * g_class)
134 {
135   GObjectClass *gobject_class = (GObjectClass *) g_class;
136   GstBaseAudioVisualizerClass *scope_class =
137       (GstBaseAudioVisualizerClass *) g_class;
138
139   gobject_class->set_property = gst_wave_scope_set_property;
140   gobject_class->get_property = gst_wave_scope_get_property;
141   gobject_class->finalize = gst_wave_scope_finalize;
142
143   scope_class->setup = GST_DEBUG_FUNCPTR (gst_wave_scope_setup);
144   scope_class->render = GST_DEBUG_FUNCPTR (gst_wave_scope_render);
145
146   g_object_class_install_property (gobject_class, PROP_STYLE,
147       g_param_spec_enum ("style", "drawing style",
148           "Drawing styles for the wave form display.",
149           GST_TYPE_WAVE_SCOPE_STYLE, STYLE_DOTS,
150           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151 }
152
153 static void
154 gst_wave_scope_init (GstWaveScope * scope, GstWaveScopeClass * g_class)
155 {
156   /* do nothing */
157 }
158
159 static void
160 gst_wave_scope_finalize (GObject * object)
161 {
162   GstWaveScope *scope = GST_WAVE_SCOPE (object);
163
164   if (scope->flt) {
165     g_free (scope->flt);
166     scope->flt = NULL;
167   }
168
169   G_OBJECT_CLASS (parent_class)->finalize (object);
170 }
171
172 static gboolean
173 gst_wave_scope_setup (GstBaseAudioVisualizer * bscope)
174 {
175   GstWaveScope *scope = GST_WAVE_SCOPE (bscope);
176
177   if (scope->flt)
178     g_free (scope->flt);
179
180   scope->flt = g_new0 (gdouble, 6 * bscope->channels);
181
182   return TRUE;
183 }
184
185 static void
186 gst_wave_scope_set_property (GObject * object, guint prop_id,
187     const GValue * value, GParamSpec * pspec)
188 {
189   GstWaveScope *scope = GST_WAVE_SCOPE (object);
190
191   switch (prop_id) {
192     case PROP_STYLE:
193       scope->style = g_value_get_enum (value);
194       switch (scope->style) {
195         case STYLE_DOTS:
196           scope->process = render_dots;
197           break;
198         case STYLE_LINES:
199           scope->process = render_lines;
200           break;
201         case STYLE_COLOR_DOTS:
202           scope->process = render_color_dots;
203           break;
204         case STYLE_COLOR_LINES:
205           scope->process = render_color_lines;
206           break;
207       }
208       break;
209     default:
210       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
211       break;
212   }
213 }
214
215 static void
216 gst_wave_scope_get_property (GObject * object, guint prop_id,
217     GValue * value, GParamSpec * pspec)
218 {
219   GstWaveScope *scope = GST_WAVE_SCOPE (object);
220
221   switch (prop_id) {
222     case PROP_STYLE:
223       g_value_set_enum (value, scope->style);
224       break;
225     default:
226       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
227       break;
228   }
229 }
230
231 #include "gstdrawhelpers.h"
232
233 static void
234 render_dots (GstBaseAudioVisualizer * base, guint32 * vdata, gint16 * adata,
235     guint num_samples)
236 {
237   gint channels = base->channels;
238   guint i, c, s, x, y, oy;
239   gfloat dx, dy;
240   guint w = base->width;
241   guint h = base->height;
242
243   /* draw dots */
244   dx = (gfloat) w / (gfloat) num_samples;
245   dy = h / 65536.0;
246   oy = h / 2;
247   for (c = 0; c < channels; c++) {
248     s = c;
249     for (i = 0; i < num_samples; i++) {
250       x = (guint) ((gfloat) i * dx);
251       y = (guint) (oy + (gfloat) adata[s] * dy);
252       s += channels;
253       draw_dot (vdata, x, y, w, 0x00FFFFFF);
254     }
255   }
256 }
257
258 static void
259 render_lines (GstBaseAudioVisualizer * base, guint32 * vdata, gint16 * adata,
260     guint num_samples)
261 {
262   gint channels = base->channels;
263   guint i, c, s, x, y, oy;
264   gfloat dx, dy;
265   guint w = base->width;
266   guint h = base->height;
267   gint x2, y2;
268
269   /* draw lines */
270   dx = (gfloat) (w - 1) / (gfloat) num_samples;
271   dy = (h - 1) / 65536.0;
272   oy = (h - 1) / 2;
273   for (c = 0; c < channels; c++) {
274     s = c;
275     x2 = 0;
276     y2 = (guint) (oy + (gfloat) adata[s] * dy);
277     for (i = 1; i < num_samples; i++) {
278       x = (guint) ((gfloat) i * dx);
279       y = (guint) (oy + (gfloat) adata[s] * dy);
280       s += channels;
281       draw_line_aa (vdata, x2, x, y2, y, w, 0x00FFFFFF);
282       x2 = x;
283       y2 = y;
284     }
285   }
286 }
287
288 #define CUTOFF_1 0.15
289 #define CUTOFF_2 0.45
290 #define RESONANCE (1.0/0.5)
291
292 #define filter(in) G_STMT_START {                                              \
293   flt[2] = in - (flt[1] * RESONANCE) - flt[0];                                 \
294   flt[1] += (flt[2] * CUTOFF_1);                                               \
295   flt[0] += (flt[1] * CUTOFF_1);                                               \
296                                                                                \
297   flt[5] = (flt[1] + flt[2]) - (flt[4] * RESONANCE) - flt[3];                  \
298   flt[4] += (flt[5] * CUTOFF_2);                                               \
299   flt[3] += (flt[4] * CUTOFF_2);                                               \
300 } G_STMT_END
301
302 static void
303 render_color_dots (GstBaseAudioVisualizer * base, guint32 * vdata,
304     gint16 * adata, guint num_samples)
305 {
306   GstWaveScope *scope = (GstWaveScope *) base;
307   gint channels = base->channels;
308   guint i, c, s, x, y, oy;
309   gfloat dx, dy;
310   guint w = base->width;
311   guint h = base->height, h1 = h - 2;
312   gdouble *flt = scope->flt;
313
314   /* draw dots */
315   dx = (gfloat) w / (gfloat) num_samples;
316   dy = h / 65536.0;
317   oy = h / 2;
318   for (c = 0; c < channels; c++) {
319     s = c;
320     for (i = 0; i < num_samples; i++) {
321       x = (guint) ((gfloat) i * dx);
322       filter ((gfloat) adata[s]);
323
324       y = (guint) (oy + flt[0] * dy);
325       y = CLAMP (y, 0, h1);
326       draw_dot_c (vdata, x, y, w, 0x00FF0000);
327
328       y = (guint) (oy + flt[3] * dy);
329       y = CLAMP (y, 0, h1);
330       draw_dot_c (vdata, x, y, w, 0x0000FF00);
331
332       y = (guint) (oy + (flt[4] + flt[5]) * dy);
333       y = CLAMP (y, 0, h1);
334       draw_dot_c (vdata, x, y, w, 0x000000FF);
335
336       s += channels;
337     }
338     flt += 6;
339   }
340 }
341
342 static void
343 render_color_lines (GstBaseAudioVisualizer * base, guint32 * vdata,
344     gint16 * adata, guint num_samples)
345 {
346   GstWaveScope *scope = (GstWaveScope *) base;
347   gint channels = base->channels;
348   guint i, c, s, x, y, oy;
349   gfloat dx, dy;
350   guint w = base->width;
351   guint h = base->height, h1 = h - 2;
352   gdouble *flt = scope->flt;
353   gint x2, y2, y3, y4;
354
355   /* draw lines */
356   dx = (gfloat) (w - 1) / (gfloat) num_samples;
357   dy = (h - 1) / 65536.0;
358   oy = (h - 1) / 2;
359   for (c = 0; c < channels; c++) {
360     s = c;
361
362     /* do first pixels */
363     x2 = 0;
364     filter ((gfloat) adata[s]);
365
366     y = (guint) (oy + flt[0] * dy);
367     y2 = CLAMP (y, 0, h1);
368
369     y = (guint) (oy + flt[3] * dy);
370     y3 = CLAMP (y, 0, h1);
371
372     y = (guint) (oy + (flt[4] + flt[5]) * dy);
373     y4 = CLAMP (y, 0, h1);
374
375     for (i = 1; i < num_samples; i++) {
376       x = (guint) ((gfloat) i * dx);
377       filter ((gfloat) adata[s]);
378
379       y = (guint) (oy + flt[0] * dy);
380       y = CLAMP (y, 0, h1);
381       draw_line_aa (vdata, x2, x, y2, y, w, 0x00FF0000);
382       y2 = y;
383
384       y = (guint) (oy + flt[3] * dy);
385       y = CLAMP (y, 0, h1);
386       draw_line_aa (vdata, x2, x, y3, y, w, 0x0000FF00);
387       y3 = y;
388
389       y = (guint) (oy + (flt[4] + flt[5]) * dy);
390       y = CLAMP (y, 0, h1);
391       draw_line_aa (vdata, x2, x, y4, y, w, 0x000000FF);
392       y4 = y;
393
394       x2 = x;
395       s += channels;
396     }
397     flt += 6;
398   }
399 }
400
401 static gboolean
402 gst_wave_scope_render (GstBaseAudioVisualizer * base, GstBuffer * audio,
403     GstBuffer * video)
404 {
405   GstWaveScope *scope = GST_WAVE_SCOPE (base);
406   guint32 *vdata = (guint32 *) GST_BUFFER_DATA (video);
407   gint16 *adata = (gint16 *) GST_BUFFER_DATA (audio);
408   guint num_samples;
409
410   num_samples = GST_BUFFER_SIZE (audio) / (base->channels * sizeof (gint16));
411   scope->process (base, vdata, adata, num_samples);
412   return TRUE;
413 }
414
415 gboolean
416 gst_wave_scope_plugin_init (GstPlugin * plugin)
417 {
418   GST_DEBUG_CATEGORY_INIT (wave_scope_debug, "wavescope", 0, "wavescope");
419
420   return gst_element_register (plugin, "wavescope", GST_RANK_NONE,
421       GST_TYPE_WAVE_SCOPE);
422 }