Change the gmp download URL to https://gmplib.org/download
[vlc/gmpfix.git] / modules / visualization / glspectrum.c
blob023b0c7704857be2a0a2220c34dbc0a2dd0568ec
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.h>
33 #include <vlc_vout_wrapper.h>
34 #include <vlc_opengl.h>
35 #include <vlc_filter.h>
36 #include <vlc_rand.h>
38 #include <GL/gl.h>
40 #include <math.h>
42 #include "visual/fft.h"
43 #include "visual/window.h"
46 /*****************************************************************************
47 * Module descriptor
48 *****************************************************************************/
49 static int Open(vlc_object_t *);
50 static void Close(vlc_object_t *);
52 #define WIDTH_TEXT N_("Video width")
53 #define WIDTH_LONGTEXT N_("The width of the visualization window, in pixels.")
55 #define HEIGHT_TEXT N_("Video height")
56 #define HEIGHT_LONGTEXT N_("The height of the visualization window, in pixels.")
58 vlc_module_begin()
59 set_shortname(N_("glSpectrum"))
60 set_description(N_("3D OpenGL spectrum visualization"))
61 set_capability("visualization", 0)
62 set_category(CAT_AUDIO)
63 set_subcategory(SUBCAT_AUDIO_VISUAL)
65 add_integer("glspectrum-width", 400, WIDTH_TEXT, WIDTH_LONGTEXT, false)
66 add_integer("glspectrum-height", 300, HEIGHT_TEXT, HEIGHT_LONGTEXT, false)
68 add_shortcut("glspectrum")
69 set_callbacks(Open, Close)
70 vlc_module_end()
73 /*****************************************************************************
74 * Local prototypes
75 *****************************************************************************/
76 struct filter_sys_t
78 vlc_thread_t thread;
79 vlc_sem_t ready;
80 bool b_error;
82 /* Audio data */
83 unsigned i_channels;
84 block_fifo_t *fifo;
85 unsigned i_prev_nb_samples;
86 int16_t *p_prev_s16_buff;
88 /* Opengl */
89 vout_thread_t *p_vout;
90 vout_display_t *p_vd;
92 float f_rotationAngle;
93 float f_rotationIncrement;
95 /* Window size */
96 int i_width;
97 int i_height;
99 /* FFT window parameters */
100 window_param wind_param;
104 static block_t *DoWork(filter_t *, block_t *);
105 static void *Thread(void *);
107 #define SPECTRUM_WIDTH 4.0
108 #define NB_BANDS 20
109 #define ROTATION_INCREMENT 0.1
110 #define BAR_DECREMENT 0.075
111 #define ROTATION_MAX 20
113 const GLfloat lightZeroColor[] = {1.0f, 1.0f, 1.0f, 1.0f};
114 const GLfloat lightZeroPosition[] = {0.0f, 3.0f, 10.0f, 0.0f};
117 * Open the module.
118 * @param p_this: the filter object
119 * @return VLC_SUCCESS or vlc error codes
121 static int Open(vlc_object_t * p_this)
123 filter_t *p_filter = (filter_t *)p_this;
124 filter_sys_t *p_sys;
126 p_sys = p_filter->p_sys = (filter_sys_t*)malloc(sizeof(*p_sys));
127 if (p_sys == NULL)
128 return VLC_ENOMEM;
130 /* Create the object for the thread */
131 vlc_sem_init(&p_sys->ready, 0);
132 p_sys->b_error = false;
133 p_sys->i_width = var_InheritInteger(p_filter, "glspectrum-width");
134 p_sys->i_height = var_InheritInteger(p_filter, "glspectrum-height");
135 p_sys->i_channels = aout_FormatNbChannels(&p_filter->fmt_in.audio);
136 p_sys->i_prev_nb_samples = 0;
137 p_sys->p_prev_s16_buff = NULL;
139 p_sys->f_rotationAngle = 0;
140 p_sys->f_rotationIncrement = ROTATION_INCREMENT;
142 /* Fetch the FFT window parameters */
143 window_get_param( VLC_OBJECT( p_filter ), &p_sys->wind_param );
145 /* Create the FIFO for the audio data. */
146 p_sys->fifo = block_FifoNew();
147 if (p_sys->fifo == NULL)
148 goto error;
150 /* Create the thread */
151 if (vlc_clone(&p_sys->thread, Thread, p_filter,
152 VLC_THREAD_PRIORITY_VIDEO))
153 goto error;
155 /* Wait for the displaying thread to be ready. */
156 vlc_sem_wait(&p_sys->ready);
157 if (p_sys->b_error)
159 vlc_join(p_sys->thread, NULL);
160 goto error;
163 p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
164 p_filter->fmt_out.audio = p_filter->fmt_in.audio;
165 p_filter->pf_audio_filter = DoWork;
167 return VLC_SUCCESS;
169 error:
170 vlc_sem_destroy(&p_sys->ready);
171 free(p_sys);
172 return VLC_EGENERIC;
177 * Close the module.
178 * @param p_this: the filter object
180 static void Close(vlc_object_t *p_this)
182 filter_t *p_filter = (filter_t *)p_this;
183 filter_sys_t *p_sys = p_filter->p_sys;
185 /* Terminate the thread. */
186 vlc_cancel(p_sys->thread);
187 vlc_join(p_sys->thread, NULL);
189 /* Free the ressources */
190 vout_DeleteDisplay(p_sys->p_vd, NULL);
191 vlc_object_release(p_sys->p_vout);
193 block_FifoRelease(p_sys->fifo);
194 free(p_sys->p_prev_s16_buff);
196 vlc_sem_destroy(&p_sys->ready);
197 free(p_sys);
202 * Do the actual work with the new sample.
203 * @param p_filter: filter object
204 * @param p_in_buf: input buffer
206 static block_t *DoWork(filter_t *p_filter, block_t *p_in_buf)
208 block_t *block = block_Duplicate(p_in_buf);
209 if (likely(block != NULL))
210 block_FifoPut(p_filter->p_sys->fifo, block);
211 return p_in_buf;
216 * Init the OpenGL scene.
218 static void initOpenGLScene(void)
220 glEnable(GL_CULL_FACE);
221 glEnable(GL_DEPTH_TEST);
222 glDepthMask(GL_TRUE);
224 glMatrixMode(GL_PROJECTION);
225 glFrustum(-1.0f, 1.0f, -1.0f, 1.0f, 0.5f, 10.0f);
227 glMatrixMode(GL_MODELVIEW);
228 glTranslatef(0.0, -2.0, -2.0);
230 // Init the light.
231 glEnable(GL_LIGHTING);
233 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
234 glEnable(GL_COLOR_MATERIAL);
236 glEnable(GL_LIGHT0);
237 glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor);
238 glLightfv(GL_LIGHT0, GL_POSITION, lightZeroPosition);
240 glShadeModel(GL_SMOOTH);
242 glEnable(GL_BLEND);
243 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
248 * Draw one bar of the Spectrum.
250 static void drawBar(void)
252 const float w = SPECTRUM_WIDTH / NB_BANDS - 0.05;
254 const GLfloat vertexCoords[] = {
255 0.0, 0.0, 0.0, w, 0.0, 0.0, 0.0, 1.0, 0.0,
256 0.0, 1.0, 0.0, w, 0.0, 0.0, w , 1.0, 0.0,
258 0.0, 0.0, -w, 0.0, 0.0, 0.0, 0.0, 1.0, -w,
259 0.0, 1.0, -w, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0,
261 w, 0.0, 0.0, w, 0.0, -w, w, 1.0, 0.0,
262 w, 1.0, 0.0, w, 0.0, -w, w, 1.0, -w,
264 w, 0.0, -w, 0.0, 0.0, -w, 0.0, 1.0, -w,
265 0.0, 1.0, -w, w, 1.0, -w, w, 0.0, -w,
267 0.0, 1.0, 0.0, w, 1.0, 0.0, w, 1.0, -w,
268 0.0, 1.0, 0.0, w, 1.0, -w, 0.0, 1.0, -w,
271 const GLfloat normals[] = {
272 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
273 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0,
275 -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0,
276 -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0,
278 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
279 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
281 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0,
282 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0,
284 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
285 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0,
288 glVertexPointer(3, GL_FLOAT, 0, vertexCoords);
289 glNormalPointer(GL_FLOAT, 0, normals);
290 glDrawArrays(GL_TRIANGLES, 0, 6 * 5);
295 * Set the color of one bar of the spectrum.
296 * @param f_height the height of the bar.
298 static void setBarColor(float f_height)
300 float r, b;
302 #define BAR_MAX_HEIGHT 4.2
303 r = -1.0 + 2 / BAR_MAX_HEIGHT * f_height;
304 b = 2.0 - 2 / BAR_MAX_HEIGHT * f_height;
305 #undef BAR_MAX_HEIGHT
307 /* Test the ranges. */
308 r = r > 1.0 ? 1.0 : r;
309 b = b > 1.0 ? 1.0 : b;
311 r = r < 0.0 ? 0.0 : r;
312 b = b < 0.0 ? 0.0 : b;
314 /* Set the bar color. */
315 glColor4f(r, 0.0, b, 1.0);
320 * Draw all the bars of the spectrum.
321 * @param heights the heights of all the bars.
323 static void drawBars(float heights[])
325 glPushMatrix();
326 glTranslatef(-2.0, 0.0, 0.0);
328 glEnableClientState(GL_VERTEX_ARRAY);
329 glEnableClientState(GL_NORMAL_ARRAY);
331 float w = SPECTRUM_WIDTH / NB_BANDS;
332 for (unsigned i = 0; i < NB_BANDS; ++i)
334 glPushMatrix();
335 glScalef(1.0, heights[i], 1.0);
336 setBarColor(heights[i]);
337 drawBar();
338 glPopMatrix();
340 glTranslatef(w, 0.0, 0.0);
343 glDisableClientState(GL_VERTEX_ARRAY);
344 glDisableClientState(GL_NORMAL_ARRAY);
346 glPopMatrix();
351 * Update thread which do the rendering
352 * @param p_this: the p_thread object
354 static void *Thread( void *p_data )
356 filter_t *p_filter = (filter_t*)p_data;
357 filter_sys_t *p_sys = p_filter->p_sys;
359 video_format_t fmt;
360 vlc_gl_t *gl;
361 unsigned int i_last_width = 0;
362 unsigned int i_last_height = 0;
364 /* Create the openGL provider */
365 p_sys->p_vout =
366 (vout_thread_t *)vlc_object_create(p_filter, sizeof(vout_thread_t));
367 if (!p_sys->p_vout)
368 goto error;
370 /* Configure the video format for the opengl provider. */
371 video_format_Init(&fmt, 0);
372 video_format_Setup(&fmt, VLC_CODEC_RGB32, p_sys->i_width, p_sys->i_height,
373 p_sys->i_width, p_sys->i_height, 0, 1 );
374 fmt.i_sar_num = 1;
375 fmt.i_sar_den = 1;
377 /* Init vout state. */
378 vout_display_state_t state;
379 memset(&state, 0, sizeof(state));
380 state.cfg.display.sar.num = 1;
381 state.cfg.display.sar.den = 1;
382 state.cfg.is_display_filled = true;
383 state.cfg.zoom.num = 1;
384 state.cfg.zoom.den = 1;
385 state.sar.num = 1;
386 state.sar.den = 1;
388 p_sys->p_vd = vout_NewDisplay(p_sys->p_vout, &fmt, &state,
389 "opengl", 1000000, 1000000);
390 if (!p_sys->p_vd)
392 vlc_object_release(p_sys->p_vout);
393 goto error;
396 gl = vout_GetDisplayOpengl(p_sys->p_vd);
397 if (!gl)
399 vout_DeleteDisplay(p_sys->p_vd, NULL);
400 vlc_object_release(p_sys->p_vout);
401 goto error;
404 vlc_sem_post(&p_sys->ready);
406 vlc_gl_MakeCurrent(gl);
407 initOpenGLScene();
408 vlc_gl_ReleaseCurrent(gl);
410 float height[NB_BANDS] = {0};
412 while (1)
414 block_t *block = block_FifoGet(p_sys->fifo);
416 int canc = vlc_savecancel();
418 vlc_gl_MakeCurrent(gl);
419 /* Manage the events */
420 vout_ManageDisplay(p_sys->p_vd, true);
421 if (p_sys->p_vd->cfg->display.width != i_last_width ||
422 p_sys->p_vd->cfg->display.height != i_last_height)
424 /* FIXME it is not perfect as we will have black bands */
425 vout_display_place_t place;
426 vout_display_PlacePicture(&place, &p_sys->p_vd->source,
427 p_sys->p_vd->cfg, false);
429 i_last_width = p_sys->p_vd->cfg->display.width;
430 i_last_height = p_sys->p_vd->cfg->display.height;
433 /* Horizontal scale for 20-band equalizer */
434 const unsigned xscale[] = {0,1,2,3,4,5,6,7,8,11,15,20,27,
435 36,47,62,82,107,141,184,255};
437 fft_state *p_state = NULL; /* internal FFT data */
438 DEFINE_WIND_CONTEXT(wind_ctx); /* internal window data */
440 unsigned i, j;
441 float p_output[FFT_BUFFER_SIZE]; /* Raw FFT Result */
442 int16_t p_buffer1[FFT_BUFFER_SIZE]; /* Buffer on which we perform
443 the FFT (first channel) */
444 int16_t p_dest[FFT_BUFFER_SIZE]; /* Adapted FFT result */
445 float *p_buffl = (float*)block->p_buffer; /* Original buffer */
447 int16_t *p_buffs; /* int16_t converted buffer */
448 int16_t *p_s16_buff; /* int16_t converted buffer */
450 if (!block->i_nb_samples) {
451 msg_Err(p_filter, "no samples yet");
452 goto release;
455 /* Allocate the buffer only if the number of samples change */
456 if (block->i_nb_samples != p_sys->i_prev_nb_samples)
458 free(p_sys->p_prev_s16_buff);
459 p_sys->p_prev_s16_buff = malloc(block->i_nb_samples *
460 p_sys->i_channels *
461 sizeof(int16_t));
462 if (!p_sys->p_prev_s16_buff)
463 goto release;
464 p_sys->i_prev_nb_samples = block->i_nb_samples;
466 p_buffs = p_s16_buff = p_sys->p_prev_s16_buff;
468 /* Convert the buffer to int16_t
469 Pasted from float32tos16.c */
470 for (i = block->i_nb_samples * p_sys->i_channels; i--;)
472 union {float f; int32_t i;} u;
474 u.f = *p_buffl + 384.0;
475 if (u.i > 0x43c07fff)
476 *p_buffs = 32767;
477 else if (u.i < 0x43bf8000)
478 *p_buffs = -32768;
479 else
480 *p_buffs = u.i - 0x43c00000;
482 p_buffl++; p_buffs++;
484 p_state = visual_fft_init();
485 if (!p_state)
487 msg_Err(p_filter,"unable to initialize FFT transform");
488 goto release;
490 if (!window_init(FFT_BUFFER_SIZE, &p_sys->wind_param, &wind_ctx))
492 msg_Err(p_filter,"unable to initialize FFT window");
493 goto release;
495 p_buffs = p_s16_buff;
496 for (i = 0 ; i < FFT_BUFFER_SIZE; i++)
498 p_output[i] = 0;
499 p_buffer1[i] = *p_buffs;
501 p_buffs += p_sys->i_channels;
502 if (p_buffs >= &p_s16_buff[block->i_nb_samples * p_sys->i_channels])
503 p_buffs = p_s16_buff;
505 window_scale_in_place (p_buffer1, &wind_ctx);
506 fft_perform (p_buffer1, p_output, p_state);
508 for (i = 0; i< FFT_BUFFER_SIZE; ++i)
509 p_dest[i] = p_output[i] * (2 ^ 16)
510 / ((FFT_BUFFER_SIZE / 2 * 32768) ^ 2);
512 for (i = 0 ; i < NB_BANDS; i++)
514 /* Decrease the previous size of the bar. */
515 height[i] -= BAR_DECREMENT;
516 if (height[i] < 0)
517 height[i] = 0;
519 int y = 0;
520 /* We search the maximum on one scale
521 to determine the current size of the bar. */
522 for (j = xscale[i]; j < xscale[i + 1]; j++)
524 if (p_dest[j] > y)
525 y = p_dest[j];
527 /* Calculate the height of the bar */
528 float new_height = y != 0 ? log(y) * 0.4 : 0;
529 height[i] = new_height > height[i]
530 ? new_height : height[i];
533 /* Determine the camera rotation angle. */
534 p_sys->f_rotationAngle += p_sys->f_rotationIncrement;
535 if (p_sys->f_rotationAngle <= -ROTATION_MAX)
536 p_sys->f_rotationIncrement = ROTATION_INCREMENT;
537 else if (p_sys->f_rotationAngle >= ROTATION_MAX)
538 p_sys->f_rotationIncrement = -ROTATION_INCREMENT;
540 /* Render the frame. */
541 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
543 glPushMatrix();
544 glRotatef(p_sys->f_rotationAngle, 0, 1, 0);
545 drawBars(height);
546 glPopMatrix();
548 /* Wait to swapp the frame on time. */
549 mwait(block->i_pts + (block->i_length / 2));
550 if (!vlc_gl_Lock(gl))
552 vlc_gl_Swap(gl);
553 vlc_gl_Unlock(gl);
556 release:
557 window_close(&wind_ctx);
558 fft_close(p_state);
559 vlc_gl_ReleaseCurrent(gl);
560 block_Release(block);
561 vlc_restorecancel(canc);
564 assert(0);
566 error:
567 p_sys->b_error = true;
568 vlc_sem_post(&p_sys->ready);
569 return NULL;