input: add input_SetProgramId
[vlc.git] / modules / visualization / projectm.cpp
bloba12ac328b8e71edcc446944f95c54372822ba656
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>
7 * Laurent Aimar
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 *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
30 #include <vlc_aout.h>
31 #include <vlc_vout_window.h>
32 #include <vlc_opengl.h>
33 #include <vlc_filter.h>
34 #include <vlc_rand.h>
36 #include <libprojectM/projectM.hpp>
38 #ifndef _WIN32
39 # include <locale.h>
40 #endif
41 #ifdef HAVE_XLOCALE_H
42 # include <xlocale.h>
43 #endif
45 /*****************************************************************************
46 * Module descriptor
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 " \
53 "module.")
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.")
79 #ifdef _WIN32
80 # define FONT_PATH "C:\\WINDOWS\\Fonts\\arial.ttf"
81 # define FONT_PATH_MENU "C:\\WINDOWS\\Fonts\\arial.ttf"
82 # define PRESET_PATH NULL
83 #else
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"
87 #endif
89 #ifdef DEFAULT_FONT_FILE
90 #undef FONT_PATH
91 #define FONT_PATH DEFAULT_FONT_FILE
92 #endif
94 #ifdef DEFAULT_MONOSPACE_FONT_FILE
95 #undef FONT_PATH_MENU
96 #define FONT_PATH_MENU DEFAULT_MONOSPACE_FONT_FILE
97 #endif
99 vlc_module_begin ()
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)
108 #else
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)
115 #endif
116 add_integer( "projectm-width", 800, WIDTH_TEXT, WIDTH_LONGTEXT,
117 false )
118 add_integer( "projectm-height", 500, HEIGHT_TEXT, HEIGHT_LONGTEXT,
119 false )
120 add_integer( "projectm-meshx", 32, MESHX_TEXT, MESHX_LONGTEXT,
121 false )
122 add_integer( "projectm-meshy", 24, MESHY_TEXT, MESHY_LONGTEXT,
123 false )
124 add_integer( "projectm-texture-size", 1024, TEXTURE_TEXT, TEXTURE_LONGTEXT,
125 false )
126 add_shortcut( "projectm" )
127 set_callback( Open )
128 vlc_module_end ()
131 /*****************************************************************************
132 * Local prototypes
133 *****************************************************************************/
134 namespace {
136 struct filter_sys_t
138 /* */
139 vlc_thread_t thread;
141 /* Opengl */
142 vlc_gl_t *gl;
144 /* audio info */
145 int i_channels;
147 /* */
148 vlc_mutex_t lock;
149 bool b_quit;
150 float *p_buffer;
151 unsigned i_buffer_size;
152 unsigned i_nb_samples;
155 } // namespace
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;
165 ops.close = Close;
167 } filter_ops;
170 * Open the module
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;
177 filter_sys_t *p_sys;
179 p_filter->p_sys = p_sys = (filter_sys_t*)malloc( sizeof( *p_sys ) );
180 if( !p_sys )
181 return VLC_ENOMEM;
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 )
200 goto error;
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 );
207 goto error;
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;
213 return VLC_SUCCESS;
215 error:
216 free (p_sys );
217 return VLC_EGENERIC;
222 * Close the module
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 );
229 /* Stop the thread
230 * XXX vlc_cleanup_push does not seems to work with C++ so no
231 * vlc_cancel()... */
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 );
241 free( p_sys );
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++ )
265 float v = 0;
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 );
273 return p_in_buf;
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;
285 locale_t loc;
286 locale_t oldloc;
288 projectM *p_projectm;
289 #ifndef HAVE_PROJECTM2
290 char *psz_config;
291 #else
292 char *psz_preset_path;
293 char *psz_title_font;
294 char *psz_menu_font;
295 projectM::Settings settings;
296 #endif
298 if( vlc_gl_MakeCurrent( gl ) != VLC_SUCCESS )
300 msg_Err( p_filter, "Can't attach gl context" );
301 return NULL;
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 );
312 free( psz_config );
313 #else
314 psz_preset_path = var_InheritString( p_filter, "projectm-preset-path" );
315 #ifdef _WIN32
316 if ( psz_preset_path == NULL )
317 psz_preset_path = config_GetSysPath(VLC_PKG_DATA_DIR, "visualization");
318 #endif
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" );
325 settings.fps = 35;
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,
349 sizeof( float ) );
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() );
355 /* */
356 for( ;; )
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;
362 bool quit;
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 );
378 if( quit )
379 break;
381 p_projectm->renderFrame();
383 /* */
384 vlc_tick_wait( i_deadline );
386 vlc_gl_Swap( gl );
389 delete p_projectm;
391 if (loc != (locale_t)0)
393 uselocale (oldloc);
394 freelocale (loc);
397 vlc_gl_ReleaseCurrent( gl );
398 return NULL;