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