1 /*****************************************************************************
2 * vsxu.cpp: visualization module wrapper for Vovoid VSXu
3 *****************************************************************************
4 * Copyright © 2009-2012 the VideoLAN team, Vovoid Media Technologies
7 * Authors: Rémi Duraffort <ivoire@videolan.org>
9 * Jonatan "jaw" Wallmander
11 * Used the projectM implementation as reference for this file.
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the Free
15 * Software Foundation; either version 2 of the License, or (at your option)
18 * This program is distributed in the hope that it will be useful, but WITHOUT
19 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
23 * You should have received a copy of the GNU General Public License along with
24 * this program; if not, write to the Free Software Foundation, Inc., 51
25 * Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
31 #ifndef __STDC_CONSTANT_MACROS
32 # define __STDC_CONSTANT_MACROS
35 #include <vlc_common.h>
36 #include <vlc_plugin.h>
39 #include <vlc_vout_wrapper.h>
40 #include <vlc_opengl.h>
42 // vsxu manager include
43 #include <vsx_manager.h>
44 #include <logo_intro.h>
46 // class to handle cyclic buffer
47 #include "cyclic_buffer.h"
49 /*****************************************************************************
51 *****************************************************************************/
52 static int Open ( vlc_object_t
* );
53 static void Close ( vlc_object_t
* );
55 #define WIDTH_TEXT N_("Video width")
56 #define WIDTH_LONGTEXT N_("The width of the video window, in pixels.")
58 #define HEIGHT_TEXT N_("Video height")
59 #define HEIGHT_LONGTEXT N_("The height of the video window, in pixels.")
62 set_shortname( N_("vsxu"))
63 set_description( N_("vsxu") )
64 set_capability( "visualization2", 0 )
65 set_category( CAT_AUDIO
)
66 set_subcategory( SUBCAT_AUDIO_VISUAL
)
67 add_integer( "vsxu-width", 1280, WIDTH_TEXT
, WIDTH_LONGTEXT
,
69 add_integer( "vsxu-height", 800, HEIGHT_TEXT
, HEIGHT_LONGTEXT
,
71 add_shortcut( "vsxu" )
72 set_callbacks( Open
, Close
)
76 /*****************************************************************************
78 *****************************************************************************/
88 // mutex around the cyclic block
89 vlc_mutex_t cyclic_block_mutex
;
91 // cyclic buffer to cache sound frames in
92 cyclic_block_queue
* vsxu_cyclic_buffer
;
102 static block_t
*DoWork( filter_t
*, block_t
* );
103 static void *Thread( void * );
107 * @param p_this: the filter object
108 * @return VLC_SUCCESS or vlc error codes
110 static int Open( vlc_object_t
* p_this
)
112 filter_t
*p_filter
= (filter_t
*)p_this
;
115 p_sys
= p_filter
->p_sys
= (filter_sys_t
*)malloc( sizeof( *p_sys
) );
116 if( unlikely( !p_sys
) )
121 /* Create the object for the thread */
122 vlc_sem_init( &p_sys
->ready
, 0 );
123 p_sys
->b_error
= false;
124 p_sys
->b_quit
= false;
125 p_sys
->i_width
= var_InheritInteger( p_filter
, "vsxu-width" );
126 p_sys
->i_height
= var_InheritInteger( p_filter
, "vsxu-height" );
127 p_sys
->i_channels
= aout_FormatNbChannels( &p_filter
->fmt_in
.audio
);
128 vlc_mutex_init( &p_sys
->lock
);
129 vlc_mutex_init( &p_sys
->cyclic_block_mutex
);
130 p_sys
->vsxu_cyclic_buffer
= new cyclic_block_queue();
133 /* Create the thread */
134 if( vlc_clone( &p_sys
->thread
, Thread
, p_filter
, VLC_THREAD_PRIORITY_LOW
) )
138 vlc_sem_wait( &p_sys
->ready
);
141 vlc_join( p_sys
->thread
, NULL
);
145 p_filter
->fmt_in
.audio
.i_format
= VLC_CODEC_FL32
;
146 p_filter
->fmt_out
.audio
= p_filter
->fmt_in
.audio
;
147 p_filter
->pf_audio_filter
= DoWork
;
152 vlc_sem_destroy( &p_sys
->ready
);
159 * @param p_this: the filter object
161 static void Close( vlc_object_t
*p_this
)
163 filter_t
*p_filter
= (filter_t
*)p_this
;
164 filter_sys_t
*p_sys
= p_filter
->p_sys
;
166 vlc_mutex_lock( &p_sys
->lock
);
167 p_sys
->b_quit
= true;
168 vlc_mutex_unlock( &p_sys
->lock
);
170 vlc_join( p_sys
->thread
, NULL
);
172 /* Free the ressources */
173 vlc_sem_destroy( &p_sys
->ready
);
174 vlc_mutex_destroy( &p_sys
->lock
);
175 delete p_sys
->vsxu_cyclic_buffer
;
180 * Do the actual work with the new sample
181 * @param p_filter: filter object
182 * @param p_in_buf: input buffer
184 static block_t
*DoWork( filter_t
*p_filter
, block_t
*p_in_buf
)
186 filter_sys_t
*p_sys
= p_filter
->p_sys
;
188 vlc_mutex_lock( &p_sys
->lock
);
189 vlc_mutex_lock( &p_sys
->cyclic_block_mutex
);
191 unsigned i_nb_samples
= __MIN( 1024,
192 p_in_buf
->i_nb_samples
);
194 const float *p_src
= (float*)p_in_buf
->p_buffer
;
195 // iterate block holder
196 size_t i_bh_data_iter
= 0;
198 // calc 512-byte-aligned sample count to grab, we don't need more
199 unsigned i_num_samples
= i_nb_samples
- i_nb_samples
% 512;
201 // muls are cheaper than divs
202 float f_onedivchannels
= 1.0f
/ (float)p_sys
->i_channels
;
204 block_holder
* p_block_holder
= p_sys
->vsxu_cyclic_buffer
->get_insertion_object();
205 p_block_holder
->pts
= p_in_buf
->i_pts
;
206 for( unsigned i
= 0; i
< i_num_samples
; i
++ )
209 for( int j
= 0; j
< p_sys
->i_channels
; j
++ )
211 f_v
+= p_src
[p_sys
->i_channels
* i
+ j
];
214 // insert into our little cyclic buffer
215 p_block_holder
->data
[i_bh_data_iter
] = f_v
* f_onedivchannels
;
217 if (i_bh_data_iter
== 512 && i
< i_num_samples
-256)
219 p_block_holder
= p_sys
->vsxu_cyclic_buffer
->get_insertion_object();
220 p_block_holder
->pts
= p_in_buf
->i_pts
+ 11609;
226 vlc_mutex_unlock( &p_sys
->cyclic_block_mutex
);
227 vlc_mutex_unlock( &p_sys
->lock
);
232 * Variable callback for the dummy vout
234 static int VoutCallback( vlc_object_t
*p_vout
, char const *psz_name
,
235 vlc_value_t oldv
, vlc_value_t newv
, void *p_data
)
237 VLC_UNUSED( p_vout
); VLC_UNUSED( oldv
);
238 vout_display_t
*p_vd
= (vout_display_t
*)p_data
;
240 if( !strcmp(psz_name
, "fullscreen") )
242 vout_SetDisplayFullscreen( p_vd
, newv
.b_bool
);
248 * VSXu update thread which do the rendering
249 * @param p_this: the p_thread object
251 static void *Thread( void *p_data
)
253 filter_t
*p_filter
= (filter_t
*)p_data
;
254 filter_sys_t
*p_sys
= p_filter
->p_sys
;
256 // our abstract manager holder
257 vsx_manager_abs
* manager
= 0;
259 // temp audio buffer for sending to vsxu through manager
260 float f_sample_buf
[512];
263 vsx_logo_intro
* intro
= 0;
265 vout_display_t
*p_vd
;
270 unsigned int i_last_width
= 0;
271 unsigned int i_last_height
= 0;
275 /* Create the openGL provider */
276 vout_thread_t
*p_vout
;
278 p_vout
= (vout_thread_t
*)vlc_object_create( p_filter
, sizeof(vout_thread_t
) );
282 video_format_Init( &fmt
, 0 );
283 video_format_Setup( &fmt
, VLC_CODEC_RGB32
,
284 p_sys
->i_width
, p_sys
->i_height
, 0, 1 );
288 vout_display_state_t state
;
289 memset( &state
, 0, sizeof(state
) );
290 state
.cfg
.display
.sar
.num
= 1;
291 state
.cfg
.display
.sar
.den
= 1;
292 state
.cfg
.is_display_filled
= true;
293 state
.cfg
.zoom
.num
= 1;
294 state
.cfg
.zoom
.den
= 1;
298 p_vd
= vout_NewDisplay( p_vout
, &fmt
, &state
, "opengl", 300000, 1000000 );
301 vlc_object_release( p_vout
);
304 var_Create( p_vout
, "fullscreen", VLC_VAR_BOOL
);
305 var_AddCallback( p_vout
, "fullscreen", VoutCallback
, p_vd
);
307 gl
= vout_GetDisplayOpengl( p_vd
);
310 var_DelCallback( p_vout
, "fullscreen", VoutCallback
, p_vd
);
311 vout_DeleteDisplay( p_vd
, NULL
);
312 vlc_object_release( p_vout
);
316 // tell main thread we are ready
317 vlc_sem_post( &p_sys
->ready
);
321 /* Manage the events */
322 vout_ManageDisplay( p_vd
, true );
323 if( p_vd
->cfg
->display
.width
!= i_last_width
||
324 p_vd
->cfg
->display
.height
!= i_last_height
)
326 /* FIXME it is not perfect as we will have black bands */
327 vout_display_place_t place
;
328 vout_display_PlacePicture( &place
, &p_vd
->source
, p_vd
->cfg
, false );
330 i_last_width
= p_vd
->cfg
->display
.width
;
331 i_last_height
= p_vd
->cfg
->display
.height
;
334 // look for control commands from outside the thread
335 vlc_mutex_lock( &p_sys
->lock
);
340 vlc_mutex_unlock( &p_sys
->lock
);
344 // only run this once
347 // create a new manager
348 manager
= manager_factory();
350 // init manager with the shared path and sound input type.
351 manager
->init( 0, "media_player" );
353 // only show logo once
354 // keep track of iterations
355 static int i_iterations
= 0;
356 if ( i_iterations
++ < 1 )
358 intro
= new vsx_logo_intro();
359 intro
->set_destroy_textures( false );
363 // lock cyclic buffer mutex and copy floats
364 vlc_mutex_lock( &p_sys
->cyclic_block_mutex
);
365 block_holder
* bh
= p_sys
->vsxu_cyclic_buffer
->consume();
366 memcpy( &f_sample_buf
[0], (void*)(&bh
->data
[0]), sizeof(float) * 512 );
367 vlc_mutex_unlock( &p_sys
->cyclic_block_mutex
);
369 // send sound pointer to vsxu
370 manager
->set_sound_wave( &f_sample_buf
[0] );
372 // render vsxu engine
373 if (manager
) manager
->render();
376 if (intro
) intro
->draw();
379 if( !vlc_gl_Lock(gl
) )
386 // stop vsxu nicely (unloads textures and frees memory)
387 if (manager
) manager
->stop();
389 // call manager factory to destruct our manager object
390 if (manager
) manager_destroy( manager
);
392 // delete the intro (if ever allocated)
393 if (intro
) delete intro
;
395 var_DelCallback( p_vout
, "fullscreen", VoutCallback
, p_vd
);
397 // clean out vlc opengl stuff
398 vout_DeleteDisplay( p_vd
, NULL
);
399 vlc_object_release( p_vout
);
401 // clean up the cyclic buffer
402 vlc_mutex_lock( &p_sys
->cyclic_block_mutex
);
403 p_sys
->vsxu_cyclic_buffer
->reset();
404 vlc_mutex_unlock( &p_sys
->cyclic_block_mutex
);
410 p_sys
->b_error
= true;
411 vlc_sem_post( &p_sys
->ready
);