]> git.sesse.net Git - vlc/blob - modules/visualization/projectm.cpp
macosx: fix main menu initialization order on startup
[vlc] / modules / visualization / projectm.cpp
1 /*****************************************************************************
2  * projectm.cpp: visualization module based on libprojectM
3  *****************************************************************************
4  * Copyright © 2009-2011 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Rémi Duraffort <ivoire@videolan.org>
8  *          Laurent Aimar
9  *
10   * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28 #ifndef __STDC_CONSTANT_MACROS
29 # define __STDC_CONSTANT_MACROS
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_aout.h>
35 #include <vlc_vout_window.h>
36 #include <vlc_opengl.h>
37 #include <vlc_filter.h>
38 #include <vlc_rand.h>
39
40 #include <libprojectM/projectM.hpp>
41
42 /*****************************************************************************
43  * Module descriptor
44  *****************************************************************************/
45 static int  Open         ( vlc_object_t * );
46 static void Close        ( vlc_object_t * );
47
48 #define CONFIG_TEXT N_("projectM configuration file")
49 #define CONFIG_LONGTEXT N_("File that will be used to configure the projectM " \
50                            "module.")
51
52 #define PRESET_PATH_TXT N_("projectM preset path")
53 #define PRESET_PATH_LONGTXT N_("Path to the projectM preset directory")
54
55 #define TITLE_FONT_TXT N_("Title font")
56 #define TITLE_FONT_LONGTXT N_("Font used for the titles")
57
58 #define MENU_FONT_TXT N_("Font menu")
59 #define MENU_FONT_LONGTXT N_("Font used for the menus")
60
61 #define WIDTH_TEXT N_("Video width")
62 #define WIDTH_LONGTEXT N_("The width of the video window, in pixels.")
63
64 #define HEIGHT_TEXT N_("Video height")
65 #define HEIGHT_LONGTEXT N_("The height of the video window, in pixels.")
66
67 #define MESHX_TEXT N_("Mesh width")
68 #define MESHX_LONGTEXT N_("The width of the mesh, in pixels.")
69
70 #define MESHY_TEXT N_("Mesh height")
71 #define MESHY_LONGTEXT N_("The height of the mesh, in pixels.")
72
73 #define TEXTURE_TEXT N_("Texture size")
74 #define TEXTURE_LONGTEXT N_("The size of the texture, in pixels.")
75
76 #ifdef _WIN32
77 # define FONT_PATH      "C:\\WINDOWS\\Fonts\\arial.ttf"
78 # define FONT_PATH_MENU "C:\\WINDOWS\\Fonts\\arial.ttf"
79 # define PRESET_PATH    NULL
80 #else
81 # define FONT_PATH      "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"
82 # define FONT_PATH_MENU "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf"
83 # define PRESET_PATH    "/usr/share/projectM/presets"
84 #endif
85
86 #ifdef DEFAULT_FONT_FILE
87 #undef FONT_PATH
88 #define FONT_PATH DEFAULT_FONT_FILE
89 #endif
90
91 #ifdef DEFAULT_MONOSPACE_FONT_FILE
92 #undef FONT_PATH_MENU
93 #define FONT_PATH_MENU DEFAULT_MONOSPACE_FONT_FILE
94 #endif
95
96 vlc_module_begin ()
97     set_shortname( N_("projectM"))
98     set_description( N_("libprojectM effect") )
99     set_capability( "visualization", 0 )
100     set_category( CAT_AUDIO )
101     set_subcategory( SUBCAT_AUDIO_VISUAL )
102 #ifndef HAVE_PROJECTM2
103     add_loadfile( "projectm-config", "/usr/share/projectM/config.inp",
104                   CONFIG_TEXT, CONFIG_LONGTEXT, true )
105 #else
106     add_directory( "projectm-preset-path", PRESET_PATH,
107                   PRESET_PATH_TXT, PRESET_PATH_LONGTXT, true )
108     add_loadfile( "projectm-title-font", FONT_PATH,
109                   TITLE_FONT_TXT, TITLE_FONT_LONGTXT, true )
110     add_loadfile( "projectm-menu-font", FONT_PATH_MENU,
111                   MENU_FONT_TXT, MENU_FONT_LONGTXT, true )
112 #endif
113     add_integer( "projectm-width", 800, WIDTH_TEXT, WIDTH_LONGTEXT,
114                  false )
115     add_integer( "projectm-height", 500, HEIGHT_TEXT, HEIGHT_LONGTEXT,
116                  false )
117     add_integer( "projectm-meshx", 32, MESHX_TEXT, MESHX_LONGTEXT,
118                  false )
119     add_integer( "projectm-meshy", 24, MESHY_TEXT, MESHY_LONGTEXT,
120                  false )
121     add_integer( "projectm-texture-size", 1024, TEXTURE_TEXT, TEXTURE_LONGTEXT,
122                  false )
123     add_shortcut( "projectm" )
124     set_callbacks( Open, Close )
125 vlc_module_end ()
126
127
128 /*****************************************************************************
129  * Local prototypes
130  *****************************************************************************/
131 struct filter_sys_t
132 {
133     /* */
134     vlc_thread_t thread;
135
136     /* Opengl */
137     vlc_gl_t  *gl;
138
139     /* audio info */
140     int i_channels;
141
142     /* */
143     vlc_mutex_t lock;
144     bool  b_quit;
145     float *p_buffer;
146     unsigned i_buffer_size;
147     unsigned i_nb_samples;
148 };
149
150
151 static block_t *DoWork( filter_t *, block_t * );
152 static void *Thread( void * );
153
154 /**
155  * Open the module
156  * @param p_this: the filter object
157  * @return VLC_SUCCESS or vlc error codes
158  */
159 static int Open( vlc_object_t * p_this )
160 {
161     filter_t     *p_filter = (filter_t *)p_this;
162     filter_sys_t *p_sys;
163
164     p_sys = p_filter->p_sys = (filter_sys_t*)malloc( sizeof( *p_sys ) );
165     if( !p_sys )
166         return VLC_ENOMEM;
167
168     /* Create the object for the thread */
169     p_sys->b_quit        = false;
170     p_sys->i_channels    = aout_FormatNbChannels( &p_filter->fmt_in.audio );
171     vlc_mutex_init( &p_sys->lock );
172     p_sys->p_buffer      = NULL;
173     p_sys->i_buffer_size = 0;
174     p_sys->i_nb_samples  = 0;
175
176     /* Create the OpenGL context */
177     vout_window_cfg_t cfg;
178
179     memset(&cfg, 0, sizeof (cfg));
180     cfg.width = var_CreateGetInteger( p_filter, "projectm-width" );
181     cfg.height = var_CreateGetInteger( p_filter, "projectm-height" );
182
183     p_sys->gl = vlc_gl_surface_Create( VLC_OBJECT(p_filter), &cfg, NULL );
184     if( p_sys->gl == NULL )
185         goto error;
186
187     /* Create the thread */
188     if( vlc_clone( &p_sys->thread, Thread, p_filter,
189                    VLC_THREAD_PRIORITY_LOW ) )
190     {
191         vlc_gl_surface_Destroy( p_sys->gl );
192         goto error;
193     }
194
195     p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
196     p_filter->fmt_out.audio = p_filter->fmt_in.audio;
197     p_filter->pf_audio_filter = DoWork;
198     return VLC_SUCCESS;
199
200 error:
201     vlc_mutex_destroy( &p_sys->lock );
202     free (p_sys );
203     return VLC_EGENERIC;
204 }
205
206
207 /**
208  * Close the module
209  * @param p_this: the filter object
210  */
211 static void Close( vlc_object_t *p_this )
212 {
213     filter_t  *p_filter = (filter_t *)p_this;
214     filter_sys_t *p_sys = p_filter->p_sys;
215
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 );
222
223     vlc_join( p_sys->thread, NULL );
224
225     /* Free the ressources */
226     vlc_gl_surface_Destroy( p_sys->gl );
227     vlc_mutex_destroy( &p_sys->lock );
228     free( p_sys->p_buffer );
229     free( p_sys );
230 }
231
232
233 /**
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
239  */
240 static block_t *DoWork( filter_t *p_filter, block_t *p_in_buf )
241 {
242     filter_sys_t *p_sys = p_filter->p_sys;
243
244     vlc_mutex_lock( &p_sys->lock );
245     if( p_sys->i_buffer_size > 0 )
246     {
247         p_sys->i_nb_samples = __MIN( p_sys->i_buffer_size,
248                                      p_in_buf->i_nb_samples );
249
250         const float *p_src = (float*)p_in_buf->p_buffer;
251         for( unsigned i = 0; i < p_sys->i_nb_samples; i++ )
252         {
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;
257         }
258     }
259     vlc_mutex_unlock( &p_sys->lock );
260
261     return p_in_buf;
262 }
263
264 /**
265  * ProjectM update thread which do the rendering
266  * @param p_this: the p_thread object
267  */
268 static void *Thread( void *p_data )
269 {
270     filter_t  *p_filter = (filter_t*)p_data;
271     filter_sys_t *p_sys = p_filter->p_sys;
272     vlc_gl_t *gl = p_sys->gl;
273     locale_t loc;
274     locale_t oldloc;
275
276     projectM *p_projectm;
277 #ifndef HAVE_PROJECTM2
278     char *psz_config;
279 #else
280     char *psz_preset_path;
281     char *psz_title_font;
282     char *psz_menu_font;
283     projectM::Settings settings;
284 #endif
285
286     vlc_gl_MakeCurrent( gl );
287
288     /* Work-around the projectM locale bug */
289     loc = newlocale (LC_NUMERIC_MASK, "C", NULL);
290     oldloc = uselocale (loc);
291
292     /* Create the projectM object */
293 #ifndef HAVE_PROJECTM2
294     psz_config = var_InheritString( p_filter, "projectm-config" );
295     p_projectm = new projectM( psz_config );
296     free( psz_config );
297 #else
298     psz_preset_path = var_InheritString( p_filter, "projectm-preset-path" );
299 #ifdef _WIN32
300     if ( psz_preset_path == NULL )
301     {
302         char *psz_data_path = config_GetDataDir();
303         asprintf( &psz_preset_path, "%s" DIR_SEP "visualization", psz_data_path );
304         free( psz_data_path );
305     }
306 #endif
307
308     psz_title_font                = var_InheritString( p_filter, "projectm-title-font" );
309     psz_menu_font                 = var_InheritString( p_filter, "projectm-menu-font" );
310
311     settings.meshX                = var_InheritInteger( p_filter, "projectm-meshx" );
312     settings.meshY                = var_InheritInteger( p_filter, "projectm-meshy" );
313     settings.fps                  = 35;
314     settings.textureSize          = var_InheritInteger( p_filter, "projectm-texture-size" );
315     settings.windowWidth          = var_InheritInteger( p_filter, "projectm-width" );
316     settings.windowHeight         = var_CreateGetInteger( p_filter, "projectm-height" );
317     settings.presetURL            = psz_preset_path;
318     settings.titleFontURL         = psz_title_font;
319     settings.menuFontURL          = psz_menu_font;
320     settings.smoothPresetDuration = 5;
321     settings.presetDuration       = 30;
322     settings.beatSensitivity      = 10;
323     settings.aspectCorrection     = 1;
324     settings.easterEgg            = 1;
325     settings.shuffleEnabled       = 1;
326
327     p_projectm = new projectM( settings );
328
329     free( psz_menu_font );
330     free( psz_title_font );
331     free( psz_preset_path );
332 #endif /* HAVE_PROJECTM2 */
333
334     p_sys->i_buffer_size = p_projectm->pcm()->maxsamples;
335     p_sys->p_buffer = (float*)calloc( p_sys->i_buffer_size,
336                                       sizeof( float ) );
337
338     /* Choose a preset randomly or projectM will always show the first one */
339     if ( p_projectm->getPlaylistSize() > 0 )
340         p_projectm->selectPreset( (unsigned)vlc_mrand48() % p_projectm->getPlaylistSize() );
341
342     /* */
343     for( ;; )
344     {
345         const mtime_t i_deadline = mdate() + CLOCK_FREQ / 50; /* 50 fps max */
346
347         /* Manage the events */
348         unsigned width, height;
349         bool quit;
350
351         if( vlc_gl_surface_CheckSize( gl, &width, &height ) )
352             p_projectm->projectM_resetGL( width, height );
353
354         /* Render the image and swap the buffers */
355         vlc_mutex_lock( &p_sys->lock );
356         if( p_sys->i_nb_samples > 0 )
357         {
358             p_projectm->pcm()->addPCMfloat( p_sys->p_buffer,
359                                             p_sys->i_nb_samples );
360             p_sys->i_nb_samples = 0;
361         }
362         quit = p_sys->b_quit;
363         vlc_mutex_unlock( &p_sys->lock );
364
365         if( quit )
366             break;
367
368         p_projectm->renderFrame();
369
370         /* */
371         mwait( i_deadline );
372
373         if( !vlc_gl_Lock(gl) )
374         {
375             vlc_gl_Swap( gl );
376             vlc_gl_Unlock( gl );
377         }
378     }
379
380     delete p_projectm;
381
382     if (loc != (locale_t)0)
383     {
384         uselocale (oldloc);
385         freelocale (loc);
386     }
387
388     vlc_gl_ReleaseCurrent( gl );
389     return NULL;
390 }