how about that ... a monitor/main section .. GUI is still unfinished .. several small...
[ardour.git] / gtk2_ardour / monitor_section.cc
1 #include <gdkmm/pixbuf.h>
2
3 #include "pbd/compose.h"
4 #include "pbd/error.h"
5
6 #include "gtkmm2ext/bindable_button.h"
7 #include "gtkmm2ext/tearoff.h"
8 #include "gtkmm2ext/actions.h"
9
10 #include "ardour/dB.h"
11 #include "ardour/monitor_processor.h"
12 #include "ardour/route.h"
13 #include "ardour/utils.h"
14
15 #include "monitor_section.h"
16 #include "utils.h"
17 #include "volume_controller.h"
18
19 #include "i18n.h"
20
21 using namespace ARDOUR;
22 using namespace Gtk;
23 using namespace Gtkmm2ext;
24 using namespace PBD;
25 using namespace std;
26
27 Glib::RefPtr<ActionGroup> MonitorSection::monitor_actions;
28 Glib::RefPtr<Gdk::Pixbuf> MonitorSection::big_knob_pixbuf;
29 Glib::RefPtr<Gdk::Pixbuf> MonitorSection::little_knob_pixbuf;
30
31 MonitorSection::MonitorSection (Session* s)
32         : AxisView (s)
33         , RouteUI (s)
34         , main_table (2, 3)
35         , meter (s)
36         , _tearoff (0)
37         , gain_adjustment (1.0, 0.0, 2.0, 0.01, 0.1)
38         , gain_control (0)
39         , dim_adjustment (0.2, 0.0, 1.0, 0.01, 0.1) 
40         , dim_control (0)
41         , solo_boost_adjustment (1.0, 1.0, 2.0, 0.01, 0.1) 
42         , solo_boost_control (0)
43         , solo_in_place_button (solo_model_group, _("SiP"))
44         , afl_button (solo_model_group, _("AFL"))
45         , pfl_button (solo_model_group, _("PFL"))
46         , cut_all_button (_("MUTE"))
47         , dim_all_button (_("DIM"))
48
49 {
50         Glib::RefPtr<Action> act;
51
52         if (!monitor_actions) {
53
54                 /* do some static stuff */
55
56                 register_actions ();
57
58         }
59         
60         _route = _session->control_out ();
61
62         if (!_route) {
63                 throw failed_constructor ();
64         }
65
66         _monitor = _route->monitor_control ();
67
68         if (!_monitor) {
69                 throw failed_constructor ();
70         }
71
72         HBox* sub_knob_packer = manage (new HBox);
73         sub_knob_packer->set_spacing (12);
74
75         VBox* spin_packer;
76         Label* spin_label;
77
78         gain_control = new VolumeController (big_knob_pixbuf, &gain_adjustment, true);
79         gain_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MonitorSection::gain_value_changed));
80         gain_control->spinner().signal_output().connect (sigc::bind (sigc::mem_fun (*this, &MonitorSection::nonlinear_gain_printer), 
81                                                                      &gain_control->spinner()));
82
83         HBox* center_gain_control = manage (new HBox);
84         center_gain_control->pack_start (*gain_control, true, true);
85
86         spin_label = manage (new Label (_("Gain (dB)")));
87         spin_packer = manage (new VBox);
88         spin_packer->set_spacing (6);
89         spin_packer->pack_start (*center_gain_control, false, false);
90         spin_packer->pack_start (*spin_label, false, false);
91
92         knob_packer.pack_start (*spin_packer, false, false);
93                 
94         dim_control = new VolumeController (little_knob_pixbuf, &dim_adjustment, true, 30, 30);
95         dim_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MonitorSection::dim_level_changed));
96         dim_control->spinner().signal_output().connect (sigc::bind (sigc::mem_fun (*this, &MonitorSection::linear_gain_printer), 
97                                                                     &dim_control->spinner()));
98
99         spin_label = manage (new Label (_("Dim Cut (dB)")));
100         spin_packer = manage (new VBox);
101         spin_packer->set_spacing (6);
102         spin_packer->pack_start (*dim_control, false, false);
103         spin_packer->pack_start (*spin_label, false, false); 
104
105         sub_knob_packer->pack_start (*spin_packer, false, true);
106
107         solo_boost_control = new VolumeController (little_knob_pixbuf, &solo_boost_adjustment, true, 30, 30);
108         solo_boost_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MonitorSection::solo_boost_changed));
109         solo_boost_control->spinner().signal_output().connect (sigc::bind (sigc::mem_fun (*this, &MonitorSection::linear_gain_printer),
110                                                                            &solo_boost_control->spinner()));
111
112         spin_label = manage (new Label (_("Solo Boost (dB)")));
113         spin_packer = manage (new VBox);
114         spin_packer->set_spacing (6);
115         spin_packer->pack_start (*solo_boost_control, false, false);
116         spin_packer->pack_start (*spin_label, false, false); 
117
118         sub_knob_packer->pack_start (*spin_packer, false, true);
119
120         knob_packer.pack_start (*sub_knob_packer, false, true);
121
122         sub_knob_packer->show ();
123         knob_packer.show ();
124         gain_control->show_all ();
125         dim_control->show_all ();
126         solo_boost_control->show_all ();
127
128         meter.set_meter (&_route->peak_meter());
129         meter.setup_meters (300, 5);
130         
131         table_knob_packer.pack_start (main_table, true, true);
132         table_knob_packer.pack_start (knob_packer, false, false);
133
134         table_knob_packer.show ();
135
136         solo_model_box.set_spacing (12);
137         solo_model_box.pack_start (solo_in_place_button, false, false);
138         solo_model_box.pack_start (afl_button, false, false);
139         solo_model_box.pack_start (pfl_button, false, false);
140
141         solo_in_place_button.show ();
142         afl_button.show ();
143         pfl_button.show ();
144         solo_model_box.show ();
145
146         upper_packer.pack_start (solo_model_box, false, false);
147
148         act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
149         if (act) {
150                 act->connect_proxy (cut_all_button);
151         } 
152
153         act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
154         if (act) {
155                 act->connect_proxy (dim_all_button);
156         } 
157
158         cut_all_button.show ();
159         dim_all_button.show ();
160
161         lower_packer.pack_start (dim_all_button, false, false);
162         lower_packer.pack_start (cut_all_button, false, false);
163
164         vpacker.set_border_width (12);
165         vpacker.set_spacing (12);
166         vpacker.pack_start (upper_packer, false, false);
167         vpacker.pack_start (table_knob_packer, false, false);
168         vpacker.pack_start (lower_packer, false, false);
169
170         VBox* keep_meter_under_control = manage (new VBox);
171         keep_meter_under_control->pack_start (meter, false, false);
172         keep_meter_under_control->show ();
173
174         hpacker.set_border_width (12);
175         hpacker.set_spacing (12);
176         hpacker.pack_start (*keep_meter_under_control, false, false);
177         hpacker.pack_start (vpacker, true, true);
178
179         main_table.show ();
180         hpacker.show ();
181         upper_packer.show ();
182         lower_packer.show ();
183         vpacker.show ();
184         meter.show_all ();
185
186         populate_buttons ();
187         map_state ();
188
189         _tearoff = new TearOff (hpacker);
190         /* if torn off, make this a normal window */
191         _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
192         _tearoff->tearoff_window().set_title (X_("Monitor"));
193 }
194
195 MonitorSection::~MonitorSection ()
196 {
197         delete _tearoff;
198         delete gain_control;
199         delete dim_control;
200         delete solo_boost_control;
201 }
202
203 void
204 MonitorSection::populate_buttons ()
205 {
206         Glib::RefPtr<Action> act;
207         uint32_t nchans = _route->monitor_control()->output_streams().n_audio();
208         
209         main_table.resize (nchans+1, 5);
210         main_table.set_col_spacings (6);
211         main_table.set_row_spacings (6);
212         main_table.set_homogeneous (true);
213
214         Label* l1 = manage (new Label (X_("out")));
215         main_table.attach (*l1, 0, 1, 0, 1, SHRINK|FILL, SHRINK|FILL);
216         l1 = manage (new Label (X_("cut")));
217         main_table.attach (*l1, 1, 2, 0, 1, SHRINK|FILL, SHRINK|FILL);
218         l1 = manage (new Label (X_("dim")));
219         main_table.attach (*l1, 2, 3, 0, 1, SHRINK|FILL, SHRINK|FILL);
220         l1 = manage (new Label (X_("solo")));
221         main_table.attach (*l1, 3, 4, 0, 1, SHRINK|FILL, SHRINK|FILL);
222         l1 = manage (new Label (X_("inv")));
223         main_table.attach (*l1, 4, 5, 0, 1, SHRINK|FILL, SHRINK|FILL);
224
225 #if 0
226         /* the "all" buttons for cut & dim */
227         
228         Label *la = manage (new Label (X_("all")));
229         main_table.attach (*la, 0, 1, 1, 2, SHRINK|FILL, SHRINK|FILL);
230
231
232         /* cut all */
233
234         BindableToggleButton* ca = manage (new BindableToggleButton (X_("")));
235         ca->set_name (X_("MixerMuteButton"));
236         gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (ca->gobj()), false);
237         main_table.attach (*ca, 1, 2, 1, 2, SHRINK|FILL, SHRINK|FILL);
238
239         act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
240         if (act) {
241                 act->connect_proxy (*ca);
242         } 
243
244         /* dim all */
245
246         BindableToggleButton* da = manage (new BindableToggleButton (X_("")));
247         da->set_name (X_("MixerMuteButton"));
248         gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (da->gobj()), false);
249         main_table.attach (*da, 2, 3, 1, 2, SHRINK|FILL, SHRINK|FILL);
250
251         act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
252         if (act) {
253                 act->connect_proxy (*da);
254         } 
255
256         uint32_t row_offset = 2;
257 #else
258         uint32_t row_offset = 1;
259 #endif
260
261         for (uint32_t i = 0; i < nchans; ++i) {
262                 
263                 string l;
264                 char buf[64];
265
266                 if (nchans == 2) {
267                         if (i == 0) {
268                                 l = "L";
269                         } else {
270                                 l = "R";
271                         }
272                 } else {
273                         char buf[32];
274                         snprintf (buf, sizeof (buf), "%d", i+1);
275                         l = buf;
276                 }
277
278                 Label* c1 = manage (new Label (l));
279                 main_table.attach (*c1, 0, 1, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
280                 
281                 /* Cut */
282
283                 BindableToggleButton* c2 = manage (new BindableToggleButton (X_("")));
284                 c2->set_name (X_("MixerMuteButton"));
285                 gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (c2->gobj()), false);
286                 main_table.attach (*c2, 1, 2, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
287                 
288                 snprintf (buf, sizeof (buf), "monitor-cut-%u", i+1);
289                 act = ActionManager::get_action (X_("Monitor"), buf);
290                 if (act) {
291                         act->connect_proxy (*c2);
292                 } 
293
294                 /* Dim */
295
296                 BindableToggleButton* c3 = manage (new BindableToggleButton (X_("")));
297                 c3->set_name (X_("MixerMuteButton"));
298                 gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (c2->gobj()), false);
299                 main_table.attach (*c3, 2, 3, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
300
301                 snprintf (buf, sizeof (buf), "monitor-dim-%u", i+1);
302                 act = ActionManager::get_action (X_("Monitor"), buf);
303                 if (act) {
304                         act->connect_proxy (*c3);
305                 }
306
307                 /* Solo */
308
309                 BindableToggleButton* c4 = manage (new BindableToggleButton (X_("")));
310                 c4->set_name (X_("MixerSoloButton"));
311                 gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (c2->gobj()), false);
312                 main_table.attach (*c4, 3, 4, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
313
314                 snprintf (buf, sizeof (buf), "monitor-solo-%u", i+1);
315                 act = ActionManager::get_action (X_("Monitor"), buf);
316                 if (act) {
317                         act->connect_proxy (*c4);
318                 }
319
320                 /* Invert (Polarity/Phase) */
321
322                 BindableToggleButton* c5 = manage (new BindableToggleButton (X_("")));
323                 c5->set_name (X_("MixerPhaseInvertButton"));
324                 gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (c2->gobj()), false);
325                 main_table.attach (*c5, 4, 5, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
326
327                 snprintf (buf, sizeof (buf), "monitor-invert-%u", i+1);
328                 act = ActionManager::get_action (X_("Monitor"), buf);
329                 if (act) {
330                         act->connect_proxy (*c5);
331                 }
332
333         }
334
335         main_table.show_all ();
336 }
337
338 void 
339 MonitorSection::set_button_names ()
340 {
341         rec_enable_button_label.set_text ("rec");
342         mute_button_label.set_text ("rec");
343         solo_button_label.set_text ("rec");
344 }
345
346 Widget&
347 MonitorSection::pack_widget () const
348 {
349         return *_tearoff;
350 }
351
352 void
353 MonitorSection::dim_all ()
354 {
355         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
356         if (act) {
357                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
358                 _monitor->set_dim_all (tact->get_active());
359         }
360
361 }
362
363 void
364 MonitorSection::cut_all ()
365 {
366         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
367         if (act) {
368                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
369                 _monitor->set_cut_all (tact->get_active());
370         }
371 }
372
373 void
374 MonitorSection::cut_channel (uint32_t chn)
375 {
376         char buf[64];
377         snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
378
379         --chn; // 0-based in backend
380
381         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
382         if (act) {
383                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
384                 _monitor->set_cut (chn, tact->get_active());
385         }
386 }
387
388 void
389 MonitorSection::dim_channel (uint32_t chn)
390 {
391         char buf[64];
392         snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
393
394         --chn; // 0-based in backend
395
396         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
397         if (act) {
398                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
399                 _monitor->set_dim (chn, tact->get_active());
400         }
401
402 }
403
404 void
405 MonitorSection::solo_channel (uint32_t chn)
406 {
407         char buf[64];
408         snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
409
410         --chn; // 0-based in backend
411
412         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
413         if (act) {
414                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
415                 _monitor->set_solo (chn, tact->get_active());
416         }
417
418 }
419
420 void
421 MonitorSection::invert_channel (uint32_t chn)
422 {
423         char buf[64];
424
425         --chn; // 0-based in backend
426
427         snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
428         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
429         if (act) {
430                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
431                 _monitor->set_polarity (chn, tact->get_active());
432         }
433
434 }
435
436 void
437 MonitorSection::register_actions ()
438 {
439         string action_name;
440         string action_descr;
441
442         monitor_actions = ActionGroup::create (X_("Monitor"));
443         ActionManager::add_action_group (monitor_actions);
444
445         ActionManager::register_toggle_action (monitor_actions, "monitor-cut-all", "", 
446                                                sigc::mem_fun (*this, &MonitorSection::cut_all));
447
448         ActionManager::register_toggle_action (monitor_actions, "monitor-dim-all", "", 
449                                                sigc::mem_fun (*this, &MonitorSection::dim_all));
450
451         /* note the 1-based counting for naming vs. 0-based for action */
452
453         for (uint32_t chn = 1; chn <= 16; ++chn) {
454
455                 /* for the time being, do not use the action description because it always
456                    shows up in the buttons, which is undesirable.
457                 */
458
459                 action_name = string_compose (X_("monitor-cut-%1"), chn);
460                 action_descr = string_compose (_("Cut Monitor Chn %1"), chn);
461                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", 
462                                                        sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
463
464                 action_name = string_compose (X_("monitor-dim-%1"), chn);
465                 action_descr = string_compose (_("Dim Monitor Chn %1"), chn+1);
466                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", 
467                                                        sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
468
469                 action_name = string_compose (X_("monitor-solo-%1"), chn);
470                 action_descr = string_compose (_("Solo Monitor Chn %1"), chn);
471                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", 
472                                                        sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
473
474                 action_name = string_compose (X_("monitor-invert-%1"), chn);
475                 action_descr = string_compose (_("Invert Monitor Chn %1"), chn);
476                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", 
477                                                        sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
478
479         }
480 }
481
482 void
483 MonitorSection::fast_update ()
484 {
485         meter.update_meters ();
486 }
487
488 void
489 MonitorSection::setup_knob_images ()
490 {
491         try {
492                 
493                 big_knob_pixbuf = ::get_icon ("knob");
494                 
495         }  catch (...) {
496                 
497                 error << "No knob image found (or not loadable) at "
498                       << " .... "
499                       << endmsg;
500                 throw failed_constructor ();
501         }
502         
503         try {
504                 
505                 little_knob_pixbuf = ::get_icon ("littleknob");
506                 
507         }  catch (...) {
508                 
509                 error << "No knob image found (or not loadable) at "
510                       << " .... "
511                       << endmsg;
512                 throw failed_constructor ();
513         }
514 }
515
516 void
517 MonitorSection::gain_value_changed ()
518 {
519         _route->set_gain (slider_position_to_gain (gain_adjustment.get_value()), this);
520 }
521
522 void
523 MonitorSection::dim_level_changed ()
524 {
525         _monitor->set_dim_level (dim_adjustment.get_value());
526 }
527
528 void
529 MonitorSection::solo_boost_changed ()
530 {
531         _monitor->set_solo_boost_level (solo_boost_adjustment.get_value());
532 }
533
534 bool
535 MonitorSection::nonlinear_gain_printer (SpinButton* button)
536 {
537         double val = button->get_adjustment()->get_value();
538         char buf[16];
539         snprintf (buf, sizeof (buf), "%.1f", accurate_coefficient_to_dB (slider_position_to_gain (val)));
540         button->set_text (buf);
541         return true;
542 }
543
544 bool
545 MonitorSection::linear_gain_printer (SpinButton* button)
546 {
547         double val = button->get_adjustment()->get_value();
548         char buf[16];
549         snprintf (buf, sizeof (buf), "%.1f", accurate_coefficient_to_dB (val));
550         button->set_text (buf);
551         return true;
552 }
553
554 void
555 MonitorSection::map_state ()
556 {
557         cerr << "route gain = " << _route->gain_control()->get_value() << endl;
558
559         gain_control->get_adjustment()->set_value (gain_to_slider_position (_route->gain_control()->get_value()));
560         dim_control->get_adjustment()->set_value (_monitor->dim_level());
561         solo_boost_control->get_adjustment()->set_value (_monitor->solo_boost_level());
562 }