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