8874b47cf3626d26cd3b635f77a9b4c4db03a1a3
[ardour.git] / gtk2_ardour / gain_meter.cc
1 /*
2   Copyright (C) 2002 Paul Davis
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13
14   You should have received a copy of the GNU General Public License
15   along with this program; if not, write to the Free Software
16   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18   $Id$
19 */
20
21 #include <limits.h>
22
23 #include <ardour/io.h>
24 #include <ardour/route.h>
25 #include <ardour/route_group.h>
26 #include <ardour/session.h>
27 #include <ardour/session_route.h>
28 #include <ardour/dB.h>
29
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/fastmeter.h>
32 #include <gtkmm2ext/stop_signal.h>
33 #include <gtkmm2ext/barcontroller.h>
34 #include <midi++/manager.h>
35 #include <pbd/fastlog.h>
36
37 #include "ardour_ui.h"
38 #include "gain_meter.h"
39 #include "utils.h"
40 #include "logmeter.h"
41 #include "gui_thread.h"
42 #include "keyboard.h"
43
44 #include <ardour/session.h>
45 #include <ardour/route.h>
46
47 #include "i18n.h"
48
49 using namespace ARDOUR;
50 using namespace Gtkmm2ext;
51 using namespace Gtk;
52 using namespace sigc;
53 using namespace std;
54
55 sigc::signal<void> GainMeter::ResetAllPeakDisplays;
56 sigc::signal<void,RouteGroup*> GainMeter::ResetGroupPeakDisplays;
57 Glib::RefPtr<Gdk::Pixbuf> GainMeter::slider;
58 Glib::RefPtr<Gdk::Pixbuf> GainMeter::rail;
59 map<string,Glib::RefPtr<Gdk::Pixmap> > GainMeter::metric_pixmaps;
60
61 int
62 GainMeter::setup_slider_pix ()
63 {
64         string path = ARDOUR::find_data_file("vslider02_slider.xpm", "pixmaps");
65         if (path.empty()) {
66                 error << _("cannot find images for fader slider") << endmsg;
67                 return -1;
68         }
69         slider = Gdk::Pixbuf::create_from_file (path);
70
71         path = ARDOUR::find_data_file("vslider02_rail.xpm", "pixmaps");
72         if (path.empty()) {
73                 error << _("cannot find images for fader rail") << endmsg;
74                 return -1;
75         }
76         rail = Gdk::Pixbuf::create_from_file (path);
77
78         return 0;
79 }
80
81 GainMeter::GainMeter (IO& io, Session& s)
82         : _io (io),
83           _session (s),
84           gain_slider (0),
85           // 0.781787 is the value needed for gain to be set to 0.
86           gain_adjustment (0.781787, 0.0, 1.0, 0.01, 0.1),
87           gain_display (&gain_adjustment, "MixerStripGainDisplay"),
88           gain_unit_label (_("dbFS")),
89           meter_point_label (_("pre")),
90           top_table (1, 2)
91         
92 {
93         if (slider == 0) {
94                 setup_slider_pix ();
95         }
96
97         ignore_toggle = false;
98         meter_menu = 0;
99         
100         gain_slider = manage (new VSliderController (slider, rail,
101                                                      &gain_adjustment,
102                                                      & _io.midi_gain_control(),
103                                                      false));
104
105         gain_slider->signal_button_press_event().connect (mem_fun(*this, &GainMeter::start_gain_touch));
106         gain_slider->signal_button_release_event().connect (mem_fun(*this, &GainMeter::end_gain_touch));
107         gain_slider->set_name ("MixerGainMeter");
108
109         if (_session.midi_port()) {
110                 _io.set_midi_to_gain_function (slider_position_to_gain);
111                 _io.set_gain_to_midi_function (gain_to_slider_position);
112         }
113
114         gain_display.set_print_func (_gain_printer, this);
115
116         gain_unit_button.add (gain_unit_label);
117         gain_unit_button.set_name ("MixerStripGainUnitButton");
118         gain_unit_label.set_name ("MixerStripGainUnitButton");
119
120         meter_point_button.signal_button_press_event().connect (mem_fun (*this, &GainMeter::meter_press), false);
121         meter_point_button.signal_button_release_event().connect (mem_fun (*this, &GainMeter::meter_release), false);
122         
123
124         top_table.set_col_spacings (2);
125         top_table.set_homogeneous (true);
126         top_table.attach (gain_unit_button, 0, 1, 0, 1);
127
128         Route* r;
129
130         if ((r = dynamic_cast<Route*> (&_io)) != 0) {
131
132                 r->meter_change.connect (mem_fun(*this, &GainMeter::meter_changed));
133                 meter_point_button.add (meter_point_label);
134                 meter_point_button.set_name ("MixerStripMeterPreButton");
135                 meter_point_label.set_name ("MixerStripMeterPreButton");
136                 
137                 switch (r->meter_point()) {
138                 case MeterInput:
139                         meter_point_label.set_text (_("input"));
140                         break;
141                         
142                 case MeterPreFader:
143                         meter_point_label.set_text (_("pre"));
144                         break;
145                         
146                 case MeterPostFader:
147                         meter_point_label.set_text (_("post"));
148                         break;
149                 }
150                 
151                 /* TRANSLATORS: this string should be longest of the strings
152                    used to describe meter points. In english, its "input".
153                 */
154                 
155                 set_size_request_to_display_given_text (meter_point_button, _("tupni"), 2, 2);
156
157
158                 top_table.attach (meter_point_button, 1, 2, 0, 1);
159         }
160         gain_display_box.set_spacing (2);
161         set_size_request_to_display_given_text (gain_display_frame, "-86.0", 2, 2);
162         gain_display_frame.set_shadow_type (Gtk::SHADOW_IN);
163         gain_display_frame.set_name ("BaseFrame");
164         gain_display_frame.add (gain_display);
165         gain_display_box.pack_start (gain_display_frame,  Gtk::PACK_SHRINK);
166
167         peak_display.set_name ("MixerStripPeakDisplay");
168         peak_display.add (peak_display_label);
169         set_size_request_to_display_given_text (peak_display_frame, "-86.0", 2, 2);
170         peak_display_frame.set_shadow_type (Gtk::SHADOW_IN);
171         peak_display_frame.set_name ("BaseFrame");
172         peak_display_frame.add (peak_display);
173         max_peak = minus_infinity();
174         peak_display_label.set_text (_("-inf"));
175
176         gain_display_box.pack_end (peak_display_frame,  Gtk::PACK_SHRINK);
177
178         meter_metric_area.set_size_request (25, -1);
179         meter_metric_area.set_name ("MeterMetricsStrip");
180
181         meter_packer.set_spacing (2);
182
183         hbox.set_spacing (4);
184         hbox.pack_start (*gain_slider, false, false, 2);
185         hbox.pack_start (meter_packer, true, false);
186
187         set_spacing (4);
188
189         pack_start (top_table,  Gtk::PACK_SHRINK);
190         pack_start (gain_display_box,  Gtk::PACK_SHRINK);
191         pack_start (hbox,  Gtk::PACK_SHRINK);
192
193         _io.gain_changed.connect (mem_fun(*this, &GainMeter::gain_changed));
194
195         meter_metric_area.signal_expose_event().connect (mem_fun(*this, &GainMeter::meter_metrics_expose));
196         gain_adjustment.signal_value_changed().connect (mem_fun(*this, &GainMeter::gain_adjusted));
197         peak_display.signal_button_release_event().connect (mem_fun(*this, &GainMeter::peak_button_release));
198
199         _session.MeterHoldChanged.connect (mem_fun(*this, &GainMeter::meter_hold_changed));
200         
201         gain_changed (0);
202         update_gain_sensitive ();
203
204         ResetAllPeakDisplays.connect (mem_fun(*this, &GainMeter::reset_peak_display));
205         ResetGroupPeakDisplays.connect (mem_fun(*this, &GainMeter::reset_group_peak_display));
206 }
207
208 void
209 GainMeter::set_width (Width w)
210 {
211         switch (w) {
212         case Wide:
213                 peak_display_frame.show_all();
214                 break;
215         case Narrow:
216                 peak_display_frame.hide_all();
217                 break;
218         }
219
220         _width = w;
221         setup_meters ();
222 }
223
224 Glib::RefPtr<Gdk::Pixmap>
225 GainMeter::render_metrics (Gtk::Widget& w)
226 {
227         Glib::RefPtr<Gdk::Window> win (w.get_window());
228         Glib::RefPtr<Gdk::GC> fg_gc (w.get_style()->get_fg_gc (Gtk::STATE_NORMAL));
229         Glib::RefPtr<Gdk::GC> bg_gc (w.get_style()->get_bg_gc (Gtk::STATE_NORMAL));
230         gint x, y, width, height, depth;
231         int  db_points[] = { -50, -40, -20, -30, -10, -3, 0, 4 };
232         char buf[32];
233         int theight;
234         int twidth;
235
236         win->get_geometry (x, y, width, height, depth);
237         
238         Glib::RefPtr<Gdk::Pixmap> pixmap = Gdk::Pixmap::create (win, width, height);
239
240         metric_pixmaps[w.get_name()] = pixmap;
241
242         pixmap->draw_rectangle (bg_gc, true, 0, 0, width, height);
243
244         Glib::RefPtr<Pango::Layout> layout = w.create_pango_layout("");
245
246         for (uint32_t i = 0; i < sizeof (db_points)/sizeof (db_points[0]); ++i) {
247
248                 float fraction = log_meter (db_points[i]);
249                 gint pos = height - (gint) floor (height * fraction);
250
251                 snprintf (buf, sizeof (buf), "%d", abs (db_points[i]));
252
253                 layout->set_text (buf);
254                 layout->get_pixel_size (twidth, theight);
255
256                 pixmap->draw_line (fg_gc, 0, pos, 4, pos);
257                 pixmap->draw_layout (fg_gc, 6, pos - (theight/2), layout);
258         }
259
260         return pixmap;
261 }
262
263 gint
264 GainMeter::meter_metrics_expose (GdkEventExpose *ev)
265 {
266         Glib::RefPtr<Gdk::Window> win (meter_metric_area.get_window());
267         Glib::RefPtr<Gdk::GC> fg_gc (meter_metric_area.get_style()->get_fg_gc (Gtk::STATE_NORMAL));
268         Glib::RefPtr<Gdk::GC> bg_gc (meter_metric_area.get_style()->get_bg_gc (Gtk::STATE_NORMAL));
269         GdkRectangle base_rect;
270         GdkRectangle draw_rect;
271         gint x, y, width, height, depth;
272
273         win->get_geometry (x, y, width, height, depth);
274         
275         base_rect.width = width;
276         base_rect.height = height;
277         base_rect.x = 0;
278         base_rect.y = 0;
279
280         Glib::RefPtr<Gdk::Pixmap> pixmap;
281         std::map<string,Glib::RefPtr<Gdk::Pixmap> >::iterator i = metric_pixmaps.find (meter_metric_area.get_name());
282
283         if (i == metric_pixmaps.end()) {
284                 pixmap = render_metrics (meter_metric_area);
285         } else {
286                 pixmap = i->second;
287         }
288
289         gdk_rectangle_intersect (&ev->area, &base_rect, &draw_rect);
290         win->draw_rectangle (bg_gc, true, draw_rect.x, draw_rect.y, draw_rect.width, draw_rect.height);
291         win->draw_drawable (bg_gc, pixmap, draw_rect.x, draw_rect.y, draw_rect.x, draw_rect.y, draw_rect.width, draw_rect.height);
292         
293         return true;
294 }
295
296 GainMeter::~GainMeter ()
297 {
298         if (meter_menu) {
299                 delete meter_menu;
300         }
301
302         for (vector<MeterInfo>::iterator i = meters.begin(); i != meters.end(); i++) {
303                 if ((*i).meter) {
304                         delete (*i).meter;
305                 }
306         }
307 }
308
309 void
310 GainMeter::update_meters ()
311 {
312         vector<MeterInfo>::iterator i;
313         uint32_t n;
314         float peak;
315         char buf[32];
316         
317         for (n = 0, i = meters.begin(); i != meters.end(); ++i, ++n) {
318                 if ((*i).packed) {
319                         peak = _io.peak_input_power (n);
320
321                         if (_session.meter_falloff() == 0.0f || peak > (*i).meter->get_user_level()) {
322                                 (*i).meter->set (log_meter (peak), peak);
323                         }
324
325                         if (peak > max_peak) {
326                                 max_peak = peak;
327                                 /* set peak display */
328                                 snprintf (buf, sizeof(buf), "%.1f", max_peak);
329                                 peak_display_label.set_text (buf);
330
331                                 if (max_peak >= 0.0f) {
332                                         peak_display.set_name ("MixerStripPeakDisplayPeak");
333                                 }
334                         }
335                 }
336         }
337
338 }
339
340 void
341 GainMeter::update_meters_falloff ()
342 {
343         vector<MeterInfo>::iterator i;
344         uint32_t n;
345         float dbpeak;
346         
347         for (n = 0, i = meters.begin(); i != meters.end(); ++i, ++n) {
348                 if ((*i).packed) {
349                         // just do falloff
350                         //peak = (*i).meter->get_level() * _falloff_rate;
351                         dbpeak = (*i).meter->get_user_level() - _session.meter_falloff();
352
353                         dbpeak = std::max(dbpeak, -200.0f);
354                         
355                         // cerr << "tmplevel: " << tmplevel << endl;
356                         (*i).meter->set (log_meter (dbpeak), dbpeak);
357                 }
358         }
359         
360 }
361
362
363 void
364 GainMeter::meter_hold_changed()
365 {
366         ENSURE_GUI_THREAD(mem_fun(*this, &GainMeter::meter_hold_changed));
367         
368         vector<MeterInfo>::iterator i;
369         uint32_t n;
370         
371         for (n = 0, i = meters.begin(); i != meters.end(); ++i, ++n) {
372                 
373                 (*i).meter->set_hold_count ((uint32_t) floor(_session.meter_hold()));
374         }
375 }
376
377 void
378 GainMeter::hide_all_meters ()
379 {
380         bool remove_metric_area = false;
381
382         for (vector<MeterInfo>::iterator i = meters.begin(); i != meters.end(); ++i) {
383                 if ((*i).packed) {
384                         remove_metric_area = true;
385                         meter_packer.remove (*((*i).meter));
386                         (*i).packed = false;
387                 }
388         }
389
390         if (remove_metric_area) {
391                 if (meter_metric_area.get_parent()) {
392                         meter_packer.remove (meter_metric_area);
393                 }
394         }
395 }
396
397 void
398 GainMeter::setup_meters ()
399 {
400         uint32_t nmeters = _io.n_outputs();
401         guint16 width;
402
403         hide_all_meters ();
404
405         Route* r;
406
407         if ((r = dynamic_cast<Route*> (&_io)) != 0) {
408
409                 switch (r->meter_point()) {
410                 case MeterPreFader:
411                 case MeterInput:
412                         nmeters = r->n_inputs();
413                         break;
414                 case MeterPostFader:
415                         nmeters = r->n_outputs();
416                         break;
417                 }
418
419         } else {
420
421                 nmeters = _io.n_outputs();
422
423         }
424
425         if (nmeters == 0) {
426                 return;
427         }
428
429         if (nmeters <= 2) {
430                 width = regular_meter_width;
431         } else {
432                 width = thin_meter_width;
433         }
434
435         while (meters.size() < nmeters) {
436                 meters.push_back (MeterInfo());
437         }
438
439         for (uint32_t n = 0; n < nmeters; ++n) {
440                 if (meters[n].width != width) {
441                         delete meters[n].meter;
442                         meters[n].meter = new FastMeter ((uint32_t) floor (_session.meter_hold()), width, FastMeter::Vertical);
443                         meters[n].width = width;
444
445                         meters[n].meter->add_events (Gdk::BUTTON_RELEASE_MASK);
446                         meters[n].meter->signal_button_release_event().connect (bind (mem_fun(*this, &GainMeter::meter_button_release), n));
447                 }
448
449                 meter_packer.pack_start (*meters[n].meter, Gtk::PACK_SHRINK);
450                 meters[n].meter->show_all ();
451                 meters[n].packed = true;
452         }
453
454         if (_width == Wide) {
455                 meter_packer.pack_start (meter_metric_area, Gtk::PACK_SHRINK);
456                 meter_metric_area.show_all ();
457         }
458 }       
459
460 gint
461 GainMeter::peak_button_release (GdkEventButton* ev)
462 {
463         /* reset peak label */
464
465         if (ev->button == 1 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control|Keyboard::Shift)) {
466                 ResetAllPeakDisplays ();
467         } else if (ev->button == 1 && Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
468                 Route* r;
469                 if ((r = dynamic_cast<Route*> (&_io)) != 0) {
470                         ResetGroupPeakDisplays (r->mix_group());
471                 }
472         } else {
473                 reset_peak_display ();
474         }
475         return TRUE;
476 }
477
478 void
479 GainMeter::reset_peak_display ()
480 {
481         max_peak = minus_infinity();
482         peak_display_label.set_text (_("-inf"));
483         peak_display.set_name ("MixerStripPeakDisplay");
484 }
485
486 void
487 GainMeter::reset_group_peak_display (RouteGroup* group)
488 {
489         Route* r;
490         if ((r = dynamic_cast<Route*> (&_io)) != 0) {
491                 if (group == r->mix_group()) {
492                         reset_peak_display ();
493                 }
494         }
495 }
496
497 gint
498 GainMeter::meter_button_release (GdkEventButton* ev, uint32_t which)
499 {
500         switch (ev->button) {
501         case 1:
502                 meters[which].meter->clear();
503                 max_peak = minus_infinity();
504                 peak_display_label.set_text (_("-inf"));
505                 peak_display.set_name ("MixerStripPeakDisplay");
506                 break;
507
508         case 3:
509                 // popup_meter_menu (ev);
510                 break;
511         };
512
513         return TRUE;
514 }
515
516 void
517 GainMeter::popup_meter_menu (GdkEventButton *ev)
518 {
519         using namespace Menu_Helpers;
520
521         if (meter_menu == 0) {
522                 meter_menu = new Gtk::Menu;
523                 MenuList& items = meter_menu->items();
524
525                 items.push_back (MenuElem ("-inf .. +0dBFS"));
526                 items.push_back (MenuElem ("-10dB .. +0dBFS"));
527                 items.push_back (MenuElem ("-4 .. +0dBFS"));
528                 items.push_back (SeparatorElem());
529                 items.push_back (MenuElem ("-inf .. -2dBFS"));
530                 items.push_back (MenuElem ("-10dB .. -2dBFS"));
531                 items.push_back (MenuElem ("-4 .. -2dBFS"));
532         }
533
534         meter_menu->popup (1, ev->time);
535 }
536
537 void
538 GainMeter::_gain_printer (char buf[32], Gtk::Adjustment& adj, void *arg)
539 {
540         static_cast<GainMeter *>(arg)->gain_printer (buf, adj);
541 }
542
543 void
544 GainMeter::gain_printer (char buf[32], Gtk::Adjustment& adj)
545 {
546         float v = adj.get_value();
547
548         if (v == 0.0) {
549                 strcpy (buf, _("-inf"));
550         } else {
551                 snprintf (buf, 32, "%.1f", coefficient_to_dB (slider_position_to_gain (v)));
552         }
553 }
554
555 void
556 GainMeter::gain_adjusted ()
557 {
558         if (!ignore_toggle) {
559                 _io.set_gain (slider_position_to_gain (gain_adjustment.get_value()), this);
560         }
561 }
562
563 void
564 GainMeter::effective_gain_display ()
565 {
566         gfloat value = gain_to_slider_position (_io.effective_gain());
567         
568         if (gain_adjustment.get_value() != value) {
569                 ignore_toggle = true;
570                 gain_adjustment.set_value (value);
571                 ignore_toggle = false;
572         }
573 }
574
575 void
576 GainMeter::gain_changed (void *src)
577 {
578         Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &GainMeter::effective_gain_display));
579 }
580
581 void
582 GainMeter::set_meter_strip_name (const char * name)
583 {
584         meter_metric_area.set_name (name);
585 }
586
587 void
588 GainMeter::set_fader_name (const char * name)
589 {
590         gain_slider->set_name (name);
591 }
592
593 void
594 GainMeter::update_gain_sensitive ()
595 {
596         static_cast<Gtkmm2ext::SliderController*>(gain_slider)->set_sensitive (!(_io.gain_automation_state() & Play));
597 }
598
599
600 static MeterPoint
601 next_meter_point (MeterPoint mp)
602 {
603         switch (mp) {
604         case MeterInput:
605                 return MeterPreFader;
606                 break;
607                 
608         case MeterPreFader:
609                 return MeterPostFader;
610                 break;
611                 
612         case MeterPostFader:
613                 return MeterInput;
614                 break;
615         }
616         /*NOTREACHED*/
617         return MeterInput;
618 }
619
620 gint
621 GainMeter::meter_press(GdkEventButton* ev)
622 {
623         Route* _route;
624
625         wait_for_release = false;
626
627         if ((_route = dynamic_cast<Route*>(&_io)) == 0) {
628                 return FALSE;
629         }
630
631         if (!ignore_toggle) {
632
633                 if (Keyboard::is_context_menu_event (ev)) {
634                         
635                         // no menu at this time.
636
637                 } else {
638
639                         if (ev->button == 2) {
640
641                                 // ctrl-button2 click is the midi binding click
642                                 // button2-click is "momentary"
643                                 
644                                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control))) {
645                                         wait_for_release = true;
646                                         old_meter_point = _route->meter_point ();
647                                 }
648                         }
649
650                         if (ev->button == 1 || ev->button == 2) {
651
652                                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::Control|Keyboard::Shift))) {
653
654                                         /* ctrl-shift-click applies change to all routes */
655
656                                         _session.foreach_route (this, &GainMeter::set_meter_point, next_meter_point (_route->meter_point()));
657                                         
658                                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::Control)) {
659
660                                         /* ctrl-click: solo mix group.
661                                            ctrl-button2 is MIDI learn.
662                                         */
663                                         
664                                         if (ev->button == 1) {
665                                                 set_mix_group_meter_point (*_route, next_meter_point (_route->meter_point()));
666                                         }
667                                         
668                                 } else {
669                                         
670                                         /* click: solo this route */
671                                         
672                                         _route->set_meter_point (next_meter_point (_route->meter_point()), this);
673                                 }
674                         }
675                 }
676         }
677
678         return true;
679
680 }
681
682 gint
683 GainMeter::meter_release(GdkEventButton* ev)
684 {
685
686         if(!ignore_toggle){
687                 if (wait_for_release){
688                         wait_for_release = false;
689                         set_meter_point (*(dynamic_cast<Route*>(&_io)), old_meter_point);
690                 }
691         }
692         return true;
693 }
694
695 void
696 GainMeter::set_meter_point (Route& route, MeterPoint mp)
697 {
698         route.set_meter_point (mp, this);
699 }
700
701 void
702 GainMeter::set_mix_group_meter_point (Route& route, MeterPoint mp)
703 {
704         RouteGroup* mix_group;
705
706         if((mix_group = route.mix_group()) != 0){
707                 mix_group->apply (&Route::set_meter_point, mp, this);
708         } else {
709                 route.set_meter_point (mp, this);
710         }
711 }
712
713 void
714 GainMeter::meter_changed (void *src)
715 {
716         Route* r;
717
718         ENSURE_GUI_THREAD (bind (mem_fun(*this, &GainMeter::meter_changed), src));
719
720         if ((r = dynamic_cast<Route*> (&_io)) != 0) {
721
722                 switch (r->meter_point()) {
723                 case MeterInput:
724                         meter_point_label.set_text (_("input"));
725                         break;
726                         
727                 case MeterPreFader:
728                         meter_point_label.set_text (_("pre"));
729                         break;
730                         
731                 case MeterPostFader:
732                         meter_point_label.set_text (_("post"));
733                         break;
734                 }
735
736                 setup_meters ();
737         }
738 }
739
740 void
741 GainMeter::meter_point_clicked ()
742 {
743         Route* r;
744
745         if ((r = dynamic_cast<Route*> (&_io)) != 0) {
746
747         }
748 }
749
750 gint
751 GainMeter::start_gain_touch (GdkEventButton* ev)
752 {
753         _io.start_gain_touch ();
754         return FALSE;
755 }
756
757 gint
758 GainMeter::end_gain_touch (GdkEventButton* ev)
759 {
760         _io.end_gain_touch ();
761         return FALSE;
762 }