Visualisation: fix preferences, warnings and cosmetics
[vlc/vlc-skelet.git] / modules / visualization / projectm.cpp
blob8491709c8cfc8d68f867d7d00f5966d15860023c
1 /*****************************************************************************
2 * projectm: visualization module based on libprojectM
3 *****************************************************************************
4 * Copyright © 2009-2011 the VideoLAN team
5 * $Id$
7 * Authors: Rémi Duraffort <ivoire@videolan.org>
8 * Laurent Aimar
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the Free
12 * Software Foundation; either version 2 of the License, or (at your option)
13 * any later version.
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * more details.
20 * You should have received a copy of the GNU General Public License along with
21 * this program; if not, write to the Free Software Foundation, Inc., 51
22 * Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28 #ifndef __STDC_CONSTANT_MACROS
29 # define __STDC_CONSTANT_MACROS
30 #endif
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_aout.h>
35 #include <vlc_vout.h>
36 #include <vlc_vout_wrapper.h>
37 #include <vlc_opengl.h>
38 #include <vlc_filter.h>
39 #include <vlc_rand.h>
41 #include <libprojectM/projectM.hpp>
43 /*****************************************************************************
44 * Module descriptor
45 *****************************************************************************/
46 static int Open ( vlc_object_t * );
47 static void Close ( vlc_object_t * );
49 #define CONFIG_TEXT N_("projectM configuration file")
50 #define CONFIG_LONGTEXT N_("File that will be used to configure the projectM " \
51 "module.")
53 #define PRESET_PATH_TXT N_("projectM preset path")
54 #define PRESET_PATH_LONGTXT N_("Path to the projectM preset directory")
56 #define TITLE_FONT_TXT N_("Title font")
57 #define TITLE_FONT_LONGTXT N_("Font used for the titles")
59 #define MENU_FONT_TXT N_("Font menu")
60 #define MENU_FONT_LONGTXT N_("Font used for the menus")
62 #define WIDTH_TEXT N_("Video width")
63 #define WIDTH_LONGTEXT N_("The width of the video window, in pixels.")
65 #define HEIGHT_TEXT N_("Video height")
66 #define HEIGHT_LONGTEXT N_("The height of the video window, in pixels.")
68 #define MESHX_TEXT N_("Mesh width")
69 #define MESHX_LONGTEXT N_("The width of the mesh, in pixels.")
71 #define MESHY_TEXT N_("Mesh height")
72 #define MESHY_LONGTEXT N_("The height of the mesh, in pixels.")
74 #define TEXTURE_TEXT N_("Texture size")
75 #define TEXTURE_LONGTEXT N_("The size of the texture, in pixels.")
77 vlc_module_begin ()
78 set_shortname( N_("projectM"))
79 set_description( N_("libprojectM effect") )
80 set_capability( "visualization2", 0 )
81 set_category( CAT_AUDIO )
82 set_subcategory( SUBCAT_AUDIO_VISUAL )
83 #ifndef HAVE_PROJECTM2
84 add_loadfile( "projectm-config", "/usr/share/projectM/config.inp",
85 CONFIG_TEXT, CONFIG_LONGTEXT, true )
86 #else
87 #ifdef WIN32
88 add_directory( "projectm-preset-path", NULL,
89 #else
90 add_directory( "projectm-preset-path", "/usr/share/projectM/presets",
91 #endif
92 PRESET_PATH_TXT, PRESET_PATH_LONGTXT, true )
93 add_loadfile( "projectm-title-font", "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf",
94 TITLE_FONT_TXT, TITLE_FONT_LONGTXT, true )
95 add_loadfile( "projectm-menu-font", "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf",
96 MENU_FONT_TXT, MENU_FONT_LONGTXT, true )
97 #endif
98 add_integer( "projectm-width", 800, WIDTH_TEXT, WIDTH_LONGTEXT,
99 false )
100 add_integer( "projectm-height", 640, HEIGHT_TEXT, HEIGHT_LONGTEXT,
101 false )
102 add_integer( "projectm-meshx", 32, MESHX_TEXT, MESHX_LONGTEXT,
103 false )
104 add_integer( "projectm-meshy", 24, MESHY_TEXT, MESHY_LONGTEXT,
105 false )
106 add_integer( "projectm-texture-size", 1024, TEXTURE_TEXT, TEXTURE_LONGTEXT,
107 false )
108 add_shortcut( "projectm" )
109 set_callbacks( Open, Close )
110 vlc_module_end ()
113 /*****************************************************************************
114 * Local prototypes
115 *****************************************************************************/
116 struct filter_sys_t
118 /* */
119 vlc_thread_t thread;
120 vlc_sem_t ready;
121 bool b_error;
123 /* Opengl */
124 vout_thread_t *p_vout;
125 vout_display_t *p_vd;
127 /* Window size */
128 int i_width;
129 int i_height;
131 /* audio info */
132 int i_channels;
134 /* */
135 vlc_mutex_t lock;
136 bool b_quit;
137 float *p_buffer;
138 int i_buffer_size;
139 int i_nb_samples;
143 static block_t *DoWork( filter_t *, block_t * );
144 static void *Thread( void * );
147 * Open the module
148 * @param p_this: the filter object
149 * @return VLC_SUCCESS or vlc error codes
151 static int Open( vlc_object_t * p_this )
153 filter_t *p_filter = (filter_t *)p_this;
154 filter_sys_t *p_sys;
156 /* Test the audio format */
157 if( p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32 ||
158 p_filter->fmt_out.audio.i_format != VLC_CODEC_FL32 )
160 msg_Warn( p_filter, "bad input or output format" );
161 return VLC_EGENERIC;
163 if( !AOUT_FMTS_SIMILAR( &p_filter->fmt_in.audio, &p_filter->fmt_out.audio ) )
165 msg_Warn( p_filter, "input and outut are not similar" );
166 return VLC_EGENERIC;
169 p_filter->pf_audio_filter = DoWork;
171 p_sys = p_filter->p_sys = (filter_sys_t*)malloc( sizeof( *p_sys ) );
172 if( !p_sys )
173 return VLC_ENOMEM;
175 /* Create the object for the thread */
176 vlc_sem_init( &p_sys->ready, 0 );
177 p_sys->b_error = false;
178 p_sys->b_quit = false;
179 p_sys->i_width = var_InheritInteger( p_filter, "projectm-width" );
180 p_sys->i_height = var_InheritInteger( p_filter, "projectm-height" );
181 p_sys->i_channels = aout_FormatNbChannels( &p_filter->fmt_in.audio );
182 vlc_mutex_init( &p_sys->lock );
183 p_sys->p_buffer = NULL;
184 p_sys->i_buffer_size = 0;
185 p_sys->i_nb_samples = 0;
187 /* Create the thread */
188 if( vlc_clone( &p_sys->thread, Thread, p_filter, VLC_THREAD_PRIORITY_LOW ) )
189 goto error;
191 vlc_sem_wait( &p_sys->ready );
192 if( p_sys->b_error )
194 vlc_join( p_sys->thread, NULL );
195 goto error;
198 return VLC_SUCCESS;
200 error:
201 vlc_sem_destroy( &p_sys->ready );
202 free (p_sys );
203 return VLC_EGENERIC;
208 * Close the module
209 * @param p_this: the filter object
211 static void Close( vlc_object_t *p_this )
213 filter_t *p_filter = (filter_t *)p_this;
214 filter_sys_t *p_sys = p_filter->p_sys;
216 /* Stop the thread
217 * XXX vlc_cleanup_push does not seems to work with C++ so no
218 * vlc_cancel()... */
219 vlc_mutex_lock( &p_sys->lock );
220 p_sys->b_quit = true;
221 vlc_mutex_unlock( &p_sys->lock );
223 vlc_join( p_sys->thread, NULL );
225 /* Free the ressources */
226 vlc_sem_destroy( &p_sys->ready );
227 vlc_mutex_destroy( &p_sys->lock );
228 free( p_sys->p_buffer );
229 free( p_sys );
234 * Do the actual work with the new sample
235 * @param p_aout: audio output object
236 * @param p_filter: filter object
237 * @param p_in_buf: input buffer
238 * @param p_out_buf: output buffer
240 static block_t *DoWork( filter_t *p_filter, block_t *p_in_buf )
242 filter_sys_t *p_sys = p_filter->p_sys;
244 vlc_mutex_lock( &p_sys->lock );
245 if( p_sys->i_buffer_size > 0 )
247 p_sys->i_nb_samples = __MIN( p_sys->i_buffer_size,
248 p_in_buf->i_nb_samples );
250 const float *p_src = (float*)p_in_buf->p_buffer;
251 for( int i = 0; i < p_sys->i_nb_samples; i++ )
253 float v = 0;
254 for( int j = 0; j < p_sys->i_channels; j++ )
255 v += p_src[p_sys->i_channels * i + j];
256 p_sys->p_buffer[i] = v / p_sys->i_channels;
259 vlc_mutex_unlock( &p_sys->lock );
261 return p_in_buf;
265 * Variable callback for the dummy vout
267 static int VoutCallback( vlc_object_t *p_vout, char const *psz_name,
268 vlc_value_t oldv, vlc_value_t newv, void *p_data )
270 VLC_UNUSED( p_vout ); VLC_UNUSED( oldv );
271 vout_display_t *p_vd = (vout_display_t*)p_data;
273 if( !strcmp(psz_name, "fullscreen") )
275 vout_SetDisplayFullscreen( p_vd, newv.b_bool );
277 return VLC_SUCCESS;
281 * ProjectM update thread which do the rendering
282 * @param p_this: the p_thread object
284 static void *Thread( void *p_data )
286 filter_t *p_filter = (filter_t*)p_data;
287 filter_sys_t *p_sys = p_filter->p_sys;
289 video_format_t fmt;
290 vlc_gl_t *gl;
291 unsigned int i_last_width = 0;
292 unsigned int i_last_height = 0;
293 locale_t loc;
294 locale_t oldloc;
296 projectM *p_projectm;
297 #ifndef HAVE_PROJECTM2
298 char *psz_config;
299 #else
300 char *psz_preset_path;
301 char *psz_title_font;
302 char *psz_menu_font;
303 projectM::Settings settings;
304 #endif
306 vlc_savecancel();
308 /* Create the openGL provider */
309 p_sys->p_vout =
310 (vout_thread_t *)vlc_object_create( p_filter, sizeof(vout_thread_t) );
311 if( !p_sys->p_vout )
312 goto error;
314 vlc_object_attach( p_sys->p_vout, p_filter );
316 /* */
317 video_format_Init( &fmt, 0 );
318 video_format_Setup( &fmt, VLC_CODEC_RGB32,
319 p_sys->i_width, p_sys->i_height, 0, 1 );
320 fmt.i_sar_num = 1;
321 fmt.i_sar_den = 1;
323 vout_display_state_t state;
324 memset( &state, 0, sizeof(state) );
325 state.cfg.display.sar.num = 1;
326 state.cfg.display.sar.den = 1;
327 state.cfg.is_display_filled = true;
328 state.cfg.zoom.num = 1;
329 state.cfg.zoom.den = 1;
330 state.sar.num = 1;
331 state.sar.den = 1;
333 p_sys->p_vd = vout_NewDisplay( p_sys->p_vout, &fmt, &state, "opengl",
334 300000, 1000000 );
335 if( !p_sys->p_vd )
337 vlc_object_release( p_sys->p_vout );
338 goto error;
340 var_Create( p_sys->p_vout, "fullscreen", VLC_VAR_BOOL );
341 var_AddCallback( p_sys->p_vout, "fullscreen", VoutCallback, p_sys->p_vd );
343 gl = vout_GetDisplayOpengl( p_sys->p_vd );
344 if( !gl )
346 vout_DeleteDisplay( p_sys->p_vd, NULL );
347 vlc_object_release( p_sys->p_vout );
348 goto error;
351 /* Work-around the projectM locale bug */
352 loc = newlocale (LC_NUMERIC_MASK, "C", NULL);
353 oldloc = uselocale (loc);
355 /* Create the projectM object */
356 #ifndef HAVE_PROJECTM2
357 psz_config = var_InheritString( p_filter, "projectm-config" );
358 p_projectm = new projectM( psz_config );
359 free( psz_config );
360 #else
361 psz_preset_path = var_InheritString( p_filter, "projectm-preset-path" );
362 #ifdef WIN32
363 if ( psz_preset_path == NULL )
365 char *psz_data_path = config_GetDataDir( p_filter );
366 asprintf( &psz_preset_path, "%s" DIR_SEP "visualization", psz_data_path );
367 free( psz_data_path );
369 #endif
371 psz_title_font = var_InheritString( p_filter, "projectm-title-font" );
372 psz_menu_font = var_InheritString( p_filter, "projectm-menu-font" );
374 settings.meshX = var_InheritInteger( p_filter, "projectm-meshx" );
375 settings.meshY = var_InheritInteger( p_filter, "projectm-meshy" );
376 settings.fps = 35;
377 settings.textureSize = var_InheritInteger( p_filter, "projectm-texture-size" );
378 settings.windowWidth = p_sys->i_width;
379 settings.windowHeight = p_sys->i_height;
380 settings.presetURL = psz_preset_path;
381 settings.titleFontURL = psz_title_font;
382 settings.menuFontURL = psz_menu_font;
383 settings.smoothPresetDuration = 5;
384 settings.presetDuration = 30;
385 settings.beatSensitivity = 10;
386 settings.aspectCorrection = 1;
387 settings.easterEgg = 1;
388 settings.shuffleEnabled = 1;
390 p_projectm = new projectM( settings );
392 free( psz_menu_font );
393 free( psz_title_font );
394 free( psz_preset_path );
395 #endif /* HAVE_PROJECTM2 */
397 p_sys->i_buffer_size = p_projectm->pcm()->maxsamples;
398 p_sys->p_buffer = (float*)calloc( p_sys->i_buffer_size,
399 sizeof( float ) );
401 vlc_sem_post( &p_sys->ready );
403 /* Choose a preset randomly or projectM will always show the first one */
404 if ( p_projectm->getPlaylistSize() > 0 )
405 p_projectm->selectPreset( (unsigned)vlc_mrand48() % p_projectm->getPlaylistSize() );
407 /* */
408 for( ;; )
410 const mtime_t i_deadline = mdate() + CLOCK_FREQ / 50; /* 50 fps max */
411 /* Manage the events */
412 vout_ManageDisplay( p_sys->p_vd, true );
413 if( p_sys->p_vd->cfg->display.width != i_last_width ||
414 p_sys->p_vd->cfg->display.height != i_last_height )
416 /* FIXME it is not perfect as we will have black bands */
417 vout_display_place_t place;
418 vout_display_PlacePicture( &place, &p_sys->p_vd->source, p_sys->p_vd->cfg, false );
419 p_projectm->projectM_resetGL( place.width, place.height );
421 i_last_width = p_sys->p_vd->cfg->display.width;
422 i_last_height = p_sys->p_vd->cfg->display.height;
425 /* Render the image and swap the buffers */
426 vlc_mutex_lock( &p_sys->lock );
427 if( p_sys->i_nb_samples > 0 )
429 p_projectm->pcm()->addPCMfloat( p_sys->p_buffer,
430 p_sys->i_nb_samples );
431 p_sys->i_nb_samples = 0;
433 if( p_sys->b_quit )
435 vlc_mutex_unlock( &p_sys->lock );
437 delete p_projectm;
438 vout_DeleteDisplay( p_sys->p_vd, NULL );
439 vlc_object_release( p_sys->p_vout );
440 if (loc != (locale_t)0)
442 uselocale (oldloc);
443 freelocale (loc);
445 return NULL;
447 vlc_mutex_unlock( &p_sys->lock );
449 p_projectm->renderFrame();
451 /* */
452 mwait( i_deadline );
454 if( !vlc_gl_Lock(gl) )
456 vlc_gl_Swap( gl );
457 vlc_gl_Unlock( gl );
460 abort();
462 error:
463 p_sys->b_error = true;
464 vlc_sem_post( &p_sys->ready );
465 return NULL;