]> git.sesse.net Git - vlc/blob - modules/visualization/projectm.cpp
Removed es_format_t::i_aspect.
[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 WIDTH_TEXT N_("Video width")
52 #define WIDTH_LONGTEXT N_("The width of the video window, in pixels.")
53
54 #define HEIGHT_TEXT N_("Video height")
55 #define HEIGHT_LONGTEXT N_("The height of the video window, in pixels.")
56
57 vlc_module_begin ()
58     set_shortname( N_("projectM"))
59     set_description( N_("libprojectM effect") )
60     set_capability( "visualization2", 0 )
61     set_category( CAT_AUDIO )
62     set_subcategory( SUBCAT_AUDIO_VISUAL )
63     add_file( "projectm-config", "/usr/share/projectM/config.inp", NULL,
64                 CONFIG_TEXT, CONFIG_LONGTEXT, true )
65     add_integer( "projectm-width", 800, NULL, WIDTH_TEXT, WIDTH_LONGTEXT,
66                  false )
67     add_integer( "projectm-height", 640, NULL, HEIGHT_TEXT, HEIGHT_LONGTEXT,
68                  false )
69     add_shortcut( "projectm" )
70     set_callbacks( Open, Close )
71 vlc_module_end ()
72
73
74 /*****************************************************************************
75  * Local prototypes
76  *****************************************************************************/
77 struct filter_sys_t
78 {
79     /* */
80     vlc_thread_t thread;
81     vlc_sem_t    ready;
82     bool         b_error;
83
84     /* Opengl */
85     vout_thread_t  *p_vout;
86     vout_display_t *p_vd;
87
88     /* libprojectM objects */
89     projectM      *p_projectm;
90     char          *psz_config;
91
92     /* Window size */
93     int i_width;
94     int i_height;
95
96     /* audio info */
97     int i_channels;
98
99     vlc_mutex_t lock;
100     float *p_buffer;
101     int   i_buffer_size;
102     int   i_nb_samples;
103 };
104
105
106 static block_t *DoWork( filter_t *, block_t * );
107 static void *Thread( void * );
108
109 /**
110  * Open the module
111  * @param p_this: the filter object
112  * @return VLC_SUCCESS or vlc error codes
113  */
114 static int Open( vlc_object_t * p_this )
115 {
116     filter_t     *p_filter = (filter_t *)p_this;
117     filter_sys_t *p_sys;
118
119     /* Test the audio format */
120     if( p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32 ||
121         p_filter->fmt_out.audio.i_format != VLC_CODEC_FL32 )
122     {
123         msg_Warn( p_filter, "bad input or output format" );
124         return VLC_EGENERIC;
125     }
126     if( !AOUT_FMTS_SIMILAR( &p_filter->fmt_in.audio, &p_filter->fmt_out.audio ) )
127     {
128         msg_Warn( p_filter, "input and outut are not similar" );
129         return VLC_EGENERIC;
130     }
131
132     p_filter->pf_audio_filter = DoWork;
133
134     p_sys = p_filter->p_sys = (filter_sys_t*)malloc( sizeof( *p_sys ) );
135     if( !p_sys )
136         return VLC_ENOMEM;
137
138     /* Create the object for the thread */
139     vlc_sem_init( &p_sys->ready, 0 );
140     p_sys->b_error  = false;
141     p_sys->i_width  = var_CreateGetInteger( p_filter, "projectm-width" );
142     p_sys->i_height = var_CreateGetInteger( p_filter, "projectm-height" );
143     p_sys->i_channels = aout_FormatNbChannels( &p_filter->fmt_in.audio );
144     p_sys->psz_config = var_CreateGetString( p_filter, "projectm-config" );
145     vlc_mutex_init( &p_sys->lock );
146     p_sys->p_buffer = NULL;
147     p_sys->i_buffer_size = 0;
148     p_sys->i_nb_samples = 0;
149
150     /* Create the thread */
151     if( vlc_clone( &p_sys->thread, Thread, p_filter, VLC_THREAD_PRIORITY_LOW ) )
152         goto error;
153
154     vlc_sem_wait( &p_sys->ready );
155     if( p_sys->b_error )
156     {
157         vlc_join( p_sys->thread, NULL );
158         goto error;
159     }
160
161     return VLC_SUCCESS;
162
163 error:
164     vlc_sem_destroy( &p_sys->ready );
165     free (p_sys );
166     return VLC_EGENERIC;
167 }
168
169
170 /**
171  * Close the module
172  * @param p_this: the filter object
173  */
174 static void Close( vlc_object_t *p_this )
175 {
176     filter_t     *p_filter = (filter_t *)p_this;
177     filter_sys_t *p_sys = p_filter->p_sys;
178
179     /* Stop the thread */
180     vlc_cancel( p_sys->thread );
181     vlc_join( p_sys->thread, NULL );
182
183     /* Free the ressources */
184     vlc_sem_destroy( &p_sys->ready );
185     vlc_mutex_destroy( &p_sys->lock );
186     free( p_sys->p_buffer );
187     free( p_sys->psz_config );
188     free( p_sys );
189 }
190
191
192 /**
193  * Do the actual work with the new sample
194  * @param p_aout: audio output object
195  * @param p_filter: filter object
196  * @param p_in_buf: input buffer
197  * @param p_out_buf: output buffer
198  */
199 static block_t *DoWork( filter_t *p_filter, block_t *p_in_buf )
200 {
201     filter_sys_t *p_sys = p_filter->p_sys;
202
203     vlc_mutex_lock( &p_sys->lock );
204     if( p_sys->i_buffer_size > 0 )
205     {
206         p_sys->i_nb_samples = __MIN( p_sys->i_buffer_size,
207                                      p_in_buf->i_nb_samples );
208
209         const float *p_src = (float*)p_in_buf->p_buffer;
210         for( int i = 0; i < p_sys->i_nb_samples; i++ )
211         {
212             float v = 0;
213             for( int j = 0; j < p_sys->i_channels; j++ )
214                 v += p_src[p_sys->i_channels * i + j];
215             p_sys->p_buffer[i] = v / p_sys->i_channels;
216         }
217     }
218     vlc_mutex_unlock( &p_sys->lock );
219
220     return p_in_buf;
221 }
222
223 /**
224  * Variable callback for the dummy vout
225  */
226 static int VoutCallback( vlc_object_t *p_vout, char const *psz_name,
227                          vlc_value_t oldv, vlc_value_t newv, void *p_data )
228 {
229     vout_display_t *p_vd = (vout_display_t*)p_data;
230
231     if( !strcmp(psz_name, "fullscreen") )
232     {
233         vout_SetDisplayFullscreen( p_vd, newv.b_bool );
234     }
235 }
236
237 /**
238  * Clean up function when Thread() is cancelled.
239  */
240 static void ThreadCleanup( void *p_data )
241 {
242     filter_t     *p_filter = (filter_t*)p_data;
243     filter_sys_t *p_sys = p_filter->p_sys;
244
245     /* Cleanup */
246     delete p_sys->p_projectm;
247
248     /* Free the openGL provider */
249     vout_DeleteDisplay( p_sys->p_vd, NULL );
250     vlc_object_release( p_sys->p_vout );
251 }
252
253
254 /**
255  * ProjectM update thread which do the rendering
256  * @param p_this: the p_thread object
257  */
258 static void *Thread( void *p_data )
259 {
260     filter_t     *p_filter = (filter_t*)p_data;
261     filter_sys_t *p_sys = p_filter->p_sys;
262     int cancel = vlc_savecancel();
263     video_format_t fmt;
264     vout_opengl_t *gl;
265
266     /* Create the openGL provider */
267     p_sys->p_vout =
268         (vout_thread_t *)vlc_object_create( p_filter, sizeof(vout_thread_t) );
269     if( !p_sys->p_vout )
270         goto error;
271
272     /* */
273     video_format_Init( &fmt, 0 );
274     video_format_Setup( &fmt, VLC_CODEC_RGB32,
275                         p_sys->i_width, p_sys->i_height, 0, 1 );
276     fmt.i_sar_num = 1;
277     fmt.i_sar_den = 1;
278
279     vout_display_state_t state;
280     memset( &state, 0, sizeof(state) );
281     state.cfg.display.sar.num = 1;
282     state.cfg.display.sar.den = 1;
283     state.cfg.is_display_filled = true;
284     state.cfg.zoom.num = 1;
285     state.cfg.zoom.den = 1;
286     state.sar.num = 1;
287     state.sar.den = 1;
288
289     p_sys->p_vd = vout_NewDisplay( p_sys->p_vout, &fmt, &state, "opengl",
290                                    300000, 1000000 );
291     if( !p_sys->p_vd )
292     {
293         vlc_object_release( p_sys->p_vout );
294         goto error;
295     }
296     var_Create( p_sys->p_vout, "fullscreen", VLC_VAR_BOOL );
297     var_AddCallback( p_sys->p_vout, "fullscreen", VoutCallback, p_sys->p_vd );
298
299     gl = vout_GetDisplayOpengl( p_sys->p_vd );
300     if( !gl )
301     {
302         vout_DeleteDisplay( p_sys->p_vd, NULL );
303         vlc_object_release( p_sys->p_vout );
304         goto error;
305     }
306     vlc_cleanup_push( ThreadCleanup, p_filter );
307
308     /* Create the projectM object */
309     p_sys->p_projectm = new projectM( p_sys->psz_config );
310     p_sys->i_buffer_size = p_sys->p_projectm->pcm()->maxsamples;
311     p_sys->p_buffer = (float*)calloc( p_sys->i_buffer_size,
312                                       sizeof( float ) );
313
314     vlc_sem_post( &p_sys->ready );
315
316     /* TODO: Give to projectm the name of the input
317     p_sys->p_projectm->projectM_setTitle( "" ); */
318
319     /* */
320     int i_last_width  = 0;
321     int i_last_height = 0;
322     for( ;; )
323     {
324         const mtime_t i_deadline = mdate() + CLOCK_FREQ / 50; /* 50 fps max */
325         /* Manage the events */
326         vout_ManageDisplay( p_sys->p_vd, true );
327         if( p_sys->p_vd->cfg->display.width  != i_last_width ||
328             p_sys->p_vd->cfg->display.height != i_last_height )
329         {
330             /* FIXME it is not perfect as we will have black bands */
331             vout_display_place_t place;
332             vout_display_PlacePicture( &place, &p_sys->p_vd->source, p_sys->p_vd->cfg, false );
333             p_sys->p_projectm->projectM_resetGL( place.width, place.height );
334
335             i_last_width  = p_sys->p_vd->cfg->display.width;
336             i_last_height = p_sys->p_vd->cfg->display.height;
337         }
338
339         /* Render the image and swap the buffers */
340         vlc_mutex_lock( &p_sys->lock );
341         if( p_sys->i_nb_samples > 0 )
342         {
343             p_sys->p_projectm->pcm()->addPCMfloat( p_sys->p_buffer,
344                                                    p_sys->i_nb_samples );
345             p_sys->i_nb_samples = 0;
346         }
347         vlc_mutex_unlock( &p_sys->lock );
348
349         p_sys->p_projectm->renderFrame();
350
351         /* */
352         vlc_restorecancel( cancel );
353         mwait( i_deadline );
354         cancel = vlc_savecancel();
355
356         if( !vout_opengl_Lock(gl) )
357         {
358             vout_opengl_Swap( gl );
359             vout_opengl_Unlock( gl );
360         }
361     }
362     vlc_cleanup_pop();
363     abort();
364
365 error:
366     p_sys->b_error = true;
367     vlc_sem_post( &p_sys->ready );
368     return NULL;
369
370 }
371