1 /*****************************************************************************
2 * projectm: 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 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 )
88 add_directory( "projectm-preset-path", NULL
,
90 add_directory( "projectm-preset-path", "/usr/share/projectM/presets",
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 )
98 add_integer( "projectm-width", 800, WIDTH_TEXT
, WIDTH_LONGTEXT
,
100 add_integer( "projectm-height", 640, HEIGHT_TEXT
, HEIGHT_LONGTEXT
,
102 add_integer( "projectm-meshx", 32, MESHX_TEXT
, MESHX_LONGTEXT
,
104 add_integer( "projectm-meshy", 24, MESHY_TEXT
, MESHY_LONGTEXT
,
106 add_integer( "projectm-texture-size", 1024, TEXTURE_TEXT
, TEXTURE_LONGTEXT
,
108 add_shortcut( "projectm" )
109 set_callbacks( Open
, Close
)
113 /*****************************************************************************
115 *****************************************************************************/
124 vout_thread_t
*p_vout
;
125 vout_display_t
*p_vd
;
143 static block_t
*DoWork( filter_t
*, block_t
* );
144 static void *Thread( void * );
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
;
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" );
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" );
169 p_filter
->pf_audio_filter
= DoWork
;
171 p_sys
= p_filter
->p_sys
= (filter_sys_t
*)malloc( sizeof( *p_sys
) );
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
) )
191 vlc_sem_wait( &p_sys
->ready
);
194 vlc_join( p_sys
->thread
, NULL
);
201 vlc_sem_destroy( &p_sys
->ready
);
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
;
217 * XXX vlc_cleanup_push does not seems to work with C++ so no
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
);
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
++ )
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
);
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
);
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
;
291 unsigned int i_last_width
= 0;
292 unsigned int i_last_height
= 0;
296 projectM
*p_projectm
;
297 #ifndef HAVE_PROJECTM2
300 char *psz_preset_path
;
301 char *psz_title_font
;
303 projectM::Settings settings
;
308 /* Create the openGL provider */
310 (vout_thread_t
*)vlc_object_create( p_filter
, sizeof(vout_thread_t
) );
314 vlc_object_attach( p_sys
->p_vout
, p_filter
);
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 );
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;
333 p_sys
->p_vd
= vout_NewDisplay( p_sys
->p_vout
, &fmt
, &state
, "opengl",
337 vlc_object_release( p_sys
->p_vout
);
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
);
346 vout_DeleteDisplay( p_sys
->p_vd
, NULL
);
347 vlc_object_release( p_sys
->p_vout
);
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
);
361 psz_preset_path
= var_InheritString( p_filter
, "projectm-preset-path" );
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
);
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" );
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
,
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() );
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;
435 vlc_mutex_unlock( &p_sys
->lock
);
438 vout_DeleteDisplay( p_sys
->p_vd
, NULL
);
439 vlc_object_release( p_sys
->p_vout
);
440 if (loc
!= (locale_t
)0)
447 vlc_mutex_unlock( &p_sys
->lock
);
449 p_projectm
->renderFrame();
454 if( !vlc_gl_Lock(gl
) )
463 p_sys
->b_error
= true;
464 vlc_sem_post( &p_sys
->ready
);