]> git.sesse.net Git - vlc/blob - modules/video_filter/audiobargraph_v.c
vlc.desktop: add missing --started-from-file option (fixes #8839)
[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
218     /* */
219     p_sys->p_blend = NULL;
220     if( !b_sub )
221     {
222
223         p_sys->p_blend = filter_NewBlend( VLC_OBJECT(p_filter),
224                                           &p_filter->fmt_in.video );
225         if( !p_sys->p_blend )
226         {
227             //free( p_BarGraph );
228             free( p_sys );
229             return VLC_EGENERIC;
230         }
231     }
232
233     /* */
234     config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options,
235                        p_filter->p_cfg );
236
237     /* create and initialize variables */
238     p_sys->i_pos = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-position" );
239     p_sys->i_pos_x = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-x" );
240     p_sys->i_pos_y = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-y" );
241     p_BarGraph->i_alpha = var_CreateGetIntegerCommand( p_filter,
242                                                         "audiobargraph_v-transparency" );
243     p_BarGraph->i_alpha = VLC_CLIP( p_BarGraph->i_alpha, 0, 255 );
244     i_values = var_CreateGetStringCommand( p_filter, "audiobargraph_v-i_values" );
245     //p_BarGraph->nbChannels = 0;
246     //p_BarGraph->i_values = NULL;
247     parse_i_values(p_BarGraph, i_values);
248     p_BarGraph->alarm = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-alarm" );
249     p_BarGraph->barWidth = var_CreateGetIntegerCommand( p_filter, "audiobargraph_v-barWidth" );
250     p_BarGraph->scale = 400;
251
252     /* Ignore aligment if a position is given for video filter */
253     if( !b_sub && p_sys->i_pos_x >= 0 && p_sys->i_pos_y >= 0 )
254         p_sys->i_pos = 0;
255
256     vlc_mutex_init( &p_sys->lock );
257     LoadBarGraph( p_this, p_BarGraph );
258     p_sys->b_spu_update = true;
259
260     for( int i = 0; ppsz_filter_callbacks[i]; i++ )
261         var_AddCallback( p_filter, ppsz_filter_callbacks[i],
262                          BarGraphCallback, p_sys );
263
264     /* Misc init */
265     if( b_sub )
266     {
267         p_filter->pf_sub_source = FilterSub;
268     }
269     else
270     {
271         p_filter->pf_video_filter = FilterVideo;
272     }
273
274     free( i_values );
275     return VLC_SUCCESS;
276 }
277
278 /**
279  * Common close function
280  */
281 static void Close( vlc_object_t *p_this )
282 {
283     filter_t *p_filter = (filter_t *)p_this;
284     filter_sys_t *p_sys = p_filter->p_sys;
285     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
286
287     for( int i = 0; ppsz_filter_callbacks[i]; i++ )
288         var_DelCallback( p_filter, ppsz_filter_callbacks[i],
289                          BarGraphCallback, p_sys );
290
291     if( p_sys->p_blend )
292         filter_DeleteBlend( p_sys->p_blend );
293
294     vlc_mutex_destroy( &p_sys->lock );
295
296     if( p_BarGraph->p_pic )
297     {
298         picture_Release( p_BarGraph->p_pic );
299         p_BarGraph->p_pic = NULL;
300     }
301     free( p_BarGraph->i_values );
302
303     free( p_sys );
304 }
305
306 /**
307  * Sub source
308  */
309 static subpicture_t *FilterSub( filter_t *p_filter, mtime_t date )
310 {
311     filter_sys_t *p_sys = p_filter->p_sys;
312     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
313
314     subpicture_t *p_spu;
315     subpicture_region_t *p_region;
316     video_format_t fmt;
317     picture_t *p_pic;
318
319     vlc_mutex_lock( &p_sys->lock );
320     /* Basic test:  b_spu_update occurs on a dynamic change */
321     if( !p_sys->b_spu_update )
322     {
323         vlc_mutex_unlock( &p_sys->lock );
324         return 0;
325     }
326
327     p_pic = p_BarGraph->p_pic;
328
329     /* Allocate the subpicture internal data. */
330     p_spu = filter_NewSubpicture( p_filter );
331     if( !p_spu )
332         goto exit;
333
334     p_spu->b_absolute = p_sys->b_absolute;
335     p_spu->i_start = date;
336     p_spu->i_stop = 0;
337     p_spu->b_ephemer = true;
338
339     /* Send an empty subpicture to clear the display when needed */
340     if( !p_pic || !p_BarGraph->i_alpha )
341         goto exit;
342
343     /* Create new SPU region */
344     memset( &fmt, 0, sizeof(video_format_t) );
345     fmt.i_chroma = VLC_CODEC_YUVA;
346     fmt.i_sar_num = fmt.i_sar_den = 1;
347     fmt.i_width = fmt.i_visible_width = p_pic->p[Y_PLANE].i_visible_pitch;
348     fmt.i_height = fmt.i_visible_height = p_pic->p[Y_PLANE].i_visible_lines;
349     fmt.i_x_offset = fmt.i_y_offset = 0;
350     p_region = subpicture_region_New( &fmt );
351     if( !p_region )
352     {
353         msg_Err( p_filter, "cannot allocate SPU region" );
354         p_filter->pf_sub_buffer_del( p_filter, p_spu );
355         p_spu = NULL;
356         goto exit;
357     }
358
359     /* */
360     picture_Copy( p_region->p_picture, p_pic );
361
362     /*  where to locate the bar graph: */
363     if( p_sys->i_pos < 0 )
364     {   /*  set to an absolute xy */
365         p_region->i_align = SUBPICTURE_ALIGN_RIGHT | SUBPICTURE_ALIGN_TOP;
366         p_spu->b_absolute = true;
367     }
368     else
369     {   /* set to one of the 9 relative locations */
370         p_region->i_align = p_sys->i_pos;
371         p_spu->b_absolute = false;
372     }
373
374     p_region->i_x = p_sys->i_pos_x;
375     p_region->i_y = p_sys->i_pos_y;
376
377     p_spu->p_region = p_region;
378
379     p_spu->i_alpha = p_BarGraph->i_alpha ;
380
381 exit:
382     vlc_mutex_unlock( &p_sys->lock );
383
384     return p_spu;
385 }
386
387 /**
388  * Video filter
389  */
390 static picture_t *FilterVideo( filter_t *p_filter, picture_t *p_src )
391 {
392     filter_sys_t *p_sys = p_filter->p_sys;
393     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
394
395     picture_t *p_dst = filter_NewPicture( p_filter );
396     if( !p_dst )
397         goto exit;
398
399     picture_Copy( p_dst, p_src );
400
401     /* */
402     vlc_mutex_lock( &p_sys->lock );
403
404     /* */
405     const picture_t *p_pic = p_BarGraph->p_pic;
406     if( p_pic )
407     {
408         const video_format_t *p_fmt = &p_pic->format;
409         const int i_dst_w = p_filter->fmt_out.video.i_visible_width;
410         const int i_dst_h = p_filter->fmt_out.video.i_visible_height;
411
412         if( p_sys->i_pos )
413         {
414             if( p_sys->i_pos & SUBPICTURE_ALIGN_BOTTOM )
415             {
416                 p_sys->i_pos_y = i_dst_h - p_fmt->i_visible_height;
417             }
418             else if ( !(p_sys->i_pos & SUBPICTURE_ALIGN_TOP) )
419             {
420                 p_sys->i_pos_y = ( i_dst_h - p_fmt->i_visible_height ) / 2;
421             }
422             else
423             {
424                 p_sys->i_pos_y = 0;
425             }
426
427             if( p_sys->i_pos & SUBPICTURE_ALIGN_RIGHT )
428             {
429                 p_sys->i_pos_x = i_dst_w - p_fmt->i_visible_width;
430             }
431             else if ( !(p_sys->i_pos & SUBPICTURE_ALIGN_LEFT) )
432             {
433                 p_sys->i_pos_x = ( i_dst_w - p_fmt->i_visible_width ) / 2;
434             }
435             else
436             {
437                 p_sys->i_pos_x = 0;
438             }
439         }
440
441         /* */
442         const int i_alpha = p_BarGraph->i_alpha;
443         if( filter_ConfigureBlend( p_sys->p_blend, i_dst_w, i_dst_h, p_fmt ) ||
444             filter_Blend( p_sys->p_blend, p_dst, p_sys->i_pos_x, p_sys->i_pos_y,
445                           p_pic, i_alpha ) )
446         {
447             msg_Err( p_filter, "failed to blend a picture" );
448         }
449     }
450     vlc_mutex_unlock( &p_sys->lock );
451
452 exit:
453     picture_Release( p_src );
454     return p_dst;
455 }
456
457 /*****************************************************************************
458  * Callback to update params on the fly
459  *****************************************************************************/
460 static int BarGraphCallback( vlc_object_t *p_this, char const *psz_var,
461                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
462 {
463     VLC_UNUSED(oldval);
464     filter_sys_t *p_sys = (filter_sys_t *)p_data;
465     BarGraph_t *p_BarGraph = &(p_sys->p_BarGraph);
466     char* res = NULL;
467
468     vlc_mutex_lock( &p_sys->lock );
469     if ( !strcmp( psz_var, "audiobargraph_v-x" ) )
470     {
471         p_sys->i_pos_x = newval.i_int;
472     }
473     else if ( !strcmp( psz_var, "audiobargraph_v-y" ) )
474     {
475         p_sys->i_pos_y = newval.i_int;
476     }
477     else if ( !strcmp( psz_var, "audiobargraph_v-position" ) )
478     {
479         p_sys->i_pos = newval.i_int;
480     }
481     else if ( !strcmp( psz_var, "audiobargraph_v-transparency" ) )
482     {
483         p_BarGraph->i_alpha = VLC_CLIP( newval.i_int, 0, 255 );
484     }
485     else if ( !strcmp( psz_var, "audiobargraph_v-i_values" ) )
486     {
487         if( p_BarGraph->p_pic )
488         {
489             picture_Release( p_BarGraph->p_pic );
490             p_BarGraph->p_pic = NULL;
491         }
492         char *psz_i_values = strdup( newval.psz_string );
493         free(p_BarGraph->i_values);
494         //p_BarGraph->i_values = NULL;
495         //p_BarGraph->nbChannels = 0;
496         // in case many answer are received at the same time, only keep one
497         res = strchr(psz_i_values, '@');
498         if (res)
499             *res = 0;
500         parse_i_values( p_BarGraph, psz_i_values);
501         free( psz_i_values );
502         LoadBarGraph(p_this,p_BarGraph);
503     }
504     else if ( !strcmp( psz_var, "audiobargraph_v-alarm" ) )
505     {
506         if( p_BarGraph->p_pic )
507         {
508             picture_Release( p_BarGraph->p_pic );
509             p_BarGraph->p_pic = NULL;
510         }
511         p_BarGraph->alarm = newval.i_int;
512         LoadBarGraph(p_this,p_BarGraph);
513     }
514     else if ( !strcmp( psz_var, "audiobargraph_v-barWidth" ) )
515     {
516         if( p_BarGraph->p_pic )
517         {
518             picture_Release( p_BarGraph->p_pic );
519             p_BarGraph->p_pic = NULL;
520         }
521         p_BarGraph->barWidth = newval.i_int;
522         LoadBarGraph(p_this,p_BarGraph);
523     }
524     p_sys->b_spu_update = true;
525     vlc_mutex_unlock( &p_sys->lock );
526
527     return VLC_SUCCESS;
528 }
529
530 /*****************************************************************************
531  * LoadImage: creates and returns the bar graph image
532  *****************************************************************************/
533 static picture_t *LoadImage( vlc_object_t *p_this, int nbChannels, int* i_values, int scale, int alarm, int barWidth)
534 {
535     VLC_UNUSED(p_this);
536     picture_t *p_pic;
537     int i, j, pi;
538     int i_width = 0;
539     int i_line;
540     int minus8, minus10, minus18, minus20, minus30, minus40, minus50, minus60;
541
542     if (nbChannels == 0) {
543         i_width = 20;
544     } else {
545         i_width = 2 * nbChannels * barWidth + 10;
546     }
547     minus8  = iec_scale(-8)*scale + 20;
548     minus10 = iec_scale(-10)*scale + 20;
549     minus18 = iec_scale(-18)*scale + 20;
550     minus20 = iec_scale(-20)*scale + 20;
551     minus30 = iec_scale(-30)*scale + 20;
552     minus40 = iec_scale(-40)*scale + 20;
553     minus50 = iec_scale(-50)*scale + 20;
554     minus60 = iec_scale(-60)*scale + 20;
555
556     p_pic = picture_New(VLC_FOURCC('Y','U','V','A'), i_width+20, scale+30, 1, 1);
557
558
559 #define DrawLine(a,b,Y,U,V,A) \
560         for (i=a; i<b; i++) {\
561             *(p_pic->p[0].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[0].i_pitch + i ) = Y;\
562             *(p_pic->p[1].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[1].i_pitch + i ) = U;\
563             *(p_pic->p[2].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[2].i_pitch + i ) = V;\
564             *(p_pic->p[3].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[3].i_pitch + i ) = A; \
565         }
566 #define DrawLineBlack(a,b) DrawLine(a,b,0,128,128,0xFF)
567 #define DrawLineWhite(a,b) DrawLine(a,b,255,128,128,0xFF)
568
569     // blacken the whole picture
570     for( i = 0 ; i < p_pic->i_planes ; i++ )
571     {
572         memset( p_pic->p[i].p_pixels, 0x00,
573                 p_pic->p[i].i_visible_lines * p_pic->p[i].i_pitch );
574     }
575
576
577     // side bar
578     for ( i_line = 20; i_line < scale+20; i_line++ ) {
579         // vertical line
580         DrawLineBlack(20,22);
581         DrawLineWhite(22,24);
582
583         // -10dB
584         if (i_line == minus10 - 2) {
585             // 1
586             DrawLineBlack(14,15);
587             // 0
588             DrawLineBlack(16,19);
589         }
590         if (i_line == minus10 - 1) {
591             // 1
592             DrawLineBlack(14,15);
593             // 0
594             DrawLineBlack(16,17);
595             DrawLineBlack(18,19);
596             // limit
597             DrawLineWhite(24,27); //White
598         }
599         if (i_line == minus10) {
600             // 1
601             DrawLineBlack(14,15);
602             // 0
603             DrawLineBlack(16,17);
604             DrawLineBlack(18,19);
605             // limit
606             DrawLineBlack(24,27);
607         }
608         if (i_line == minus10 + 1) {
609             // 1
610             DrawLineBlack(14,15);
611             // 0
612             DrawLineBlack(16,17);
613             DrawLineBlack(18,19);
614             // limit
615             DrawLineBlack(24,27);
616         }
617         if (i_line == minus10 + 2) {
618             // 1
619             DrawLineBlack(14,15);
620             // 0
621             DrawLineBlack(16,19);
622         }
623
624         // -20dB
625         if (i_line == minus20 - 2) {
626             // 2
627             DrawLineBlack(12,15);
628             // 0
629             DrawLineBlack(16,19);
630         }
631         if (i_line == minus20 - 1) {
632             // 2
633             DrawLineBlack(12,13);
634             // 0
635             DrawLineBlack(16,17);
636             DrawLineBlack(18,19);
637             // limit
638             DrawLineWhite(24,27); //White
639         }
640         if (i_line == minus20) {
641             // 2
642             DrawLineBlack(12,15);
643             // 0
644             DrawLineBlack(16,17);
645             DrawLineBlack(18,19);
646             // limit
647             DrawLineBlack(24,27);
648         }
649         if (i_line == minus20 + 1) {
650             // 2
651             DrawLineBlack(14,15);
652             // 0
653             DrawLineBlack(16,17);
654             DrawLineBlack(18,19);
655             // limit
656             DrawLineBlack(24,27);
657         }
658         if (i_line == minus20 + 2) {
659             // 2
660             DrawLineBlack(12,15);
661             // 0
662             DrawLineBlack(16,19);
663         }
664
665         // -30dB
666         if (i_line == minus30 - 2) {
667             // 3
668             DrawLineBlack(12,15);
669             // 0
670             DrawLineBlack(16,19);
671         }
672         if (i_line == minus30 - 1) {
673             // 3
674             DrawLineBlack(14,15);
675             // 0
676             DrawLineBlack(16,17);
677             DrawLineBlack(18,19);
678             // limit
679             DrawLineWhite(24,27); //White
680         }
681         if (i_line == minus30) {
682             // 3
683             DrawLineBlack(12,15);
684             // 0
685             DrawLineBlack(16,17);
686             DrawLineBlack(18,19);
687             // limit
688             DrawLineBlack(24,27);
689         }
690         if (i_line == minus30 + 1) {
691             // 3
692             DrawLineBlack(14,15);
693             // 0
694             DrawLineBlack(16,17);
695             DrawLineBlack(18,19);
696             // limit
697             DrawLineBlack(24,27);
698         }
699         if (i_line == minus30 + 2) {
700             // 3
701             DrawLineBlack(12,15);
702             // 0
703             DrawLineBlack(16,19);
704         }
705
706         // -40dB
707         if (i_line == minus40 - 2) {
708             // 4
709             DrawLineBlack(14,15);
710             // 0
711             DrawLineBlack(16,19);
712         }
713         if (i_line == minus40 - 1) {
714             // 4
715             DrawLineBlack(14,15);
716             // 0
717             DrawLineBlack(16,17);
718             DrawLineBlack(18,19);
719             // limit
720             DrawLineWhite(24,27); // white
721         }
722         if (i_line == minus40) {
723             // 4
724             DrawLineBlack(12,15);
725             // 0
726             DrawLineBlack(16,17);
727             DrawLineBlack(18,19);
728             // limit
729             DrawLineBlack(24,27);
730         }
731         if (i_line == minus40 + 1) {
732             // 4
733             DrawLineBlack(12,13);
734             DrawLineBlack(14,15);
735             // 0
736             DrawLineBlack(16,17);
737             DrawLineBlack(18,19);
738             // limit
739             DrawLineBlack(24,27);
740         }
741         if (i_line == minus40 + 2) {
742             // 4
743             DrawLineBlack(12,13);
744             DrawLineBlack(14,15);
745             // 0
746             DrawLineBlack(16,19);
747         }
748
749         // -50dB
750         if (i_line == minus50 - 2) {
751             // 5
752             DrawLineBlack(12,15);
753             // 0
754             DrawLineBlack(16,19);
755         }
756         if (i_line == minus50 - 1) {
757             // 5
758             DrawLineBlack(14,15);
759             // 0
760             DrawLineBlack(16,17);
761             DrawLineBlack(18,19);
762             // limit
763             DrawLineWhite(24,27); //White
764         }
765         if (i_line == minus50) {
766             // 5
767             DrawLineBlack(12,15);
768             // 0
769             DrawLineBlack(16,17);
770             DrawLineBlack(18,19);
771             // limit
772             DrawLineBlack(24,27);
773         }
774         if (i_line == minus50 + 1) {
775             // 5
776             DrawLineBlack(12,13);
777             // 0
778             DrawLineBlack(16,17);
779             DrawLineBlack(18,19);
780             // limit
781             DrawLineBlack(24,27);
782         }
783         if (i_line == minus50 + 2) {
784             // 2
785             DrawLineBlack(12,15);
786             // 0
787             DrawLineBlack(16,19);
788         }
789         // -60dB
790         if (i_line == minus60 - 2) {
791             // 6
792             DrawLineBlack(12,15);
793             // 0
794             DrawLineBlack(16,19);
795         }
796         if (i_line == minus60 - 1) {
797             // 6
798             DrawLineBlack(12,13);
799             DrawLineBlack(14,15);
800             // 0
801             DrawLineBlack(16,17);
802             DrawLineBlack(18,19);
803             // limit
804             DrawLineWhite(24,27); //White
805         }
806         if (i_line == minus60) {
807             // 6
808             DrawLineBlack(12,15);
809             // 0
810             DrawLineBlack(16,17);
811             DrawLineBlack(18,19);
812             // limit
813             DrawLineBlack(24,27);
814         }
815         if (i_line == minus60 + 1) {
816             // 6
817             DrawLineBlack(12,13);
818             // 0
819             DrawLineBlack(16,17);
820             DrawLineBlack(18,19);
821             // limit
822             DrawLineBlack(24,27);
823         }
824         if (i_line == minus60 + 2) {
825             // 6
826             DrawLineBlack(12,15);
827             // 0
828             DrawLineBlack(16,19);
829         }
830     }
831
832 #define drawPoint(offset,y,u,v,a)  \
833                 *(p_pic->p[0].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[0].i_pitch + offset ) = y; \
834                 *(p_pic->p[1].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[1].i_pitch + offset ) = u; \
835                 *(p_pic->p[2].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[2].i_pitch + offset ) = v; \
836                 *(p_pic->p[3].p_pixels + (scale + 30 - i_line - 1) * p_pic->p[3].i_pitch + offset ) = a;
837
838
839     // draw the bars and channel indicators
840     for (i=0; i<nbChannels; i++) {
841         pi =  25 + ((i+1)*5) + (i*barWidth) ;  // 25 separació amb indicador, 5 separació entre barres
842
843         for( j = pi; j < pi + barWidth; j++)
844         {
845             // channel indicators
846             for ( i_line = 12; i_line < 20; i_line++ ) {
847                 if( alarm ) {
848                     drawPoint(j,76,85,0xFF,0xFF);  // red
849                 }
850                 else {
851                     drawPoint(j,0,128,128,0xFF); // black             DrawLine(pi,pf,0xFF,128,128,0xFF);
852                 }
853             }
854
855             // bars
856             for( i_line = 20; i_line < i_values[i]+20; i_line++ )
857             {
858                 if (i_line < minus18) { // green if < -18 dB
859                     drawPoint(j,150,44,21,0xFF);
860                     //DrawLine(pi,pf,150,44,21,0xFF);
861                 } else if (i_line < minus8) { // yellow if > -18dB and < -8dB
862                     drawPoint(j,226,1,148,0xFF);
863                     //DrawLine(pi,pf,226,1,148,0xFF);
864                 } else { // red if > -8 dB
865                     drawPoint(j,76,85,0xFF,0xFF);
866                     //DrawLine(pi,pf,76,85,255,0xFF);
867                 }
868             }
869             // bars no signal
870             for( ; i_line < scale+20; i_line++ )
871             {
872                 if (i_line < minus18) { // green if < -18 dB
873                     drawPoint(j,74,85,74,0xFF);
874                     //DrawLine(pi,pf,74,85,74,0xFF);
875                 } else if (i_line < minus8) { // yellow if > -18dB and < -8dB
876                     drawPoint(j,112,64,138,0xFF);
877                     //DrawLine(pi,pf,112,64,138,0xFF);
878                 } else { // red if > -8 dB
879                     drawPoint(j,37,106,191,0xFF);
880                     //DrawLine(pi,pf,37,106,191,0xFF);
881                 }
882             }
883         }
884     }
885
886     return p_pic;
887 }
888
889 /*****************************************************************************
890  * LoadBarGraph: loads the BarGraph images into memory
891  *****************************************************************************/
892 static void LoadBarGraph( vlc_object_t *p_this, BarGraph_t *p_BarGraph )
893 {
894
895     p_BarGraph->p_pic = LoadImage( p_this, p_BarGraph->nbChannels, p_BarGraph->i_values, p_BarGraph->scale, p_BarGraph->alarm, p_BarGraph->barWidth);
896     if( !p_BarGraph->p_pic )
897     {
898         msg_Warn( p_this, "error while creating picture" );
899     }
900
901 }
902
903 /*****************************************************************************
904  * parse_i_values : parse i_values parameter and store the corresponding values
905  *****************************************************************************/
906 void parse_i_values( BarGraph_t *p_BarGraph, char *i_values)
907 {
908     char* res = NULL;
909     char delim[] = ":";
910     char* tok;
911     float db;
912
913     p_BarGraph->nbChannels = 0;
914     p_BarGraph->i_values = NULL;
915     res = strtok_r(i_values, delim, &tok);
916     while (res != NULL) {
917         p_BarGraph->nbChannels++;
918         p_BarGraph->i_values = xrealloc(p_BarGraph->i_values,
919                                           p_BarGraph->nbChannels*sizeof(int));
920         db = log10(atof(res)) * 20;
921         p_BarGraph->i_values[p_BarGraph->nbChannels-1] = VLC_CLIP( iec_scale(db)*p_BarGraph->scale, 0, p_BarGraph->scale );
922         res = strtok_r(NULL, delim, &tok);
923     }
924
925 }
926
927 /*****************************************************************************
928  * IEC 268-18  Source: meterbridge
929  *****************************************************************************/
930 static float iec_scale(float dB)
931 {
932        float fScale = 1.0f;
933
934        if (dB < -70.0f)
935               fScale = 0.0f;
936        else if (dB < -60.0f)
937               fScale = (dB + 70.0f) * 0.0025f;
938        else if (dB < -50.0f)
939               fScale = (dB + 60.0f) * 0.005f + 0.025f;
940        else if (dB < -40.0)
941               fScale = (dB + 50.0f) * 0.0075f + 0.075f;
942        else if (dB < -30.0f)
943               fScale = (dB + 40.0f) * 0.015f + 0.15f;
944        else if (dB < -20.0f)
945               fScale = (dB + 30.0f) * 0.02f + 0.3f;
946        else if (dB < -0.001f || dB > 0.001f)  /* if (dB < 0.0f) */
947               fScale = (dB + 20.0f) * 0.025f + 0.5f;
948
949        return fScale;
950 }