1 /***************************************************************************
2  *   Copyright (C) 2009 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 "bodeplot.h"
22 #include "colors.h"
23 #include "guiconstants.h"
24 #include "../../dsp/fastmath.h"
25 
26 #include <cmath>
27 #include <ctime>
28 #include <gtkmm/main.h>
29 #include <gdkmm/cursor.h>
30 
31 #include <iostream>
32 #include <iomanip>
33 #include <cstring>
34 
35 //Use the DSP code to generate digital filter coefs
36 #include "../../dsp/filter.h"
37 
38 #define SPLINE_TENSION 0.2
39 #define BALL_DETECTION_PIXELS 12
40 
PlotEQCurve(int iNumOfBands,int channels)41 PlotEQCurve::PlotEQCurve(int iNumOfBands, int channels)
42 :width(PLOT_WIDTH),
43 height(PLOT_HIGHT),
44 m_TotalBandsCount(iNumOfBands),
45 m_NumChannels(channels),
46 m_Bypass(false),
47 bMotionIsConnected(false),
48 bBandFocus(false),
49 m_BandRedraw(false),
50 m_fullRedraw(false),
51 m_justRedraw(false),
52 SampleRate(0), //Initially zero to force the freq vectors initialization
53 m_FftActive(false),
54 m_minFreq(MIN_FREQ),
55 m_maxFreq(MAX_FREQ),
56 m_dB_plot_range(50.0),
57 fft_gain(0.0),
58 fft_range(80.0),
59 m_bIsSpectrogram(false),
60 m_bFftHold(false)
61 {
62   //Allocate memory for filter data strcuts
63   m_filters = new FilterBandParams*[m_TotalBandsCount];
64   for (int i = 0; i<m_TotalBandsCount; i++)
65   {
66     m_filters[i] = new FilterBandParams;
67   }
68 
69   //Allocate memory for f and pixels arrays
70   f = new double[CURVE_NUM_OF_POINTS];
71   xPixels = new int[CURVE_NUM_OF_POINTS];
72 
73   //Allocate memory for Y axes arrays
74   main_y = new double*[m_NumChannels];
75   for (int i = 0; i<m_NumChannels; i++)
76   {
77     main_y[i] = new double[CURVE_NUM_OF_POINTS];
78   }
79 
80   band_y = new double*[m_TotalBandsCount];
81   band_state = new MSState[m_TotalBandsCount];
82   for (int i = 0; i<m_TotalBandsCount; i++)
83   {
84     band_y[i] = new double[CURVE_NUM_OF_POINTS];
85     if(m_NumChannels == 2)
86     {
87       band_state[i] = DUAL;
88     }
89     else
90     {
91       band_state[i] = MONO;
92     }
93   }
94 
95   //init curves to zero state
96   for(int i = 0; i<CURVE_NUM_OF_POINTS; i++)
97   {
98     for(int j = 0; j <m_NumChannels; j++)
99     {
100       main_y[j][i] = 0.0;
101     }
102     for(int j = 0; j <m_TotalBandsCount; j++)
103     {
104             band_y[j][i] = 0.0;
105     }
106   }
107 
108   //Allocate memory for band redraw vector
109   m_Bands2Redraw = new bool[m_TotalBandsCount];
110   m_curve_surface_ptr = new  Cairo::RefPtr<Cairo::ImageSurface> [m_TotalBandsCount];
111 
112   //Allocate memory for FFT data
113   xPixels_fft = new double[(FFT_N/2) + 1];
114   xPixels_fft_bins = new double[(FFT_N/2) + 1];
115   fft_pink_noise = new double[(FFT_N/2) + 1];
116   fft_plot = new double[(FFT_N/2) + 1];
117   fft_ant_data = new double [(FFT_N/2) + 1];
118 
119   fft_log_lut = GenerateLog10LUT();
120   resetCurve();
121 
122   set_size_request(width, height);
123 
124   //Init zoom widget
125   m_zoom_widget.center_focus = false;
126   m_zoom_widget.center_press = false;
127   m_zoom_widget.f1_focus = false;
128   m_zoom_widget.f1_press = false;
129   m_zoom_widget.f2_focus = false;
130   m_zoom_widget.f2_press = false;
131   m_zoom_widget.x1 = 0;
132   m_zoom_widget.x2 = 0;
133   m_zoom_widget.x_ant = 0;
134 
135 
136   //Connect mouse signals
137   add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK | Gdk::SCROLL_MASK | Gdk::LEAVE_NOTIFY_MASK);
138   signal_button_press_event().connect(sigc::mem_fun(*this, &PlotEQCurve::on_button_press_event),true);
139   signal_button_release_event().connect(sigc::mem_fun(*this, &PlotEQCurve::on_button_release_event),true);
140   signal_scroll_event().connect(sigc::mem_fun(*this, &PlotEQCurve::on_scrollwheel_event),true);
141 
142   //The timeout signal used to refresh the display is now connected at first run of on_expose_event to run it with freq vector correctly initialized
143   signal_motion_notify_event().connect(sigc::mem_fun(*this, &PlotEQCurve::on_mouse_motion_event),true);
144   signal_leave_notify_event().connect(sigc::mem_fun(*this, &PlotEQCurve::on_mouse_leave_widget),true);
145 
146 
147   //Init FFT vectors
148   setSampleRate(44.1e3);
149 
150   //Allow this widget to get keyboard focus
151   set_can_focus(true);
152 }
153 
~PlotEQCurve()154 PlotEQCurve::~PlotEQCurve()
155 {
156   //Delete filter structs
157   for (int i = 0; i<m_TotalBandsCount; i++)
158   {
159     delete m_filters[i];
160   }
161   delete[] m_filters;
162   delete[] m_Bands2Redraw;
163 
164   //Delete freq and pixels pointers
165   delete[] f;
166   delete[] xPixels;
167 
168   //Delete Y array pointers
169   for (int i = 0; i<m_NumChannels; i++)
170   {
171     delete[] main_y[i];
172   }
173   delete[] main_y;
174 
175   for (int i = 0; i<m_TotalBandsCount; i++)
176   {
177     delete[] band_y[i];
178   }
179   delete[] band_y;
180 
181   delete[] band_state;
182 
183   //Delete FFT plots
184   delete[] fft_pink_noise;
185   delete[] xPixels_fft;
186   delete[] xPixels_fft_bins;
187   delete[] fft_plot;
188   delete[] fft_ant_data;
189   delete[] m_curve_surface_ptr;
190   free(fft_log_lut);
191 }
192 
resetCenterSpan()193 void PlotEQCurve::resetCenterSpan()
194 {
195   //Compute center and span for the full range spectrum
196   double sp = log10(MAX_FREQ/MIN_FREQ);
197   double cn = MIN_FREQ * sqrt(pow(10,sp));
198   setCenterSpan(cn, sp);
199 }
200 
setCenterSpan(double center,double span)201 void PlotEQCurve::setCenterSpan(double center, double span)
202 {
203   m_minFreq = center / sqrt(pow(10,span));
204   m_maxFreq = center * sqrt(pow(10,span));
205 
206   //Initalize the grid
207   const double f_grid[GRID_VERTICAL_LINES] = {20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0,
208                         100.0, 200.0, 300.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0,
209                         1000.0, 2000.0, 3000.0, 4000.0, 5000.0, 6000.0, 7000.0, 8000.0, 9000.0,
210                         10000.0, 20000.0};
211 
212   for(int i=0; i < GRID_VERTICAL_LINES; i++)
213   {
214     xPixels_Grid[i] = freq2Pixels(f_grid[i]);
215   }
216 
217   //Initialize freq vector and pixels for the new limits
218   for(int i=0; i < CURVE_NUM_OF_POINTS; i++)
219   {
220     xPixels[i] = (double)(i)*(((double)(width - 2*CURVE_MARGIN-CURVE_TEXT_OFFSET_X))/((double)(CURVE_NUM_OF_POINTS - 1)));
221     f[i] = Pixels2freq(xPixels[i]);
222   }
223 
224   //Recalc freq bins to fit fft into widget size
225   const double wrangePx = (freq2Pixels(MAX_FREQ) - freq2Pixels(MIN_FREQ));
226   for(int i = 0; i <= (FFT_N/2); i++)
227   {
228     xPixels_fft_bins[i] =  round(xPixels_fft[i] * wrangePx)/(wrangePx);
229   }
230 
231   //Clear spectrogram to fit the new zoom windows
232   if(m_fft_surface_ptr)
233   {
234     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_fft_surface_ptr);
235     cr->save();
236     cr->set_operator(Cairo::OPERATOR_CLEAR);
237     cr->paint();
238     cr->restore();
239   }
240 
241   //Redraw all by timer
242   m_fullRedraw = true;
243 }
244 
setCenter(double center)245 void PlotEQCurve::setCenter(double center)
246 {
247   //Limit center to the possible range according the current span
248   double sp = log10(m_maxFreq/m_minFreq);
249   double cmin = MIN_FREQ * sqrt(pow(10,sp));
250   double cmax = MAX_FREQ / sqrt(pow(10,sp));
251 
252   double cn = center;
253   cn = cn > cmax ? cmax : cn;
254   cn = cn < cmin ? cmin : cn;
255   setCenterSpan(cn, sp);
256 }
257 
setSpan(double span)258 void PlotEQCurve::setSpan(double span)
259 {
260   //Limit center to the possible range according the current span
261   double sp_act = log10(m_maxFreq/m_minFreq);
262   double cn = m_minFreq * sqrt(pow(10,sp_act));
263   double smax1 = 2.0*log10(cn/MIN_FREQ);
264   double smax2= 2.0*log10(MAX_FREQ/cn);
265   double smax = smax1 < smax2 ? smax1 : smax2;
266 
267   double sp = span > smax ? smax : span;
268   sp = sp < MIN_SPAN_DEC ? MIN_SPAN_DEC : sp;
269   setCenterSpan(cn, sp);
270 }
271 
recomputeMinFreq_fromX1Pixel(double x1)272 void PlotEQCurve::recomputeMinFreq_fromX1Pixel(double x1)
273 {
274 
275   if( m_zoom_widget.x2 - x1 < 30) return; //To avoid crossing control points
276 
277   double desp = x1 -  m_zoom_widget.x1;
278   double local_x1 =  m_zoom_widget.x1 + desp;
279   double local_x2 =  m_zoom_widget.x2 - desp;
280 
281   double fmin = MIN_FREQ*pow((MAX_FREQ/MIN_FREQ),((local_x1 - 3.5)/((double)m_zoom_surface_ptr->get_width())));
282   double fmax = MIN_FREQ*pow((MAX_FREQ/MIN_FREQ),((local_x2 + 3.5)/((double)m_zoom_surface_ptr->get_width())));
283   setSpan(log10(fmax/fmin));
284 }
285 
recomputeMaxFreq_fromX2Pixel(double x2)286 void PlotEQCurve::recomputeMaxFreq_fromX2Pixel(double x2)
287 {
288    if( x2 - m_zoom_widget.x1 < 30) return; //To avoid crossing control points
289 
290   double desp = x2 -  m_zoom_widget.x2;
291   double local_x1 =  m_zoom_widget.x1 - desp;
292   double local_x2 =  m_zoom_widget.x2 + desp;
293 
294   double fmin = MIN_FREQ*pow((MAX_FREQ/MIN_FREQ),((local_x1 - 3.5)/((double)m_zoom_surface_ptr->get_width())));
295   double fmax = MIN_FREQ*pow((MAX_FREQ/MIN_FREQ),((local_x2 + 3.5)/((double)m_zoom_surface_ptr->get_width())));
296   setSpan(log10(fmax/fmin));
297 }
298 
recomputeCenterFreq(double xDiff)299 void PlotEQCurve::recomputeCenterFreq(double xDiff)
300 {
301 
302   double local_x1 = m_zoom_widget.x1 - CURVE_MARGIN - CURVE_TEXT_OFFSET_X + xDiff;
303   double local_x2 = m_zoom_widget.x2 - CURVE_MARGIN - CURVE_TEXT_OFFSET_X + xDiff;
304 
305   double fmin = MIN_FREQ*pow((MAX_FREQ/MIN_FREQ),((local_x1 - 3.5)/((double)m_zoom_surface_ptr->get_width())));
306   double fmax = MIN_FREQ*pow((MAX_FREQ/MIN_FREQ),((local_x2 + 3.5)/((double)m_zoom_surface_ptr->get_width())));
307 
308   double sp_act = log10(fmax/fmin);
309   double cn = fmin * sqrt(pow(10,sp_act));
310   setCenter(cn);
311 }
312 
resetCurve()313 void PlotEQCurve::resetCurve()
314 {
315   for(int i = 0; i < CURVE_NUM_OF_POINTS; i++)
316   {
317     for(int j = 0; j < m_NumChannels; j++)
318     {
319       main_y[j][i] = 0.0;
320     }
321   }
322 
323   for (int i = 0; i<m_TotalBandsCount; i++)
324   {
325     m_filters[i]->bIsOn = false;
326     m_filters[i]->Freq = 20.0;
327     m_filters[i]->fType = PEAK;
328     m_filters[i]->Gain = 0.0;
329     m_filters[i]->Q = 2.0;
330 
331     //Reset band_y to zero
332     for(int j = 0; j < CURVE_NUM_OF_POINTS; j++)
333     {
334       band_y[i][j] = 0.0;
335     }
336   }
337 }
338 
ComputeFilter(int bd_ix)339 void PlotEQCurve::ComputeFilter(int bd_ix)
340 {
341   if(m_filters[bd_ix]->fType != NOT_SET)
342   {
343     CalcBand_DigitalFilter(bd_ix);
344   }
345 
346   //Compute Shape
347   for(int i = 0; i < CURVE_NUM_OF_POINTS; i++)
348   {
349     for(int j = 0; j < m_NumChannels; j++)
350     {
351       main_y[j][i] = 0.0;
352     }
353     for( int j = 0; j < m_TotalBandsCount; j++)
354     {
355       if(m_filters[j]->bIsOn)
356       {
357         switch(band_state[j])
358         {
359           case MONO:
360             main_y[0][i] += band_y[j][i];
361             break;
362 
363           case DUAL:
364             main_y[0][i] += band_y[j][i];
365             main_y[1][i] += band_y[j][i];
366             break;
367 
368           case ML:
369             main_y[0][i] += band_y[j][i];
370             break;
371 
372           case SR:
373             main_y[1][i] += band_y[j][i];
374             break;
375 
376         }
377       }
378     }
379   }
380 }
381 
382 //===============================DATA ACCESORS===================================================================
setBandGain(int bd_ix,float newGain)383 void  PlotEQCurve::setBandGain(int bd_ix, float newGain)
384 {
385   m_filters[bd_ix]->Gain = newGain;
386   cueBandRedraws(bd_ix);
387 }
388 
setBandFreq(int bd_ix,float newFreq)389 void  PlotEQCurve::setBandFreq(int bd_ix, float newFreq)
390 {
391   m_filters[bd_ix]->Freq = newFreq;
392   cueBandRedraws(bd_ix);
393 }
394 
setBandQ(int bd_ix,float newQ)395 void  PlotEQCurve::setBandQ(int bd_ix, float newQ)
396 {
397   m_filters[bd_ix]->Q = newQ;
398   cueBandRedraws(bd_ix);
399 }
400 
setBandType(int bd_ix,int newType)401 void  PlotEQCurve::setBandType(int bd_ix, int newType)
402 {
403   m_filters[bd_ix]->fType = int2FilterType(newType);
404   cueBandRedraws(bd_ix);
405 }
406 
setBandEnable(int bd_ix,bool bIsEnabled)407 void  PlotEQCurve::setBandEnable(int bd_ix, bool bIsEnabled)
408 {
409   m_filters[bd_ix]->bIsOn = bIsEnabled;
410   cueBandRedraws(bd_ix);
411 }
412 
setBypass(bool bypass)413 void PlotEQCurve::setBypass(bool bypass)
414 {
415   m_Bypass = bypass;
416   m_BandRedraw = true; //Force a redraw of curve in next timer without computing bands
417 }
418 
419 //==========================SIGNAL SLOTS===========================================================
420 //Mouse grab signal handlers
on_button_press_event(GdkEventButton * event)421 bool PlotEQCurve::on_button_press_event(GdkEventButton* event)
422 {
423   grab_focus();
424 
425   //Check if is a double click or simple
426   if(event->button == 1 && bBandFocus)
427   {
428     if(event->type == GDK_2BUTTON_PRESS) //Double click on the 1st button
429     {
430       //Emit signal button double click, this is enable or disable band
431       setBandEnable(m_iBandSel, !m_filters[m_iBandSel]->bIsOn);
432       m_BandEnabledSignal.emit(m_iBandSel, m_filters[m_iBandSel]->bIsOn);
433     }
434     else //if (!bMotionIsConnected ) // && m_filters[m_iBandSel]->bIsOn)
435     {
436       bMotionIsConnected = true;
437     }
438   }
439   //Check if is a double click or simple
440   if(event->button == 1 && (m_zoom_widget.center_focus || m_zoom_widget.f1_focus || m_zoom_widget.f2_focus))
441   {
442     if(event->type == GDK_2BUTTON_PRESS) //Double click on the 1st button
443     {
444        //Reset freq zoom
445       resetCenterSpan();
446     }
447     else
448     {
449       m_zoom_widget.center_press = m_zoom_widget.center_focus;
450       m_zoom_widget.f1_press = m_zoom_widget.f1_focus;
451       m_zoom_widget.f2_press = m_zoom_widget.f2_focus;
452       m_zoom_widget.x_ant = event->x;
453     }
454   }
455   return true;
456 }
457 
on_button_release_event(GdkEventButton * event)458 bool PlotEQCurve::on_button_release_event(GdkEventButton* event)
459 {
460   bMotionIsConnected = false;
461   m_zoom_widget.center_press = false;
462   m_zoom_widget.f1_press = false;
463   m_zoom_widget.f2_press = false;
464   return true;
465 }
466 
on_scrollwheel_event(GdkEventScroll * event)467 bool PlotEQCurve::on_scrollwheel_event(GdkEventScroll* event)
468 {
469    //Check if is over some control pointer
470 
471   const double x = event->x - CURVE_MARGIN - CURVE_TEXT_OFFSET_X;
472   const double y = event->y - CURVE_MARGIN;
473 
474   for(int i = 0; i < m_TotalBandsCount; i++)
475   {
476     if( x > freq2Pixels(m_filters[i]->Freq) - BALL_DETECTION_PIXELS &&
477 	x < freq2Pixels(m_filters[i]->Freq) + BALL_DETECTION_PIXELS &&
478 	y > dB2Pixels(m_filters[i]->Gain) - BALL_DETECTION_PIXELS &&
479 	y < dB2Pixels(m_filters[i]->Gain) + BALL_DETECTION_PIXELS )
480     {
481       if (event->direction == GDK_SCROLL_UP)
482       {
483 	// up code
484 	m_filters[i]->Q += SCROLL_EVENT_INCREMENT*m_filters[i]->Q;
485 	m_filters[i]->Q = m_filters[i]->Q > PEAK_Q_MAX ? PEAK_Q_MAX : m_filters[i]->Q;
486       }
487       else if (event->direction == GDK_SCROLL_DOWN)
488       {
489 	// down code
490 	m_filters[i]->Q -= SCROLL_EVENT_INCREMENT*m_filters[i]->Q;
491 	m_filters[i]->Q = m_filters[i]->Q < PEAK_Q_MIN ? PEAK_Q_MIN : m_filters[i]->Q;
492       }
493 
494     //Redraw with timeout
495     cueBandRedraws(m_iBandSel);
496 
497     // emit the signal
498     m_BandChangedSignal.emit( i, m_filters[i]->Gain, m_filters[i]->Freq, m_filters[i]->Q);
499     break;
500     }
501   }
502 
503   return true;
504 }
505 
on_mouse_motion_event(GdkEventMotion * event)506 bool PlotEQCurve::on_mouse_motion_event(GdkEventMotion* event)
507 {
508   const double x = event->x - CURVE_MARGIN - CURVE_TEXT_OFFSET_X;
509   const double y = event->y - CURVE_MARGIN;
510 
511   if(bMotionIsConnected)
512   {
513     //Recompute curve on current band and redraw
514     double xclipped = x > width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X - BALL_DETECTION_PIXELS ? width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X - BALL_DETECTION_PIXELS : x;
515     xclipped = xclipped <  BALL_DETECTION_PIXELS ?  BALL_DETECTION_PIXELS : xclipped;
516     m_filters[m_iBandSel]->Freq = Pixels2freq(xclipped);
517     m_filters[m_iBandSel]->Freq = m_filters[m_iBandSel]->Freq > FREQ_MAX ? FREQ_MAX : m_filters[m_iBandSel]->Freq;
518     m_filters[m_iBandSel]->Freq = m_filters[m_iBandSel]->Freq < FREQ_MIN ? FREQ_MIN : m_filters[m_iBandSel]->Freq;
519 
520     if (m_filters[m_iBandSel]->fType == PEAK ||
521 	m_filters[m_iBandSel]->fType == HIGH_SHELF ||
522 	m_filters[m_iBandSel]->fType == LOW_SHELF )
523     {
524       m_filters[m_iBandSel]->Gain = Pixels2dB(y);
525       m_filters[m_iBandSel]->Gain = m_filters[m_iBandSel]->Gain > GAIN_MAX ? GAIN_MAX : m_filters[m_iBandSel]->Gain;
526       m_filters[m_iBandSel]->Gain = m_filters[m_iBandSel]->Gain < GAIN_MIN ? GAIN_MIN : m_filters[m_iBandSel]->Gain;
527     }
528 
529     else
530     {
531       m_filters[m_iBandSel]->Gain = 0.0;
532     }
533 
534     redraw_cursor(xclipped,  dB2Pixels(m_filters[m_iBandSel]->Gain));
535 
536     //Redraw with timeout
537     cueBandRedraws(m_iBandSel);
538 
539     // emit the signal
540     setBandEnable(m_iBandSel, true); //If move control ball then enable band
541     m_BandEnabledSignal.emit(m_iBandSel, m_filters[m_iBandSel]->bIsOn);
542     m_BandChangedSignal.emit(m_iBandSel, m_filters[m_iBandSel]->Gain, m_filters[m_iBandSel]->Freq, m_filters[m_iBandSel]->Q);
543   }
544   else
545   {
546     redraw_cursor(event->x -CURVE_MARGIN - CURVE_TEXT_OFFSET_X, event->y  - CURVE_MARGIN);
547 
548     //Check if is over Zoom widget
549     if((event->x > m_zoom_widget.x1 - 10 &&
550         event->x < m_zoom_widget.x2 + 10 &&
551         event->y > height -CURVE_MARGIN - CURVE_TEXT_OFFSET_Y + ZOOM_WIDGET_BORDER_Y &&
552         event->y < height -CURVE_MARGIN ) ||
553         m_zoom_widget.center_press || m_zoom_widget.f1_press || m_zoom_widget.f2_press)
554     {
555       if(m_zoom_widget.center_press)
556       {
557         m_zoom_widget.center_focus = true;
558         m_zoom_widget.f1_focus = false;
559         m_zoom_widget.f2_focus = false;
560         int x,y;
561         get_pointer(x,y); //Using get_pinter() method is better than event->x here to avoid delays caused by unhalded mouse events due recomputeCenterFreq processing time
562         recomputeCenterFreq(x - m_zoom_widget.x_ant);
563         get_pointer(x,y);
564         m_zoom_widget.x_ant = x;
565         m_fullRedraw = true;
566       }
567       else if(m_zoom_widget.f1_press)
568       {
569         m_zoom_widget.f1_focus = true;
570         m_zoom_widget.f2_focus = false;
571         m_zoom_widget.center_focus = false;
572         recomputeMinFreq_fromX1Pixel(event->x);
573         m_fullRedraw = true;
574       }
575       else if(m_zoom_widget.f2_press)
576       {
577         m_zoom_widget.f2_focus = true;
578         m_zoom_widget.f1_focus = false;
579         m_zoom_widget.center_focus = false;
580         recomputeMaxFreq_fromX2Pixel(event->x);
581         m_fullRedraw = true;
582       }
583 
584       else if(event->x > m_zoom_widget.x1 + 10 &&
585               event->x  < m_zoom_widget.x2 - 10 )
586       {
587         //Center of the widget
588         m_zoom_widget.center_focus = true;
589         m_zoom_widget.f1_focus = false;
590         m_zoom_widget.f2_focus = false;
591         redraw_zoom_widget();
592         m_justRedraw = true;
593       }
594       else
595       {
596         if(event->x < (0.5*(m_zoom_widget.x2 - m_zoom_widget.x1) + m_zoom_widget.x1))
597         {
598           //Span of the widget f1
599           m_zoom_widget.f1_focus = true;
600           m_zoom_widget.center_focus = false;
601           m_zoom_widget.f2_focus = false;
602           redraw_zoom_widget();
603           m_justRedraw = true;
604         }
605         else
606         {
607           //Span of the widget f2
608           m_zoom_widget.f2_focus = true;
609           m_zoom_widget.center_focus = false;
610           m_zoom_widget.f1_focus = false;
611           redraw_zoom_widget();
612           m_justRedraw = true;
613         }
614       }
615     }
616     else if(m_zoom_widget.center_focus || m_zoom_widget.f1_focus || m_zoom_widget.f2_focus) //Mouse has leaved zoom widget
617     {
618       m_zoom_widget.center_focus = false;
619       m_zoom_widget.f1_focus = false;
620       m_zoom_widget.f2_focus = false;
621       redraw_zoom_widget();
622       m_justRedraw = true;
623     }
624 
625     //Check if is over some control pointer
626     bBandFocus = false;
627     bool vFocus[m_TotalBandsCount];
628     int focus_hits = 0;
629     for(int i = 0; i < m_TotalBandsCount; i++)
630     {
631       if( x > freq2Pixels(m_filters[i]->Freq) - BALL_DETECTION_PIXELS &&
632 	  x < freq2Pixels(m_filters[i]->Freq) + BALL_DETECTION_PIXELS &&
633 	  y > dB2Pixels(m_filters[i]->Gain) - BALL_DETECTION_PIXELS &&
634 	  y < dB2Pixels(m_filters[i]->Gain) + BALL_DETECTION_PIXELS  &&
635 	  x > 0 &&
636 	  x < width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X &&
637 	  y > 0 &&
638           y < height - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_Y )
639 
640       {
641 	m_iBandSel = i;
642 	bBandFocus = true;
643 	vFocus[i]=true;
644 	focus_hits++;
645       }
646       else
647       {
648 	vFocus[i]=false;
649       }
650     }
651     if(focus_hits > 1)
652     {
653       for(int i = 0; i < m_TotalBandsCount; i++)
654       {
655 	if(vFocus[i] && m_filters[i]->bIsOn)
656 	{
657 	  m_iBandSel = i;
658 	}
659       }
660     }
661 
662     if(bBandFocus)
663     {
664       m_BandSelectedSignal.emit(m_iBandSel);
665     }
666     else
667     {
668       m_BandUnselectedSignal.emit();
669     }
670 
671     m_BandRedraw = true; //Force a redraw of curve in next timer without computing bands
672   }
673 
674   if( event->x > CURVE_MARGIN + CURVE_TEXT_OFFSET_X &&
675       event->x < width - CURVE_MARGIN  &&
676       event->y > CURVE_MARGIN &&
677       event->y < height -CURVE_MARGIN - CURVE_TEXT_OFFSET_Y )
678   {
679     //Mouse on curve zone
680     get_window()->set_cursor(Gdk::Cursor(Gdk::BLANK_CURSOR));
681   }
682   else
683   {
684     //Mouse outside curve zone
685     get_window()->set_cursor(Gdk::Cursor());
686   }
687 
688   return true;
689 }
690 
on_mouse_leave_widget(GdkEventCrossing * event)691 bool PlotEQCurve::on_mouse_leave_widget(GdkEventCrossing* event)
692 {
693   m_zoom_widget.center_focus = false;
694   m_zoom_widget.f1_focus = false;
695   m_zoom_widget.f2_focus = false;
696   redraw_zoom_widget();
697   m_justRedraw = true;
698 
699   if(!bMotionIsConnected)
700   {
701     redraw_cursor(event->x - CURVE_MARGIN - CURVE_TEXT_OFFSET_X, event->y - CURVE_MARGIN);
702     bBandFocus = false;
703     m_BandUnselectedSignal.emit();
704     m_BandRedraw = true;
705   }
706   return true;
707 }
708 
709 //Timer callback for auto redraw and graph math
on_timeout_redraw()710 bool PlotEQCurve::on_timeout_redraw()
711 {
712   bool bRedraw = false;
713 
714   //Full redraw request
715   if(m_fullRedraw)
716   {
717     redraw_zoom_widget();
718     redraw_grid_widget();
719     redraw_xAxis_widget();
720     redraw_yAxis_widget();
721     for(int i = 0; i < m_TotalBandsCount; i++)
722     {
723       m_Bands2Redraw[i] = true;
724     }
725     m_BandRedraw = true;
726 
727     m_fullRedraw = false;
728     bRedraw = true;
729   }
730 
731   //Redraw if curve changed
732   if(m_BandRedraw)
733   {
734     for(int i = 0; i < m_TotalBandsCount; i++)
735     {
736       if(m_Bands2Redraw[i])
737       {
738         m_Bands2Redraw[i] = false;
739         ComputeFilter(i);
740         redraw_curve_widgets(i);
741       }
742     }
743     redraw_main_curve();
744     m_BandRedraw = false;
745     bRedraw = true;
746   }
747 
748 
749   if(bRedraw || m_justRedraw)
750   {
751     m_justRedraw = false;
752     Glib::RefPtr<Gdk::Window> win = get_window();
753     if(win)
754     {
755       Gdk::Rectangle r(0, 0, get_allocation().get_width(), get_allocation().get_height());
756       win->invalidate_rect(r, false);
757     }
758   }
759   return true;
760 }
761 
762 
763 //Override default signal handler:
on_expose_event(GdkEventExpose * event)764 bool PlotEQCurve::on_expose_event(GdkEventExpose* event)
765 {
766   Glib::RefPtr<Gdk::Window> window = get_window();
767   if(window)
768   {
769 
770     Gtk::Allocation allocation = get_allocation();
771     width = allocation.get_width();
772     height = allocation.get_height();
773 
774     if( !(m_background_surface_ptr || m_fft_surface_ptr || m_zoom_surface_ptr || m_maincurve_surface_ptr || m_grid_surface_ptr  || m_xAxis_surface_ptr || m_yAxis_surface_ptr || m_cursor_surface_ptr))
775     {
776       m_background_surface_ptr = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width, height);
777       m_fft_surface_ptr = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X, height - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_Y);
778       m_zoom_surface_ptr = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X, CURVE_TEXT_OFFSET_Y - ZOOM_WIDGET_BORDER_Y);
779 
780       //Set initial x1 and x2
781       redraw_zoom_widget();
782 
783       m_maincurve_surface_ptr = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X, height - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_Y);
784       for(int i = 0; i < m_TotalBandsCount; i++)
785       {
786         m_curve_surface_ptr[i] = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X, height - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_Y);
787       }
788       m_grid_surface_ptr = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X, height - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_Y);
789       m_xAxis_surface_ptr = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X, CURVE_TEXT_OFFSET_Y);
790       m_yAxis_surface_ptr = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, CURVE_TEXT_OFFSET_X, height - CURVE_TEXT_OFFSET_Y);
791       m_cursor_surface_ptr = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,  width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X, height - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_Y);
792       redraw_background_widget();
793       resetCenterSpan();
794       Glib::signal_timeout().connect( sigc::mem_fun(*this, &PlotEQCurve::on_timeout_redraw), AUTO_REFRESH_TIMEOUT_MS ); //Connectin timer here to avoid using plot with unitialized freq vector
795     }
796 
797     Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
798 
799     //Draw the background
800     if(m_background_surface_ptr)
801     {
802       cr->save();
803       cr->set_source(m_background_surface_ptr, 0, 0);
804       cr->paint();
805       cr->restore();
806     }
807 
808     //Draw zoom widget
809     if(m_zoom_surface_ptr)
810     {
811       cr->save();
812       cr->set_source(m_zoom_surface_ptr, CURVE_MARGIN + CURVE_TEXT_OFFSET_X, height -  CURVE_MARGIN - CURVE_TEXT_OFFSET_Y + ZOOM_WIDGET_BORDER_Y);
813       cr->paint();
814       cr->restore();
815     }
816 
817     //Draw FFT data
818     if(m_FftActive && m_fft_surface_ptr)
819     {
820       cr->save();
821       cr->set_source(m_fft_surface_ptr, CURVE_MARGIN + CURVE_TEXT_OFFSET_X, CURVE_MARGIN);
822       cr->paint();
823       cr->restore();
824     }
825 
826     //Draw the grid
827     if(m_grid_surface_ptr)
828     {
829       cr->save();
830       cr->set_source(m_grid_surface_ptr, CURVE_MARGIN + CURVE_TEXT_OFFSET_X, CURVE_MARGIN);
831       cr->paint();
832       cr->restore();
833     }
834 
835     //Db Scale Axis
836     if(m_yAxis_surface_ptr)
837     {
838       cr->save();
839       cr->set_source(m_yAxis_surface_ptr, CURVE_MARGIN, 0);
840       cr->paint();
841       cr->restore();
842     }
843 
844     //Hz scale Axis
845     if(m_xAxis_surface_ptr)
846     {
847       cr->save();
848       cr->set_source(m_xAxis_surface_ptr, CURVE_MARGIN + CURVE_TEXT_OFFSET_X, height -  CURVE_MARGIN - CURVE_TEXT_OFFSET_Y);
849       cr->paint();
850       cr->restore();
851     }
852 
853     //Draw the cursor position
854     if(m_cursor_surface_ptr)
855     {
856       cr->save();
857       cr->set_source(m_cursor_surface_ptr, CURVE_MARGIN + CURVE_TEXT_OFFSET_X, CURVE_MARGIN);
858       cr->paint();
859       cr->restore();
860     }
861 
862     //Draw the curve
863     if(m_maincurve_surface_ptr)
864     {
865       cr->save();
866       cr->set_source(m_maincurve_surface_ptr, CURVE_MARGIN + CURVE_TEXT_OFFSET_X, CURVE_MARGIN);
867       cr->paint();
868       cr->restore();
869     }
870 
871     //draw de outer grind box
872     cr->save();
873     cr->set_source_rgb(0.3, 0.3, 0.3);
874     cr->set_line_width(1);
875     cr->rectangle(CURVE_MARGIN + CURVE_TEXT_OFFSET_X + 0.5, CURVE_MARGIN + 0.5, width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X, height -2* CURVE_MARGIN - CURVE_TEXT_OFFSET_Y);
876     cr->stroke();
877     cr->restore();
878   }
879   return true;
880 }
881 
glowBand(int band)882 void PlotEQCurve::glowBand(int band)
883 {
884   m_iBandSel = band;
885   bBandFocus = true;
886   m_BandRedraw = true; //Force a redraw of curve in next timer without computing bands
887 }
888 
unglowBands()889 void PlotEQCurve::unglowBands()
890 {
891   bBandFocus = false;
892   m_BandRedraw = true; //Force a redraw of curve in next timer without computing bands
893 }
894 
cueBandRedraws(int band)895 void PlotEQCurve::cueBandRedraws(int band)
896 {
897   m_Bands2Redraw[band] = true;
898   m_BandRedraw = true;
899 }
900 
dB2Pixels(double db)901 double PlotEQCurve::dB2Pixels(double db)
902 {
903   return ((((double)height)/2.0) - ((((double)height) - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_Y)/m_dB_plot_range)*db - CURVE_TEXT_OFFSET_Y/2 - CURVE_MARGIN);
904 }
905 
freq2Pixels(double f)906 double PlotEQCurve::freq2Pixels(double f)
907 {
908   return ((((double)width) - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X)/(log10(m_maxFreq/m_minFreq))*log10(f/m_minFreq)); // + CURVE_MARGIN + CURVE_TEXT_OFFSET_X);
909 }
910 
Pixels2dB(double pixels)911 double PlotEQCurve::Pixels2dB(double pixels)
912 {
913   return m_dB_plot_range*((((double)height)-CURVE_TEXT_OFFSET_Y- 2*CURVE_MARGIN -2*pixels)/(2*((double)height) - 4*CURVE_MARGIN - 2*CURVE_TEXT_OFFSET_Y));
914 }
915 
Pixels2freq(double pixels)916 double PlotEQCurve::Pixels2freq(double pixels)
917 {
918   return m_minFreq*pow(10, ((pixels  /*- CURVE_MARGIN- CURVE_TEXT_OFFSET_X */)/((((double)width) - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET_X)/(log10(m_maxFreq/m_minFreq)))));
919 }
920 
921 
signal_changed()922 PlotEQCurve::signal_BandChanged PlotEQCurve::signal_changed()
923 {
924   return m_BandChangedSignal;
925 }
926 
signal_enabled()927 PlotEQCurve::signal_BandEnabled PlotEQCurve::signal_enabled()
928 {
929   return m_BandEnabledSignal;
930 }
931 
signal_selected()932 PlotEQCurve::signal_BandSelected PlotEQCurve::signal_selected()
933 {
934   return m_BandSelectedSignal;
935 }
936 
signal_unselected()937 PlotEQCurve::signal_BandUnselected PlotEQCurve::signal_unselected()
938 {
939   return m_BandUnselectedSignal;
940 }
941 
942 
CalcBand_DigitalFilter(int bd_ix)943 void PlotEQCurve::CalcBand_DigitalFilter(int bd_ix)
944 {
945   //Init Filter to avoid coef interpolation
946   Filter m_fil;
947   m_fil.gain = pow(10,((m_filters[bd_ix]->Gain)/20));
948   m_fil.freq = m_filters[bd_ix]-> Freq;
949   m_fil.q = m_filters[bd_ix]->Q;
950   m_fil.enable = 1.0f;
951   m_fil.iType = m_filters[bd_ix]->fType;
952   m_fil.fs = SampleRate;
953   m_fil.InterK = 0.0f;
954   m_fil.useInterpolation = 0.0f;
955 
956   //Calc coefs
957   calcCoefs(&m_fil, m_fil.gain, m_fil.freq, m_fil.q, m_fil.iType, m_fil.enable);
958 
959   //Digital filter magnitude response
960   double w, A, B, C, D, sinW, cosW;
961   //Precalculables
962   double AK = m_fil.b0 + m_fil.b2;
963   double BK = m_fil.b0 - m_fil.b2;
964   double CK = 1 + m_fil.a2;
965   double DK = 1 - m_fil.a2;
966 
967 
968   for(int i=0; i<CURVE_NUM_OF_POINTS; i++)
969   {
970     w=2*PI*f[i] / m_fil.fs;
971     sinW = sin(w);
972     cosW = cos(w);
973     A = m_fil.b1 + AK*cosW;
974     B = BK*sinW;
975     C = m_fil.a1 + CK*cosW;
976     D = DK*sinW;
977     band_y[bd_ix][i]=(double)20*log10(sqrt(pow(A*C + B*D, 2) + pow(B*C - A*D, 2))/(C*C + D*D));
978   }
979 
980   //Compute 3 and 4 order m_filters
981   if(m_fil.filter_order)
982   {
983     //Precalculables
984     double AK = m_fil.b1_0 + m_fil.b1_2;
985     double BK = m_fil.b1_0 - m_fil.b1_2;
986     double CK = 1 + m_fil.a1_2;
987     double DK = 1 - m_fil.a1_2;
988 
989     for(int i=0; i<CURVE_NUM_OF_POINTS; i++)
990     {
991       w=2*PI*f[i] / m_fil.fs;
992       sinW = sin(w);
993       cosW = cos(w);
994       A = m_fil.b1_1 + AK*cosW;
995       B = BK*sinW;
996       C = m_fil.a1_1 + CK*cosW;
997       D = DK*sinW;
998       band_y[bd_ix][i]+=(double)20*log10(sqrt(pow(A*C + B*D, 2) + pow(B*C - A*D, 2))/(C*C + D*D));
999     }
1000   }
1001 }
1002 
setSampleRate(double samplerate)1003 void PlotEQCurve::setSampleRate(double samplerate)
1004 {
1005   if(samplerate != SampleRate)
1006   {
1007     SampleRate = samplerate;
1008 
1009     if( !(m_background_surface_ptr || m_fft_surface_ptr || m_zoom_surface_ptr || m_maincurve_surface_ptr || m_grid_surface_ptr  || m_xAxis_surface_ptr || m_yAxis_surface_ptr))
1010     {
1011       //Init FFT vectors using real sampleRate
1012       double fft_raw_freq;
1013       for(int i = 0; i <= (FFT_N/2); i++)
1014       {
1015         fft_raw_freq = (SampleRate * (double)i) /  ((double)(FFT_N));
1016         xPixels_fft[i] = log10(fft_raw_freq/MIN_FREQ)/log10(MAX_FREQ/MIN_FREQ);
1017         fft_pink_noise[i] =  3.0*(log10(fft_raw_freq/20.0)/log10(2));
1018         fft_plot[i]= 0.0;
1019         fft_ant_data[i] = 0.0;
1020         //std::cout<<"freq["<<i<<"] = "<<  fft_raw_freq[i]<< "Pixels = "<< xPixels_fft[i] <<std::endl;
1021       }
1022 
1023       //Redraw all by timer
1024       m_fullRedraw = true;
1025     }
1026   }
1027 }
1028 
setFftData(double * fft_data)1029 void PlotEQCurve::setFftData(double *fft_data)
1030 {
1031   fft_raw_data = fft_data;
1032   if(m_fft_surface_ptr && !m_bFftHold)
1033   {
1034     redraw_fft_widget();
1035     m_justRedraw = true;
1036   }
1037 }
1038 
setFftActive(bool active,bool isSpectrogram)1039 void PlotEQCurve::setFftActive(bool active, bool isSpectrogram)
1040 {
1041   m_FftActive = active;
1042   m_bIsSpectrogram = isSpectrogram;
1043 
1044   //Clear plot screen
1045   Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_fft_surface_ptr);
1046   cr->save();
1047   cr->set_operator(Cairo::OPERATOR_CLEAR);
1048   cr->paint();
1049   cr->restore();
1050 
1051 
1052   m_justRedraw = true;
1053 }
1054 
setFftHold(bool hold)1055 void PlotEQCurve::setFftHold(bool hold)
1056 {
1057   m_bFftHold = hold;
1058 }
1059 
1060 
setFftGain(double g)1061 void PlotEQCurve::setFftGain(double g)
1062 {
1063   fft_gain = g;
1064 }
1065 
setFftRange(double r)1066 void PlotEQCurve::setFftRange(double r)
1067 {
1068   fft_range = r;
1069 }
1070 
setPlotdBRange(double range)1071 void PlotEQCurve::setPlotdBRange(double range)
1072 {
1073   m_dB_plot_range = 2.0*range;
1074 
1075   //Redraw all by timer
1076   m_fullRedraw = true;
1077 }
1078 
redraw_background_widget()1079 void PlotEQCurve::redraw_background_widget()
1080 {
1081   if(m_background_surface_ptr)
1082   {
1083     //Create cairo context using the buffer surface
1084     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_background_surface_ptr);
1085 
1086     //Paint backgroud
1087     cr->save();
1088     cr->set_source_rgb(BACKGROUND_R, BACKGROUND_G, BACKGROUND_B);
1089     cr->paint(); //Fill all with background color
1090     cr->restore();
1091 
1092 
1093     //Draw an interesting frame
1094     cr->save();
1095     double radius = height / 50.0;
1096     double degrees = M_PI / 180.0;
1097     cr->begin_new_sub_path();
1098     cr->arc (width - CURVE_BORDER - radius, CURVE_BORDER + radius, radius, -90 * degrees, 0 * degrees);
1099     cr->arc (width - CURVE_BORDER - radius, height - CURVE_BORDER - radius, radius, 0 * degrees, 90 * degrees);
1100     cr->arc (CURVE_BORDER + radius, height- CURVE_BORDER - radius, radius, 90 * degrees, 180 * degrees);
1101     cr->arc ( CURVE_BORDER + radius, CURVE_BORDER + radius, radius, 180 * degrees, 270 * degrees);
1102     cr->close_path();
1103     Cairo::RefPtr<Cairo::LinearGradient> bkg_gradient_ptr = Cairo::LinearGradient::create(width/2, CURVE_BORDER, width/2, height - CURVE_BORDER);
1104     bkg_gradient_ptr->add_color_stop_rgba (0.0, 0.1, 0.1, 0.1, 0.6 );
1105     bkg_gradient_ptr->add_color_stop_rgba (0.5, 0.2, 0.3, 0.3, 0.3 );
1106     bkg_gradient_ptr->add_color_stop_rgba (1.0, 0.1, 0.1, 0.1, 0.6 );
1107     cr->set_source(bkg_gradient_ptr);
1108     cr->fill_preserve();
1109     cr->set_line_width(1.0);
1110     cr->set_source_rgb(0.3, 0.3, 0.4);
1111     cr->stroke();
1112     cr->restore();
1113   }
1114 
1115 }
1116 
redraw_zoom_widget()1117 void PlotEQCurve::redraw_zoom_widget()
1118 {
1119   if(m_zoom_surface_ptr)
1120   {
1121     //Create cairo context using the buffer surface
1122     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_zoom_surface_ptr);
1123 
1124     //Clear current context
1125     cr->save();
1126     cr->set_operator(Cairo::OPERATOR_CLEAR);
1127     cr->paint();
1128     cr->restore();
1129 
1130     //Draw a box
1131     cr->save();
1132     cr->begin_new_sub_path();
1133     cr->arc( 3.5, 3.5, 3, M_PI, -0.5*M_PI);
1134     cr->arc( m_zoom_surface_ptr->get_width() - 3.5, 3.5, 3, -0.5*M_PI, 0);
1135     cr->arc( m_zoom_surface_ptr->get_width() - 3.5, m_zoom_surface_ptr->get_height() - 3.5, 3, 0.0,  0.5*M_PI);
1136     cr->arc( 3.5, m_zoom_surface_ptr->get_height() - 3.5, 3, 0.5*M_PI, M_PI);
1137     cr->close_path();
1138     cr->set_source_rgba(0.1,0.1,0.1,0.2);
1139     cr->fill_preserve();
1140     cr->set_line_width(1);
1141     cr->set_source_rgba(0.6, 0.6, 0.6, 0.6);
1142     cr->stroke();
1143     cr->restore();
1144 
1145     //Draw a freq axis
1146     cr->save();
1147     cr->set_source_rgb(0.6, 0.6, 0.6);
1148     Glib::RefPtr<Pango::Layout> pangoLayout = Pango::Layout::create(cr);
1149     Pango::FontDescription font_desc("sans 8px");
1150     pangoLayout->set_font_description(font_desc);
1151     pangoLayout->set_alignment(Pango::ALIGN_RIGHT);
1152 
1153     //Initalize the grid
1154     const double f_grid[4] = {20.0, 100.0, 1000.0, 10000.0};
1155     int gridPix[4];
1156     for(int i=0; i < 4; i++)
1157     {
1158       gridPix[i] = round(((double)m_zoom_surface_ptr->get_width())/(log10(MAX_FREQ/MIN_FREQ))*log10(f_grid[i]/MIN_FREQ)) +  CURVE_MARGIN + CURVE_TEXT_OFFSET_X;
1159     }
1160 
1161     //Hz scale 20 Hz
1162     cr->move_to( gridPix[0] - 5  - CURVE_MARGIN - CURVE_TEXT_OFFSET_X, 0.5*m_zoom_surface_ptr->get_height() - 4);
1163     pangoLayout->set_text("20");
1164     pangoLayout->show_in_cairo_context(cr);
1165     cr->stroke();
1166 
1167     //Hz scale 100 Hz
1168     cr->move_to( gridPix[1] - 5  - CURVE_MARGIN - CURVE_TEXT_OFFSET_X, 0.5*m_zoom_surface_ptr->get_height() - 4);
1169     pangoLayout->set_text("100");
1170     pangoLayout->show_in_cairo_context(cr);
1171     cr->stroke();
1172 
1173     //Hz scale 1 kHz
1174     cr->move_to( gridPix[2] - 5  - CURVE_MARGIN - CURVE_TEXT_OFFSET_X, 0.5*m_zoom_surface_ptr->get_height() - 4);
1175     pangoLayout->set_text("1k");
1176     pangoLayout->show_in_cairo_context(cr);
1177     cr->stroke();
1178 
1179     //Hz scale 10 kHz
1180     cr->move_to( gridPix[3] - 5  - CURVE_MARGIN - CURVE_TEXT_OFFSET_X, 0.5*m_zoom_surface_ptr->get_height() - 4);
1181     pangoLayout->set_text("10k");
1182     pangoLayout->show_in_cairo_context(cr);
1183     cr->stroke();
1184     cr->restore();
1185 
1186     //Get pixel position in surface acording max freq span range (freq2pixels function is not valid here!)
1187     m_zoom_widget.x1 = round(((double)m_zoom_surface_ptr->get_width())/(log10(MAX_FREQ/MIN_FREQ))*log10(m_minFreq/MIN_FREQ)) + 3.5;
1188     m_zoom_widget.x2 = round(((double)m_zoom_surface_ptr->get_width())/(log10(MAX_FREQ/MIN_FREQ))*log10(m_maxFreq/MIN_FREQ)) - 3.5;
1189 
1190     //Draw rectangles at borders in case of span focus
1191     if(m_zoom_widget.f1_focus || m_zoom_widget.f2_focus)
1192     {
1193       cr->save();
1194       cr->rectangle(m_zoom_widget.x1 - 5, 2, 10, m_zoom_surface_ptr->get_height() - 4);
1195       cr->set_source_rgb(0,0.9,0.9);
1196       cr->fill();
1197       cr->restore();
1198 
1199       cr->save();
1200       cr->rectangle(m_zoom_widget.x2 - 5, 2, 10, m_zoom_surface_ptr->get_height() - 4);
1201       cr->set_source_rgb(0,0.9,0.9);
1202       cr->fill();
1203       cr->restore();
1204     }
1205 
1206 
1207     //Draw the button
1208     cr->save();
1209     cr->begin_new_sub_path();
1210     cr->arc( m_zoom_widget.x1, 6.5, 3, M_PI, -0.5*M_PI);
1211     cr->arc( m_zoom_widget.x2, 6.5, 3, -0.5*M_PI, 0);
1212     cr->arc( m_zoom_widget.x2,  m_zoom_surface_ptr->get_height() - 6.5, 3, 0.0,  0.5*M_PI);
1213     cr->arc( m_zoom_widget.x1, m_zoom_surface_ptr->get_height() - 6.5, 3, 0.5*M_PI, M_PI);
1214     cr->close_path();
1215 
1216     Cairo::RefPtr<Cairo::LinearGradient> bkg_gradient_ptr = Cairo::LinearGradient::create(0, 0, 0,  m_zoom_surface_ptr->get_height());
1217     if(m_zoom_widget.center_focus)
1218     {
1219       bkg_gradient_ptr->add_color_stop_rgba (0.0, 0.2, 0.3, 0.3, 0.7);
1220       bkg_gradient_ptr->add_color_stop_rgba (0.4, 0.2, 0.4, 0.5, 1 );
1221       bkg_gradient_ptr->add_color_stop_rgba (0.6, 0.2, 0.4, 0.5, 1 );
1222       bkg_gradient_ptr->add_color_stop_rgba (1.0, 0.2, 0.3, 0.3, 0.7 );
1223     }
1224     else
1225     {
1226       bkg_gradient_ptr->add_color_stop_rgba (0.0, 0.2, 0.2, 0.2, 0.7 );
1227       bkg_gradient_ptr->add_color_stop_rgba (0.4, 0.3, 0.3, 0.3, 1 );
1228       bkg_gradient_ptr->add_color_stop_rgba (0.6, 0.3, 0.3, 0.3, 1 );
1229       bkg_gradient_ptr->add_color_stop_rgba (1.0, 0.2, 0.2, 0.2, 0.7 );
1230     }
1231     cr->set_source(bkg_gradient_ptr);
1232     cr->fill_preserve();
1233     cr->set_line_width(1);
1234     cr->set_source_rgba(1,1,1, 0.7);
1235     cr->stroke();
1236     cr->restore();
1237 
1238     //Draw text on button
1239     cr->save();
1240     cr->set_source_rgb(0.6, 0.6, 0.6);
1241     pangoLayout->set_alignment(Pango::ALIGN_LEFT);
1242     std::stringstream ss1;
1243     if(m_minFreq<1000)
1244     {
1245       ss1<< std::fixed << std::setprecision(1)<< m_minFreq;
1246     }
1247     else
1248     {
1249       ss1<< std::fixed << std::setprecision(0)<< floor(m_minFreq/1000) <<"k";
1250       int divider = ((int)m_minFreq % 1000)/100;
1251       if(divider > 0)
1252       {
1253         ss1<<divider;
1254       }
1255     }
1256 
1257     cr->move_to( m_zoom_widget.x1 + 5,  m_zoom_surface_ptr->get_height()/2 - 4);
1258     pangoLayout->set_text( ss1.str() );
1259     pangoLayout->show_in_cairo_context(cr);
1260     cr->move_to( m_zoom_widget.x1 + 0.5*(m_zoom_widget.x2 - m_zoom_widget.x1) - 15,  m_zoom_surface_ptr->get_height()/2 - 4);
1261     pangoLayout->set_text("~Zoom~");
1262     pangoLayout->show_in_cairo_context(cr);
1263     cr->move_to( m_zoom_widget.x2 - 25,  m_zoom_surface_ptr->get_height()/2 - 4);
1264 
1265     std::stringstream ss2;
1266     if(m_maxFreq<1000)
1267     {
1268       ss2<< std::fixed << std::setprecision(1)<< m_maxFreq;
1269     }
1270     else
1271     {
1272       ss2<< std::fixed << std::setprecision(0)<< floor(m_maxFreq/1000) <<"k";
1273       int divider = ((int)m_maxFreq % 1000)/100;
1274       if(divider > 0)
1275       {
1276         ss2<<divider;
1277       }
1278     }
1279 
1280     pangoLayout->set_text( ss2.str() );
1281     pangoLayout->show_in_cairo_context(cr);
1282     cr->stroke();
1283     cr->restore();
1284 
1285     //Update coords including global offsets
1286     m_zoom_widget.x1 += CURVE_MARGIN + CURVE_TEXT_OFFSET_X;
1287     m_zoom_widget.x2 += CURVE_MARGIN + CURVE_TEXT_OFFSET_X;
1288   }
1289 }
1290 
redraw_curve_widgets(int band)1291 void PlotEQCurve::redraw_curve_widgets(int band)
1292 {
1293   if(m_curve_surface_ptr[band])
1294   {
1295     //Create cairo context using the buffer surface
1296     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_curve_surface_ptr[band]);
1297 
1298     //Clear current context
1299     cr->save();
1300     cr->set_operator(Cairo::OPERATOR_CLEAR);
1301     cr->paint();
1302     cr->restore();
1303 
1304     //Draw curve area in band color
1305     cr->save();
1306     double Y_fil0, Y_fil1;
1307     switch(m_filters[band]->fType)
1308     {
1309       case PEAK:
1310       case LOW_SHELF:
1311       case HIGH_SHELF:
1312 	Y_fil0 = dB2Pixels( m_filters[band]->Gain);
1313 	Y_fil1 = dB2Pixels((-1.0)* m_filters[band]->Gain);
1314 	break;
1315 
1316       case NOTCH:
1317 	Y_fil0 = (double)m_curve_surface_ptr[band]->get_height();
1318 	Y_fil1 = 0;
1319 	break;
1320 
1321       default:
1322 	Y_fil0 = 0.75*(double)m_curve_surface_ptr[band]->get_height();
1323 	Y_fil1 = 0.25*(double)m_curve_surface_ptr[band]->get_height();
1324     }
1325 
1326     Cairo::RefPtr<Cairo::LinearGradient> bd_gradient_ptr = Cairo::LinearGradient::create(0, Y_fil0, 0, Y_fil1);
1327     if(m_filters[band]->bIsOn and !m_Bypass) //If band is enabled and not bypass
1328     {
1329       Gdk::Color color(bandColorLUT[band]);
1330       bd_gradient_ptr->add_color_stop_rgba(0, color.get_red_p(), color.get_green_p(), color.get_blue_p(), 0.3);
1331       bd_gradient_ptr->add_color_stop_rgba(0.5, color.get_red_p(), color.get_green_p(), color.get_blue_p(), 0.01);
1332       bd_gradient_ptr->add_color_stop_rgba(1, color.get_red_p(), color.get_green_p(), color.get_blue_p(), 0.3);
1333     }
1334     else //band is disabled
1335     {
1336       bd_gradient_ptr->add_color_stop_rgba(0, 1,1,1, 0.2);
1337       bd_gradient_ptr->add_color_stop_rgba(0.5, 1,1,1, 0.01);
1338       bd_gradient_ptr->add_color_stop_rgba(1, 1,1,1, 0.2);
1339     }
1340     cr->set_source(bd_gradient_ptr);
1341     cr->move_to(0, dB2Pixels(0.0));
1342     for (int j = 0; j < CURVE_NUM_OF_POINTS; j++)
1343     {
1344       cr->line_to(xPixels[j], dB2Pixels(band_y[band][j]));
1345     }
1346     cr->line_to(m_curve_surface_ptr[band]->get_width(), dB2Pixels(0.0));
1347     cr->line_to( 0, dB2Pixels(0.0));
1348     cr->fill();
1349     cr->restore();
1350   }
1351 }
1352 
redraw_main_curve()1353 void PlotEQCurve::redraw_main_curve()
1354 {
1355  if(m_maincurve_surface_ptr)
1356   {
1357     //Create cairo context using the buffer surface
1358     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_maincurve_surface_ptr);
1359 
1360     //Clear current context
1361     cr->save();
1362     cr->set_operator(Cairo::OPERATOR_CLEAR);
1363     cr->paint();
1364     cr->restore();
1365 
1366     for(int i = 0; i < m_TotalBandsCount; i++) //for each band
1367     {
1368       //Draw the curve color area for each band
1369       if(m_curve_surface_ptr[i])
1370       {
1371         cr->save();
1372         cr->set_source(m_curve_surface_ptr[i], 0, 0);
1373         cr->paint();
1374         cr->restore();
1375       }
1376     }
1377 
1378     if (!m_Bypass)
1379     {
1380       //Draw the main curve
1381       cr->save();
1382 
1383       cr->set_line_width(1);
1384       for(int j = 0; j < m_NumChannels; j++)
1385       {
1386         if(m_NumChannels == 1 || j == 1)
1387         {
1388           cr->set_source_rgb(1, 1, 1);
1389         }
1390         else
1391         {
1392           cr->set_source_rgb(0, 1, 1);
1393         }
1394 
1395         cr->move_to(xPixels[0], dB2Pixels(main_y[j][0]) + 0.5);
1396         for (int i = 1; i < CURVE_NUM_OF_POINTS; i++)
1397         {
1398           cr->line_to(xPixels[i], dB2Pixels(main_y[j][i]) + 0.5);
1399         }
1400         cr->stroke();
1401       }
1402       cr->restore();
1403 
1404       //Draw curve control ball
1405       cr->save();
1406       double ball_x, ball_y;
1407       Cairo::RefPtr< Cairo::RadialGradient > ball_gradient_ptr;
1408       for(int i = 0; i < m_TotalBandsCount; i++) //for each band
1409       {
1410           ball_x = (double)freq2Pixels(m_filters[i]->Freq);
1411           if( m_filters[i]->fType == PEAK ||
1412               m_filters[i]->fType == LOW_SHELF ||
1413               m_filters[i]->fType == HIGH_SHELF )
1414           {
1415             ball_y = (double)dB2Pixels(m_filters[i]->Gain);
1416           }
1417           else
1418           {
1419             ball_y =  (double)dB2Pixels(0.0);
1420             m_filters[i]->Gain = 0.0;
1421           }
1422 
1423           Gdk::Color color(bandColorLUT[i]);
1424           ball_gradient_ptr = Cairo::RadialGradient::create( ball_x - 2, ball_y - 2, 0,  ball_x - 2, ball_y - 2, 8);
1425           ball_gradient_ptr->add_color_stop_rgba (0.0, 1.0, 1.0, 1.0, 0.7);
1426           ball_gradient_ptr->add_color_stop_rgba (1.0, 0.0, 0.0, 0.0, 0.0);
1427           cr->arc(ball_x, ball_y, 5.0, 0.0, 6.28318530717958647693);
1428           cr->set_source_rgb(color.get_red_p(), color.get_green_p(), color.get_blue_p());
1429           cr->fill_preserve();
1430           cr->set_source(ball_gradient_ptr);
1431           cr->fill_preserve();
1432           cr->set_line_width(1);
1433           cr->set_source_rgb(0.1,0.1,0.1);
1434           cr->stroke();
1435       }
1436 
1437       //Draw Focused band
1438       if(bMotionIsConnected || bBandFocus)
1439       {
1440           ball_x = (double)freq2Pixels(m_filters[m_iBandSel]->Freq);
1441           if( m_filters[m_iBandSel]->fType == PEAK ||
1442               m_filters[m_iBandSel]->fType == LOW_SHELF ||
1443               m_filters[m_iBandSel]->fType == HIGH_SHELF )
1444           {
1445             ball_y = (double)dB2Pixels(m_filters[m_iBandSel]->Gain);
1446           }
1447           else
1448           {
1449             ball_y =  (double)dB2Pixels(0.0);
1450             m_filters[m_iBandSel]->Gain = 0.0;
1451           }
1452 
1453           Gdk::Color color("#00FFFF");
1454           cr->set_line_width(1);
1455           cr->set_source_rgb(color.get_red_p(), color.get_green_p(), color.get_blue_p());
1456           cr->arc(ball_x, ball_y, 6.0, 0.0, 6.28318530717958647693);
1457           cr->stroke();
1458       }
1459       cr->restore();
1460 
1461     }// end Bypass check
1462   }
1463 }
1464 
1465 
redraw_grid_widget()1466 void PlotEQCurve::redraw_grid_widget()
1467 {
1468   if(m_grid_surface_ptr)
1469   {
1470     //Create cairo context using the buffer surface
1471     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_grid_surface_ptr);
1472 
1473     //Clear current context
1474     cr->save();
1475     cr->set_operator(Cairo::OPERATOR_CLEAR);
1476     cr->paint();
1477     cr->restore();
1478 
1479     cr->save();
1480     cr->set_source_rgb(0.3, 0.3, 0.3);
1481     cr->set_line_width(1);
1482     for(int i = 0; i < GRID_VERTICAL_LINES; i++)
1483     {
1484       cr->move_to(xPixels_Grid[i] + 0.5, 0.0);
1485       cr->line_to(xPixels_Grid[i] + 0.5,  m_grid_surface_ptr->get_height());
1486       cr->stroke();
1487     }
1488 
1489     for(int i = -m_dB_plot_range/2; i <= m_dB_plot_range/2; i+=(int)(m_dB_plot_range/10.0))
1490     {
1491       cr->move_to(0, dB2Pixels(i) + 0.5);
1492       cr->line_to(m_grid_surface_ptr->get_width(), dB2Pixels(i) + 0.5);
1493       cr->stroke();
1494     }
1495     cr->restore();
1496   }
1497 }
1498 
redraw_xAxis_widget()1499 void PlotEQCurve::redraw_xAxis_widget()
1500 {
1501   if(m_xAxis_surface_ptr)
1502   {
1503     //Create cairo context using the buffer surface
1504     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_xAxis_surface_ptr);
1505 
1506     //Clear current context
1507     cr->save();
1508     cr->set_operator(Cairo::OPERATOR_CLEAR);
1509     cr->paint();
1510     cr->restore();
1511 
1512     //Draw text with pango to grid
1513     cr->save();
1514     cr->set_source_rgb(0.6, 0.6, 0.6);
1515     Glib::RefPtr<Pango::Layout> pangoLayout = Pango::Layout::create(cr);
1516     Pango::FontDescription font_desc("sans 9px");
1517     pangoLayout->set_font_description(font_desc);
1518     pangoLayout->set_alignment(Pango::ALIGN_RIGHT);
1519 
1520     cr->move_to( xPixels_Grid[0] - 5, 3.5);
1521     pangoLayout->set_text("20");
1522     pangoLayout->show_in_cairo_context(cr);
1523     cr->stroke();
1524 
1525     //Hz scale 50 Hz
1526     cr->move_to( xPixels_Grid[3] - 5, 3.5);
1527     pangoLayout->set_text("50");
1528     pangoLayout->show_in_cairo_context(cr);
1529     cr->stroke();
1530 
1531     //Hz scale 100 Hz
1532     cr->move_to( xPixels_Grid[8] - 10, 3.5);
1533     pangoLayout->set_text("100");
1534     pangoLayout->show_in_cairo_context(cr);
1535     cr->stroke();
1536 
1537     //Hz scale 200 Hz
1538     cr->move_to( xPixels_Grid[9] - 10, 3.5);
1539     pangoLayout->set_text("200");
1540     pangoLayout->show_in_cairo_context(cr);
1541     cr->stroke();
1542 
1543     //Hz scale 500 Hz
1544     cr->move_to( xPixels_Grid[12] - 10, 3.5);
1545     pangoLayout->set_text("500");
1546     pangoLayout->show_in_cairo_context(cr);
1547     cr->stroke();
1548 
1549     //Hz scale 1 KHz
1550     cr->move_to( xPixels_Grid[17] - 5, 3.5);
1551     pangoLayout->set_text("1k");
1552     pangoLayout->show_in_cairo_context(cr);
1553     cr->stroke();
1554 
1555     //Hz scale 2 KHz
1556     cr->move_to( xPixels_Grid[18] - 5, 3.5);
1557     pangoLayout->set_text("2k");
1558     pangoLayout->show_in_cairo_context(cr);
1559     cr->stroke();
1560 
1561     //Hz scale 5 KHz
1562     cr->move_to( xPixels_Grid[21] - 5, 3.5);
1563     pangoLayout->set_text("5k");
1564     pangoLayout->show_in_cairo_context(cr);
1565     cr->stroke();
1566 
1567     //Hz scale 10 KHz
1568     cr->move_to( xPixels_Grid[26] - 5, 3.5);
1569     pangoLayout->set_text("10k");
1570     pangoLayout->show_in_cairo_context(cr);
1571     cr->stroke();
1572 
1573     //Hz scale 20 KHz
1574     cr->move_to( xPixels_Grid[27] - 10, 3.5);
1575     pangoLayout->set_text("20k");
1576     pangoLayout->show_in_cairo_context(cr);
1577     cr->stroke();
1578     cr->restore();
1579   }
1580 }
1581 
redraw_yAxis_widget()1582 void PlotEQCurve::redraw_yAxis_widget()
1583 {
1584   if(m_yAxis_surface_ptr)
1585   {
1586     //Create cairo context using the buffer surface
1587     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_yAxis_surface_ptr);
1588 
1589     //Clear current context
1590     cr->save();
1591     cr->set_operator(Cairo::OPERATOR_CLEAR);
1592     cr->paint();
1593     cr->restore();
1594 
1595     //Draw text with pango to grid
1596     cr->save();
1597     cr->set_source_rgb(0.6, 0.6, 0.6);
1598     Glib::RefPtr<Pango::Layout> pangoLayout = Pango::Layout::create(cr);
1599     Pango::FontDescription font_desc("sans 9px");
1600     pangoLayout->set_font_description(font_desc);
1601     pangoLayout->set_alignment(Pango::ALIGN_RIGHT);
1602 
1603     for(int i = -m_dB_plot_range/2; i <= m_dB_plot_range/2; i+=(int)(m_dB_plot_range/10.0))
1604     {
1605       std::stringstream ss;
1606       ss<< std::setprecision(2) << i;
1607       cr->move_to( 0, dB2Pixels(i) - 3.5 + CURVE_MARGIN);
1608       pangoLayout->set_text(ss.str());
1609       pangoLayout->show_in_cairo_context(cr);
1610       cr->stroke();
1611     }
1612 
1613   }
1614 }
1615 
redraw_cursor(double x,double y)1616 void PlotEQCurve::redraw_cursor(double x, double y)
1617 {
1618   if(m_cursor_surface_ptr)
1619   {
1620     //Create cairo context using the buffer surface
1621     Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_cursor_surface_ptr);
1622 
1623     //Clear current context
1624     cr->save();
1625     cr->set_operator(Cairo::OPERATOR_CLEAR);
1626     cr->paint();
1627     cr->restore();
1628 
1629     //Draw text with pango to grid
1630     if( x > 0 &&
1631         x < m_cursor_surface_ptr->get_width()  &&
1632         y > 0 &&
1633         y < m_cursor_surface_ptr->get_height() )
1634     {
1635 
1636       if(bBandFocus)
1637       {
1638 	x = freq2Pixels(m_filters[m_iBandSel]->Freq);
1639 	y = dB2Pixels( m_filters[m_iBandSel]->Gain );
1640       }
1641 
1642       cr->save();
1643       cr->set_source_rgba(0.9, 0.9, 0.9, 1.0);
1644       cr->set_line_width(1);
1645       cr->move_to(x + 0.5, 0);
1646       cr->line_to(x + 0.5, y - 0.5*BALL_DETECTION_PIXELS);
1647       cr->move_to(x + 0.5, y + 0.5*BALL_DETECTION_PIXELS);
1648       cr->line_to(x + 0.5, m_cursor_surface_ptr->get_height());
1649       cr->move_to(0, y + 0.5);
1650       cr->line_to(x - 0.5*BALL_DETECTION_PIXELS , y + 0.5);
1651       cr->move_to(x + 0.5*BALL_DETECTION_PIXELS, y + 0.5);
1652       cr->line_to(m_cursor_surface_ptr->get_width(), y + 0.5);
1653       cr->stroke();
1654 
1655       Glib::RefPtr<Pango::Layout> pangoLayout = Pango::Layout::create(cr);
1656       Pango::FontDescription font_desc("sans 9px");
1657       pangoLayout->set_font_description(font_desc);
1658       std::stringstream ss;
1659       double freq = Pixels2freq(x);
1660       double gain = Pixels2dB(y);
1661       int precision = 1;
1662       if(freq < 100 || (freq >= 1e3 && freq < 1e4)) precision = 2;
1663       if(freq >= 1000)
1664       {
1665 	ss<< std::setprecision(precision) << std::fixed << 0.001*freq  << " kHz" ;
1666       }
1667       else
1668       {
1669 	ss<< std::setprecision(precision) << std::fixed << freq  << " Hz" ;
1670       }
1671       if( x > (m_cursor_surface_ptr->get_width() - 45))
1672       {
1673 	cr->move_to( x - 45, m_cursor_surface_ptr->get_height() - 10);
1674       }
1675       else
1676       {
1677 	cr->move_to( x + 2, m_cursor_surface_ptr->get_height() - 10);
1678       }
1679       pangoLayout->set_text(ss.str());
1680       pangoLayout->show_in_cairo_context(cr);
1681       cr->stroke();
1682       ss.str("");
1683       ss<< std::setprecision(2) << std::fixed << gain  << " dB" ;
1684       if(gain > 0)
1685       {
1686 	cr->move_to( 2, y +1 );
1687       }
1688       else
1689       {
1690 	cr->move_to( 2, y - 10);
1691       }
1692       pangoLayout->set_text(ss.str());
1693       pangoLayout->show_in_cairo_context(cr);
1694       cr->stroke();
1695       cr->restore();
1696     }
1697   }
1698 }
1699 
1700 
redraw_fft_widget()1701 void PlotEQCurve::redraw_fft_widget()
1702 {
1703   const double m = (-1.0)/(fft_range);
1704   float val;
1705 
1706   Cairo::RefPtr<Cairo::LinearGradient> fft_gradient_ptr = Cairo::LinearGradient::create(0, 0, 1.0, 0);
1707 
1708   double binMax = 1e6;
1709   double binX[(FFT_N/2) + 1];
1710   double binY[(FFT_N/2) + 1];
1711   int binCount = 0;
1712   fft_plot[0] = 1e6; //I don't care about DC
1713 
1714   for (int i = 1; i <= (FFT_N/2); i++)
1715   {
1716     if(m_bIsSpectrogram)
1717     {
1718       val = sqrt((float)fft_raw_data[i]);
1719     }
1720     else
1721     {
1722       fft_ant_data[i] = fft_raw_data[i] >  fft_ant_data[i] ? fft_raw_data[i] : fft_raw_data[i] + 0.5 * fft_ant_data[i];
1723       val = sqrt((float)fft_ant_data[i]);
1724     }
1725     fft_plot[i] = m*(20.0f*fastLog10((int*)(&val), fft_log_lut) + fft_gain + fft_pink_noise[i]);
1726 
1727     if(xPixels_fft_bins[i] == xPixels_fft_bins[i-1])
1728     {
1729       //Inside bin code
1730       binMax = fft_plot[i] < binMax ? fft_plot[i] : binMax; //Yes it is reversed because binMax is really a pixel min
1731     }
1732     else
1733     {
1734       binX[binCount] = xPixels_fft_bins[i-1];
1735       binY[binCount] = binMax;
1736       fft_gradient_ptr->add_color_stop_rgba (binX[binCount], 0.5, -1.0*binMax + 1.0,  1.0,  -1.0*binMax + 1.0);
1737       binCount++;
1738       binMax =  fft_plot[i];
1739 
1740     }
1741   }
1742 
1743   //Create cairo context using the buffer surface
1744   Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(m_fft_surface_ptr);
1745 
1746   //Store a copy of the image
1747   Cairo::RefPtr<Cairo::ImageSurface> img_ant = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,  m_fft_surface_ptr->get_width(),  m_fft_surface_ptr->get_height());
1748   Cairo::RefPtr<Cairo::Context> cr_ant = Cairo::Context::create(img_ant);
1749   cr_ant->save();
1750   cr_ant->set_source (m_fft_surface_ptr, 0, 0);
1751   cr_ant->paint();
1752   cr_ant->restore();
1753 
1754   //Clear current context
1755   cr->save();
1756   cr->set_operator(Cairo::OPERATOR_CLEAR);
1757   cr->paint();
1758   cr->restore();
1759 
1760   if(m_bIsSpectrogram)
1761   {
1762     //Draw the FFT spectrogram
1763     cr->save();
1764     cr->set_source (img_ant, 0, SPECTROGRAM_LINE_THICKNESS);
1765     cr->rectangle(0, SPECTROGRAM_LINE_THICKNESS, m_fft_surface_ptr->get_width(), m_fft_surface_ptr->get_height() - SPECTROGRAM_LINE_THICKNESS);
1766     cr->fill();
1767     cr->restore();
1768 
1769     cr->save();
1770     cr->translate(freq2Pixels(MIN_FREQ), 0);
1771     cr->scale(freq2Pixels(MAX_FREQ) - freq2Pixels(MIN_FREQ),  m_fft_surface_ptr->get_height());
1772     cr->rectangle(0, 0, 1.0, SPECTROGRAM_LINE_THICKNESS/( m_fft_surface_ptr->get_height()));
1773     cr->set_source(fft_gradient_ptr);
1774     cr->fill();
1775     cr->restore();
1776   }
1777   else
1778   {
1779 
1780     //Draw the FFT plot Curve
1781     cr->save();
1782     cr->translate(freq2Pixels(MIN_FREQ), 0);
1783     cr->scale(freq2Pixels(MAX_FREQ) - freq2Pixels(MIN_FREQ),  m_fft_surface_ptr->get_height());
1784 
1785     cr->move_to(0.0, 1.0);
1786 
1787     //Curve smooth version
1788     double Ax, Ay, Bx, By;
1789     for(int i = 1; i<binCount; i++)
1790     {
1791       if(i == 1)
1792       {
1793         //Limit left A = Pk-1
1794         Ax = binX[0];
1795         Ay =  binY[0];
1796       }
1797       else
1798       {
1799         //Calc ctl point A
1800         Ax = binX[i - 1] + SPLINE_TENSION * ( binX[i] -  binX[i - 2] );
1801         Ay = binY[i - 1] + SPLINE_TENSION * ( binY[i] -  binY[i - 2] );
1802       }
1803 
1804       if(i == (binCount - 1))
1805       {
1806         //Limit rigth A = Pk
1807         Bx = binX[i];
1808         By = binY[i];
1809       }
1810       else
1811       {
1812         //Calc ctl point A
1813         Bx = binX[i] - SPLINE_TENSION * ( binX[i + 1] -  binX[i - 1] );
1814         By = binY[i] - SPLINE_TENSION * ( binY[i + 1] -  binY[i - 1] );
1815       }
1816       cr->curve_to(Ax, Ay, Bx, By, binX[i], binY[i]);
1817     }
1818 
1819     cr->line_to(1.0, 1.0);
1820     cr->line_to(0.0, 1.0);
1821     cr->set_source_rgba(0.21, 0.15, 0.78, 0.7);
1822     cr->fill_preserve();
1823     cr->set_source(fft_gradient_ptr);
1824     cr->fill();
1825     cr->restore();
1826   }
1827 }
1828 
setStereoState(int band,PlotEQCurve::MSState state)1829 void PlotEQCurve::setStereoState(int band, PlotEQCurve::MSState state)
1830 {
1831   if(m_NumChannels == 2)
1832   {
1833     band_state[band] = state;
1834     cueBandRedraws(band);
1835   }
1836 }
1837 
1838