]> git.sesse.net Git - vlc/blob - modules/video_filter/audiobargraph_v.c
2ca709a30a16d2899defe34f91d4f13feea2b77d
[vlc] / modules / video_filter / audiobargraph_v.c
1 /*****************************************************************************
2  * audiobargraph_v.c : audiobargraph video plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2003-2006 the VideoLAN team
5  *
6  * Authors: Clement CHESNIN <clement.chesnin@gmail.com>
7  *          Philippe COENT <philippe.coent@tdf.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 #include <assert.h>
32 #include <string.h>
33
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_filter.h>
37
38 #include <vlc_image.h>
39 #include <vlc_osd.h>
40
41 #include <vlc_memory.h>
42
43 #ifdef LoadImage
44 #   undef LoadImage
45 #endif
46
47 /*****************************************************************************
48  * Module descriptor
49  *****************************************************************************/
50  
51 #define I_VALUES_TEXT N_("Value of the audio channels levels")
52 #define I_VALUES_LONGTEXT N_("Value of the audio level of each channels between 0 and 1" \
53     "Each level should be separated with ':'.")
54 #define POSX_TEXT N_("X coordinate")
55 #define POSX_LONGTEXT N_("X coordinate of the bargraph." )
56 #define POSY_TEXT N_("Y coordinate")
57 #define POSY_LONGTEXT N_("Y coordinate of the bargraph." )
58 #define TRANS_TEXT N_("Transparency of the bargraph")
59 #define TRANS_LONGTEXT N_("Bargraph transparency value " \
60   "(from 0 for full transparency to 255 for full opacity)." )
61 #define POS_TEXT N_("Bargraph position")
62 #define POS_LONGTEXT N_( \
63   "Enforce the bargraph position on the video " \
64   "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
65   "also use combinations of these values, eg 6 = top-right).")
66 #define ALARM_TEXT N_("Alarm")
67 #define ALARM_LONGTEXT N_("Signals a silence and displays and alert " \
68                 "(0=no alarm, 1=alarm).")
69 #define BARWIDTH_TEXT N_("Bar width in pixel (default : 10)")
70 #define BARWIDTH_LONGTEXT N_("Width in pixel of each bar in the BarGraph to be displayed " \
71                 "(default : 10).")
72
73 #define CFG_PREFIX "audiobargraph_v-"
74
75 static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
76 static const char *const ppsz_pos_descriptions[] =
77 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
78   N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
79
80 static int  OpenSub  ( vlc_object_t * );
81 static int  OpenVideo( vlc_object_t * );
82 static void Close    ( vlc_object_t * );
83
84 vlc_module_begin ()
85
86     set_category( CAT_VIDEO )
87     set_subcategory( SUBCAT_VIDEO_SUBPIC )
88
89     set_capability( "sub filter", 0 )
90     set_callbacks( OpenSub, Close )
91     set_description( N_("Audio Bar Graph Video sub filter") )
92     set_shortname( N_("Audio Bar Graph Video") )
93     add_shortcut( "audiobargraph_v" )
94
95     add_string( CFG_PREFIX "i_values", NULL, NULL, I_VALUES_TEXT, I_VALUES_LONGTEXT, false )
96     add_integer( CFG_PREFIX "x", 0, NULL, POSX_TEXT, POSX_LONGTEXT, true )
97     add_integer( CFG_PREFIX "y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, true )
98     add_integer_with_range( CFG_PREFIX "transparency", 255, 0, 255, NULL,
99         TRANS_TEXT, TRANS_LONGTEXT, false )
100     add_integer( CFG_PREFIX "position", -1, NULL, POS_TEXT, POS_LONGTEXT, false )
101         change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL )
102     add_integer( CFG_PREFIX "alarm", 0, NULL, ALARM_TEXT, ALARM_LONGTEXT, true )
103     add_integer( CFG_PREFIX "barWidth", 10, NULL, BARWIDTH_TEXT, BARWIDTH_LONGTEXT, true )
104
105     /* video output filter submodule */
106     add_submodule ()
107     set_capability( "video filter2", 0 )
108     set_callbacks( OpenVideo, Close )
109     set_description( N_("Audio Bar Graph Video sub filter") )
110     add_shortcut( "audiobargraph_v" )
111 vlc_module_end ()
112
113
114 /*****************************************************************************
115  * Local prototypes
116  *****************************************************************************/
117
118 /*****************************************************************************
119  * Structure to hold the Bar Graph properties
120  ****************************************************************************/
121 typedef struct
122 {
123     int i_alpha;       /* -1 means use default alpha */
124     int nbChannels;
125     int *i_values;
126     picture_t *p_pic;
127     mtime_t date;
128     int scale;
129     int alarm;
130     int barWidth;
131
132 } BarGraph_t;
133
134 /**
135  * Private data holder
136  */
137 struct filter_sys_t
138 {
139     filter_t *p_blend;
140
141     vlc_mutex_t lock;
142     
143     BarGraph_t p_BarGraph;
144
145     int i_pos;
146     int i_pos_x;
147     int i_pos_y;
148     bool b_absolute;
149
150     /* On the fly control variable */
151     bool b_spu_update;
152 };
153
154 static const char *const ppsz_filter_options[] = {
155     "i_values", "x", "y", "transparency", "position", "alarm", "barWidth", NULL
156 };
157
158 static const char *const ppsz_filter_callbacks[] = {
159     "audiobargraph_v-i_values",
160     "audiobargraph_v-x",
161     "audiobargraph_v-y",
162     "audiobargraph_v-transparency",
163     "audiobargraph_v-position",
164     "audiobargraph_v-alarm",
165     "audiobargraph_v-barWidth",
166     NULL
167 };
168
169 static int OpenCommon( vlc_object_t *, bool b_sub );
170
171 static subpicture_t *FilterSub( filter_t *, mtime_t );
172 static picture_t    *FilterVideo( filter_t *, picture_t * );
173
174 static int BarGraphCallback( vlc_object_t *, char const *,
175                          vlc_value_t, vlc_value_t, void * );
176
177 static void LoadBarGraph( vlc_object_t *, BarGraph_t *);
178 void parse_i_values( BarGraph_t *p_BarGraph, char *i_values);
179
180 /**
181  * Open the sub filter
182  */
183 static int OpenSub( vlc_object_t *p_this )
184 {
185     return OpenCommon( p_this, true );
186 }
187
188 /**
189  * Open the video filter
190  */
191 static int OpenVideo( vlc_object_t *p_this )
192 {
193     return OpenCommon( p_this, false );
194 }
195
196 /**
197  * Common open function
198  */
199 static int OpenCommon( vlc_object_t *p_this, bool b_sub )
200 {
201     filter_t *p_filter = (filter_t *)p_this;
202     filter_sys_t *p_sys;
203     BarGraph_t *p_BarGraph;
204     char* i_values = NULL;
205
206     /* */
207     if( !b_sub && !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) )
208     {
209         msg_Err( p_filter, "Input and output format does not match" );
210         return VLC_EGENERIC;
211     }
212
213
214     /* */
215     p_filter->p_sys = p_sys = malloc( sizeof( *p_sys ) );
216     if( !p_sys )
217         return VLC_ENOMEM;
218     p_BarGraph = &(p_sys->p_BarGraph);
219
220     /* */
221     p_sys->p_blend = NULL;
222     if( !b_sub )
223     {
224
225         p_sys->p_blend = filter_NewBlend( VLC_OBJECT(p_filter),
226                                           &p_filter->fmt_in.video );
227         if( !p_sys->p_blend )
228         {
229             //free( p_BarGraph );
230             free( p_sys );
231             return VLC_EGENERIC;
232         }
233     }
234
235     /* */
236     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
237                        p_filter->p_cfg );
238
239     /* create and initialize variables */
240     p_sys->i_pos = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-position" );
241     p_sys->i_pos_x = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-x" );
242     p_sys->i_pos_y = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-y" );
243     p_BarGraph->i_alpha = var_CreateGetIntegerCommand( p_filter,
244                                                         "audiobargraph_v-transparency" );
245     p_BarGraph->i_alpha = __MAX( __MIN( p_BarGraph->i_alpha, 255 ), 0 );
246     i_values = var_CreateGetStringCommand( p_filter, "audiobargraph_v-i_values" );
247     //p_BarGraph->nbChannels = 0;
248     //p_BarGraph->i_values = NULL;
249     parse_i_values(p_BarGraph, i_values);
250     p_BarGraph->alarm = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-alarm" );
251     p_BarGraph->barWidth = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-barWidth" );
252     p_BarGraph->scale = 400;
253
254     /* Ignore aligment if a position is given for video filter */
255     if( !b_sub && p_sys->i_pos_x >= 0 && p_sys->i_pos_y >= 0 )
256         p_sys->i_pos = 0;
257
258     vlc_mutex_init( &p_sys->lock );
259     LoadBarGraph( p_this, p_BarGraph );
260     p_sys->b_spu_update = true;
261
262     for( int i = 0; ppsz_filter_callbacks[i]; i++ )
263         var_AddCallback( p_filter, ppsz_filter_callbacks[i],
264                          BarGraphCallback, p_sys );
265
266     /* Misc init */
267     if( b_sub )
268     {
269         p_filter->pf_sub_filter = FilterSub;
270     }
271     else
272     {
273         p_filter->pf_video_filter = FilterVideo;
274     }
275
276     free( i_values );
277     return VLC_SUCCESS;
278 }
279
280 /**
281  * Common close function
282  */
283 static void Close( vlc_object_t *p_this )
284 {
285     filter_t *p_filter = (filter_t *)p_this;
286     filter_sys_t *p_sys = p_filter->p_sys;
287     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
288
289     for( int i = 0; ppsz_filter_callbacks[i]; i++ )
290         var_DelCallback( p_filter, ppsz_filter_callbacks[i],
291                          BarGraphCallback, p_sys );
292
293     if( p_sys->p_blend )
294         filter_DeleteBlend( p_sys->p_blend );
295
296     vlc_mutex_destroy( &p_sys->lock );
297     
298     if( p_BarGraph->p_pic )
299     {
300         picture_Release( p_BarGraph->p_pic );
301         p_BarGraph->p_pic = NULL;
302     }
303     free( p_BarGraph->i_values );
304     
305     free( p_sys );
306 }
307
308 /**
309  * Sub filter
310  */
311 static subpicture_t *FilterSub( filter_t *p_filter, mtime_t date )
312 {
313     filter_sys_t *p_sys = p_filter->p_sys;
314     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
315
316     subpicture_t *p_spu;
317     subpicture_region_t *p_region;
318     video_format_t fmt;
319     picture_t *p_pic;
320
321     vlc_mutex_lock( &p_sys->lock );
322     /* Basic test:  b_spu_update occurs on a dynamic change */
323     if( !p_sys->b_spu_update )
324     {
325         vlc_mutex_unlock( &p_sys->lock );
326         return 0;
327     }
328
329     p_pic = p_BarGraph->p_pic;
330
331     /* Allocate the subpicture internal data. */
332     p_spu = filter_NewSubpicture( p_filter );
333     if( !p_spu )
334         goto exit;
335
336     p_spu->b_absolute = p_sys->b_absolute;
337     p_spu->i_start = date;
338     p_spu->i_stop = 0;
339     p_spu->b_ephemer = true;
340
341     /* Send an empty subpicture to clear the display when needed */
342     if( !p_pic || !p_BarGraph->i_alpha )
343         goto exit;
344
345     /* Create new SPU region */
346     memset( &fmt, 0, sizeof(video_format_t) );
347     fmt.i_chroma = VLC_CODEC_YUVA;
348     fmt.i_aspect = VOUT_ASPECT_FACTOR;
349     fmt.i_sar_num = fmt.i_sar_den = 1;
350     fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
351     fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
352     fmt.i_x_offset = fmt.i_y_offset = 0;
353     p_region = subpicture_region_New( &fmt );
354     if( !p_region )
355     {
356         msg_Err( p_filter, "cannot allocate SPU region" );
357         p_filter->pf_sub_buffer_del( p_filter, p_spu );
358         p_spu = NULL;
359         goto exit;
360     }
361
362     /* */
363     picture_Copy( p_region->p_picture, p_pic );
364
365     /*  where to locate the bar graph: */
366     if( p_sys->i_pos < 0 )
367     {   /*  set to an absolute xy */
368         p_region->i_align = OSD_ALIGN_RIGHT | OSD_ALIGN_TOP;
369         p_spu->b_absolute = true;
370     }
371     else
372     {   /* set to one of the 9 relative locations */
373         p_region->i_align = p_sys->i_pos;
374         p_spu->b_absolute = false;
375     }
376
377     p_region->i_x = p_sys->i_pos_x;
378     p_region->i_y = p_sys->i_pos_y;
379
380     p_spu->p_region = p_region;
381
382     p_spu->i_alpha = p_BarGraph->i_alpha ;
383
384 exit:
385     vlc_mutex_unlock( &p_sys->lock );
386
387     return p_spu;
388 }
389
390 /**
391  * Video filter
392  */
393 static picture_t *FilterVideo( filter_t *p_filter, picture_t *p_src )
394 {
395     filter_sys_t *p_sys = p_filter->p_sys;
396     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
397
398     picture_t *p_dst = filter_NewPicture( p_filter );
399     if( !p_dst )
400         goto exit;
401
402     picture_Copy( p_dst, p_src );
403
404     /* */
405     vlc_mutex_lock( &p_sys->lock );
406
407     /* */
408     const picture_t *p_pic = p_BarGraph->p_pic;
409     if( p_pic )
410     {
411         const video_format_t *p_fmt = &p_pic->format;
412         const int i_dst_w = p_filter->fmt_out.video.i_visible_width;
413         const int i_dst_h = p_filter->fmt_out.video.i_visible_height;
414
415         if( p_sys->i_pos )
416         {
417             if( p_sys->i_pos & SUBPICTURE_ALIGN_BOTTOM )
418             {
419                 p_sys->i_pos_y = i_dst_h - p_fmt->i_visible_height;
420             }
421             else if ( !(p_sys->i_pos & SUBPICTURE_ALIGN_TOP) )
422             {
423                 p_sys->i_pos_y = ( i_dst_h - p_fmt->i_visible_height ) / 2;
424             }
425             else
426             {
427                 p_sys->i_pos_y = 0;
428             }
429
430             if( p_sys->i_pos & SUBPICTURE_ALIGN_RIGHT )
431             {
432                 p_sys->i_pos_x = i_dst_w - p_fmt->i_visible_width;
433             }
434             else if ( !(p_sys->i_pos & SUBPICTURE_ALIGN_LEFT) )
435             {
436                 p_sys->i_pos_x = ( i_dst_w - p_fmt->i_visible_width ) / 2;
437             }
438             else
439             {
440                 p_sys->i_pos_x = 0;
441             }
442         }
443
444         /* */
445         const int i_alpha = p_BarGraph->i_alpha;
446         if( filter_ConfigureBlend( p_sys->p_blend, i_dst_w, i_dst_h, p_fmt ) ||
447             filter_Blend( p_sys->p_blend, p_dst, p_sys->i_pos_x, p_sys->i_pos_y,
448                           p_pic, i_alpha ) )
449         {
450             msg_Err( p_filter, "failed to blend a picture" );
451         }
452     }
453     vlc_mutex_unlock( &p_sys->lock );
454
455 exit:
456     picture_Release( p_src );
457     return p_dst;
458 }
459
460 /*****************************************************************************
461  * Callback to update params on the fly
462  *****************************************************************************/
463 static int BarGraphCallback( vlc_object_t *p_this, char const *psz_var,
464                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
465 {
466     VLC_UNUSED(oldval);
467     filter_sys_t *p_sys = (filter_sys_t *)p_data;
468     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
469     char* i_values;
470     char* res = NULL;
471
472     vlc_mutex_lock( &p_sys->lock );
473     if ( !strcmp( psz_var, "audiobargraph_v-x" ) )
474     {
475         p_sys->i_pos_x = newval.i_int;
476     }
477     else if ( !strcmp( psz_var, "audiobargraph_v-y" ) )
478     {
479         p_sys->i_pos_y = newval.i_int;
480     }
481     else if ( !strcmp( psz_var, "audiobargraph_v-position" ) )
482     {
483         p_sys->i_pos = newval.i_int;
484     }
485     else if ( !strcmp( psz_var, "audiobargraph_v-transparency" ) )
486     {
487         p_BarGraph->i_alpha = __MAX( __MIN( newval.i_int, 255 ), 0 );
488     }
489     else if ( !strcmp( psz_var, "audiobargraph_v-i_values" ) )
490     {
491         if( p_BarGraph->p_pic )
492         {
493             picture_Release( p_BarGraph->p_pic );
494             p_BarGraph->p_pic = NULL;
495         }
496         i_values = strdup( newval.psz_string );
497         free(p_BarGraph->i_values);
498         //p_BarGraph->i_values = NULL;
499         //p_BarGraph->nbChannels = 0;
500         // in case many answer are received at the same time, only keep one
501         res = strchr(i_values, '@');
502         if (res)
503             *res = 0;
504         parse_i_values( p_BarGraph, i_values);
505         LoadBarGraph(p_this,p_BarGraph);
506     }
507     else if ( !strcmp( psz_var, "audiobargraph_v-alarm" ) )
508     {
509         if( p_BarGraph->p_pic )
510         {
511             picture_Release( p_BarGraph->p_pic );
512             p_BarGraph->p_pic = NULL;
513         }
514         p_BarGraph->alarm = newval.i_int;
515         LoadBarGraph(p_this,p_BarGraph);
516     }
517     else if ( !strcmp( psz_var, "audiobargraph_v-barWidth" ) )
518     {
519         if( p_BarGraph->p_pic )
520         {
521             picture_Release( p_BarGraph->p_pic );
522             p_BarGraph->p_pic = NULL;
523         }
524         p_BarGraph->barWidth = newval.i_int;
525         LoadBarGraph(p_this,p_BarGraph);
526     }
527     p_sys->b_spu_update = true;
528     vlc_mutex_unlock( &p_sys->lock );
529
530     return VLC_SUCCESS;
531 }
532
533 /*****************************************************************************
534  * LoadImage: creates and returns the bar graph image
535  *****************************************************************************/
536 static picture_t *LoadImage( vlc_object_t *p_this, int nbChannels, int* i_values, int scale, int alarm, int barWidth)
537 {
538     VLC_UNUSED(p_this);
539     picture_t *p_pic;
540     int i,j;
541     int i_width = 0;
542     int i_line;
543     int moinsTrois, moinsCinq, moinsSept, moinsDix, moinsVingt;
544     
545     if (nbChannels == 0) {
546         i_width = 20;
547     } else {
548         i_width = 2 * nbChannels * barWidth + 10;
549     }
550     
551     moinsTrois = 0.71*scale + 20;
552     moinsCinq = 0.56*scale + 20;
553     moinsSept = 0.45*scale + 20;
554     moinsDix = 0.32*scale + 20;
555     moinsVingt = 0.1*scale + 20;
556     
557     p_pic = picture_New(VLC_FOURCC('Y','U','V','A'), i_width+20, scale+30, VOUT_ASPECT_FACTOR * (i_width+20)/(scale+30));
558     
559     // blacken the whole picture
560     for( i = 0 ; i < p_pic->i_planes ; i++ )
561     {
562         memset( p_pic->p[i].p_pixels, 0x00,
563                 p_pic->p[i].i_visible_lines * p_pic->p[i].i_pitch );
564     }
565     
566     // side bar
567     for ( i_line = 20; i_line < scale+20; i_line++ ) {
568     
569 #define DrawPointsBlack(a,b) {\
570         for (i=a; i<b; i++) {\
571             *(p_pic->p[0].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[0].i_pitch + i ) = 0x00; \
572             *(p_pic->p[1].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[1].i_pitch + i ) = 128; \
573             *(p_pic->p[2].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[2].i_pitch + i ) = 128; \
574             *(p_pic->p[3].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[3].i_pitch + i ) = 0xFF; \
575         }\
576     }
577 #define DrawPointsWhite(a,b) {\
578         for (i=a; i<b; i++) {\
579             *(p_pic->p[0].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[0].i_pitch + i ) = 0xFF;\
580             *(p_pic->p[1].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[1].i_pitch + i ) = 128;\
581             *(p_pic->p[2].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[2].i_pitch + i ) = 128;\
582             *(p_pic->p[3].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[3].i_pitch + i ) = 0xFF; \
583         }\
584     }    
585         
586         // vertical line
587         DrawPointsBlack(20,22);
588         DrawPointsWhite(22,24);
589     
590         // -3dB
591         if (i_line == moinsTrois - 2) {
592             // 3
593             DrawPointsBlack(16,19);
594         }
595         if (i_line == moinsTrois - 1) {
596             // 3
597             DrawPointsBlack(18,19);
598             // limit
599             DrawPointsWhite(24,27);
600         }
601         if (i_line == moinsTrois) {
602             // 3
603             DrawPointsBlack(16,19);
604             // limit
605             DrawPointsBlack(24,27);
606         }
607         if (i_line == moinsTrois + 1) {
608             // 3
609             DrawPointsBlack(18,19);
610             // limit
611             DrawPointsBlack(24,27);
612         }
613         if (i_line == moinsTrois + 2) {
614             // 3
615             DrawPointsBlack(16,19);
616         }
617         
618         // -5dB
619         if (i_line == moinsCinq - 2) {
620             // 5
621             DrawPointsBlack(16,19);
622         }
623         if (i_line == moinsCinq - 1) {
624             // 5
625             DrawPointsBlack(18,19);
626             // limit
627             DrawPointsWhite(24,27);
628         }
629         if (i_line == moinsCinq) {
630             // 5
631             DrawPointsBlack(16,19);
632             // limit
633             DrawPointsBlack(24,27);
634         }
635         if (i_line == moinsCinq + 1) {
636             // 5
637             DrawPointsBlack(16,17);
638             // limit
639             DrawPointsBlack(24,27);
640         }
641         if (i_line == moinsCinq + 2) {
642             // 5
643             DrawPointsBlack(16,19);
644         }
645         
646         // -7dB
647         if (i_line == moinsSept - 2) {
648             // 7
649             DrawPointsBlack(18,19);
650         }
651         if (i_line == moinsSept - 1) {
652             // 7
653             DrawPointsBlack(18,19);
654             // limit
655             DrawPointsWhite(24,27);
656         }
657         if (i_line == moinsSept) {
658             // 7
659             DrawPointsBlack(18,19);
660             // limit
661             DrawPointsBlack(24,27);
662         }
663         if (i_line == moinsSept + 1) {
664             // 7
665             DrawPointsBlack(18,19);
666             // limit
667             DrawPointsBlack(24,27);
668         }
669         if (i_line == moinsSept + 2) {
670             // 7
671             DrawPointsBlack(16,19);
672         }
673         
674         
675         // -10dB
676         if (i_line == moinsDix - 2) {
677             // 1
678             DrawPointsBlack(14,15);
679             // 0
680             DrawPointsBlack(16,19);
681         }
682         if (i_line == moinsDix - 1) {
683             // 1
684             DrawPointsBlack(14,15);
685             // 0
686             DrawPointsBlack(16,17);
687             DrawPointsBlack(18,19);
688             // limit
689             DrawPointsWhite(24,27);
690         }
691         if (i_line == moinsDix) {
692             // 1
693             DrawPointsBlack(14,15);
694             // 0
695             DrawPointsBlack(16,17);
696             DrawPointsBlack(18,19);
697             // limit
698             DrawPointsBlack(24,27);
699         }
700         if (i_line == moinsDix + 1) {
701             // 1
702             DrawPointsBlack(14,15);
703             // 0
704             DrawPointsBlack(16,17);
705             DrawPointsBlack(18,19);
706             // limit
707             DrawPointsBlack(24,27);
708         }
709         if (i_line == moinsDix + 2) {
710             // 1
711             DrawPointsBlack(14,15);
712             // 0
713             DrawPointsBlack(16,19);
714         }
715         
716         // -20dB
717         if (i_line == moinsVingt - 2) {
718             // 2
719             DrawPointsBlack(12,15);
720             // 0
721             DrawPointsBlack(16,19);
722         }
723         if (i_line == moinsVingt - 1) {
724             // 2
725             DrawPointsBlack(12,13);
726             // 0
727             DrawPointsBlack(16,17);
728             DrawPointsBlack(18,19);
729             // limit
730             DrawPointsWhite(24,27);
731         }
732         if (i_line == moinsVingt) {
733             // 2
734             DrawPointsBlack(12,15);
735             // 0
736             DrawPointsBlack(16,17);
737             DrawPointsBlack(18,19);
738             // limit
739             DrawPointsBlack(24,27);
740         }
741         if (i_line == moinsVingt + 1) {
742             // 2
743             DrawPointsBlack(14,15);
744             // 0
745             DrawPointsBlack(16,17);
746             DrawPointsBlack(18,19);
747             // limit
748             DrawPointsBlack(24,27);
749         }
750         if (i_line == moinsVingt + 2) {
751             // 2
752             DrawPointsBlack(12,15);
753             // 0
754             DrawPointsBlack(16,19);
755         }
756         
757         
758     }
759     
760     // draw the bars and channel indicators
761     for (i=0; i<nbChannels; i++) {
762         for( j = barWidth+20 ; j < 2*barWidth+20; j++)
763         {
764             // channel indicators
765             for ( i_line = 12; i_line < 18; i_line++ ) {
766                 // white
767                 *(p_pic->p[0].p_pixels +
768                     (scale + 30 - i_line - 1) *
769                     p_pic->p[0].i_pitch +
770                     ( (2*i*barWidth)+j ) ) = 255;
771                 *(p_pic->p[1].p_pixels +
772                     (scale + 30 - i_line - 1) *
773                     p_pic->p[1].i_pitch +
774                     ( (2*i*barWidth)+j ) ) = 128;
775                 *(p_pic->p[2].p_pixels +
776                     (scale + 30 - i_line - 1) *
777                     p_pic->p[2].i_pitch +
778                     ( (2*i*barWidth)+j ) ) = 128;
779                 *(p_pic->p[3].p_pixels +
780                     (scale + 30 - i_line - 1) *
781                     p_pic->p[3].i_pitch +
782                     ( (2*i*barWidth)+j )) = 0xFF;
783             }
784             // bars
785             for( i_line = 20; i_line < i_values[i]+20; i_line++ )
786             {
787                 if (i_line < moinsDix) { // green if < -10 dB
788                     *(p_pic->p[0].p_pixels +
789                         (scale + 30 - i_line - 1) *
790                         p_pic->p[0].i_pitch +
791                         ( (2*i*barWidth)+j ) ) = 150;
792                     *(p_pic->p[1].p_pixels +
793                         (scale + 30 - i_line - 1) *
794                         p_pic->p[1].i_pitch +
795                         ( (2*i*barWidth)+j ) ) = 44;
796                     *(p_pic->p[2].p_pixels +
797                         (scale + 30 - i_line - 1) *
798                         p_pic->p[2].i_pitch +
799                         ( (2*i*barWidth)+j ) ) = 21;
800                     *(p_pic->p[3].p_pixels +
801                         (scale + 30 - i_line - 1) *
802                         p_pic->p[3].i_pitch +
803                         ( (2*i*barWidth)+j )) = 0xFF;
804                 } else if (i_line < moinsTrois) { // yellow if > -10dB and < -3dB
805                     *(p_pic->p[0].p_pixels +
806                         (scale + 30 - i_line - 1) *
807                         p_pic->p[0].i_pitch +
808                         ( (2*i*barWidth)+j ) ) = 226;
809                     *(p_pic->p[1].p_pixels +
810                         (scale + 30 - i_line - 1) *
811                         p_pic->p[1].i_pitch +
812                         ( (2*i*barWidth)+j ) ) = 1;
813                     *(p_pic->p[2].p_pixels +
814                         (scale + 30 - i_line - 1) *
815                         p_pic->p[2].i_pitch +
816                         ( (2*i*barWidth)+j ) ) = 148;
817                     *(p_pic->p[3].p_pixels +
818                         (scale + 30 - i_line - 1) *
819                         p_pic->p[3].i_pitch +
820                         ( (2*i*barWidth)+j )) = 0xFF;
821                 } else { // red if > -3 dB
822                     *(p_pic->p[0].p_pixels +
823                         (scale + 30 - i_line - 1) *
824                         p_pic->p[0].i_pitch +
825                         ( (2*i*barWidth)+j ) ) = 76;
826                     *(p_pic->p[1].p_pixels +
827                         (scale + 30 - i_line - 1) *
828                         p_pic->p[1].i_pitch +
829                         ( (2*i*barWidth)+j ) ) = 85;
830                     *(p_pic->p[2].p_pixels +
831                         (scale + 30 - i_line - 1) *
832                         p_pic->p[2].i_pitch +
833                         ( (2*i*barWidth)+j ) ) = 0xFF;
834                     *(p_pic->p[3].p_pixels +
835                         (scale + 30 - i_line - 1) *
836                         p_pic->p[3].i_pitch +
837                         ( (2*i*barWidth)+j )) = 0xFF;
838                 }
839             }
840         }
841     }
842     
843     
844     
845     if (alarm) {// draw the alarm square
846         // bottom
847         for ( i_line = 0; i_line < 10; i_line++ ) {
848             for (i=0; i<i_width+20; i++) {
849                 *(p_pic->p[0].p_pixels +
850                     (scale + 30 - i_line - 1) *
851                     p_pic->p[0].i_pitch + i ) = 76;
852                 *(p_pic->p[1].p_pixels +
853                     (scale + 30 - i_line - 1) *
854                     p_pic->p[1].i_pitch + i ) = 85;
855                 *(p_pic->p[2].p_pixels +
856                     (scale + 30 - i_line - 1) *
857                     p_pic->p[2].i_pitch + i ) = 0xFF;
858                 *(p_pic->p[3].p_pixels +
859                     (scale + 30 - i_line - 1) *
860                     p_pic->p[3].i_pitch + i ) = 0xFF;
861             }
862         }
863         // top
864         for ( i_line = scale+21; i_line < scale+30; i_line++ ) {
865             for (i=0; i<i_width+20; i++) {
866                 *(p_pic->p[0].p_pixels +
867                     (scale + 30 - i_line - 1) *
868                     p_pic->p[0].i_pitch + i ) = 76;
869                 *(p_pic->p[1].p_pixels +
870                     (scale + 30 - i_line - 1) *
871                     p_pic->p[1].i_pitch + i ) = 85;
872                 *(p_pic->p[2].p_pixels +
873                     (scale + 30 - i_line - 1) *
874                     p_pic->p[2].i_pitch + i ) = 0xFF;
875                 *(p_pic->p[3].p_pixels +
876                     (scale + 30 - i_line - 1) *
877                     p_pic->p[3].i_pitch + i ) = 0xFF;
878             }
879         }
880         // sides
881         for ( i_line = 9; i_line < scale+21; i_line++ ) {
882             for (i=0; i<10; i++) {
883                 *(p_pic->p[0].p_pixels +
884                     (scale + 30 - i_line - 1) *
885                     p_pic->p[0].i_pitch + i ) = 76;
886                 *(p_pic->p[1].p_pixels +
887                     (scale + 30 - i_line - 1) *
888                     p_pic->p[1].i_pitch + i ) = 85;
889                 *(p_pic->p[2].p_pixels +
890                     (scale + 30 - i_line - 1) *
891                     p_pic->p[2].i_pitch + i ) = 0xFF;
892                 *(p_pic->p[3].p_pixels +
893                     (scale + 30 - i_line - 1) *
894                     p_pic->p[3].i_pitch + i ) = 0xFF;
895             }
896             for (i=i_width+11; i<i_width+20; i++) {
897                 *(p_pic->p[0].p_pixels +
898                     (scale + 30 - i_line - 1) *
899                     p_pic->p[0].i_pitch + i ) = 76;
900                 *(p_pic->p[1].p_pixels +
901                     (scale + 30 - i_line - 1) *
902                     p_pic->p[1].i_pitch + i ) = 85;
903                 *(p_pic->p[2].p_pixels +
904                     (scale + 30 - i_line - 1) *
905                     p_pic->p[2].i_pitch + i ) = 0xFF;
906                 *(p_pic->p[3].p_pixels +
907                     (scale + 30 - i_line - 1) *
908                     p_pic->p[3].i_pitch + i ) = 0xFF;
909             }
910         }
911     }
912     
913
914     return p_pic;
915 }
916
917 /*****************************************************************************
918  * LoadBarGraph: loads the BarGraph images into memory
919  *****************************************************************************/
920 static void LoadBarGraph( vlc_object_t *p_this, BarGraph_t *p_BarGraph )
921 {
922
923     p_BarGraph->p_pic = LoadImage( p_this, p_BarGraph->nbChannels, p_BarGraph->i_values, p_BarGraph->scale, p_BarGraph->alarm, p_BarGraph->barWidth);
924     if( !p_BarGraph->p_pic )
925     {
926         msg_Warn( p_this, "error while creating picture" );
927     }
928
929 }
930
931 /*****************************************************************************
932  * parse_i_values : parse i_values parameter and store the corresponding values
933  *****************************************************************************/
934 void parse_i_values( BarGraph_t *p_BarGraph, char *i_values)
935 {
936     char* res = NULL;
937     char delim[] = ":";
938     char* tok;
939
940     p_BarGraph->nbChannels = 0;
941     p_BarGraph->i_values = NULL;
942     res = strtok_r(i_values, delim, &tok);
943     while (res != NULL) {
944         p_BarGraph->nbChannels++;
945         p_BarGraph->i_values = realloc_or_free(p_BarGraph->i_values,
946                                           p_BarGraph->nbChannels*sizeof(int));
947         assert(p_BarGraph->i_values);
948         p_BarGraph->i_values[p_BarGraph->nbChannels-1] = __MAX( __MIN( atof(res)*p_BarGraph->scale, p_BarGraph->scale ), 0 );
949         res = strtok_r(NULL, delim, &tok);
950     }
951
952 }