1 /***************************************************************************
2 * Copyright (C) 2015 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 "dynplot.h"
22 #include "colors.h"
23 #include <iostream>
24 #include <iomanip>
25 #include <cstring>
26 #include <cmath>
27
28 #define CURVE_BORDER 1.5
29 #define CURVE_MARGIN 15
30 #define CURVE_TEXT_OFFSET 18
31 #define PLOT_WIDTH_HEIGHT 250
32 #define DATA_RANGE_MIN -60.0
33 #define DATA_RANGE_MAX 10.0
34 #define GRID_STEP 10.0
35
PlotDynCurve(bool isCompressor)36 PlotDynCurve::PlotDynCurve(bool isCompressor):
37 m_Ratio(1.0), m_Range(-100.0), m_Knee(0.0), m_Threshold(0.0), m_Makeup(0.0), m_GainReduction(0.0), m_InputVu(-100.0), m_bIsCompressor(isCompressor)
38 {
39 set_size_request(PLOT_WIDTH_HEIGHT, PLOT_WIDTH_HEIGHT);
40 }
41
~PlotDynCurve()42 PlotDynCurve::~PlotDynCurve()
43 {
44
45 }
46
set_gainreduction(double gainreduction)47 void PlotDynCurve::set_gainreduction(double gainreduction)
48 {
49 m_GainReduction = gainreduction != 0.0 ? 20.0*log10(gainreduction) : -100.0;
50 redraw();
51 }
52
set_inputvu(double inputvu)53 void PlotDynCurve::set_inputvu(double inputvu)
54 {
55 m_InputVu = inputvu != 0.0 ? 20.0*log10(inputvu) : -100.0;
56 redraw();
57 }
58
set_knee(double knee)59 void PlotDynCurve::set_knee(double knee)
60 {
61 m_Knee = knee;
62 redraw();
63 }
64
set_makeup(double makeup)65 void PlotDynCurve::set_makeup(double makeup)
66 {
67 m_Makeup = makeup;
68 redraw();
69 }
70
set_range(double range)71 void PlotDynCurve::set_range(double range)
72 {
73 m_Range = range;
74 redraw();
75 }
76
set_ratio(double ratio)77 void PlotDynCurve::set_ratio(double ratio)
78 {
79 m_Ratio = ratio;
80 redraw();
81 }
82
set_threshold(double threshold)83 void PlotDynCurve::set_threshold(double threshold)
84 {
85 m_Threshold = threshold;
86 redraw();
87 }
88
redraw()89 void PlotDynCurve::redraw()
90 {
91 Glib::RefPtr<Gdk::Window> win = get_window();
92 if(win)
93 {
94 Gdk::Rectangle r(0, 0, get_allocation().get_width(), get_allocation().get_height());
95 win->invalidate_rect(r, false);
96 }
97 }
98
dB2PixelsX(double db)99 double PlotDynCurve::dB2PixelsX(double db)
100 {
101 const double m = ((double)width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET)/((double)(DATA_RANGE_MAX - DATA_RANGE_MIN));
102 const double n = CURVE_MARGIN + CURVE_TEXT_OFFSET - m*DATA_RANGE_MIN;
103 return m*db + n;
104 }
105
dB2PixelsY(double db)106 double PlotDynCurve::dB2PixelsY(double db)
107 {
108 const double m = (2*CURVE_MARGIN + CURVE_TEXT_OFFSET - (double)height)/((double)(DATA_RANGE_MAX - DATA_RANGE_MIN));
109 const double n = ((double)height) - CURVE_MARGIN - CURVE_TEXT_OFFSET - m*DATA_RANGE_MIN;
110 return m*db + n;
111 }
112
on_expose_event(GdkEventExpose * event)113 bool PlotDynCurve::on_expose_event(GdkEventExpose* event)
114 {
115
116 Glib::RefPtr<Gdk::Window> window = get_window();
117 if(window)
118 {
119
120 Gtk::Allocation allocation = get_allocation();
121 width = allocation.get_width();
122 height = allocation.get_height();
123
124
125 Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
126
127 //Paint backgroud
128 cr->save();
129 cr->set_source_rgb(BACKGROUND_R, BACKGROUND_G, BACKGROUND_B);
130 cr->paint(); //Fill all with background color
131 cr->restore();
132
133
134 //Draw an interesting frame
135 cr->save();
136 double radius = height / 50.0;
137 double degrees = M_PI / 180.0;
138 cr->begin_new_sub_path();
139 cr->arc (width - CURVE_BORDER - radius, CURVE_BORDER + radius, radius, -90 * degrees, 0 * degrees);
140 cr->arc (width - CURVE_BORDER - radius, height - CURVE_BORDER - radius, radius, 0 * degrees, 90 * degrees);
141 cr->arc (CURVE_BORDER + radius, height- CURVE_BORDER - radius, radius, 90 * degrees, 180 * degrees);
142 cr->arc ( CURVE_BORDER + radius, CURVE_BORDER + radius, radius, 180 * degrees, 270 * degrees);
143 cr->close_path();
144 Cairo::RefPtr<Cairo::LinearGradient> bkg_gradient_ptr = Cairo::LinearGradient::create(width/2, CURVE_BORDER, width/2, height - CURVE_BORDER);
145 bkg_gradient_ptr->add_color_stop_rgba (0.0, 0.1, 0.1, 0.1, 0.6 );
146 bkg_gradient_ptr->add_color_stop_rgba (0.5, 0.2, 0.3, 0.3, 0.3 );
147 bkg_gradient_ptr->add_color_stop_rgba (1.0, 0.1, 0.1, 0.1, 0.6 );
148 cr->set_source(bkg_gradient_ptr);
149 cr->fill_preserve();
150 cr->set_line_width(1.0);
151 cr->set_source_rgb(0.3, 0.3, 0.4);
152 cr->stroke();
153 cr->restore();
154
155 //Draw the grid
156 cr->save();
157 cr->set_source_rgb(0.3, 0.3, 0.3);
158 cr->set_line_width(1);
159 for(double i = DATA_RANGE_MIN; i <= DATA_RANGE_MAX; i+=GRID_STEP)
160 {
161 //Vertical grid
162 cr->move_to( dB2PixelsX(i) + 0.5, CURVE_MARGIN);
163 cr->line_to( dB2PixelsX(i) + 0.5, height - CURVE_MARGIN - CURVE_TEXT_OFFSET);
164 //Horizontal grid
165 cr->move_to( CURVE_MARGIN + CURVE_TEXT_OFFSET, dB2PixelsY(i) + 0.5);
166 cr->line_to( width - CURVE_MARGIN, dB2PixelsY(i) + 0.5);
167 cr->stroke();
168 }
169 cr->restore();
170
171 //Draw text with pango to grid
172 cr->save();
173 cr->set_source_rgb(0.6, 0.6, 0.6);
174 Glib::RefPtr<Pango::Layout> pangoLayout = Pango::Layout::create(cr);
175 Pango::FontDescription font_desc("sans 9px");
176 pangoLayout->set_font_description(font_desc);
177 pangoLayout->set_alignment(Pango::ALIGN_RIGHT);
178 for(double i = DATA_RANGE_MIN; i <= DATA_RANGE_MAX; i+=GRID_STEP)
179 {
180 std::stringstream ss;
181 ss<< std::setprecision(2) << i;
182 pangoLayout->set_text(ss.str());
183 cr->move_to(dB2PixelsX(i) - 3.5, height - CURVE_MARGIN - CURVE_TEXT_OFFSET + 3.5);
184 pangoLayout->show_in_cairo_context(cr);
185 cr->move_to(CURVE_MARGIN, dB2PixelsY(i) - 3.5);
186 pangoLayout->show_in_cairo_context(cr);
187 cr->stroke();
188 }
189 cr->restore();
190
191 //Plot InputVU and GainReduction
192 double prange = 0.05*(m_InputVu - DATA_RANGE_MIN);
193 cr->save();
194 cr->rectangle(CURVE_MARGIN + CURVE_TEXT_OFFSET + 0.5, CURVE_MARGIN + 0.5, width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET, height - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET);
195 cr->clip();
196 cr->begin_new_sub_path();
197 cr->move_to(dB2PixelsX(DATA_RANGE_MIN), dB2PixelsY(DATA_RANGE_MIN));
198 cr->line_to(dB2PixelsX(DATA_RANGE_MIN), dB2PixelsY(DATA_RANGE_MIN + m_Makeup));
199 cr->line_to( dB2PixelsX(m_InputVu + prange), dB2PixelsY(m_InputVu + prange - m_GainReduction + m_Makeup) );
200 cr->line_to( dB2PixelsX(m_InputVu + prange), dB2PixelsY(DATA_RANGE_MIN));
201 cr->close_path();
202 bkg_gradient_ptr = Cairo::LinearGradient::create(dB2PixelsX(DATA_RANGE_MIN), dB2PixelsY(DATA_RANGE_MIN),dB2PixelsX(m_InputVu + prange), dB2PixelsY(DATA_RANGE_MIN) );
203 bkg_gradient_ptr->add_color_stop_rgba (0.0, 0.1, 0.2, 0.8, 0.4 );
204 bkg_gradient_ptr->add_color_stop_rgba (0.9, 0.1, 0.6, 0.4, 0.3 );
205 bkg_gradient_ptr->add_color_stop_rgba (1.0, 0.1, 0.6, 0.4, 0.0 );
206 cr->set_source(bkg_gradient_ptr);
207 cr->fill();
208 cr->restore();
209
210
211 //Draw the curve
212 cr->save();
213 cr->rectangle(CURVE_MARGIN + CURVE_TEXT_OFFSET + 0.5, CURVE_MARGIN + 0.5, width - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET, height - 2*CURVE_MARGIN - CURVE_TEXT_OFFSET);
214 cr->clip();
215 cr->move_to( dB2PixelsX(DATA_RANGE_MIN) + 0.5, dB2PixelsY(DATA_RANGE_MIN) + 0.5 );
216 double knee_range, y_dB;
217
218 if(m_bIsCompressor)
219 {
220 for(double x_dB = DATA_RANGE_MIN; x_dB <= DATA_RANGE_MAX; x_dB += 1.0)
221 {
222 knee_range = 2.0*(x_dB - m_Threshold);
223 if (knee_range < -m_Knee)
224 {
225 //Under Threshold
226 y_dB = x_dB;
227 }
228 else if(knee_range >= m_Knee )
229 {
230 //Over Threshold
231 y_dB = m_Threshold + (x_dB - m_Threshold)/m_Ratio;
232 }
233 else
234 {
235 //On Knee
236 y_dB = x_dB + ((1.0/m_Ratio -1.0)*(x_dB - m_Threshold + m_Knee/2.0)*(x_dB - m_Threshold + m_Knee/2.0))/(2.0*m_Knee);
237 }
238 y_dB += m_Makeup;
239 cr->line_to( dB2PixelsX(x_dB) + 0.5, dB2PixelsY(y_dB) + 0.5);
240 }
241 }
242 else
243 {
244 //Draw Expander/Gate
245 for(double x_dB = DATA_RANGE_MIN; x_dB <= DATA_RANGE_MAX; x_dB += 1.0)
246 {
247 knee_range = 2.0*(x_dB - m_Threshold);
248 if (knee_range < -m_Knee)
249 {
250 //Under Threshold
251 y_dB = m_Threshold + (x_dB - m_Threshold)*m_Ratio;
252 }
253 else if(knee_range >= m_Knee )
254 {
255 //Over Threshold
256 y_dB = x_dB;
257 }
258 else
259 {
260 //On Knee
261 y_dB = x_dB + ((1.0 - m_Ratio)*(x_dB - m_Threshold - m_Knee/2)*(x_dB - m_Threshold - m_Knee/2))/(2*m_Knee);
262 }
263
264 if( y_dB < x_dB + m_Range )
265 {
266 y_dB = x_dB + m_Range;
267 }
268 cr->line_to( dB2PixelsX(x_dB) + 0.5, dB2PixelsY(y_dB) + 0.5);
269 }
270
271 }
272 cr->set_line_width(1.0);
273 cr->set_line_cap(Cairo::LINE_CAP_ROUND);
274 cr->set_source_rgb(1,1,1);
275 cr->stroke();
276 cr->restore();
277
278
279 //draw de outer grind box
280 cr->save();
281 cr->set_source_rgb(0.3, 0.3, 0.3);
282 cr->set_line_width(1);
283 cr->move_to(CURVE_MARGIN + CURVE_TEXT_OFFSET + 0.5, CURVE_MARGIN + 0.5);
284 cr->line_to(width - CURVE_MARGIN + 0.5, CURVE_MARGIN + 0.5);
285 cr->line_to(width - CURVE_MARGIN + 0.5, height - CURVE_MARGIN - CURVE_TEXT_OFFSET + 0.5);
286 cr->line_to(CURVE_MARGIN + CURVE_TEXT_OFFSET + 0.5 , height - CURVE_MARGIN - CURVE_TEXT_OFFSET + 0.5);
287 cr->line_to(CURVE_MARGIN + CURVE_TEXT_OFFSET + 0.5, CURVE_MARGIN + 0.5);
288 cr->stroke();
289 cr->restore();
290 }
291 return true;
292 }
293