Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / gst / audiovisualizers / gstsynaescope.c
1 /* GStreamer
2  * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net>
3  *
4  * gstsynaescope.c: frequency spectrum scope
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-synaescope
22  * @see_also: goom
23  *
24  * Synaescope is an audio visualisation element. It analyzes frequencies and
25  * out-of phase properties of audio and draws this as clouds of stars.
26  *
27  * <refsect2>
28  * <title>Example launch line</title>
29  * |[
30  * gst-launch audiotestsrc ! audioconvert ! synaescope ! ximagesink
31  * ]|
32  * </refsect2>
33  */
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #include "gstsynaescope.h"
39
40 static GstStaticPadTemplate gst_synae_scope_src_template =
41 GST_STATIC_PAD_TEMPLATE ("src",
42     GST_PAD_SRC,
43     GST_PAD_ALWAYS,
44     GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)
45     );
46
47 static GstStaticPadTemplate gst_synae_scope_sink_template =
48 GST_STATIC_PAD_TEMPLATE ("sink",
49     GST_PAD_SINK,
50     GST_PAD_ALWAYS,
51     GST_STATIC_CAPS (GST_AUDIO_INT_STANDARD_PAD_TEMPLATE_CAPS)
52     );
53
54
55 GST_DEBUG_CATEGORY_STATIC (synae_scope_debug);
56 #define GST_CAT_DEFAULT synae_scope_debug
57
58 static void gst_synae_scope_finalize (GObject * object);
59
60 static gboolean gst_synae_scope_setup (GstBaseAudioVisualizer * scope);
61 static gboolean gst_synae_scope_render (GstBaseAudioVisualizer * scope,
62     GstBuffer * audio, GstBuffer * video);
63
64
65 GST_BOILERPLATE (GstSynaeScope, gst_synae_scope, GstBaseAudioVisualizer,
66     GST_TYPE_BASE_AUDIO_VISUALIZER);
67
68 static void
69 gst_synae_scope_base_init (gpointer g_class)
70 {
71   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
72
73   gst_element_class_set_details_simple (element_class, "Synaescope",
74       "Visualization",
75       "Creates video visualizations of audio input, using stereo and pitch information",
76       "Stefan Kost <ensonic@users.sf.net>");
77
78   gst_element_class_add_static_pad_template (element_class,
79       &gst_synae_scope_src_template);
80   gst_element_class_add_static_pad_template (element_class,
81       &gst_synae_scope_sink_template);
82 }
83
84 static void
85 gst_synae_scope_class_init (GstSynaeScopeClass * g_class)
86 {
87   GObjectClass *gobject_class = (GObjectClass *) g_class;
88   GstBaseAudioVisualizerClass *scope_class =
89       (GstBaseAudioVisualizerClass *) g_class;
90
91   gobject_class->finalize = gst_synae_scope_finalize;
92
93   scope_class->setup = GST_DEBUG_FUNCPTR (gst_synae_scope_setup);
94   scope_class->render = GST_DEBUG_FUNCPTR (gst_synae_scope_render);
95 }
96
97 static void
98 gst_synae_scope_init (GstSynaeScope * scope, GstSynaeScopeClass * g_class)
99 {
100   guint32 *colors = scope->colors;
101   guint *shade = scope->shade;
102   guint i, r, g, b;
103
104 #define BOUND(x) ((x) > 255 ? 255 : (x))
105 #define PEAKIFY(x) BOUND((x) - (x)*(255-(x))/255/2)
106
107   for (i = 0; i < 256; i++) {
108     r = PEAKIFY ((i & 15 * 16));
109     g = PEAKIFY ((i & 15) * 16 + (i & 15 * 16) / 4);
110     b = PEAKIFY ((i & 15) * 16);
111
112     colors[i] = (r << 16) | (g << 8) | b;
113   }
114 #undef BOUND
115 #undef PEAKIFY
116
117   for (i = 0; i < 256; i++)
118     shade[i] = i * 200 >> 8;
119 }
120
121 static void
122 gst_synae_scope_finalize (GObject * object)
123 {
124   GstSynaeScope *scope = GST_SYNAE_SCOPE (object);
125
126   if (scope->fft_ctx) {
127     gst_fft_s16_free (scope->fft_ctx);
128     scope->fft_ctx = NULL;
129   }
130   if (scope->freq_data_l) {
131     g_free (scope->freq_data_l);
132     scope->freq_data_l = NULL;
133   }
134   if (scope->freq_data_r) {
135     g_free (scope->freq_data_r);
136     scope->freq_data_r = NULL;
137   }
138   if (scope->adata_l) {
139     g_free (scope->adata_l);
140     scope->adata_l = NULL;
141   }
142   if (scope->adata_r) {
143     g_free (scope->adata_r);
144     scope->adata_r = NULL;
145   }
146
147   G_OBJECT_CLASS (parent_class)->finalize (object);
148 }
149
150 static gboolean
151 gst_synae_scope_setup (GstBaseAudioVisualizer * bscope)
152 {
153   GstSynaeScope *scope = GST_SYNAE_SCOPE (bscope);
154   guint num_freq = bscope->height + 1;
155
156   if (scope->fft_ctx)
157     gst_fft_s16_free (scope->fft_ctx);
158   g_free (scope->freq_data_l);
159   g_free (scope->freq_data_r);
160   g_free (scope->adata_l);
161   g_free (scope->adata_r);
162
163   /* FIXME: we could have horizontal or vertical layout */
164
165   /* we'd need this amount of samples per render() call */
166   bscope->req_spf = num_freq * 2 - 2;
167   scope->fft_ctx = gst_fft_s16_new (bscope->req_spf, FALSE);
168   scope->freq_data_l = g_new (GstFFTS16Complex, num_freq);
169   scope->freq_data_r = g_new (GstFFTS16Complex, num_freq);
170
171   scope->adata_l = g_new (gint16, bscope->req_spf);
172   scope->adata_r = g_new (gint16, bscope->req_spf);
173
174   return TRUE;
175 }
176
177 static inline void
178 add_pixel (guint32 * _p, guint32 _c)
179 {
180   guint8 *p = (guint8 *) _p;
181   guint8 *c = (guint8 *) & _c;
182
183   if (p[0] < 255 - c[0])
184     p[0] += c[0];
185   else
186     p[0] = 255;
187   if (p[1] < 255 - c[1])
188     p[1] += c[1];
189   else
190     p[1] = 255;
191   if (p[2] < 255 - c[2])
192     p[2] += c[2];
193   else
194     p[2] = 255;
195   if (p[3] < 255 - c[3])
196     p[3] += c[3];
197   else
198     p[3] = 255;
199 }
200
201 static gboolean
202 gst_synae_scope_render (GstBaseAudioVisualizer * bscope, GstBuffer * audio,
203     GstBuffer * video)
204 {
205   GstSynaeScope *scope = GST_SYNAE_SCOPE (bscope);
206   guint32 *vdata = (guint32 *) GST_BUFFER_DATA (video);
207   gint16 *adata = (gint16 *) GST_BUFFER_DATA (audio);
208   gint16 *adata_l = scope->adata_l;
209   gint16 *adata_r = scope->adata_r;
210   GstFFTS16Complex *fdata_l = scope->freq_data_l;
211   GstFFTS16Complex *fdata_r = scope->freq_data_r;
212   gint x, y;
213   guint off;
214   guint w = bscope->width;
215   guint h = bscope->height;
216   guint32 *colors = scope->colors, c;
217   guint *shade = scope->shade;
218   //guint w2 = w /2;
219   guint ch = bscope->channels;
220   guint num_samples = GST_BUFFER_SIZE (audio) / (ch * sizeof (gint16));
221   gint i, j, b;
222   gint br, br1, br2;
223   gint clarity;
224   gdouble fc, r, l, rr, ll;
225   gdouble frl, fil, frr, fir;
226   const guint sl = 30;
227
228   /* deinterleave */
229   for (i = 0, j = 0; i < num_samples; i++) {
230     adata_l[i] = adata[j++];
231     adata_r[i] = adata[j++];
232   }
233
234   /* run fft */
235   /*gst_fft_s16_window (scope->fft_ctx, adata_l, GST_FFT_WINDOW_HAMMING); */
236   gst_fft_s16_fft (scope->fft_ctx, adata_l, fdata_l);
237   /*gst_fft_s16_window (scope->fft_ctx, adata_r, GST_FFT_WINDOW_HAMMING); */
238   gst_fft_s16_fft (scope->fft_ctx, adata_r, fdata_r);
239
240   /* draw stars */
241   for (y = 0; y < h; y++) {
242     b = h - y;
243     frl = (gdouble) fdata_l[b].r;
244     fil = (gdouble) fdata_l[b].i;
245     frr = (gdouble) fdata_r[b].r;
246     fir = (gdouble) fdata_r[b].i;
247
248     ll = (frl + fil) * (frl + fil) + (frr - fir) * (frr - fir);
249     l = sqrt (ll);
250     rr = (frl - fil) * (frl - fil) + (frr + fir) * (frr + fir);
251     r = sqrt (rr);
252     /* out-of-phase'ness for this frequency component */
253     clarity = (gint) (
254         ((frl + fil) * (frl - fil) + (frr + fir) * (frr - fir)) /
255         (ll + rr) * 256);
256     fc = r + l;
257
258     x = (guint) (r * w / fc);
259     /* the brighness scaling factor was picked by experimenting */
260     br = b * fc * 0.01;
261
262     br1 = br * (clarity + 128) >> 8;
263     br2 = br * (128 - clarity) >> 8;
264     br1 = CLAMP (br1, 0, 255);
265     br2 = CLAMP (br2, 0, 255);
266
267     GST_DEBUG ("y %3d fc %10.6f clarity %d br %d br1 %d br2 %d", y, fc, clarity,
268         br, br1, br2);
269
270     /* draw a star */
271     off = (y * w) + x;
272     c = colors[(br1 >> 4) | (br2 & 0xf0)];
273     add_pixel (&vdata[off], c);
274     if ((x > (sl - 1)) && (x < (w - sl)) && (y > (sl - 1)) && (y < (h - sl))) {
275       for (i = 1; br1 || br2; i++, br1 = shade[br1], br2 = shade[br2]) {
276         c = colors[(br1 >> 4) + (br2 & 0xf0)];
277         add_pixel (&vdata[off - i], c);
278         add_pixel (&vdata[off + i], c);
279         add_pixel (&vdata[off - i * w], c);
280         add_pixel (&vdata[off + i * w], c);
281       }
282     } else {
283       for (i = 1; br1 || br2; i++, br1 = shade[br1], br2 = shade[br2]) {
284         c = colors[(br1 >> 4) | (br2 & 0xf0)];
285         if (x - i > 0)
286           add_pixel (&vdata[off - i], c);
287         if (x + i < (w - 1))
288           add_pixel (&vdata[off + i], c);
289         if (y - i > 0)
290           add_pixel (&vdata[off - i * w], c);
291         if (y + i < (h - 1))
292           add_pixel (&vdata[off + i * w], c);
293       }
294     }
295   }
296
297   return TRUE;
298 }
299
300 gboolean
301 gst_synae_scope_plugin_init (GstPlugin * plugin)
302 {
303   GST_DEBUG_CATEGORY_INIT (synae_scope_debug, "synaescope", 0, "synaescope");
304
305   return gst_element_register (plugin, "synaescope", GST_RANK_NONE,
306       GST_TYPE_SYNAE_SCOPE);
307 }