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