+
+ setup_meters ();
+ hbox.show_all ();
+}
+
+int
+GainMeter::get_gm_width ()
+{
+ Gtk::Requisition sz;
+ hbox.size_request (sz);
+ return sz.width;
+}
+
+cairo_pattern_t*
+GainMeter::render_metrics (Gtk::Widget& w, vector<DataType> types)
+{
+ Glib::RefPtr<Gdk::Window> win (w.get_window());
+
+ gint width, height;
+ win->get_size (width, height);
+
+ cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+ cairo_t* cr = cairo_create (surface);
+ PangoLayout* layout = gtk_widget_create_pango_layout (w.gobj(), "");
+
+ cairo_move_to (cr, 0, 0);
+ cairo_rectangle (cr, 0, 0, width, height);
+ {
+ Gdk::Color c = w.get_style()->get_bg (Gtk::STATE_NORMAL);
+ cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
+ }
+ cairo_fill (cr);
+
+ for (vector<DataType>::const_iterator i = types.begin(); i != types.end(); ++i) {
+
+ Gdk::Color c;
+
+ if (types.size() > 1) {
+ /* we're overlaying more than 1 set of marks, so use different colours */
+ Gdk::Color c;
+ switch (*i) {
+ case DataType::AUDIO:
+ c = w.get_style()->get_fg (Gtk::STATE_NORMAL);
+ cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
+ break;
+ case DataType::MIDI:
+ c = w.get_style()->get_fg (Gtk::STATE_ACTIVE);
+ cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
+ break;
+ }
+ } else {
+ c = w.get_style()->get_fg (Gtk::STATE_NORMAL);
+ cairo_set_source_rgb (cr, c.get_red_p(), c.get_green_p(), c.get_blue_p());
+ }
+
+ vector<int> points;
+
+ switch (*i) {
+ case DataType::AUDIO:
+ points.push_back (-50);
+ points.push_back (-40);
+ points.push_back (-30);
+ points.push_back (-20);
+ points.push_back (-10);
+ points.push_back (-3);
+ points.push_back (0);
+ points.push_back (4);
+ break;
+
+ case DataType::MIDI:
+ points.push_back (0);
+ if (types.size() == 1) {
+ points.push_back (32);
+ } else {
+ /* tweak so as not to overlay the -30dB mark */
+ points.push_back (48);
+ }
+ points.push_back (64);
+ points.push_back (96);
+ points.push_back (127);
+ break;
+ }
+
+ char buf[32];
+
+ for (vector<int>::const_iterator j = points.begin(); j != points.end(); ++j) {
+
+ float fraction = 0;
+ switch (*i) {
+ case DataType::AUDIO:
+ fraction = log_meter (*j);
+ break;
+ case DataType::MIDI:
+ fraction = *j / 127.0;
+ break;
+ }
+
+ gint const pos = height - (gint) floor (height * fraction);
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to (cr, 0, pos);
+ cairo_line_to (cr, 4, pos);
+ cairo_stroke (cr);
+
+ snprintf (buf, sizeof (buf), "%d", abs (*j));
+ pango_layout_set_text (layout, buf, strlen (buf));
+
+ /* we want logical extents, not ink extents here */
+
+ int tw, th;
+ pango_layout_get_pixel_size (layout, &tw, &th);
+
+ int p = pos - (th / 2);
+ p = min (p, height - th);
+ p = max (p, 0);
+
+ cairo_move_to (cr, 6, p);
+ pango_cairo_show_layout (cr, layout);
+ }
+ }
+
+ cairo_pattern_t* pattern = cairo_pattern_create_for_surface (surface);
+ MetricPatterns::iterator p;
+
+ if ((p = metric_patterns.find (w.get_name())) != metric_patterns.end()) {
+ cairo_pattern_destroy (p->second);
+ }
+
+ metric_patterns[w.get_name()] = pattern;
+
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+ g_object_unref (layout);
+
+ return pattern;
+}
+
+gint
+GainMeter::meter_metrics_expose (GdkEventExpose *ev)
+{
+ Glib::RefPtr<Gdk::Window> win (meter_metric_area.get_window());
+ cairo_t* cr;
+
+ cr = gdk_cairo_create (win->gobj());
+
+ /* clip to expose area */
+
+ gdk_cairo_rectangle (cr, &ev->area);
+ cairo_clip (cr);
+
+ cairo_pattern_t* pattern;
+ MetricPatterns::iterator i = metric_patterns.find (meter_metric_area.get_name());
+
+ if (i == metric_patterns.end() || style_changed || dpi_changed) {
+ pattern = render_metrics (meter_metric_area, _types);
+ } else {
+ pattern = i->second;
+ }
+
+ cairo_move_to (cr, 0, 0);
+ cairo_set_source (cr, pattern);
+
+ gint width, height;
+ win->get_size (width, height);
+
+ cairo_rectangle (cr, 0, 0, width, height);
+ cairo_fill (cr);
+
+ style_changed = false;
+ dpi_changed = false;
+
+ cairo_destroy (cr);
+
+ return true;
+}
+
+boost::shared_ptr<PBD::Controllable>
+GainMeterBase::get_controllable()
+{
+ if (_amp) {
+ return _amp->gain_control();
+ } else {
+ return boost::shared_ptr<PBD::Controllable>();
+ }
+}
+
+void
+GainMeter::meter_configuration_changed (ChanCount c)
+{
+ _types.clear ();
+
+ for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
+ if (c.get (*i) > 0) {
+ _types.push_back (*i);
+ }
+ }
+
+ style_changed = true;
+ meter_metric_area.queue_draw ();