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 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
32 #include <vlc_vout_window.h>
33 #include <vlc_opengl.h>
34 #include <vlc_filter.h>
41 #include "visual/fft.h"
42 #include "visual/window.h"
45 /*****************************************************************************
47 *****************************************************************************/
48 static int Open(vlc_object_t *);
49 static void Close(vlc_object_t *);
51 #define WIDTH_TEXT N_("Video width")
52 #define WIDTH_LONGTEXT N_("The width of the visualization window, in pixels.")
54 #define HEIGHT_TEXT N_("Video height")
55 #define HEIGHT_LONGTEXT N_("The height of the visualization window, in pixels.")
58 set_shortname(N_("glSpectrum"))
59 set_description(N_("3D OpenGL spectrum visualization"))
60 set_capability("visualization", 0)
61 set_category(CAT_AUDIO)
62 set_subcategory(SUBCAT_AUDIO_VISUAL)
64 add_integer("glspectrum-width", 400, WIDTH_TEXT, WIDTH_LONGTEXT, false)
65 add_integer("glspectrum-height", 300, HEIGHT_TEXT, HEIGHT_LONGTEXT, false)
67 add_shortcut("glspectrum")
68 set_callbacks(Open, Close)
72 /*****************************************************************************
74 *****************************************************************************/
82 unsigned i_prev_nb_samples;
83 int16_t *p_prev_s16_buff;
88 float f_rotationAngle;
89 float f_rotationIncrement;
91 /* FFT window parameters */
92 window_param wind_param;
96 static block_t *DoWork(filter_t *, block_t *);
97 static void *Thread(void *);
99 #define SPECTRUM_WIDTH 4.f
101 #define ROTATION_INCREMENT .1f
102 #define BAR_DECREMENT .075f
103 #define ROTATION_MAX 20
105 const GLfloat lightZeroColor[] = {1.0f, 1.0f, 1.0f, 1.0f};
106 const GLfloat lightZeroPosition[] = {0.0f, 3.0f, 10.0f, 0.0f};
110 * @param p_this: the filter object
111 * @return VLC_SUCCESS or vlc error codes
113 static int Open(vlc_object_t * p_this)
115 filter_t *p_filter = (filter_t *)p_this;
118 p_sys = p_filter->p_sys = (filter_sys_t*)malloc(sizeof(*p_sys));
122 /* Create the object for the thread */
123 p_sys->i_channels = aout_FormatNbChannels(&p_filter->fmt_in.audio);
124 p_sys->i_prev_nb_samples = 0;
125 p_sys->p_prev_s16_buff = NULL;
127 p_sys->f_rotationAngle = 0;
128 p_sys->f_rotationIncrement = ROTATION_INCREMENT;
130 /* Fetch the FFT window parameters */
131 window_get_param( VLC_OBJECT( p_filter ), &p_sys->wind_param );
133 /* Create the FIFO for the audio data. */
134 p_sys->fifo = block_FifoNew();
135 if (p_sys->fifo == NULL)
138 /* Create the openGL provider */
139 vout_window_cfg_t cfg = {
140 .width = var_InheritInteger(p_filter, "glspectrum-width"),
141 .height = var_InheritInteger(p_filter, "glspectrum-height"),
144 p_sys->gl = vlc_gl_surface_Create(p_this, &cfg, NULL);
145 if (p_sys->gl == NULL)
147 block_FifoRelease(p_sys->fifo);
151 /* Create the thread */
152 if (vlc_clone(&p_sys->thread, Thread, p_filter,
153 VLC_THREAD_PRIORITY_VIDEO))
156 p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
157 p_filter->fmt_out.audio = p_filter->fmt_in.audio;
158 p_filter->pf_audio_filter = DoWork;
170 * @param p_this: the filter object
172 static void Close(vlc_object_t *p_this)
174 filter_t *p_filter = (filter_t *)p_this;
175 filter_sys_t *p_sys = p_filter->p_sys;
177 /* Terminate the thread. */
178 vlc_cancel(p_sys->thread);
179 vlc_join(p_sys->thread, NULL);
181 /* Free the ressources */
182 vlc_gl_surface_Destroy(p_sys->gl);
183 block_FifoRelease(p_sys->fifo);
184 free(p_sys->p_prev_s16_buff);
190 * Do the actual work with the new sample.
191 * @param p_filter: filter object
192 * @param p_in_buf: input buffer
194 static block_t *DoWork(filter_t *p_filter, block_t *p_in_buf)
196 block_t *block = block_Duplicate(p_in_buf);
197 if (likely(block != NULL))
198 block_FifoPut(p_filter->p_sys->fifo, block);
204 * Init the OpenGL scene.
206 static void initOpenGLScene(void)
208 glEnable(GL_CULL_FACE);
209 glEnable(GL_DEPTH_TEST);
210 glDepthMask(GL_TRUE);
212 glMatrixMode(GL_PROJECTION);
213 glFrustum(-1.0f, 1.0f, -1.0f, 1.0f, 0.5f, 10.0f);
215 glMatrixMode(GL_MODELVIEW);
216 glTranslatef(0.0, -2.0, -2.0);
219 glEnable(GL_LIGHTING);
221 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
222 glEnable(GL_COLOR_MATERIAL);
225 glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor);
226 glLightfv(GL_LIGHT0, GL_POSITION, lightZeroPosition);
228 glShadeModel(GL_SMOOTH);
231 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
236 * Draw one bar of the Spectrum.
238 static void drawBar(void)
240 const float w = SPECTRUM_WIDTH / NB_BANDS - 0.05f;
242 const GLfloat vertexCoords[] = {
243 0.f, 0.f, 0.f, w, 0.f, 0.f, 0.f, 1.f, 0.f,
244 0.f, 1.f, 0.f, w, 0.f, 0.f, w, 1.f, 0.f,
246 0.f, 0.f, -w, 0.f, 0.f, 0.f, 0.f, 1.f, -w,
247 0.f, 1.f, -w, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f,
249 w, 0.f, 0.f, w, 0.f, -w, w, 1.f, 0.f,
250 w, 1.f, 0.f, w, 0.f, -w, w, 1.f, -w,
252 w, 0.f, -w, 0.f, 0.f, -w, 0.f, 1.f, -w,
253 0.f, 1.f, -w, w, 1.f, -w, w, 0.f, -w,
255 0.f, 1.f, 0.f, w, 1.f, 0.f, w, 1.f, -w,
256 0.f, 1.f, 0.f, w, 1.f, -w, 0.f, 1.f, -w,
259 const GLfloat normals[] = {
260 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f,
261 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f,
263 -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f,
264 -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f,
266 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f,
267 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f,
269 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f,
270 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f,
272 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f,
273 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f,
276 glVertexPointer(3, GL_FLOAT, 0, vertexCoords);
277 glNormalPointer(GL_FLOAT, 0, normals);
278 glDrawArrays(GL_TRIANGLES, 0, 6 * 5);
283 * Set the color of one bar of the spectrum.
284 * @param f_height the height of the bar.
286 static void setBarColor(float f_height)
290 #define BAR_MAX_HEIGHT 4.2f
291 r = -1.f + 2 / BAR_MAX_HEIGHT * f_height;
292 b = 2.f - 2 / BAR_MAX_HEIGHT * f_height;
293 #undef BAR_MAX_HEIGHT
295 /* Test the ranges. */
296 r = r > 1.f ? 1.f : r;
297 b = b > 1.f ? 1.f : b;
299 r = r < 0.f ? 0.f : r;
300 b = b < 0.f ? 0.f : b;
302 /* Set the bar color. */
303 glColor4f(r, 0.f, b, 1.f);
308 * Draw all the bars of the spectrum.
309 * @param heights the heights of all the bars.
311 static void drawBars(float heights[])
314 glTranslatef(-2.f, 0.f, 0.f);
316 glEnableClientState(GL_VERTEX_ARRAY);
317 glEnableClientState(GL_NORMAL_ARRAY);
319 float w = SPECTRUM_WIDTH / NB_BANDS;
320 for (unsigned i = 0; i < NB_BANDS; ++i)
323 glScalef(1.f, heights[i], 1.f);
324 setBarColor(heights[i]);
328 glTranslatef(w, 0.f, 0.f);
331 glDisableClientState(GL_VERTEX_ARRAY);
332 glDisableClientState(GL_NORMAL_ARRAY);
339 * Update thread which do the rendering
340 * @param p_this: the p_thread object
342 static void *Thread( void *p_data )
344 filter_t *p_filter = (filter_t*)p_data;
345 filter_sys_t *p_sys = p_filter->p_sys;
346 vlc_gl_t *gl = p_sys->gl;
348 vlc_gl_MakeCurrent(gl);
350 vlc_gl_ReleaseCurrent(gl);
352 float height[NB_BANDS] = {0};
356 block_t *block = block_FifoGet(p_sys->fifo);
358 int canc = vlc_savecancel();
359 unsigned win_width, win_height;
361 vlc_gl_MakeCurrent(gl);
362 if (vlc_gl_surface_CheckSize(gl, &win_width, &win_height))
363 glViewport(0, 0, win_width, win_height);
365 /* Horizontal scale for 20-band equalizer */
366 const unsigned xscale[] = {0,1,2,3,4,5,6,7,8,11,15,20,27,
367 36,47,62,82,107,141,184,255};
369 fft_state *p_state = NULL; /* internal FFT data */
370 DEFINE_WIND_CONTEXT(wind_ctx); /* internal window data */
373 float p_output[FFT_BUFFER_SIZE]; /* Raw FFT Result */
374 int16_t p_buffer1[FFT_BUFFER_SIZE]; /* Buffer on which we perform
375 the FFT (first channel) */
376 int16_t p_dest[FFT_BUFFER_SIZE]; /* Adapted FFT result */
377 float *p_buffl = (float*)block->p_buffer; /* Original buffer */
379 int16_t *p_buffs; /* int16_t converted buffer */
380 int16_t *p_s16_buff; /* int16_t converted buffer */
382 if (!block->i_nb_samples) {
383 msg_Err(p_filter, "no samples yet");
387 /* Allocate the buffer only if the number of samples change */
388 if (block->i_nb_samples != p_sys->i_prev_nb_samples)
390 free(p_sys->p_prev_s16_buff);
391 p_sys->p_prev_s16_buff = malloc(block->i_nb_samples *
394 if (!p_sys->p_prev_s16_buff)
396 p_sys->i_prev_nb_samples = block->i_nb_samples;
398 p_buffs = p_s16_buff = p_sys->p_prev_s16_buff;
400 /* Convert the buffer to int16_t
401 Pasted from float32tos16.c */
402 for (i = block->i_nb_samples * p_sys->i_channels; i--;)
404 union {float f; int32_t i;} u;
406 u.f = *p_buffl + 384.f;
407 if (u.i > 0x43c07fff)
409 else if (u.i < 0x43bf8000)
412 *p_buffs = u.i - 0x43c00000;
414 p_buffl++; p_buffs++;
416 p_state = visual_fft_init();
419 msg_Err(p_filter,"unable to initialize FFT transform");
422 if (!window_init(FFT_BUFFER_SIZE, &p_sys->wind_param, &wind_ctx))
424 msg_Err(p_filter,"unable to initialize FFT window");
427 p_buffs = p_s16_buff;
428 for (i = 0 ; i < FFT_BUFFER_SIZE; i++)
431 p_buffer1[i] = *p_buffs;
433 p_buffs += p_sys->i_channels;
434 if (p_buffs >= &p_s16_buff[block->i_nb_samples * p_sys->i_channels])
435 p_buffs = p_s16_buff;
437 window_scale_in_place (p_buffer1, &wind_ctx);
438 fft_perform (p_buffer1, p_output, p_state);
440 for (i = 0; i< FFT_BUFFER_SIZE; ++i)
441 p_dest[i] = p_output[i] * (2 ^ 16)
442 / ((FFT_BUFFER_SIZE / 2 * 32768) ^ 2);
444 for (i = 0 ; i < NB_BANDS; i++)
446 /* Decrease the previous size of the bar. */
447 height[i] -= BAR_DECREMENT;
452 /* We search the maximum on one scale
453 to determine the current size of the bar. */
454 for (j = xscale[i]; j < xscale[i + 1]; j++)
459 /* Calculate the height of the bar */
460 float new_height = y != 0 ? logf(y) * 0.4f : 0;
461 height[i] = new_height > height[i]
462 ? new_height : height[i];
465 /* Determine the camera rotation angle. */
466 p_sys->f_rotationAngle += p_sys->f_rotationIncrement;
467 if (p_sys->f_rotationAngle <= -ROTATION_MAX)
468 p_sys->f_rotationIncrement = ROTATION_INCREMENT;
469 else if (p_sys->f_rotationAngle >= ROTATION_MAX)
470 p_sys->f_rotationIncrement = -ROTATION_INCREMENT;
472 /* Render the frame. */
473 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
476 glRotatef(p_sys->f_rotationAngle, 0, 1, 0);
480 /* Wait to swapp the frame on time. */
481 mwait(block->i_pts + (block->i_length / 2));
482 if (!vlc_gl_Lock(gl))
489 window_close(&wind_ctx);
491 vlc_gl_ReleaseCurrent(gl);
492 block_Release(block);
493 vlc_restorecancel(canc);
496 vlc_assert_unreachable();