]> git.sesse.net Git - vlc/blob - modules/visualization/projectm.cpp
cbb158a97e45d7fe8c785bf62cb98664c019a7e8
[vlc] / modules / visualization / projectm.cpp
1 /*****************************************************************************
2  * projectm: visualization module based on libprojectM
3  *****************************************************************************
4  * Copyright (C) 2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: RĂ©mi Duraffort <ivoire@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by the Free
11  * Software Foundation; either version 2 of the License, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17  * more details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program; if not, write to the Free Software Foundation, Inc., 51
21  * Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27 #ifndef __STDC_CONSTANT_MACROS
28 # define __STDC_CONSTANT_MACROS
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_aout.h>
34 #include <vlc_vout.h>
35 #include <vlc_vout_wrapper.h>
36 #include <vlc_filter.h>
37
38 #include <libprojectM/projectM.hpp>
39
40
41 /*****************************************************************************
42  * Module descriptor
43  *****************************************************************************/
44 static int  Open         ( vlc_object_t * );
45 static void Close        ( vlc_object_t * );
46
47 #define CONFIG_TEXT N_("projectM configuration file")
48 #define CONFIG_LONGTEXT N_("File that will be used to configure the projectM " \
49                            "module.")
50
51 #define PRESET_PATH_TXT N_("projectM preset path")
52 #define PRESET_PATH_LONGTXT N_("Path to the projectM preset directory")
53
54 #define TITLE_FONT_TXT N_("Title font")
55 #define TITLE_FONT_LONGTXT N_("Font used for the titles")
56
57 #define MENU_FONT_TXT N_("Font menu")
58 #define MENU_FONT_LONGTXT N_("Font used for the menus")
59
60 #define WIDTH_TEXT N_("Video width")
61 #define WIDTH_LONGTEXT N_("The width of the video window, in pixels.")
62
63 #define HEIGHT_TEXT N_("Video height")
64 #define HEIGHT_LONGTEXT N_("The height of the video window, in pixels.")
65
66 vlc_module_begin ()
67     set_shortname( N_("projectM"))
68     set_description( N_("libprojectM effect") )
69     set_capability( "visualization2", 0 )
70     set_category( CAT_AUDIO )
71     set_subcategory( SUBCAT_AUDIO_VISUAL )
72 #ifndef HAVE_PROJECTM2
73     add_file( "projectm-config", "/usr/share/projectM/config.inp", NULL,
74               CONFIG_TEXT, CONFIG_LONGTEXT, true )
75 #else
76     add_file( "projectm-preset-path", "/usr/share/projectM/presets", NULL,
77               PRESET_PATH_TXT, PRESET_PATH_LONGTXT, true )
78     add_file( "projectm-title-font", "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", NULL,
79               TITLE_FONT_TXT, TITLE_FONT_LONGTXT, true )
80     add_file( "projectm-menu-font", "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf", NULL,
81               MENU_FONT_TXT, MENU_FONT_LONGTXT, true )
82 #endif
83     add_integer( "projectm-width", 800, NULL, WIDTH_TEXT, WIDTH_LONGTEXT,
84                  false )
85     add_integer( "projectm-height", 640, NULL, HEIGHT_TEXT, HEIGHT_LONGTEXT,
86                  false )
87     add_shortcut( "projectm" )
88     set_callbacks( Open, Close )
89 vlc_module_end ()
90
91
92 /*****************************************************************************
93  * Local prototypes
94  *****************************************************************************/
95 struct filter_sys_t
96 {
97     /* */
98     vlc_thread_t thread;
99     vlc_sem_t    ready;
100     bool         b_error;
101
102     /* Opengl */
103     vout_thread_t  *p_vout;
104     vout_display_t *p_vd;
105
106     /* libprojectM objects */
107     projectM      *p_projectm;
108 #ifndef HAVE_PROJECTM2
109     char          *psz_config;
110 #else
111     char          *psz_preset_path;
112     char          *psz_title_font;
113     char          *psz_menu_font;
114 #endif
115
116     /* Window size */
117     int i_width;
118     int i_height;
119
120     /* audio info */
121     int i_channels;
122
123     /* */
124     vlc_mutex_t lock;
125     bool  b_quit;
126     float *p_buffer;
127     int   i_buffer_size;
128     int   i_nb_samples;
129 };
130
131
132 static block_t *DoWork( filter_t *, block_t * );
133 static void *Thread( void * );
134
135 /**
136  * Open the module
137  * @param p_this: the filter object
138  * @return VLC_SUCCESS or vlc error codes
139  */
140 static int Open( vlc_object_t * p_this )
141 {
142     filter_t     *p_filter = (filter_t *)p_this;
143     filter_sys_t *p_sys;
144
145     /* Test the audio format */
146     if( p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32 ||
147         p_filter->fmt_out.audio.i_format != VLC_CODEC_FL32 )
148     {
149         msg_Warn( p_filter, "bad input or output format" );
150         return VLC_EGENERIC;
151     }
152     if( !AOUT_FMTS_SIMILAR( &p_filter->fmt_in.audio, &p_filter->fmt_out.audio ) )
153     {
154         msg_Warn( p_filter, "input and outut are not similar" );
155         return VLC_EGENERIC;
156     }
157
158     p_filter->pf_audio_filter = DoWork;
159
160     p_sys = p_filter->p_sys = (filter_sys_t*)malloc( sizeof( *p_sys ) );
161     if( !p_sys )
162         return VLC_ENOMEM;
163
164     /* Create the object for the thread */
165     vlc_sem_init( &p_sys->ready, 0 );
166     p_sys->b_error  = false;
167     p_sys->b_quit   = false;
168     p_sys->i_width  = var_InheritInteger( p_filter, "projectm-width" );
169     p_sys->i_height = var_InheritInteger( p_filter, "projectm-height" );
170     p_sys->i_channels = aout_FormatNbChannels( &p_filter->fmt_in.audio );
171 #ifndef HAVE_PROJECTM2
172     p_sys->psz_config = var_InheritString( p_filter, "projectm-config" );
173 #else
174     p_sys->psz_preset_path = var_InheritString( p_filter, "projectm-preset-path" );
175     p_sys->psz_title_font = var_InheritString( p_filter, "projectm-title-font" );
176     p_sys->psz_menu_font = var_InheritString( p_filter, "projectm-menu-font" );
177 #endif
178     vlc_mutex_init( &p_sys->lock );
179     p_sys->p_buffer = NULL;
180     p_sys->i_buffer_size = 0;
181     p_sys->i_nb_samples = 0;
182
183     /* Create the thread */
184     if( vlc_clone( &p_sys->thread, Thread, p_filter, VLC_THREAD_PRIORITY_LOW ) )
185         goto error;
186
187     vlc_sem_wait( &p_sys->ready );
188     if( p_sys->b_error )
189     {
190         vlc_join( p_sys->thread, NULL );
191         goto error;
192     }
193
194     return VLC_SUCCESS;
195
196 error:
197     vlc_sem_destroy( &p_sys->ready );
198     free (p_sys );
199     return VLC_EGENERIC;
200 }
201
202
203 /**
204  * Close the module
205  * @param p_this: the filter object
206  */
207 static void Close( vlc_object_t *p_this )
208 {
209     filter_t     *p_filter = (filter_t *)p_this;
210     filter_sys_t *p_sys = p_filter->p_sys;
211
212     /* Stop the thread
213      * XXX vlc_cleanup_push does not seems to work with C++ so no
214      * vlc_cancel()... */
215     vlc_mutex_lock( &p_sys->lock );
216     p_sys->b_quit = true;
217     vlc_mutex_unlock( &p_sys->lock );
218
219     vlc_join( p_sys->thread, NULL );
220
221     /* Free the ressources */
222     vlc_sem_destroy( &p_sys->ready );
223     vlc_mutex_destroy( &p_sys->lock );
224     free( p_sys->p_buffer );
225 #ifndef HAVE_PROJECTM2
226     free( p_sys->psz_config );
227 #else
228     free( p_sys->psz_preset_path );
229     free( p_sys->psz_title_font );
230     free( p_sys->psz_menu_font );
231 #endif
232     free( p_sys );
233 }
234
235
236 /**
237  * Do the actual work with the new sample
238  * @param p_aout: audio output object
239  * @param p_filter: filter object
240  * @param p_in_buf: input buffer
241  * @param p_out_buf: output buffer
242  */
243 static block_t *DoWork( filter_t *p_filter, block_t *p_in_buf )
244 {
245     filter_sys_t *p_sys = p_filter->p_sys;
246
247     vlc_mutex_lock( &p_sys->lock );
248     if( p_sys->i_buffer_size > 0 )
249     {
250         p_sys->i_nb_samples = __MIN( p_sys->i_buffer_size,
251                                      p_in_buf->i_nb_samples );
252
253         const float *p_src = (float*)p_in_buf->p_buffer;
254         for( int i = 0; i < p_sys->i_nb_samples; i++ )
255         {
256             float v = 0;
257             for( int j = 0; j < p_sys->i_channels; j++ )
258                 v += p_src[p_sys->i_channels * i + j];
259             p_sys->p_buffer[i] = v / p_sys->i_channels;
260         }
261     }
262     vlc_mutex_unlock( &p_sys->lock );
263
264     return p_in_buf;
265 }
266
267 /**
268  * Variable callback for the dummy vout
269  */
270 static int VoutCallback( vlc_object_t *p_vout, char const *psz_name,
271                          vlc_value_t oldv, vlc_value_t newv, void *p_data )
272 {
273     vout_display_t *p_vd = (vout_display_t*)p_data;
274
275     if( !strcmp(psz_name, "fullscreen") )
276     {
277         vout_SetDisplayFullscreen( p_vd, newv.b_bool );
278     }
279     return VLC_SUCCESS;
280 }
281
282 /**
283  * ProjectM update thread which do the rendering
284  * @param p_this: the p_thread object
285  */
286 static void *Thread( void *p_data )
287 {
288     filter_t     *p_filter = (filter_t*)p_data;
289     filter_sys_t *p_sys = p_filter->p_sys;
290     int cancel = vlc_savecancel();
291     video_format_t fmt;
292     vout_opengl_t *gl;
293     int i_last_width  = 0;
294     int i_last_height = 0;
295 #ifdef HAVE_PROJECTM2
296     projectM::Settings settings;
297 #endif
298
299     /* Create the openGL provider */
300     p_sys->p_vout =
301         (vout_thread_t *)vlc_object_create( p_filter, sizeof(vout_thread_t) );
302     if( !p_sys->p_vout )
303         goto error;
304
305     /* */
306     video_format_Init( &fmt, 0 );
307     video_format_Setup( &fmt, VLC_CODEC_RGB32,
308                         p_sys->i_width, p_sys->i_height, 0, 1 );
309     fmt.i_sar_num = 1;
310     fmt.i_sar_den = 1;
311
312     vout_display_state_t state;
313     memset( &state, 0, sizeof(state) );
314     state.cfg.display.sar.num = 1;
315     state.cfg.display.sar.den = 1;
316     state.cfg.is_display_filled = true;
317     state.cfg.zoom.num = 1;
318     state.cfg.zoom.den = 1;
319     state.sar.num = 1;
320     state.sar.den = 1;
321
322     p_sys->p_vd = vout_NewDisplay( p_sys->p_vout, &fmt, &state, "opengl",
323                                    300000, 1000000 );
324     if( !p_sys->p_vd )
325     {
326         vlc_object_release( p_sys->p_vout );
327         goto error;
328     }
329     var_Create( p_sys->p_vout, "fullscreen", VLC_VAR_BOOL );
330     var_AddCallback( p_sys->p_vout, "fullscreen", VoutCallback, p_sys->p_vd );
331
332     gl = vout_GetDisplayOpengl( p_sys->p_vd );
333     if( !gl )
334     {
335         vout_DeleteDisplay( p_sys->p_vd, NULL );
336         vlc_object_release( p_sys->p_vout );
337         goto error;
338     }
339
340     /* Create the projectM object */
341 #ifndef HAVE_PROJECTM2
342     p_sys->p_projectm = new projectM( p_sys->psz_config );
343 #else
344     settings.meshX = 32;
345     settings.meshY = 24;
346     settings.fps = 35;
347     settings.textureSize = 1024;
348     settings.windowWidth = p_sys->i_width;
349     settings.windowHeight = p_sys->i_height;
350     settings.presetURL = p_sys->psz_preset_path;
351     settings.titleFontURL =  p_sys->psz_title_font;
352     settings.menuFontURL = p_sys->psz_menu_font;
353     settings.smoothPresetDuration = 5;
354     settings.presetDuration = 30;
355     settings.beatSensitivity = 10;
356     settings.aspectCorrection = 1;
357     settings.easterEgg = 1;
358     settings.shuffleEnabled = 1;
359     p_sys->p_projectm = new projectM( settings );
360 #endif
361     p_sys->i_buffer_size = p_sys->p_projectm->pcm()->maxsamples;
362     p_sys->p_buffer = (float*)calloc( p_sys->i_buffer_size,
363                                       sizeof( float ) );
364
365     vlc_sem_post( &p_sys->ready );
366
367     /* TODO: Give to projectm the name of the input
368     p_sys->p_projectm->projectM_setTitle( "" ); */
369
370     /* */
371     for( ;; )
372     {
373         const mtime_t i_deadline = mdate() + CLOCK_FREQ / 50; /* 50 fps max */
374         /* Manage the events */
375         vout_ManageDisplay( p_sys->p_vd, true );
376         if( p_sys->p_vd->cfg->display.width  != i_last_width ||
377             p_sys->p_vd->cfg->display.height != i_last_height )
378         {
379             /* FIXME it is not perfect as we will have black bands */
380             vout_display_place_t place;
381             vout_display_PlacePicture( &place, &p_sys->p_vd->source, p_sys->p_vd->cfg, false );
382             p_sys->p_projectm->projectM_resetGL( place.width, place.height );
383
384             i_last_width  = p_sys->p_vd->cfg->display.width;
385             i_last_height = p_sys->p_vd->cfg->display.height;
386         }
387
388         /* Render the image and swap the buffers */
389         vlc_mutex_lock( &p_sys->lock );
390         if( p_sys->i_nb_samples > 0 )
391         {
392             p_sys->p_projectm->pcm()->addPCMfloat( p_sys->p_buffer,
393                                                    p_sys->i_nb_samples );
394             p_sys->i_nb_samples = 0;
395         }
396         if( p_sys->b_quit )
397         {
398             vlc_mutex_unlock( &p_sys->lock );
399
400             delete p_sys->p_projectm;
401             vout_DeleteDisplay( p_sys->p_vd, NULL );
402             vlc_object_release( p_sys->p_vout );
403             return NULL;
404         }
405         vlc_mutex_unlock( &p_sys->lock );
406
407         p_sys->p_projectm->renderFrame();
408
409         /* */
410         mwait( i_deadline );
411
412         if( !vout_opengl_Lock(gl) )
413         {
414             vout_opengl_Swap( gl );
415             vout_opengl_Unlock( gl );
416         }
417     }
418     abort();
419
420 error:
421     p_sys->b_error = true;
422     vlc_sem_post( &p_sys->ready );
423     return NULL;
424 }
425