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