contrib: soxr: enable by default
[vlc.git] / modules / visualization / glspectrum.c
blob06f8d1bdf729da54bdfe37266b812c357557dd05
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 struct filter_sys_t
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;
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 if (likely(block != NULL))
202 block_FifoPut(p_filter->p_sys->fifo, block);
203 return p_in_buf;
208 * Init the OpenGL scene.
210 static void initOpenGLScene(void)
212 glEnable(GL_CULL_FACE);
213 glEnable(GL_DEPTH_TEST);
214 glDepthMask(GL_TRUE);
216 glMatrixMode(GL_PROJECTION);
217 glFrustum(-1.0f, 1.0f, -1.0f, 1.0f, 0.5f, 10.0f);
219 glMatrixMode(GL_MODELVIEW);
220 glTranslatef(0.0, -2.0, -2.0);
222 // Init the light.
223 glEnable(GL_LIGHTING);
225 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
226 glEnable(GL_COLOR_MATERIAL);
228 glEnable(GL_LIGHT0);
229 glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor);
230 glLightfv(GL_LIGHT0, GL_POSITION, lightZeroPosition);
232 glShadeModel(GL_SMOOTH);
234 glEnable(GL_BLEND);
235 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
240 * Draw one bar of the Spectrum.
242 static void drawBar(void)
244 const float w = SPECTRUM_WIDTH / NB_BANDS - 0.05f;
246 const GLfloat vertexCoords[] = {
247 0.f, 0.f, 0.f, w, 0.f, 0.f, 0.f, 1.f, 0.f,
248 0.f, 1.f, 0.f, w, 0.f, 0.f, w, 1.f, 0.f,
250 0.f, 0.f, -w, 0.f, 0.f, 0.f, 0.f, 1.f, -w,
251 0.f, 1.f, -w, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f,
253 w, 0.f, 0.f, w, 0.f, -w, w, 1.f, 0.f,
254 w, 1.f, 0.f, w, 0.f, -w, w, 1.f, -w,
256 w, 0.f, -w, 0.f, 0.f, -w, 0.f, 1.f, -w,
257 0.f, 1.f, -w, w, 1.f, -w, w, 0.f, -w,
259 0.f, 1.f, 0.f, w, 1.f, 0.f, w, 1.f, -w,
260 0.f, 1.f, 0.f, w, 1.f, -w, 0.f, 1.f, -w,
263 const GLfloat normals[] = {
264 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f,
265 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f,
267 -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f,
268 -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f,
270 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,
273 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f,
274 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f,
276 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f,
277 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f,
280 glVertexPointer(3, GL_FLOAT, 0, vertexCoords);
281 glNormalPointer(GL_FLOAT, 0, normals);
282 glDrawArrays(GL_TRIANGLES, 0, 6 * 5);
287 * Set the color of one bar of the spectrum.
288 * @param f_height the height of the bar.
290 static void setBarColor(float f_height)
292 float r, b;
294 #define BAR_MAX_HEIGHT 4.2f
295 r = -1.f + 2 / BAR_MAX_HEIGHT * f_height;
296 b = 2.f - 2 / BAR_MAX_HEIGHT * f_height;
297 #undef BAR_MAX_HEIGHT
299 /* Test the ranges. */
300 r = r > 1.f ? 1.f : r;
301 b = b > 1.f ? 1.f : b;
303 r = r < 0.f ? 0.f : r;
304 b = b < 0.f ? 0.f : b;
306 /* Set the bar color. */
307 glColor4f(r, 0.f, b, 1.f);
312 * Draw all the bars of the spectrum.
313 * @param heights the heights of all the bars.
315 static void drawBars(float heights[])
317 glPushMatrix();
318 glTranslatef(-2.f, 0.f, 0.f);
320 glEnableClientState(GL_VERTEX_ARRAY);
321 glEnableClientState(GL_NORMAL_ARRAY);
323 float w = SPECTRUM_WIDTH / NB_BANDS;
324 for (unsigned i = 0; i < NB_BANDS; ++i)
326 glPushMatrix();
327 glScalef(1.f, heights[i], 1.f);
328 setBarColor(heights[i]);
329 drawBar();
330 glPopMatrix();
332 glTranslatef(w, 0.f, 0.f);
335 glDisableClientState(GL_VERTEX_ARRAY);
336 glDisableClientState(GL_NORMAL_ARRAY);
338 glPopMatrix();
343 * Update thread which do the rendering
344 * @param p_this: the p_thread object
346 static void *Thread( void *p_data )
348 filter_t *p_filter = (filter_t*)p_data;
349 filter_sys_t *p_sys = p_filter->p_sys;
350 vlc_gl_t *gl = p_sys->gl;
352 if (vlc_gl_MakeCurrent(gl) != VLC_SUCCESS)
354 msg_Err(p_filter, "Can't attach gl context");
355 return NULL;
357 initOpenGLScene();
358 vlc_gl_ReleaseCurrent(gl);
360 float height[NB_BANDS] = {0};
362 while (1)
364 block_t *block = block_FifoGet(p_sys->fifo);
366 int canc = vlc_savecancel();
367 unsigned win_width, win_height;
369 vlc_gl_MakeCurrent(gl);
370 if (vlc_gl_surface_CheckSize(gl, &win_width, &win_height))
371 glViewport(0, 0, win_width, win_height);
373 /* Horizontal scale for 20-band equalizer */
374 const unsigned xscale[] = {0,1,2,3,4,5,6,7,8,11,15,20,27,
375 36,47,62,82,107,141,184,255};
377 fft_state *p_state = NULL; /* internal FFT data */
378 DEFINE_WIND_CONTEXT(wind_ctx); /* internal window data */
380 unsigned i, j;
381 float p_output[FFT_BUFFER_SIZE]; /* Raw FFT Result */
382 int16_t p_buffer1[FFT_BUFFER_SIZE]; /* Buffer on which we perform
383 the FFT (first channel) */
384 int16_t p_dest[FFT_BUFFER_SIZE]; /* Adapted FFT result */
385 float *p_buffl = (float*)block->p_buffer; /* Original buffer */
387 int16_t *p_buffs; /* int16_t converted buffer */
388 int16_t *p_s16_buff; /* int16_t converted buffer */
390 if (!block->i_nb_samples) {
391 msg_Err(p_filter, "no samples yet");
392 goto release;
395 /* Allocate the buffer only if the number of samples change */
396 if (block->i_nb_samples != p_sys->i_prev_nb_samples)
398 free(p_sys->p_prev_s16_buff);
399 p_sys->p_prev_s16_buff = malloc(block->i_nb_samples *
400 p_sys->i_channels *
401 sizeof(int16_t));
402 if (!p_sys->p_prev_s16_buff)
403 goto release;
404 p_sys->i_prev_nb_samples = block->i_nb_samples;
406 p_buffs = p_s16_buff = p_sys->p_prev_s16_buff;
408 /* Convert the buffer to int16_t
409 Pasted from float32tos16.c */
410 for (i = block->i_nb_samples * p_sys->i_channels; i--;)
412 union {float f; int32_t i;} u;
414 u.f = *p_buffl + 384.f;
415 if (u.i > 0x43c07fff)
416 *p_buffs = 32767;
417 else if (u.i < 0x43bf8000)
418 *p_buffs = -32768;
419 else
420 *p_buffs = u.i - 0x43c00000;
422 p_buffl++; p_buffs++;
424 p_state = visual_fft_init();
425 if (!p_state)
427 msg_Err(p_filter,"unable to initialize FFT transform");
428 goto release;
430 if (!window_init(FFT_BUFFER_SIZE, &p_sys->wind_param, &wind_ctx))
432 msg_Err(p_filter,"unable to initialize FFT window");
433 goto release;
435 p_buffs = p_s16_buff;
436 for (i = 0 ; i < FFT_BUFFER_SIZE; i++)
438 p_output[i] = 0;
439 p_buffer1[i] = *p_buffs;
441 p_buffs += p_sys->i_channels;
442 if (p_buffs >= &p_s16_buff[block->i_nb_samples * p_sys->i_channels])
443 p_buffs = p_s16_buff;
445 window_scale_in_place (p_buffer1, &wind_ctx);
446 fft_perform (p_buffer1, p_output, p_state);
448 for (i = 0; i< FFT_BUFFER_SIZE; ++i)
449 p_dest[i] = p_output[i] * (2 ^ 16)
450 / ((FFT_BUFFER_SIZE / 2 * 32768) ^ 2);
452 for (i = 0 ; i < NB_BANDS; i++)
454 /* Decrease the previous size of the bar. */
455 height[i] -= BAR_DECREMENT;
456 if (height[i] < 0)
457 height[i] = 0;
459 int y = 0;
460 /* We search the maximum on one scale
461 to determine the current size of the bar. */
462 for (j = xscale[i]; j < xscale[i + 1]; j++)
464 if (p_dest[j] > y)
465 y = p_dest[j];
467 /* Calculate the height of the bar */
468 float new_height = y != 0 ? logf(y) * 0.4f : 0;
469 height[i] = new_height > height[i]
470 ? new_height : height[i];
473 /* Determine the camera rotation angle. */
474 p_sys->f_rotationAngle += p_sys->f_rotationIncrement;
475 if (p_sys->f_rotationAngle <= -ROTATION_MAX)
476 p_sys->f_rotationIncrement = ROTATION_INCREMENT;
477 else if (p_sys->f_rotationAngle >= ROTATION_MAX)
478 p_sys->f_rotationIncrement = -ROTATION_INCREMENT;
480 /* Render the frame. */
481 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
483 glPushMatrix();
484 glRotatef(p_sys->f_rotationAngle, 0, 1, 0);
485 drawBars(height);
486 glPopMatrix();
488 /* Wait to swapp the frame on time. */
489 mwait(block->i_pts + (block->i_length / 2));
490 vlc_gl_Swap(gl);
492 release:
493 window_close(&wind_ctx);
494 fft_close(p_state);
495 vlc_gl_ReleaseCurrent(gl);
496 block_Release(block);
497 vlc_restorecancel(canc);
500 vlc_assert_unreachable();