fix rendering of meter metrics w/o types
[ardour.git] / gtk2_ardour / meter_patterns.cc
1 /*
2     Copyright (C) 2013 Paul Davis
3     Author: Robin Gareus
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 Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <gtkmm2ext/cairo_widget.h>
21 #include <gtkmm2ext/gtk_ui.h>
22 #include <gtkmm2ext/utils.h>
23 #include <gtkmm2ext/rgb_macros.h>
24
25 #include <ardour/rc_configuration.h>
26 #include "ardour_ui.h"
27 #include "utils.h"
28 #include "logmeter.h"
29 #include "meter_patterns.h"
30
31 #include "i18n.h"
32
33 using namespace ARDOUR;
34 using namespace PBD;
35 using namespace Gtk;
36 using namespace Gtkmm2ext;
37 using namespace std;
38
39
40 static const int max_pattern_metric_size = 1026;
41
42 sigc::signal<void> ResetAllPeakDisplays;
43 sigc::signal<void,ARDOUR::Route*> ResetRoutePeakDisplays;
44 sigc::signal<void,ARDOUR::RouteGroup*> ResetGroupPeakDisplays;
45 sigc::signal<void> RedrawMetrics;
46
47 cairo_pattern_t*
48 meter_render_ticks (Gtk::Widget& w, vector<ARDOUR::DataType> types)
49 {
50         Glib::RefPtr<Gdk::Window> win (w.get_window());
51
52         bool background;
53         gint width, height;
54         win->get_size (width, height);
55         background = types.size() == 0
56                 || w.get_name().substr(w.get_name().length() - 4) == "Left"
57                 || w.get_name().substr(w.get_name().length() - 5) == "Right";
58
59         cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
60         cairo_t* cr = cairo_create (surface);
61
62         cairo_move_to (cr, 0, 0);
63         cairo_rectangle (cr, 0, 0, width, height);
64         {
65                 Gdk::Color c = w.get_style()->get_bg (background ? Gtk::STATE_ACTIVE : Gtk::STATE_NORMAL);
66                 cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
67         }
68         cairo_fill (cr);
69
70         height = min(max_pattern_metric_size, height);
71         uint32_t peakcolor = ARDOUR_UI::config()->color_by_name ("meterbridge peaklabel");
72
73         for (vector<DataType>::const_iterator i = types.begin(); i != types.end(); ++i) {
74
75                 Gdk::Color c;
76                 c = w.get_style()->get_fg (Gtk::STATE_NORMAL);
77
78                 if (types.size() > 1) {
79                         /* we're overlaying more than 1 set of marks, so use different colours */
80                         switch (*i) {
81                         case DataType::AUDIO:
82                                 c = w.get_style()->get_fg (Gtk::STATE_NORMAL);
83                                 cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
84                                 break;
85                         case DataType::MIDI:
86                                 c = w.get_style()->get_fg (Gtk::STATE_ACTIVE);
87                                 cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
88                                 break;
89                         }
90                 } else {
91                         c = w.get_style()->get_fg (Gtk::STATE_NORMAL);
92                         cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
93                 }
94
95                 std::map<int,float> points;
96
97                 switch (*i) {
98                 case DataType::AUDIO:
99                         points.insert (std::pair<int,float>(-60, 0.5));
100                         points.insert (std::pair<int,float>(-50, 0.5));
101                         points.insert (std::pair<int,float>(-40, 0.5));
102                         points.insert (std::pair<int,float>(-30, 0.5));
103                         if (Config->get_meter_line_up_level() == MeteringLineUp24) {
104                                 points.insert (std::pair<int,float>(-24, 0.5));
105                         } else {
106                                 points.insert (std::pair<int,float>(-25, 0.5));
107                         }
108                         points.insert (std::pair<int,float>(-20, 1.0));
109
110                         points.insert (std::pair<int,float>(-19, 0.5));
111                         points.insert (std::pair<int,float>(-18, 1.0));
112                         points.insert (std::pair<int,float>(-17, 0.5));
113                         points.insert (std::pair<int,float>(-16, 0.5));
114                         points.insert (std::pair<int,float>(-15, 1.0));
115
116                         points.insert (std::pair<int,float>(-14, 0.5));
117                         points.insert (std::pair<int,float>(-13, 0.5));
118                         points.insert (std::pair<int,float>(-12, 0.5));
119                         points.insert (std::pair<int,float>(-11, 0.5));
120                         points.insert (std::pair<int,float>(-10, 1.0));
121
122                         points.insert (std::pair<int,float>( -9, 0.5));
123                         points.insert (std::pair<int,float>( -8, 0.5));
124                         points.insert (std::pair<int,float>( -7, 0.5));
125                         points.insert (std::pair<int,float>( -6, 0.5));
126                         points.insert (std::pair<int,float>( -5, 1.0));
127                         points.insert (std::pair<int,float>( -4, 0.5));
128                         points.insert (std::pair<int,float>( -3, 0.5));
129                         points.insert (std::pair<int,float>( -2, 0.5));
130                         points.insert (std::pair<int,float>( -1, 0.5));
131
132                         points.insert (std::pair<int,float>(  0, 1.0));
133                         points.insert (std::pair<int,float>(  1, 0.5));
134                         points.insert (std::pair<int,float>(  2, 0.5));
135                         points.insert (std::pair<int,float>(  3, 0.5));
136                         points.insert (std::pair<int,float>(  4, 0.5));
137                         points.insert (std::pair<int,float>(  5, 0.5));
138                         break;
139
140                 case DataType::MIDI:
141                         points.insert (std::pair<int,float>(  0, 1.0));
142                         points.insert (std::pair<int,float>( 16, 0.5));
143                         points.insert (std::pair<int,float>( 32, 0.5));
144                         points.insert (std::pair<int,float>( 48, 0.5));
145                         points.insert (std::pair<int,float>( 64, 1.0));
146                         points.insert (std::pair<int,float>( 80, 0.5));
147                         points.insert (std::pair<int,float>( 96, 0.5));
148                         points.insert (std::pair<int,float>(100, 1.0));
149                         points.insert (std::pair<int,float>(112, 0.5));
150                         points.insert (std::pair<int,float>(127, 1.0));
151                         break;
152                 }
153
154                 for (std::map<int,float>::const_iterator j = points.begin(); j != points.end(); ++j) {
155                         cairo_set_line_width (cr, (j->second));
156
157                         float fraction = 0;
158                         gint pos;
159
160                         switch (*i) {
161                         case DataType::AUDIO:
162                                 if (j->first >= 0 || j->first == -9) {
163                                         cairo_set_source_rgb (cr,
164                                                         UINT_RGBA_R_FLT(peakcolor),
165                                                         UINT_RGBA_G_FLT(peakcolor),
166                                                         UINT_RGBA_B_FLT(peakcolor));
167                                 } else {
168                                         cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
169                                 }
170                                 fraction = log_meter (j->first);
171                                 pos = height - (gint) floor (height * fraction);
172                                 cairo_move_to(cr, 0, pos + .5);
173                                 cairo_line_to(cr, 3, pos + .5);
174                                 cairo_stroke (cr);
175                                 break;
176                         case DataType::MIDI:
177                                 fraction = (j->first) / 127.0;
178                                 pos = 1 + height - (gint) floor (height * fraction);
179                                 pos = min (pos, height);
180                                 cairo_arc(cr, 1.5, pos + .5, 1.0, 0, 2 * M_PI);
181                                 cairo_fill(cr);
182                                 break;
183                         }
184                 }
185         }
186
187         cairo_pattern_t* pattern = cairo_pattern_create_for_surface (surface);
188
189         cairo_destroy (cr);
190         cairo_surface_destroy (surface);
191
192         return pattern;
193 }
194
195
196 cairo_pattern_t*
197 meter_render_metrics (Gtk::Widget& w, vector<DataType> types)
198 {
199         Glib::RefPtr<Gdk::Window> win (w.get_window());
200
201         bool tickleft;
202         bool background;
203         gint width, height;
204         win->get_size (width, height);
205
206         tickleft = w.get_name().substr(w.get_name().length() - 4) == "Left";
207         background = types.size() == 0 || tickleft || w.get_name().substr(w.get_name().length() - 5) == "Right";
208
209         cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
210         cairo_t* cr = cairo_create (surface);
211         Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create(w.get_pango_context());
212
213         Pango::AttrList audio_font_attributes;
214         Pango::AttrList midi_font_attributes;
215         Pango::AttrList unit_font_attributes;
216
217         Pango::AttrFontDesc* font_attr;
218         Pango::FontDescription font;
219
220         font = Pango::FontDescription (""); // use defaults
221         //font = get_font_for_style("gain-fader");
222         //font = w.get_style()->get_font();
223
224         font.set_weight (Pango::WEIGHT_NORMAL);
225         font.set_size (9.0 * PANGO_SCALE);
226         font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font));
227         audio_font_attributes.change (*font_attr);
228         delete font_attr;
229
230         font.set_weight (Pango::WEIGHT_ULTRALIGHT);
231         font.set_stretch (Pango::STRETCH_ULTRA_CONDENSED);
232         font.set_size (7.5 * PANGO_SCALE);
233         font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font));
234         midi_font_attributes.change (*font_attr);
235         delete font_attr;
236
237         font.set_size (7.0 * PANGO_SCALE);
238         font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font));
239         unit_font_attributes.change (*font_attr);
240         delete font_attr;
241
242         cairo_move_to (cr, 0, 0);
243         cairo_rectangle (cr, 0, 0, width, height);
244         {
245                 Gdk::Color c = w.get_style()->get_bg (background ? Gtk::STATE_ACTIVE : Gtk::STATE_NORMAL);
246                 cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
247         }
248         cairo_fill (cr);
249
250         height = min(max_pattern_metric_size, height);
251         uint32_t peakcolor = ARDOUR_UI::config()->color_by_name ("meterbridge peaklabel");
252
253         for (vector<DataType>::const_iterator i = types.begin(); i != types.end(); ++i) {
254
255                 Gdk::Color c;
256
257                 if (types.size() > 1) {
258                         /* we're overlaying more than 1 set of marks, so use different colours */
259                         Gdk::Color c;
260                         switch (*i) {
261                         case DataType::AUDIO:
262                                 c = w.get_style()->get_fg (Gtk::STATE_NORMAL);
263                                 cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
264                                 break;
265                         case DataType::MIDI:
266                                 c = w.get_style()->get_fg (Gtk::STATE_ACTIVE);
267                                 cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
268                                 break;
269                         }
270                 } else {
271                         c = w.get_style()->get_fg (Gtk::STATE_NORMAL);
272                         cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
273                 }
274
275                 std::map<int,float> points;
276
277                 switch (*i) {
278                 case DataType::AUDIO:
279                         layout->set_attributes (audio_font_attributes);
280                         points.insert (std::pair<int,float>(-50, 0.5));
281                         points.insert (std::pair<int,float>(-40, 0.5));
282                         points.insert (std::pair<int,float>(-30, 0.5));
283                         points.insert (std::pair<int,float>(-20, 1.0));
284                         if (types.size() == 1) {
285                                 if (Config->get_meter_line_up_level() == MeteringLineUp24) {
286                                         points.insert (std::pair<int,float>(-24, 0.5));
287                                 } else {
288                                         points.insert (std::pair<int,float>(-25, 0.5));
289                                 }
290                                 points.insert (std::pair<int,float>(-15, 1.0));
291                         }
292                         points.insert (std::pair<int,float>(-18, 1.0));
293                         points.insert (std::pair<int,float>(-10, 1.0));
294                         points.insert (std::pair<int,float>( -5, 1.0));
295                         points.insert (std::pair<int,float>( -3, 0.5));
296                         points.insert (std::pair<int,float>(  0, 1.0));
297                         points.insert (std::pair<int,float>(  3, 0.5));
298                         break;
299
300                 case DataType::MIDI:
301                         layout->set_attributes (midi_font_attributes);
302                         points.insert (std::pair<int,float>(  0, 1.0));
303                         if (types.size() == 1) {
304                                 points.insert (std::pair<int,float>( 16, 0.5));
305                                 points.insert (std::pair<int,float>( 32, 0.5));
306                                 points.insert (std::pair<int,float>( 48, 0.5));
307                                 points.insert (std::pair<int,float>( 64, 1.0));
308                                 points.insert (std::pair<int,float>( 80, 0.5));
309                                 points.insert (std::pair<int,float>( 96, 0.5));
310                                 points.insert (std::pair<int,float>(100, 0.5));
311                                 points.insert (std::pair<int,float>(112, 0.5));
312                         } else {
313                                 /* labels that don't overlay with dB */
314                                 points.insert (std::pair<int,float>( 24, 0.5));
315                                 points.insert (std::pair<int,float>( 48, 0.5));
316                                 points.insert (std::pair<int,float>( 72, 0.5));
317                         }
318                         points.insert (std::pair<int,float>(127, 1.0));
319                         break;
320                 }
321
322                 char buf[32];
323                 gint pos;
324
325                 for (std::map<int,float>::const_iterator j = points.begin(); j != points.end(); ++j) {
326
327                         float fraction = 0;
328                         switch (*i) {
329                         case DataType::AUDIO:
330                                 cairo_set_line_width (cr, (j->second));
331                                 if (j->first >= 0) {
332                                         cairo_set_source_rgb (cr,
333                                                         UINT_RGBA_R_FLT(peakcolor),
334                                                         UINT_RGBA_G_FLT(peakcolor),
335                                                         UINT_RGBA_B_FLT(peakcolor));
336                                 }
337                                 fraction = log_meter (j->first);
338                                 snprintf (buf, sizeof (buf), "%+2d", j->first);
339                                 pos = height - (gint) floor (height * fraction);
340                                 if (tickleft) {
341                                         cairo_move_to(cr, width-2.5, pos + .5);
342                                         cairo_line_to(cr, width, pos + .5);
343                                 } else {
344                                         cairo_move_to(cr, 0, pos + .5);
345                                         cairo_line_to(cr, 2.5, pos + .5);
346                                 }
347                                 cairo_stroke (cr);
348                                 break;
349                         case DataType::MIDI:
350                                 cairo_set_line_width (cr, 1.0);
351                                 fraction = (j->first) / 127.0;
352                                 snprintf (buf, sizeof (buf), "%3d", j->first);
353                                 pos = 1 + height - (gint) rintf (height * fraction);
354                                 pos = min (pos, height);
355                                 if (tickleft) {
356                                         cairo_arc(cr, width - 2.0, pos + .5, 1.0, 0, 2 * M_PI);
357                                 } else {
358                                         cairo_arc(cr, 3, pos + .5, 1.0, 0, 2 * M_PI);
359                                 }
360                                 cairo_fill(cr);
361                                 break;
362                         }
363
364                         layout->set_text(buf);
365
366                         /* we want logical extents, not ink extents here */
367
368                         int tw, th;
369                         layout->get_pixel_size(tw, th);
370
371                         int p = pos - (th / 2);
372                         p = min (p, height - th);
373                         p = max (p, 0);
374
375                         cairo_move_to (cr, width-4-tw, p);
376                         pango_cairo_show_layout (cr, layout->gobj());
377                 }
378         }
379
380         if (types.size() == 1) {
381                 int tw, th;
382                 layout->set_attributes (unit_font_attributes);
383                 switch (types.at(0)) {
384                         case DataType::AUDIO:
385                                 layout->set_text("dBFS");
386                                 layout->get_pixel_size(tw, th);
387                                 break;
388                         case DataType::MIDI:
389                                 layout->set_text("vel");
390                                 layout->get_pixel_size(tw, th);
391                                 break;
392                 }
393                 Gdk::Color c = w.get_style()->get_fg (Gtk::STATE_ACTIVE);
394                 cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
395                 cairo_move_to (cr, 1, height - th - 1.5);
396                 pango_cairo_show_layout (cr, layout->gobj());
397         }
398
399         cairo_pattern_t* pattern = cairo_pattern_create_for_surface (surface);
400
401         cairo_destroy (cr);
402         cairo_surface_destroy (surface);
403
404         return pattern;
405 }
406
407
408 typedef std::map<std::string,cairo_pattern_t*> TickPatterns;
409 static  TickPatterns ticks_patterns;
410
411 gint meter_expose_ticks (GdkEventExpose *ev, std::vector<ARDOUR::DataType> types, Gtk::DrawingArea *mta)
412 {
413         Glib::RefPtr<Gdk::Window> win (mta->get_window());
414         cairo_t* cr;
415
416         cr = gdk_cairo_create (win->gobj());
417
418         /* clip to expose area */
419
420         gdk_cairo_rectangle (cr, &ev->area);
421         cairo_clip (cr);
422
423         cairo_pattern_t* pattern;
424         TickPatterns::iterator i = ticks_patterns.find (mta->get_name());
425
426         if (i == ticks_patterns.end()) {
427                 pattern = meter_render_ticks (*mta, types);
428                 ticks_patterns[mta->get_name()] = pattern;
429         } else {
430                 pattern = i->second;
431         }
432
433         cairo_move_to (cr, 0, 0);
434         cairo_set_source (cr, pattern);
435
436         gint width, height;
437         win->get_size (width, height);
438
439         cairo_rectangle (cr, 0, 0, width, height);
440         cairo_fill (cr);
441
442         cairo_destroy (cr);
443
444         return true;
445 }
446
447 typedef std::map<std::string,cairo_pattern_t*> MetricPatterns;
448 static  MetricPatterns metric_patterns;
449
450 gint meter_expose_metrics (GdkEventExpose *ev, std::vector<ARDOUR::DataType> types, Gtk::DrawingArea *mma)
451 {
452         Glib::RefPtr<Gdk::Window> win (mma->get_window());
453         cairo_t* cr;
454
455         cr = gdk_cairo_create (win->gobj());
456
457         /* clip to expose area */
458
459         gdk_cairo_rectangle (cr, &ev->area);
460         cairo_clip (cr);
461
462         cairo_pattern_t* pattern;
463         MetricPatterns::iterator i = metric_patterns.find (mma->get_name());
464
465         if (i == metric_patterns.end()) {
466                 pattern = meter_render_metrics (*mma, types);
467                 metric_patterns[mma->get_name()] = pattern;
468         } else {
469                 pattern = i->second;
470         }
471
472         cairo_move_to (cr, 0, 0);
473         cairo_set_source (cr, pattern);
474
475         gint width, height;
476         win->get_size (width, height);
477
478         cairo_rectangle (cr, 0, 0, width, height);
479         cairo_fill (cr);
480
481         cairo_destroy (cr);
482
483         return true;
484 }
485
486 void meter_clear_pattern_cache() {
487         metric_patterns.clear();
488         ticks_patterns.clear();
489         RedrawMetrics();
490 }