1 /*****************************************************************************
2 * projectm.cpp: visualization module based on libprojectM
3 *****************************************************************************
4 * Copyright © 2009-2011 VLC authors and VideoLAN
6 * Authors: Rémi Duraffort <ivoire@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
31 #include <vlc_vout_window.h>
32 #include <vlc_opengl.h>
33 #include <vlc_filter.h>
36 #include <libprojectM/projectM.hpp>
45 /*****************************************************************************
47 *****************************************************************************/
48 static int Open ( vlc_object_t
* );
49 static void Close ( filter_t
* );
51 #define CONFIG_TEXT N_("projectM configuration file")
52 #define CONFIG_LONGTEXT N_("File that will be used to configure the projectM " \
55 #define PRESET_PATH_TXT N_("projectM preset path")
56 #define PRESET_PATH_LONGTXT N_("Path to the projectM preset directory")
58 #define TITLE_FONT_TXT N_("Title font")
59 #define TITLE_FONT_LONGTXT N_("Font used for the titles")
61 #define MENU_FONT_TXT N_("Font menu")
62 #define MENU_FONT_LONGTXT N_("Font used for the menus")
64 #define WIDTH_TEXT N_("Video width")
65 #define WIDTH_LONGTEXT N_("The width of the video window, in pixels.")
67 #define HEIGHT_TEXT N_("Video height")
68 #define HEIGHT_LONGTEXT N_("The height of the video window, in pixels.")
70 #define MESHX_TEXT N_("Mesh width")
71 #define MESHX_LONGTEXT N_("The width of the mesh, in pixels.")
73 #define MESHY_TEXT N_("Mesh height")
74 #define MESHY_LONGTEXT N_("The height of the mesh, in pixels.")
76 #define TEXTURE_TEXT N_("Texture size")
77 #define TEXTURE_LONGTEXT N_("The size of the texture, in pixels.")
80 # define FONT_PATH "C:\\WINDOWS\\Fonts\\arial.ttf"
81 # define FONT_PATH_MENU "C:\\WINDOWS\\Fonts\\arial.ttf"
82 # define PRESET_PATH NULL
84 # define FONT_PATH "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"
85 # define FONT_PATH_MENU "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf"
86 # define PRESET_PATH "/usr/share/projectM/presets"
89 #ifdef DEFAULT_FONT_FILE
91 #define FONT_PATH DEFAULT_FONT_FILE
94 #ifdef DEFAULT_MONOSPACE_FONT_FILE
96 #define FONT_PATH_MENU DEFAULT_MONOSPACE_FONT_FILE
100 set_shortname( N_("projectM"))
101 set_description( N_("libprojectM effect") )
102 set_capability( "visualization", 0 )
103 set_category( CAT_AUDIO
)
104 set_subcategory( SUBCAT_AUDIO_VISUAL
)
105 #ifndef HAVE_PROJECTM2
106 add_loadfile("projectm-config", "/usr/share/projectM/config.inp",
107 CONFIG_TEXT
, CONFIG_LONGTEXT
)
109 add_directory("projectm-preset-path", PRESET_PATH
,
110 PRESET_PATH_TXT
, PRESET_PATH_LONGTXT
)
111 add_loadfile("projectm-title-font", FONT_PATH
,
112 TITLE_FONT_TXT
, TITLE_FONT_LONGTXT
)
113 add_loadfile("projectm-menu-font", FONT_PATH_MENU
,
114 MENU_FONT_TXT
, MENU_FONT_LONGTXT
)
116 add_integer( "projectm-width", 800, WIDTH_TEXT
, WIDTH_LONGTEXT
,
118 add_integer( "projectm-height", 500, HEIGHT_TEXT
, HEIGHT_LONGTEXT
,
120 add_integer( "projectm-meshx", 32, MESHX_TEXT
, MESHX_LONGTEXT
,
122 add_integer( "projectm-meshy", 24, MESHY_TEXT
, MESHY_LONGTEXT
,
124 add_integer( "projectm-texture-size", 1024, TEXTURE_TEXT
, TEXTURE_LONGTEXT
,
126 add_shortcut( "projectm" )
131 /*****************************************************************************
133 *****************************************************************************/
151 unsigned i_buffer_size
;
152 unsigned i_nb_samples
;
157 static block_t
*DoWork( filter_t
*, block_t
* );
158 static void *Thread( void * );
160 static const struct FilterOperationInitializer
{
161 struct vlc_filter_operations ops
{};
162 FilterOperationInitializer()
164 ops
.filter_audio
= DoWork
;
171 * @param p_this: the filter object
172 * @return VLC_SUCCESS or vlc error codes
174 static int Open( vlc_object_t
* p_this
)
176 filter_t
*p_filter
= (filter_t
*)p_this
;
179 p_filter
->p_sys
= p_sys
= (filter_sys_t
*)malloc( sizeof( *p_sys
) );
183 /* Create the object for the thread */
184 p_sys
->b_quit
= false;
185 p_sys
->i_channels
= aout_FormatNbChannels( &p_filter
->fmt_in
.audio
);
186 vlc_mutex_init( &p_sys
->lock
);
187 p_sys
->p_buffer
= NULL
;
188 p_sys
->i_buffer_size
= 0;
189 p_sys
->i_nb_samples
= 0;
191 /* Create the OpenGL context */
192 vout_window_cfg_t cfg
;
194 memset(&cfg
, 0, sizeof (cfg
));
195 cfg
.width
= var_CreateGetInteger( p_filter
, "projectm-width" );
196 cfg
.height
= var_CreateGetInteger( p_filter
, "projectm-height" );
198 p_sys
->gl
= vlc_gl_surface_Create( VLC_OBJECT(p_filter
), &cfg
, NULL
);
199 if( p_sys
->gl
== NULL
)
202 /* Create the thread */
203 if( vlc_clone( &p_sys
->thread
, Thread
, p_filter
,
204 VLC_THREAD_PRIORITY_LOW
) )
206 vlc_gl_surface_Destroy( p_sys
->gl
);
210 p_filter
->fmt_in
.audio
.i_format
= VLC_CODEC_FL32
;
211 p_filter
->fmt_out
.audio
= p_filter
->fmt_in
.audio
;
212 p_filter
->ops
= &filter_ops
.ops
;
223 * @param p_this: the filter object
225 static void Close( filter_t
*p_filter
)
227 filter_sys_t
*p_sys
= reinterpret_cast<filter_sys_t
*>( p_filter
->p_sys
);
230 * XXX vlc_cleanup_push does not seems to work with C++ so no
232 vlc_mutex_lock( &p_sys
->lock
);
233 p_sys
->b_quit
= true;
234 vlc_mutex_unlock( &p_sys
->lock
);
236 vlc_join( p_sys
->thread
, NULL
);
238 /* Free the ressources */
239 vlc_gl_surface_Destroy( p_sys
->gl
);
240 free( p_sys
->p_buffer
);
246 * Do the actual work with the new sample
247 * @param p_aout: audio output object
248 * @param p_filter: filter object
249 * @param p_in_buf: input buffer
250 * @param p_out_buf: output buffer
252 static block_t
*DoWork( filter_t
*p_filter
, block_t
*p_in_buf
)
254 filter_sys_t
*p_sys
= reinterpret_cast<filter_sys_t
*>( p_filter
->p_sys
);
256 vlc_mutex_lock( &p_sys
->lock
);
257 if( p_sys
->i_buffer_size
> 0 )
259 p_sys
->i_nb_samples
= __MIN( p_sys
->i_buffer_size
,
260 p_in_buf
->i_nb_samples
);
262 const float *p_src
= (float*)p_in_buf
->p_buffer
;
263 for( unsigned i
= 0; i
< p_sys
->i_nb_samples
; i
++ )
266 for( int j
= 0; j
< p_sys
->i_channels
; j
++ )
267 v
+= p_src
[p_sys
->i_channels
* i
+ j
];
268 p_sys
->p_buffer
[i
] = v
/ p_sys
->i_channels
;
271 vlc_mutex_unlock( &p_sys
->lock
);
277 * ProjectM update thread which do the rendering
278 * @param p_this: the p_thread object
280 static void *Thread( void *p_data
)
282 filter_t
*p_filter
= (filter_t
*)p_data
;
283 filter_sys_t
*p_sys
= reinterpret_cast<filter_sys_t
*>( p_filter
->p_sys
);
284 vlc_gl_t
*gl
= p_sys
->gl
;
288 projectM
*p_projectm
;
289 #ifndef HAVE_PROJECTM2
292 char *psz_preset_path
;
293 char *psz_title_font
;
295 projectM::Settings settings
;
298 if( vlc_gl_MakeCurrent( gl
) != VLC_SUCCESS
)
300 msg_Err( p_filter
, "Can't attach gl context" );
304 /* Work-around the projectM locale bug */
305 loc
= newlocale (LC_NUMERIC_MASK
, "C", NULL
);
306 oldloc
= uselocale (loc
);
308 /* Create the projectM object */
309 #ifndef HAVE_PROJECTM2
310 psz_config
= var_InheritString( p_filter
, "projectm-config" );
311 p_projectm
= new projectM( psz_config
);
314 psz_preset_path
= var_InheritString( p_filter
, "projectm-preset-path" );
316 if ( psz_preset_path
== NULL
)
317 psz_preset_path
= config_GetSysPath(VLC_PKG_DATA_DIR
, "visualization");
320 psz_title_font
= var_InheritString( p_filter
, "projectm-title-font" );
321 psz_menu_font
= var_InheritString( p_filter
, "projectm-menu-font" );
323 settings
.meshX
= var_InheritInteger( p_filter
, "projectm-meshx" );
324 settings
.meshY
= var_InheritInteger( p_filter
, "projectm-meshy" );
326 settings
.textureSize
= var_InheritInteger( p_filter
, "projectm-texture-size" );
327 settings
.windowWidth
= var_InheritInteger( p_filter
, "projectm-width" );
328 settings
.windowHeight
= var_CreateGetInteger( p_filter
, "projectm-height" );
329 settings
.presetURL
= psz_preset_path
;
330 settings
.titleFontURL
= psz_title_font
;
331 settings
.menuFontURL
= psz_menu_font
;
332 settings
.smoothPresetDuration
= 5;
333 settings
.presetDuration
= 30;
334 settings
.beatSensitivity
= 10;
335 settings
.aspectCorrection
= 1;
336 settings
.easterEgg
= 1;
337 settings
.shuffleEnabled
= 1;
338 settings
.softCutRatingsEnabled
= false;
340 p_projectm
= new projectM( settings
);
342 free( psz_menu_font
);
343 free( psz_title_font
);
344 free( psz_preset_path
);
345 #endif /* HAVE_PROJECTM2 */
347 p_sys
->i_buffer_size
= p_projectm
->pcm()->maxsamples
;
348 p_sys
->p_buffer
= (float*)calloc( p_sys
->i_buffer_size
,
351 /* Choose a preset randomly or projectM will always show the first one */
352 if ( p_projectm
->getPlaylistSize() > 0 )
353 p_projectm
->selectPreset( (unsigned)vlc_mrand48() % p_projectm
->getPlaylistSize() );
358 const vlc_tick_t i_deadline
= vlc_tick_now() + VLC_TICK_FROM_MS(20); /* 50 fps max */
360 /* Manage the events */
361 unsigned width
, height
;
364 if( vlc_gl_surface_CheckSize( gl
, &width
, &height
) )
365 p_projectm
->projectM_resetGL( width
, height
);
367 /* Render the image and swap the buffers */
368 vlc_mutex_lock( &p_sys
->lock
);
369 if( p_sys
->i_nb_samples
> 0 )
371 p_projectm
->pcm()->addPCMfloat( p_sys
->p_buffer
,
372 p_sys
->i_nb_samples
);
373 p_sys
->i_nb_samples
= 0;
375 quit
= p_sys
->b_quit
;
376 vlc_mutex_unlock( &p_sys
->lock
);
381 p_projectm
->renderFrame();
384 vlc_tick_wait( i_deadline
);
391 if (loc
!= (locale_t
)0)
397 vlc_gl_ReleaseCurrent( gl
);