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