1 /***************************************************************************
2  *   Copyright (C) 2011 by Pere Ràfols Soler                               *
3  *   sapista2@gmail.com                                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20 
21 #include <iostream>
22 #include <cmath>
23 #include <cstdio>
24 
25 #include "colors.h"
26 #include "vuwidget.h"
27 
28 #define TEXT_OFFSET 12
29 #define MARGIN 6.5
30 #define CHANNEL_WIDTH 9
31 #define MICROFADER_WIDTH 30
32 #define SCROLL_EVENT_PERCENT 0.02
33 #define WIDGET_HEIGHT 150
34 #define TOP_OFFSET 24
35 #define AUTO_REFRESH_TIMEOUT_MS 20
36 
VUWidget(int iChannels,float fMin,float fMax,std::string title,bool IsGainReduction,bool DrawThreshold)37 VUWidget::VUWidget(int iChannels, float fMin, float fMax, std::string title, bool IsGainReduction, bool DrawThreshold)
38   :m_iChannels(iChannels),
39   m_fMin(fMin),
40   m_fMax(fMax),
41   m_bIsGainReduction(IsGainReduction),
42   bMotionIsConnected(false),
43   m_fValues(new float[m_iChannels]),
44   m_fPeaks(new float[m_iChannels]),
45   m_iBuffCnt(new int[m_iChannels]),
46   m_ThFaderValue(0.0),
47   m_iThFaderPositon(0),
48   m_bDrawThreshold(DrawThreshold),
49   m_start(new timeval[m_iChannels]),
50   m_end(new timeval[m_iChannels]),
51   m_Title(title),
52   m_redraw_fader(true),
53   m_redraw_Vu(true),
54   m_FaderFocus(false)
55 {
56 
57   m_textdBseparation = (int)round((m_fMax - m_fMin)/18.0);
58 
59   for (int i = 0; i < m_iChannels; i++)
60   {
61     m_fValues[i] = -100.0;
62     m_fPeaks[i] = -100.0;
63     m_iBuffCnt[i] = 0;
64   }
65 
66   int widget_witdh;
67   if(m_bDrawThreshold)
68   {
69     widget_witdh = MARGIN + TEXT_OFFSET +  (MARGIN + CHANNEL_WIDTH)* m_iChannels + MICROFADER_WIDTH/2 + MARGIN + 2;
70   }
71   else
72   {
73     widget_witdh = MARGIN + TEXT_OFFSET +  (MARGIN + CHANNEL_WIDTH)* m_iChannels;
74   }
75   set_size_request(widget_witdh, WIDGET_HEIGHT);
76 
77 
78   //Initialize peak time counters
79   for (int i = 0; i < m_iChannels; i++)
80   {
81     gettimeofday(&m_start[i], NULL);
82     gettimeofday(&m_end[i], NULL);
83   }
84 
85   //The micro fader for threshold
86   if(m_bDrawThreshold)
87   {
88     add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK | Gdk::SCROLL_MASK | Gdk::LEAVE_NOTIFY_MASK);
89     signal_button_press_event().connect(sigc::mem_fun(*this, &VUWidget::on_button_press_event),true);
90     signal_button_release_event().connect(sigc::mem_fun(*this, &VUWidget::on_button_release_event),true);
91     signal_scroll_event().connect(sigc::mem_fun(*this, &VUWidget::on_scrollwheel_event),true);
92     signal_motion_notify_event().connect(sigc::mem_fun(*this, &VUWidget::on_mouse_motion_event),true);
93     signal_leave_notify_event().connect(sigc::mem_fun(*this, &VUWidget::on_mouse_leave_widget),true);
94   }
95 
96   Glib::signal_timeout().connect( sigc::mem_fun(*this, &VUWidget::on_timeout_redraw), AUTO_REFRESH_TIMEOUT_MS );
97 }
98 
~VUWidget()99 VUWidget::~VUWidget()
100 {
101   delete [] m_fValues;
102   delete [] m_fPeaks;
103   delete [] m_start;
104   delete [] m_end;
105   delete [] m_iBuffCnt;
106 }
107 
on_mouse_leave_widget(GdkEventCrossing * event)108 bool VUWidget::on_mouse_leave_widget(GdkEventCrossing* event)
109 {
110   if(!bMotionIsConnected)
111   {
112     m_FaderFocus = false;
113     m_redraw_fader = true;
114   }
115   return true;
116 }
117 
setValue(int iChannel,float fValue)118 void VUWidget::setValue(int iChannel, float fValue)
119 {
120   if (fValue > 0)
121   {
122     if(m_iBuffCnt[iChannel] > 0)
123     {
124        m_fValues[iChannel] = ((((double)m_iBuffCnt[iChannel])*m_fValues[iChannel]) +  20.0*log10(fValue))/((double)(m_iBuffCnt[iChannel] + 1));
125     }
126     else
127     {
128       m_fValues[iChannel] = 20.0*log10(fValue);
129     }
130     m_iBuffCnt[iChannel]++;
131   }
132   else
133   {
134      m_fValues[iChannel] = -100.0;
135   }
136 
137   m_redraw_Vu = true;
138 }
139 
clearPeak(int iChannel)140 void VUWidget::clearPeak(int iChannel)
141 {
142   m_fPeaks[iChannel] = 0.0;
143 }
144 
dB2Pixels(double dB_in)145 double VUWidget::dB2Pixels(double dB_in)
146 {
147   double m, n;
148   if(m_bIsGainReduction)
149   {
150     m = ((double)(height - 3.0*MARGIN - TOP_OFFSET))/(m_fMax - m_fMin);
151     n = (double)(MARGIN + TOP_OFFSET) - m_fMin*m;
152   }
153   else
154   {
155     m = ((double)(3.0*MARGIN + TOP_OFFSET - height ))/(m_fMax - m_fMin);
156     n = (double)(height - 2.0*MARGIN) - m_fMin*m;
157   }
158   return m*dB_in + n;
159 }
160 
161 
on_expose_event(GdkEventExpose * event)162 bool VUWidget::on_expose_event(GdkEventExpose* event)
163 {
164   Glib::RefPtr<Gdk::Window> window = get_window();
165   if(window)
166   {
167     Gtk::Allocation allocation = get_allocation();
168     width = allocation.get_width();
169     height = allocation.get_height();
170 
171     if(!(m_background_surface_ptr || m_foreground_surface_ptr || m_fader_surface_ptr ))
172     {
173       m_background_surface_ptr = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width, height);
174       redraw_background();
175       m_foreground_surface_ptr = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width, height);
176       redraw_foreground();
177       m_vu_surface_ptr = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width, height);
178       redraw_vuwidget();
179       if(m_bDrawThreshold)
180       {
181         m_fader_surface_ptr = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width, height);
182         redraw_faderwidget();
183       }
184     }
185 
186     Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
187 
188     //Draw the background
189     if(m_background_surface_ptr)
190     {
191       cr->save();
192       cr->set_source(m_background_surface_ptr, 0, 0);
193       cr->paint();
194       cr->restore();
195     }
196 
197     //Draw the VU
198     if(m_vu_surface_ptr)
199     {
200       cr->save();
201       cr->set_source(m_vu_surface_ptr, 0, 0);
202       cr->paint();
203       cr->restore();
204     }
205 
206     //Draw the foreground
207     if(m_foreground_surface_ptr)
208     {
209       cr->save();
210       cr->set_source(m_foreground_surface_ptr, 0, 0);
211       cr->paint();
212       cr->restore();
213     }
214 
215     //Draw the fader widget
216     if(m_fader_surface_ptr)
217     {
218       cr->save();
219       cr->set_source(m_fader_surface_ptr, 0, 0);
220       cr->paint();
221       cr->restore();
222     }
223   }
224   return true;
225 }
226 
set_value_th(double value)227 void VUWidget::set_value_th(double value)
228 {
229   m_ThFaderValue = value;
230   m_ThFaderValue = m_ThFaderValue < m_fMin + 2.0 ? m_fMin + 2.0 : m_ThFaderValue;
231   m_ThFaderValue = m_ThFaderValue > m_fMax - 2.0 ? m_fMax - 2.0 : m_ThFaderValue; //Limit threshols 2dB less than VU
232   m_redraw_fader = true;
233 }
234 
get_value_th()235 double VUWidget::get_value_th()
236 {
237   return m_ThFaderValue;
238 }
239 
240 //Mouse grab signal handlers
on_button_press_event(GdkEventButton * event)241 bool  VUWidget::on_button_press_event(GdkEventButton* event)
242 {
243   int x,y;
244   get_pointer(x,y);
245   if( y > m_iThFaderPositon - MICROFADER_WIDTH/2 &&
246       y < m_iThFaderPositon + MICROFADER_WIDTH/2)
247   {
248       bMotionIsConnected = true;
249   }
250   return true;
251 }
252 
on_button_release_event(GdkEventButton * event)253 bool  VUWidget::on_button_release_event(GdkEventButton* event)
254 {
255   bMotionIsConnected = false;
256   return true;
257 }
258 
on_scrollwheel_event(GdkEventScroll * event)259 bool  VUWidget::on_scrollwheel_event(GdkEventScroll* event)
260 {
261   double increment;
262 
263   increment =  SCROLL_EVENT_PERCENT*(m_fMax - m_fMin);
264   if (event->direction == GDK_SCROLL_UP)
265   {
266     // up code
267     set_value_th(m_ThFaderValue + increment);
268 
269   }
270   else if (event->direction == GDK_SCROLL_DOWN)
271   {
272     // down code
273     set_value_th(m_ThFaderValue - increment);
274   }
275   m_FaderChangedSignal.emit();
276   return true;
277 }
278 
on_mouse_motion_event(GdkEventMotion * event)279 bool  VUWidget::on_mouse_motion_event(GdkEventMotion* event)
280 {
281   if (bMotionIsConnected)
282   {
283     double m = ((double)(3.0*MARGIN + TOP_OFFSET - height ))/(m_fMax - m_fMin);
284     double n = (double)(height - 2.0*MARGIN) - m_fMin*m;
285     double faderPos = (event->y - n)/m;
286     set_value_th(faderPos);
287     m_FaderChangedSignal.emit();
288   }
289   else
290   {
291     //Check focus on fader widget
292     m_FaderFocus = event->y > m_iThFaderPositon - MICROFADER_WIDTH/2 && event->y < m_iThFaderPositon + MICROFADER_WIDTH/2 &&
293 		   event->x > width - MICROFADER_WIDTH && event->x < width ;
294     m_redraw_fader = true;
295   }
296     return true;
297 }
298 
signal_changed()299  VUWidget::signal_FaderChanged  VUWidget::signal_changed()
300  {
301   return m_FaderChangedSignal;
302  }
303 
redraw_background()304 void VUWidget::redraw_background()
305 {
306  if(m_background_surface_ptr)
307   {
308     //Create cairo context using the buffer surface
309     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_background_surface_ptr);
310 
311     //Paint background
312     cr->save();
313     cr->set_source_rgb(BACKGROUND_R, BACKGROUND_G, BACKGROUND_B);
314     cr->paint(); //Fill all with background color
315     cr->restore();
316 
317     //Draw text with pango
318     cr->save();
319     Glib::RefPtr<Pango::Layout> pangoLayout = Pango::Layout::create(cr);
320     Pango::FontDescription font_desc("mono 9px");
321     pangoLayout->set_font_description(font_desc);
322     cr->set_source_rgba(0.9, 0.9, 0.9, 0.5);
323 
324     cr->move_to(MARGIN + TEXT_OFFSET - 3, TOP_OFFSET/2);//4 is to get the text centered in VU
325     pangoLayout->set_text(m_Title.c_str());
326     pangoLayout->set_width(Pango::SCALE * (CHANNEL_WIDTH * m_iChannels + (m_iChannels - 1)*MARGIN));
327     pangoLayout->set_alignment(Pango::ALIGN_CENTER);
328     pangoLayout->show_in_cairo_context(cr);
329     cr->stroke();
330 
331     for(float fdb = m_fMin; fdb <= m_fMax; fdb = fdb + m_textdBseparation)
332     {
333       std::stringstream ss;
334       ss<<abs(round(fdb));
335       cr->move_to(MARGIN, dB2Pixels(fdb) - 4);//4 is to get the text centered in VU
336       pangoLayout->set_text(ss.str());
337       pangoLayout->set_width(Pango::SCALE * (TEXT_OFFSET - MARGIN));
338       pangoLayout->set_alignment(Pango::ALIGN_RIGHT);
339       pangoLayout->show_in_cairo_context(cr);
340       cr->stroke();
341     }
342     cr->restore();
343 
344     //Draw VU rectangle
345     double radius = height / 100.0;
346     double degrees = M_PI / 180.0;
347     for(int i = 0; i < m_iChannels; i++)
348     {
349       cr->save();
350       cr->begin_new_sub_path();
351       cr->arc (MARGIN + TEXT_OFFSET + CHANNEL_WIDTH + i*(MARGIN + CHANNEL_WIDTH + 0.5) - radius, MARGIN + TOP_OFFSET - 4 + radius, radius, -90 * degrees, 0 * degrees);
352       cr->arc (MARGIN + TEXT_OFFSET + CHANNEL_WIDTH + i*(MARGIN + CHANNEL_WIDTH + 0.5) - radius, height - 1 - MARGIN - radius, radius, 0 * degrees, 90 * degrees);
353       cr->arc (MARGIN + TEXT_OFFSET + i*(MARGIN + CHANNEL_WIDTH + 0.5) + radius, height - 1 - MARGIN - radius, radius, 90 * degrees, 180 * degrees);
354       cr->arc (MARGIN + TEXT_OFFSET + i*(MARGIN + CHANNEL_WIDTH + 0.5) + radius, MARGIN + TOP_OFFSET - 4 + radius, radius, 180 * degrees, 270 * degrees);
355       cr->close_path();
356       cr->set_source_rgb(0.15, 0.15, 0.15);
357       cr->fill_preserve();
358       cr->set_line_width(1.0);
359       cr->set_source_rgb(0.5, 0.5, 0.5);
360       cr->stroke();
361       cr->restore();
362     }
363   }
364 }
365 
redraw_foreground()366 void VUWidget::redraw_foreground()
367 {
368  if(m_foreground_surface_ptr)
369   {
370     //Create cairo context using the buffer surface
371     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_foreground_surface_ptr);
372 
373     //Draw some horitzontal lines to show dB scale over Vu
374     cr->save();
375     cr->set_line_width(1.0);
376     cr->set_source_rgba(0.8, 0.8, 0.8, 0.4);
377     for(float fdb = m_fMin; fdb <= m_fMax; fdb = fdb + m_textdBseparation)
378     {
379       cr->move_to(MARGIN + TEXT_OFFSET - 2, round(dB2Pixels(fdb)) + 0.5);
380       cr->line_to(MARGIN + TEXT_OFFSET + CHANNEL_WIDTH + (m_iChannels - 1 ) * (CHANNEL_WIDTH + MARGIN ) + 2, round(dB2Pixels(fdb)) + 0.5);
381       cr->stroke();
382     }
383     cr->restore();
384   }
385 }
386 
redraw_faderwidget()387 void VUWidget::redraw_faderwidget()
388 {
389  if(m_fader_surface_ptr)
390   {
391     //Create cairo context using the buffer surface
392     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_fader_surface_ptr);
393 
394     //Clear current context
395     cr->save();
396     cr->set_operator(Cairo::OPERATOR_CLEAR);
397     cr->paint();
398     cr->restore();
399 
400     //Draw vertical line
401     cr->save();
402     cr->move_to(width - MICROFADER_WIDTH/2 + 0.5, dB2Pixels(m_fMin + 2.0));
403     cr->line_to(width - MICROFADER_WIDTH/2 + 0.5, dB2Pixels(m_fMax - 2.0));
404 
405     cr->set_line_cap(Cairo::LINE_CAP_ROUND);
406     cr->set_line_width(3.0);
407     cr->set_source_rgba(0.7, 0.7, 0.7, 0.5);
408     cr->stroke_preserve();
409     cr->set_source_rgba(0.15, 0.15, 0.15, 1.0);
410     cr->set_line_width(1);
411     cr->stroke();
412 
413     //Draw threshold text with pango
414     Glib::RefPtr<Pango::Layout> pangoLayout_th = Pango::Layout::create(cr);
415     Pango::FontDescription font_desc_th("sans bold 8px");
416     font_desc_th.set_gravity(Pango::GRAVITY_EAST);
417     pangoLayout_th->set_font_description(font_desc_th);
418     pangoLayout_th->set_alignment(Pango::ALIGN_LEFT);
419     cr->move_to(width - MICROFADER_WIDTH/2 - 10, height - MICROFADER_WIDTH/2 - 85);
420     cr->set_source_rgba(0.9, 0.9, 0.9, 0.7);
421     pangoLayout_th->update_from_cairo_context(cr);  //gets cairo cursor position
422     pangoLayout_th->set_text("d\r\nl\r\no\r\nh\r\ns\r\ne\r\nr\r\nh\r\nT");
423     pangoLayout_th->show_in_cairo_context(cr);
424     cr->stroke();
425 
426     //Calc coords for mini fader
427     m_iThFaderPositon = (int) dB2Pixels(m_ThFaderValue);
428 
429     //Draw the fader drop down shadow
430     cr->save();
431     cr->translate(width - MICROFADER_WIDTH/2 + 2,  m_iThFaderPositon + 4);
432     cr->scale(MICROFADER_WIDTH/2, MICROFADER_WIDTH/4);
433     Cairo::RefPtr<Cairo::RadialGradient> bkg_gradient_rad_ptr = Cairo::RadialGradient::create(0, 0, 0, 0, 0, 1);
434     bkg_gradient_rad_ptr->add_color_stop_rgba (0.3, 0.2, 0.2, 0.2, 1.0);
435     bkg_gradient_rad_ptr->add_color_stop_rgba (1.0, 0.1, 0.1, 0.2, 0.0);
436     cr->set_source(bkg_gradient_rad_ptr);
437     cr->arc(0.0, 0.0, 1.0, 0.0, 2.0*M_PI);
438     cr->fill();
439     cr->restore();
440 
441     //Draw Threshold fader
442     double degrees = M_PI / 180.0;
443     cr->begin_new_sub_path();
444     cr->arc(width - 2 - MICROFADER_WIDTH/4, m_iThFaderPositon + 0.5, MICROFADER_WIDTH/4,  -90 * degrees, 90 * degrees);
445     cr->line_to( width - 2 - MICROFADER_WIDTH/2, m_iThFaderPositon + MICROFADER_WIDTH/4 + 0.5);
446     cr->line_to( width - 2 - MICROFADER_WIDTH, m_iThFaderPositon + 0.5);
447     cr->line_to( width - 2 - MICROFADER_WIDTH/2, m_iThFaderPositon - MICROFADER_WIDTH/4 + 0.5);
448     cr->close_path();
449     Cairo::RefPtr<Cairo::LinearGradient> bkg_gradient_ptr = Cairo::LinearGradient::create(width - 2 - MICROFADER_WIDTH/2, m_iThFaderPositon - MICROFADER_WIDTH/4, width - 2 - MICROFADER_WIDTH/2, m_iThFaderPositon + MICROFADER_WIDTH/4);
450     bkg_gradient_ptr->add_color_stop_rgba (0.3, 0.8, 0.8, 0.85, 1.0);
451     bkg_gradient_ptr->add_color_stop_rgba (1.0, 0.2, 0.2, 0.25, 1.0);
452     cr->set_source(bkg_gradient_ptr);
453     cr->fill_preserve();
454 
455     //Draw focus glow
456     if(m_FaderFocus)
457     {
458       Cairo::RefPtr<Cairo::RadialGradient> glow_gradient_rad_ptr = Cairo::RadialGradient::create(width - MICROFADER_WIDTH/2, m_iThFaderPositon, MICROFADER_WIDTH/2, width - MICROFADER_WIDTH/2, m_iThFaderPositon, MICROFADER_WIDTH);
459       glow_gradient_rad_ptr->add_color_stop_rgba (0.0, 0.0, 1.0, 1.0, 0.1);
460       glow_gradient_rad_ptr->add_color_stop_rgba (0.05, 1.0, 1.0, 1.0, 0.3);
461       cr->set_source(glow_gradient_rad_ptr);
462       cr->fill_preserve();
463     }
464     cr->set_source_rgba(0.1, 0.1, 0.1, 0.7);
465     cr->set_line_width(1.0);
466     cr->stroke();
467 
468     cr->move_to(width - 2 - 5*MICROFADER_WIDTH/8, m_iThFaderPositon + 0.5);
469     cr->line_to(width - 4 - MICROFADER_WIDTH/8, m_iThFaderPositon + 0.5);
470     cr->move_to(width - 2 - 5*MICROFADER_WIDTH/8, m_iThFaderPositon + 0.5 - 2);
471     cr->line_to(width - 4 - MICROFADER_WIDTH/8, m_iThFaderPositon + 0.5 - 2);
472     cr->move_to(width - 2 - 5*MICROFADER_WIDTH/8, m_iThFaderPositon + 0.5 + 2);
473     cr->line_to(width - 4 - MICROFADER_WIDTH/8, m_iThFaderPositon + 0.5 + 2);
474     cr->set_source_rgba(0.0, 0.0, 0.0, 0.2);
475     cr->set_line_width(1.0);
476     cr->stroke();
477   }
478 }
479 
redraw_vuwidget()480 void VUWidget::redraw_vuwidget()
481 {
482   if(m_vu_surface_ptr)
483   {
484     //Create cairo context using the buffer surface
485     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_vu_surface_ptr);
486 
487     //Clear current context
488     cr->save();
489     cr->set_operator(Cairo::OPERATOR_CLEAR);
490     cr->paint();
491     cr->restore();
492    //Draw the VU
493     Cairo::RefPtr<Cairo::LinearGradient> bkg_gradient_ptr;
494     for(int i = 0; i < m_iChannels; i++)
495     {
496       //Reset mean buffer
497       m_iBuffCnt[i] = 0;
498       long mtime, seconds, useconds;
499       gettimeofday(&m_end[i], NULL);
500 
501       seconds  = m_end[i].tv_sec  - m_start[i].tv_sec;
502       useconds = m_end[i].tv_usec - m_start[i].tv_usec;
503       mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;
504 
505       //Clip max
506       m_fValues[i] =  m_fValues[i] > m_fMax ? m_fMax :  m_fValues[i];
507 
508       if (m_fValues[i] >= m_fPeaks[i])
509       {
510         m_fPeaks[i] = m_fValues[i];
511         gettimeofday(&m_start[i] , NULL);
512       }
513 
514       else if (mtime > PEAK_CLEAR_TIMEOUT)
515       {
516         m_fPeaks[i] = -100.0;
517       }
518 
519 
520       cr->save();
521       cr->set_line_width(CHANNEL_WIDTH - 4);
522       cr->set_line_cap(Cairo::LINE_CAP_ROUND);
523       bkg_gradient_ptr = Cairo::LinearGradient::create(MARGIN + TEXT_OFFSET + CHANNEL_WIDTH/2.0 + i*(MARGIN + CHANNEL_WIDTH + 0.5), dB2Pixels(m_fMin), MARGIN + TEXT_OFFSET + CHANNEL_WIDTH/2.0 + i*(MARGIN + CHANNEL_WIDTH + 0.5), dB2Pixels(m_fMax));
524       if(m_bIsGainReduction)
525       {
526         bkg_gradient_ptr->add_color_stop_rgba (0.0, 1.0, 0.5, 0.0, 0.0);
527         bkg_gradient_ptr->add_color_stop_rgba (0.01, 1.0, 0.5, 0.0, 1.0);
528         bkg_gradient_ptr->add_color_stop_rgba (1.0, 1.0, 0.0, 0.0, 1.0);
529       }
530       else
531       {
532         bkg_gradient_ptr->add_color_stop_rgba (0.0, 0.0, 1.0, 0.0, 0.0);
533         bkg_gradient_ptr->add_color_stop_rgba (0.01, 0.0, 1.0, 0.0, 1.0);
534         bkg_gradient_ptr->add_color_stop_rgba (0.5, 1.0, 1.0, 0.0, 1.0);
535         bkg_gradient_ptr->add_color_stop_rgba (1.0, 1.0, 0.0, 0.0, 1.0);
536       }
537       cr->set_source(bkg_gradient_ptr);
538 
539       //The VU
540       if(m_fValues[i] >= m_fMin)
541       {
542         cr->move_to(MARGIN + TEXT_OFFSET + CHANNEL_WIDTH/2.0 + i*(MARGIN + CHANNEL_WIDTH + 0.5), dB2Pixels(m_fMin));
543         cr->line_to(MARGIN + TEXT_OFFSET + CHANNEL_WIDTH/2.0 + i*(MARGIN + CHANNEL_WIDTH + 0.5), dB2Pixels(m_fValues[i]));
544         cr->stroke();
545       }
546 
547       //The peak
548       if(m_fPeaks[i] >= m_fMin)
549       {
550         cr->move_to(MARGIN + TEXT_OFFSET + CHANNEL_WIDTH/2.0 + i*(MARGIN + CHANNEL_WIDTH + 0.5), dB2Pixels(m_fPeaks[i]));
551         cr->line_to(MARGIN + TEXT_OFFSET + CHANNEL_WIDTH/2.0 + i*(MARGIN + CHANNEL_WIDTH + 0.5), dB2Pixels(m_fPeaks[i]));
552         cr->stroke();
553         cr->restore();
554       }
555     }
556   }
557 }
558 
on_timeout_redraw()559 bool VUWidget::on_timeout_redraw()
560 {
561   bool redraw = false;
562   if(m_redraw_fader)
563   {
564     m_redraw_fader = false;
565     redraw = true;
566     redraw_faderwidget();
567   }
568 
569   if(m_redraw_Vu)
570   {
571     m_redraw_Vu = false;
572     redraw = true;
573     redraw_vuwidget();
574   }
575 
576   if(redraw)
577   {
578     Glib::RefPtr<Gdk::Window> win = get_window();
579     if(win)
580     {
581       Gdk::Rectangle r(0, 0, get_allocation().get_width(), get_allocation().get_height());
582       win->invalidate_rect(r, false);
583     }
584   }
585   return true;
586 }
587 
588