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