1 /*****************************************************************************
2 * projectm.cpp: visualization module based on libprojectM
3 *****************************************************************************
4 * Copyright © 2009-2011 the VideoLAN team
7 * Authors: Rémi Duraffort <ivoire@videolan.org>
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)
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
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 *****************************************************************************/
28 #ifndef __STDC_CONSTANT_MACROS
29 # define __STDC_CONSTANT_MACROS
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
36 #include <vlc_vout_wrapper.h>
37 #include <vlc_opengl.h>
38 #include <vlc_filter.h>
41 #include <libprojectM/projectM.hpp>
43 /*****************************************************************************
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 " \
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.")
78 # define FONT_PATH "C:\\WINDOWS\\Fonts\\arial.ttf"
79 # define FONT_PATH_MENU "C:\\WINDOWS\\Fonts\\arial.ttf"
80 # define PRESET_PATH NULL
82 # define FONT_PATH "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"
83 # define FONT_PATH_MENU "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf"
84 # define PRESET_PATH "/usr/share/projectM/presets"
87 #ifdef DEFAULT_FONT_FILE
89 #define FONT_PATH DEFAULT_FONT_FILE
92 #ifdef DEFAULT_MONOSPACE_FONT_FILE
94 #define FONT_PATH_MENU DEFAULT_MONOSPACE_FONT_FILE
98 set_shortname( N_("projectM"))
99 set_description( N_("libprojectM effect") )
100 set_capability( "visualization2", 0 )
101 set_category( CAT_AUDIO
)
102 set_subcategory( SUBCAT_AUDIO_VISUAL
)
103 #ifndef HAVE_PROJECTM2
104 add_loadfile( "projectm-config", "/usr/share/projectM/config.inp",
105 CONFIG_TEXT
, CONFIG_LONGTEXT
, true )
107 add_directory( "projectm-preset-path", PRESET_PATH
,
108 PRESET_PATH_TXT
, PRESET_PATH_LONGTXT
, true )
109 add_loadfile( "projectm-title-font", FONT_PATH
,
110 TITLE_FONT_TXT
, TITLE_FONT_LONGTXT
, true )
111 add_loadfile( "projectm-menu-font", FONT_PATH_MENU
,
112 MENU_FONT_TXT
, MENU_FONT_LONGTXT
, true )
114 add_integer( "projectm-width", 800, WIDTH_TEXT
, WIDTH_LONGTEXT
,
116 add_integer( "projectm-height", 500, HEIGHT_TEXT
, HEIGHT_LONGTEXT
,
118 add_integer( "projectm-meshx", 32, MESHX_TEXT
, MESHX_LONGTEXT
,
120 add_integer( "projectm-meshy", 24, MESHY_TEXT
, MESHY_LONGTEXT
,
122 add_integer( "projectm-texture-size", 1024, TEXTURE_TEXT
, TEXTURE_LONGTEXT
,
124 add_shortcut( "projectm" )
125 set_callbacks( Open
, Close
)
129 /*****************************************************************************
131 *****************************************************************************/
140 vout_thread_t
*p_vout
;
141 vout_display_t
*p_vd
;
154 unsigned i_buffer_size
;
155 unsigned i_nb_samples
;
159 static block_t
*DoWork( filter_t
*, block_t
* );
160 static void *Thread( void * );
164 * @param p_this: the filter object
165 * @return VLC_SUCCESS or vlc error codes
167 static int Open( vlc_object_t
* p_this
)
169 filter_t
*p_filter
= (filter_t
*)p_this
;
172 p_sys
= p_filter
->p_sys
= (filter_sys_t
*)malloc( sizeof( *p_sys
) );
176 /* Create the object for the thread */
177 vlc_sem_init( &p_sys
->ready
, 0 );
178 p_sys
->b_error
= false;
179 p_sys
->b_quit
= false;
180 p_sys
->i_width
= var_InheritInteger( p_filter
, "projectm-width" );
181 p_sys
->i_height
= var_InheritInteger( p_filter
, "projectm-height" );
182 p_sys
->i_channels
= aout_FormatNbChannels( &p_filter
->fmt_in
.audio
);
183 vlc_mutex_init( &p_sys
->lock
);
184 p_sys
->p_buffer
= NULL
;
185 p_sys
->i_buffer_size
= 0;
186 p_sys
->i_nb_samples
= 0;
188 /* Create the thread */
189 if( vlc_clone( &p_sys
->thread
, Thread
, p_filter
, VLC_THREAD_PRIORITY_LOW
) )
192 vlc_sem_wait( &p_sys
->ready
);
195 vlc_join( p_sys
->thread
, NULL
);
199 p_filter
->fmt_in
.audio
.i_format
= VLC_CODEC_FL32
;
200 p_filter
->fmt_out
.audio
= p_filter
->fmt_in
.audio
;
201 p_filter
->pf_audio_filter
= DoWork
;
205 vlc_sem_destroy( &p_sys
->ready
);
213 * @param p_this: the filter object
215 static void Close( vlc_object_t
*p_this
)
217 filter_t
*p_filter
= (filter_t
*)p_this
;
218 filter_sys_t
*p_sys
= p_filter
->p_sys
;
221 * XXX vlc_cleanup_push does not seems to work with C++ so no
223 vlc_mutex_lock( &p_sys
->lock
);
224 p_sys
->b_quit
= true;
225 vlc_mutex_unlock( &p_sys
->lock
);
227 vlc_join( p_sys
->thread
, NULL
);
229 /* Free the ressources */
230 vlc_sem_destroy( &p_sys
->ready
);
231 vlc_mutex_destroy( &p_sys
->lock
);
232 free( p_sys
->p_buffer
);
238 * Do the actual work with the new sample
239 * @param p_aout: audio output object
240 * @param p_filter: filter object
241 * @param p_in_buf: input buffer
242 * @param p_out_buf: output buffer
244 static block_t
*DoWork( filter_t
*p_filter
, block_t
*p_in_buf
)
246 filter_sys_t
*p_sys
= p_filter
->p_sys
;
248 vlc_mutex_lock( &p_sys
->lock
);
249 if( p_sys
->i_buffer_size
> 0 )
251 p_sys
->i_nb_samples
= __MIN( p_sys
->i_buffer_size
,
252 p_in_buf
->i_nb_samples
);
254 const float *p_src
= (float*)p_in_buf
->p_buffer
;
255 for( unsigned i
= 0; i
< p_sys
->i_nb_samples
; i
++ )
258 for( int j
= 0; j
< p_sys
->i_channels
; j
++ )
259 v
+= p_src
[p_sys
->i_channels
* i
+ j
];
260 p_sys
->p_buffer
[i
] = v
/ p_sys
->i_channels
;
263 vlc_mutex_unlock( &p_sys
->lock
);
269 * Variable callback for the dummy vout
271 static int VoutCallback( vlc_object_t
*p_vout
, char const *psz_name
,
272 vlc_value_t oldv
, vlc_value_t newv
, void *p_data
)
274 VLC_UNUSED( p_vout
); VLC_UNUSED( oldv
);
275 vout_display_t
*p_vd
= (vout_display_t
*)p_data
;
277 if( !strcmp(psz_name
, "fullscreen") )
279 vout_SetDisplayFullscreen( p_vd
, newv
.b_bool
);
285 * ProjectM update thread which do the rendering
286 * @param p_this: the p_thread object
288 static void *Thread( void *p_data
)
290 filter_t
*p_filter
= (filter_t
*)p_data
;
291 filter_sys_t
*p_sys
= p_filter
->p_sys
;
295 unsigned int i_last_width
= 0;
296 unsigned int i_last_height
= 0;
300 projectM
*p_projectm
;
301 #ifndef HAVE_PROJECTM2
304 char *psz_preset_path
;
305 char *psz_title_font
;
307 projectM::Settings settings
;
312 /* Create the openGL provider */
314 (vout_thread_t
*)vlc_object_create( p_filter
, sizeof(vout_thread_t
) );
319 video_format_Init( &fmt
, 0 );
320 video_format_Setup( &fmt
, VLC_CODEC_RGB32
,
321 p_sys
->i_width
, p_sys
->i_height
, 0, 1 );
325 vout_display_state_t state
;
326 memset( &state
, 0, sizeof(state
) );
327 state
.cfg
.display
.sar
.num
= 1;
328 state
.cfg
.display
.sar
.den
= 1;
329 state
.cfg
.is_display_filled
= true;
330 state
.cfg
.zoom
.num
= 1;
331 state
.cfg
.zoom
.den
= 1;
335 p_sys
->p_vd
= vout_NewDisplay( p_sys
->p_vout
, &fmt
, &state
, "opengl",
339 vlc_object_release( p_sys
->p_vout
);
342 var_Create( p_sys
->p_vout
, "fullscreen", VLC_VAR_BOOL
);
343 var_AddCallback( p_sys
->p_vout
, "fullscreen", VoutCallback
, p_sys
->p_vd
);
345 gl
= vout_GetDisplayOpengl( p_sys
->p_vd
);
348 var_DelCallback( p_sys
->p_vout
, "fullscreen", VoutCallback
, p_sys
->p_vd
);
349 vout_DeleteDisplay( p_sys
->p_vd
, NULL
);
350 vlc_object_release( p_sys
->p_vout
);
354 /* Work-around the projectM locale bug */
355 loc
= newlocale (LC_NUMERIC_MASK
, "C", NULL
);
356 oldloc
= uselocale (loc
);
358 /* Create the projectM object */
359 #ifndef HAVE_PROJECTM2
360 psz_config
= var_InheritString( p_filter
, "projectm-config" );
361 p_projectm
= new projectM( psz_config
);
364 psz_preset_path
= var_InheritString( p_filter
, "projectm-preset-path" );
366 if ( psz_preset_path
== NULL
)
368 char *psz_data_path
= config_GetDataDir();
369 asprintf( &psz_preset_path
, "%s" DIR_SEP
"visualization", psz_data_path
);
370 free( psz_data_path
);
374 psz_title_font
= var_InheritString( p_filter
, "projectm-title-font" );
375 psz_menu_font
= var_InheritString( p_filter
, "projectm-menu-font" );
377 settings
.meshX
= var_InheritInteger( p_filter
, "projectm-meshx" );
378 settings
.meshY
= var_InheritInteger( p_filter
, "projectm-meshy" );
380 settings
.textureSize
= var_InheritInteger( p_filter
, "projectm-texture-size" );
381 settings
.windowWidth
= p_sys
->i_width
;
382 settings
.windowHeight
= p_sys
->i_height
;
383 settings
.presetURL
= psz_preset_path
;
384 settings
.titleFontURL
= psz_title_font
;
385 settings
.menuFontURL
= psz_menu_font
;
386 settings
.smoothPresetDuration
= 5;
387 settings
.presetDuration
= 30;
388 settings
.beatSensitivity
= 10;
389 settings
.aspectCorrection
= 1;
390 settings
.easterEgg
= 1;
391 settings
.shuffleEnabled
= 1;
393 p_projectm
= new projectM( settings
);
395 free( psz_menu_font
);
396 free( psz_title_font
);
397 free( psz_preset_path
);
398 #endif /* HAVE_PROJECTM2 */
400 p_sys
->i_buffer_size
= p_projectm
->pcm()->maxsamples
;
401 p_sys
->p_buffer
= (float*)calloc( p_sys
->i_buffer_size
,
404 vlc_sem_post( &p_sys
->ready
);
406 /* Choose a preset randomly or projectM will always show the first one */
407 if ( p_projectm
->getPlaylistSize() > 0 )
408 p_projectm
->selectPreset( (unsigned)vlc_mrand48() % p_projectm
->getPlaylistSize() );
413 const mtime_t i_deadline
= mdate() + CLOCK_FREQ
/ 50; /* 50 fps max */
414 /* Manage the events */
415 vout_ManageDisplay( p_sys
->p_vd
, true );
416 if( p_sys
->p_vd
->cfg
->display
.width
!= i_last_width
||
417 p_sys
->p_vd
->cfg
->display
.height
!= i_last_height
)
419 /* FIXME it is not perfect as we will have black bands */
420 vout_display_place_t place
;
421 vout_display_PlacePicture( &place
, &p_sys
->p_vd
->source
, p_sys
->p_vd
->cfg
, false );
422 p_projectm
->projectM_resetGL( place
.width
, place
.height
);
424 i_last_width
= p_sys
->p_vd
->cfg
->display
.width
;
425 i_last_height
= p_sys
->p_vd
->cfg
->display
.height
;
428 /* Render the image and swap the buffers */
429 vlc_mutex_lock( &p_sys
->lock
);
430 if( p_sys
->i_nb_samples
> 0 )
432 p_projectm
->pcm()->addPCMfloat( p_sys
->p_buffer
,
433 p_sys
->i_nb_samples
);
434 p_sys
->i_nb_samples
= 0;
438 vlc_mutex_unlock( &p_sys
->lock
);
441 var_DelCallback( p_sys
->p_vout
, "fullscreen", VoutCallback
, p_sys
->p_vd
);
442 vout_DeleteDisplay( p_sys
->p_vd
, NULL
);
443 vlc_object_release( p_sys
->p_vout
);
444 if (loc
!= (locale_t
)0)
451 vlc_mutex_unlock( &p_sys
->lock
);
453 p_projectm
->renderFrame();
458 if( !vlc_gl_Lock(gl
) )
467 p_sys
->b_error
= true;
468 vlc_sem_post( &p_sys
->ready
);