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