codec: subsdec: fix variable shadowing
[vlc.git] / modules / visualization / glspectrum.c
blob84fad8f036fee0316a726064e8cc941b1d190471
1 /*****************************************************************************
2 * glspectrum.c: spectrum visualization module based on OpenGL
3 *****************************************************************************
4 * Copyright © 2009-2013 VLC authors and VideoLAN
6 * Authors: Adrien Maglo <magsoft@videolan.org>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 #include <assert.h>
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_aout.h>
32 #include <vlc_vout_window.h>
33 #include <vlc_opengl.h>
34 #include <vlc_filter.h>
35 #include <vlc_rand.h>
37 #ifdef __APPLE__
38 # include <OpenGL/gl.h>
39 #else
40 # include <GL/gl.h>
41 #endif
43 #include <math.h>
45 #include "visual/fft.h"
46 #include "visual/window.h"
49 /*****************************************************************************
50 * Module descriptor
51 *****************************************************************************/
52 static int Open(vlc_object_t *);
53 static void Close(vlc_object_t *);
55 #define WIDTH_TEXT N_("Video width")
56 #define WIDTH_LONGTEXT N_("The width of the visualization window, in pixels.")
58 #define HEIGHT_TEXT N_("Video height")
59 #define HEIGHT_LONGTEXT N_("The height of the visualization window, in pixels.")
61 vlc_module_begin()
62 set_shortname(N_("glSpectrum"))
63 set_description(N_("3D OpenGL spectrum visualization"))
64 set_capability("visualization", 0)
65 set_category(CAT_AUDIO)
66 set_subcategory(SUBCAT_AUDIO_VISUAL)
68 add_integer("glspectrum-width", 400, WIDTH_TEXT, WIDTH_LONGTEXT, false)
69 add_integer("glspectrum-height", 300, HEIGHT_TEXT, HEIGHT_LONGTEXT, false)
71 add_shortcut("glspectrum")
72 set_callbacks(Open, Close)
73 vlc_module_end()
76 /*****************************************************************************
77 * Local prototypes
78 *****************************************************************************/
79 typedef struct
81 vlc_thread_t thread;
83 /* Audio data */
84 unsigned i_channels;
85 block_fifo_t *fifo;
86 unsigned i_prev_nb_samples;
87 int16_t *p_prev_s16_buff;
89 /* Opengl */
90 vlc_gl_t *gl;
92 float f_rotationAngle;
93 float f_rotationIncrement;
95 /* FFT window parameters */
96 window_param wind_param;
97 } filter_sys_t;
100 static block_t *DoWork(filter_t *, block_t *);
101 static void *Thread(void *);
103 #define SPECTRUM_WIDTH 4.f
104 #define NB_BANDS 20
105 #define ROTATION_INCREMENT .1f
106 #define BAR_DECREMENT .075f
107 #define ROTATION_MAX 20
109 const GLfloat lightZeroColor[] = {1.0f, 1.0f, 1.0f, 1.0f};
110 const GLfloat lightZeroPosition[] = {0.0f, 3.0f, 10.0f, 0.0f};
113 * Open the module.
114 * @param p_this: the filter object
115 * @return VLC_SUCCESS or vlc error codes
117 static int Open(vlc_object_t * p_this)
119 filter_t *p_filter = (filter_t *)p_this;
120 filter_sys_t *p_sys;
122 p_sys = p_filter->p_sys = (filter_sys_t*)malloc(sizeof(*p_sys));
123 if (p_sys == NULL)
124 return VLC_ENOMEM;
126 /* Create the object for the thread */
127 p_sys->i_channels = aout_FormatNbChannels(&p_filter->fmt_in.audio);
128 p_sys->i_prev_nb_samples = 0;
129 p_sys->p_prev_s16_buff = NULL;
131 p_sys->f_rotationAngle = 0;
132 p_sys->f_rotationIncrement = ROTATION_INCREMENT;
134 /* Fetch the FFT window parameters */
135 window_get_param( VLC_OBJECT( p_filter ), &p_sys->wind_param );
137 /* Create the FIFO for the audio data. */
138 p_sys->fifo = block_FifoNew();
139 if (p_sys->fifo == NULL)
140 goto error;
142 /* Create the openGL provider */
143 vout_window_cfg_t cfg = {
144 .width = var_InheritInteger(p_filter, "glspectrum-width"),
145 .height = var_InheritInteger(p_filter, "glspectrum-height"),
148 p_sys->gl = vlc_gl_surface_Create(p_this, &cfg, NULL);
149 if (p_sys->gl == NULL)
151 block_FifoRelease(p_sys->fifo);
152 goto error;
155 /* Create the thread */
156 if (vlc_clone(&p_sys->thread, Thread, p_filter,
157 VLC_THREAD_PRIORITY_VIDEO))
158 goto error;
160 p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
161 p_filter->fmt_out.audio = p_filter->fmt_in.audio;
162 p_filter->pf_audio_filter = DoWork;
164 return VLC_SUCCESS;
166 error:
167 free(p_sys);
168 return VLC_EGENERIC;
173 * Close the module.
174 * @param p_this: the filter object
176 static void Close(vlc_object_t *p_this)
178 filter_t *p_filter = (filter_t *)p_this;
179 filter_sys_t *p_sys = p_filter->p_sys;
181 /* Terminate the thread. */
182 vlc_cancel(p_sys->thread);
183 vlc_join(p_sys->thread, NULL);
185 /* Free the ressources */
186 vlc_gl_surface_Destroy(p_sys->gl);
187 block_FifoRelease(p_sys->fifo);
188 free(p_sys->p_prev_s16_buff);
189 free(p_sys);
194 * Do the actual work with the new sample.
195 * @param p_filter: filter object
196 * @param p_in_buf: input buffer
198 static block_t *DoWork(filter_t *p_filter, block_t *p_in_buf)
200 block_t *block = block_Duplicate(p_in_buf);
201 filter_sys_t *p_sys = p_filter->p_sys;
202 if (likely(block != NULL))
203 block_FifoPut(p_sys->fifo, block);
204 return p_in_buf;
209 * Init the OpenGL scene.
211 static void initOpenGLScene(void)
213 glEnable(GL_CULL_FACE);
214 glEnable(GL_DEPTH_TEST);
215 glDepthMask(GL_TRUE);
217 glMatrixMode(GL_PROJECTION);
218 glFrustum(-1.0f, 1.0f, -1.0f, 1.0f, 0.5f, 10.0f);
220 glMatrixMode(GL_MODELVIEW);
221 glTranslatef(0.0, -2.0, -2.0);
223 // Init the light.
224 glEnable(GL_LIGHTING);
226 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
227 glEnable(GL_COLOR_MATERIAL);
229 glEnable(GL_LIGHT0);
230 glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor);
231 glLightfv(GL_LIGHT0, GL_POSITION, lightZeroPosition);
233 glShadeModel(GL_SMOOTH);
235 glEnable(GL_BLEND);
236 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
241 * Draw one bar of the Spectrum.
243 static void drawBar(void)
245 const float w = SPECTRUM_WIDTH / NB_BANDS - 0.05f;
247 const GLfloat vertexCoords[] = {
248 0.f, 0.f, 0.f, w, 0.f, 0.f, 0.f, 1.f, 0.f,
249 0.f, 1.f, 0.f, w, 0.f, 0.f, w, 1.f, 0.f,
251 0.f, 0.f, -w, 0.f, 0.f, 0.f, 0.f, 1.f, -w,
252 0.f, 1.f, -w, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f,
254 w, 0.f, 0.f, w, 0.f, -w, w, 1.f, 0.f,
255 w, 1.f, 0.f, w, 0.f, -w, w, 1.f, -w,
257 w, 0.f, -w, 0.f, 0.f, -w, 0.f, 1.f, -w,
258 0.f, 1.f, -w, w, 1.f, -w, w, 0.f, -w,
260 0.f, 1.f, 0.f, w, 1.f, 0.f, w, 1.f, -w,
261 0.f, 1.f, 0.f, w, 1.f, -w, 0.f, 1.f, -w,
264 const GLfloat normals[] = {
265 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f,
266 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f,
268 -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f,
269 -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f,
271 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f,
272 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f,
274 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f,
275 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f,
277 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f,
278 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f,
281 glVertexPointer(3, GL_FLOAT, 0, vertexCoords);
282 glNormalPointer(GL_FLOAT, 0, normals);
283 glDrawArrays(GL_TRIANGLES, 0, 6 * 5);
288 * Set the color of one bar of the spectrum.
289 * @param f_height the height of the bar.
291 static void setBarColor(float f_height)
293 float r, b;
295 #define BAR_MAX_HEIGHT 4.2f
296 r = -1.f + 2 / BAR_MAX_HEIGHT * f_height;
297 b = 2.f - 2 / BAR_MAX_HEIGHT * f_height;
298 #undef BAR_MAX_HEIGHT
300 /* Test the ranges. */
301 r = r > 1.f ? 1.f : r;
302 b = b > 1.f ? 1.f : b;
304 r = r < 0.f ? 0.f : r;
305 b = b < 0.f ? 0.f : b;
307 /* Set the bar color. */
308 glColor4f(r, 0.f, b, 1.f);
313 * Draw all the bars of the spectrum.
314 * @param heights the heights of all the bars.
316 static void drawBars(float heights[])
318 glPushMatrix();
319 glTranslatef(-2.f, 0.f, 0.f);
321 glEnableClientState(GL_VERTEX_ARRAY);
322 glEnableClientState(GL_NORMAL_ARRAY);
324 float w = SPECTRUM_WIDTH / NB_BANDS;
325 for (unsigned i = 0; i < NB_BANDS; ++i)
327 glPushMatrix();
328 glScalef(1.f, heights[i], 1.f);
329 setBarColor(heights[i]);
330 drawBar();
331 glPopMatrix();
333 glTranslatef(w, 0.f, 0.f);
336 glDisableClientState(GL_VERTEX_ARRAY);
337 glDisableClientState(GL_NORMAL_ARRAY);
339 glPopMatrix();
344 * Update thread which do the rendering
345 * @param p_this: the p_thread object
347 static void *Thread( void *p_data )
349 filter_t *p_filter = (filter_t*)p_data;
350 filter_sys_t *p_sys = p_filter->p_sys;
351 vlc_gl_t *gl = p_sys->gl;
353 if (vlc_gl_MakeCurrent(gl) != VLC_SUCCESS)
355 msg_Err(p_filter, "Can't attach gl context");
356 return NULL;
358 initOpenGLScene();
359 vlc_gl_ReleaseCurrent(gl);
361 float height[NB_BANDS] = {0};
363 while (1)
365 block_t *block = block_FifoGet(p_sys->fifo);
367 int canc = vlc_savecancel();
368 unsigned win_width, win_height;
370 vlc_gl_MakeCurrent(gl);
371 if (vlc_gl_surface_CheckSize(gl, &win_width, &win_height))
372 glViewport(0, 0, win_width, win_height);
374 /* Horizontal scale for 20-band equalizer */
375 const unsigned xscale[] = {0,1,2,3,4,5,6,7,8,11,15,20,27,
376 36,47,62,82,107,141,184,255};
378 fft_state *p_state = NULL; /* internal FFT data */
379 DEFINE_WIND_CONTEXT(wind_ctx); /* internal window data */
381 unsigned i, j;
382 float p_output[FFT_BUFFER_SIZE]; /* Raw FFT Result */
383 int16_t p_buffer1[FFT_BUFFER_SIZE]; /* Buffer on which we perform
384 the FFT (first channel) */
385 int16_t p_dest[FFT_BUFFER_SIZE]; /* Adapted FFT result */
386 float *p_buffl = (float*)block->p_buffer; /* Original buffer */
388 int16_t *p_buffs; /* int16_t converted buffer */
389 int16_t *p_s16_buff; /* int16_t converted buffer */
391 if (!block->i_nb_samples) {
392 msg_Err(p_filter, "no samples yet");
393 goto release;
396 /* Allocate the buffer only if the number of samples change */
397 if (block->i_nb_samples != p_sys->i_prev_nb_samples)
399 free(p_sys->p_prev_s16_buff);
400 p_sys->p_prev_s16_buff = malloc(block->i_nb_samples *
401 p_sys->i_channels *
402 sizeof(int16_t));
403 if (!p_sys->p_prev_s16_buff)
404 goto release;
405 p_sys->i_prev_nb_samples = block->i_nb_samples;
407 p_buffs = p_s16_buff = p_sys->p_prev_s16_buff;
409 /* Convert the buffer to int16_t
410 Pasted from float32tos16.c */
411 for (i = block->i_nb_samples * p_sys->i_channels; i--;)
413 union {float f; int32_t i;} u;
415 u.f = *p_buffl + 384.f;
416 if (u.i > 0x43c07fff)
417 *p_buffs = 32767;
418 else if (u.i < 0x43bf8000)
419 *p_buffs = -32768;
420 else
421 *p_buffs = u.i - 0x43c00000;
423 p_buffl++; p_buffs++;
425 p_state = visual_fft_init();
426 if (!p_state)
428 msg_Err(p_filter,"unable to initialize FFT transform");
429 goto release;
431 if (!window_init(FFT_BUFFER_SIZE, &p_sys->wind_param, &wind_ctx))
433 msg_Err(p_filter,"unable to initialize FFT window");
434 goto release;
436 p_buffs = p_s16_buff;
437 for (i = 0 ; i < FFT_BUFFER_SIZE; i++)
439 p_output[i] = 0;
440 p_buffer1[i] = *p_buffs;
442 p_buffs += p_sys->i_channels;
443 if (p_buffs >= &p_s16_buff[block->i_nb_samples * p_sys->i_channels])
444 p_buffs = p_s16_buff;
446 window_scale_in_place (p_buffer1, &wind_ctx);
447 fft_perform (p_buffer1, p_output, p_state);
449 for (i = 0; i< FFT_BUFFER_SIZE; ++i)
450 p_dest[i] = p_output[i] * (2 ^ 16)
451 / ((FFT_BUFFER_SIZE / 2 * 32768) ^ 2);
453 for (i = 0 ; i < NB_BANDS; i++)
455 /* Decrease the previous size of the bar. */
456 height[i] -= BAR_DECREMENT;
457 if (height[i] < 0)
458 height[i] = 0;
460 int y = 0;
461 /* We search the maximum on one scale
462 to determine the current size of the bar. */
463 for (j = xscale[i]; j < xscale[i + 1]; j++)
465 if (p_dest[j] > y)
466 y = p_dest[j];
468 /* Calculate the height of the bar */
469 float new_height = y != 0 ? logf(y) * 0.4f : 0;
470 height[i] = new_height > height[i]
471 ? new_height : height[i];
474 /* Determine the camera rotation angle. */
475 p_sys->f_rotationAngle += p_sys->f_rotationIncrement;
476 if (p_sys->f_rotationAngle <= -ROTATION_MAX)
477 p_sys->f_rotationIncrement = ROTATION_INCREMENT;
478 else if (p_sys->f_rotationAngle >= ROTATION_MAX)
479 p_sys->f_rotationIncrement = -ROTATION_INCREMENT;
481 /* Render the frame. */
482 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
484 glPushMatrix();
485 glRotatef(p_sys->f_rotationAngle, 0, 1, 0);
486 drawBars(height);
487 glPopMatrix();
489 /* Wait to swapp the frame on time. */
490 vlc_tick_wait(block->i_pts + (block->i_length / 2));
491 vlc_gl_Swap(gl);
493 release:
494 window_close(&wind_ctx);
495 fft_close(p_state);
496 vlc_gl_ReleaseCurrent(gl);
497 block_Release(block);
498 vlc_restorecancel(canc);
501 vlc_assert_unreachable();