2 Copyright (C) 2012 Paul Davis
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.
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.
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.
20 #include <gdkmm/pixbuf.h>
22 #include "pbd/compose.h"
23 #include "pbd/error.h"
24 #include "pbd/replace_all.h"
26 #include "gtkmm2ext/bindable_button.h"
27 #include "gtkmm2ext/tearoff.h"
28 #include "gtkmm2ext/actions.h"
29 #include "gtkmm2ext/motionfeedback.h"
31 #include <gtkmm/menu.h>
32 #include <gtkmm/menuitem.h>
34 #include "ardour/audioengine.h"
35 #include "ardour/monitor_processor.h"
36 #include "ardour/port.h"
37 #include "ardour/route.h"
39 #include "ardour_ui.h"
40 #include "gui_thread.h"
41 #include "monitor_section.h"
42 #include "public_editor.h"
44 #include "volume_controller.h"
49 using namespace ARDOUR;
50 using namespace ARDOUR_UI_UTILS;
52 using namespace Gtkmm2ext;
56 Glib::RefPtr<ActionGroup> MonitorSection::monitor_actions;
58 MonitorSection::MonitorSection (Session* s)
62 , channel_table_viewport (*channel_table_scroller.get_hadjustment()
63 , *channel_table_scroller.get_vadjustment ())
66 , solo_boost_control (0)
67 , solo_cut_control (0)
70 , solo_boost_display (0)
71 , solo_cut_display (0)
72 , solo_in_place_button (_("SiP"), ArdourButton::led_default_elements)
73 , afl_button (_("AFL"), ArdourButton::led_default_elements)
74 , pfl_button (_("PFL"), ArdourButton::led_default_elements)
75 , exclusive_solo_button (ArdourButton::led_default_elements)
76 , solo_mute_override_button (ArdourButton::led_default_elements)
77 , _inhibit_solo_model_update (false)
80 using namespace Menu_Helpers;
82 Glib::RefPtr<Action> act;
84 if (!monitor_actions) {
86 /* do some static stuff */
99 rude_solo_button.set_text (_("Soloing"));
100 rude_solo_button.set_name ("rude solo");
101 rude_solo_button.show ();
103 rude_iso_button.set_text (_("Isolated"));
104 rude_iso_button.set_name ("rude isolate");
105 rude_iso_button.show ();
107 rude_audition_button.set_text (_("Auditioning"));
108 rude_audition_button.set_name ("rude audition");
109 rude_audition_button.show ();
111 Timers::blink_connect (sigc::mem_fun (*this, &MonitorSection::do_blink));
113 rude_solo_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_solo), false);
114 UI::instance()->set_tip (rude_solo_button, _("When active, something is soloed.\nClick to de-solo everything"));
116 rude_iso_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_isolate), false);
117 UI::instance()->set_tip (rude_iso_button, _("When active, something is solo-isolated.\nClick to de-isolate everything"));
119 rude_audition_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_audition), false);
120 UI::instance()->set_tip (rude_audition_button, _("When active, auditioning is active.\nClick to stop the audition"));
122 solo_in_place_button.set_name ("monitor section solo model");
123 afl_button.set_name ("monitor section solo model");
124 pfl_button.set_name ("monitor section solo model");
126 solo_model_box.set_spacing (6);
127 solo_model_box.pack_start (solo_in_place_button, true, false);
128 solo_model_box.pack_start (afl_button, true, false);
129 solo_model_box.pack_start (pfl_button, true, false);
131 solo_in_place_button.show ();
134 solo_model_box.show ();
136 act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
137 ARDOUR_UI::instance()->tooltips().set_tip (solo_in_place_button, _("Solo controls affect solo-in-place"));
139 solo_in_place_button.set_related_action (act);
142 act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
143 ARDOUR_UI::instance()->tooltips().set_tip (afl_button, _("Solo controls toggle after-fader-listen"));
145 afl_button.set_related_action (act);
148 act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
149 ARDOUR_UI::instance()->tooltips().set_tip (pfl_button, _("Solo controls toggle pre-fader-listen"));
151 pfl_button.set_related_action (act);
156 solo_boost_control = new ArdourKnob ();
157 solo_boost_control->set_name("monitor knob");
158 solo_boost_control->set_size_request(40,40);
159 ARDOUR_UI::instance()->tooltips().set_tip (*solo_boost_control, _("Gain increase for soloed signals (0dB is normal)"));
161 solo_boost_display = new ArdourDisplay ();
162 solo_boost_display->set_name("monitor section cut");
163 solo_boost_display->set_size_request(80,20);
164 solo_boost_display->add_controllable_preset(_("0 dB"), 0.0);
165 solo_boost_display->add_controllable_preset(_("3 dB"), 3.0);
166 solo_boost_display->add_controllable_preset(_("6 dB"), 6.0);
167 solo_boost_display->add_controllable_preset(_("10 dB"), 10.0);
169 HBox* solo_packer = manage (new HBox);
170 solo_packer->set_spacing (6);
171 solo_packer->show ();
173 spin_label = manage (new Label (_("Solo Boost")));
174 spin_packer = manage (new VBox);
175 spin_packer->show ();
176 spin_packer->set_spacing (3);
177 spin_packer->pack_start (*spin_label, false, false);
178 spin_packer->pack_start (*solo_boost_control, false, false);
179 spin_packer->pack_start (*solo_boost_display, false, false);
181 solo_packer->pack_start (*spin_packer, true, false);
185 solo_cut_control = new ArdourKnob ();
186 solo_cut_control->set_name ("monitor knob");
187 solo_cut_control->set_size_request (40,40);
188 ARDOUR_UI::instance()->tooltips().set_tip (*solo_cut_control, _("Gain reduction non-soloed signals\nA value above -inf dB causes \"solo-in-front\""));
190 solo_cut_display = new ArdourDisplay ();
191 solo_cut_display->set_name("monitor section cut");
192 solo_cut_display->set_size_request(80,20);
193 solo_cut_display->add_controllable_preset(_("0 dB"), 0.0);
194 solo_cut_display->add_controllable_preset(_("-6 dB"), -6.0);
195 solo_cut_display->add_controllable_preset(_("-12 dB"), -12.0);
196 solo_cut_display->add_controllable_preset(_("-20 dB"), -20.0);
197 solo_cut_display->add_controllable_preset(_("OFF"), -1200.0);
199 spin_label = manage (new Label (_("SiP Cut")));
200 spin_packer = manage (new VBox);
201 spin_packer->show ();
202 spin_packer->set_spacing (3);
203 spin_packer->pack_start (*spin_label, false, false);
204 spin_packer->pack_start (*solo_cut_control, false, false);
205 spin_packer->pack_start (*solo_cut_display, false, false);
207 solo_packer->pack_start (*spin_packer, true, false);
211 dim_control = new ArdourKnob ();
212 dim_control->set_name ("monitor knob");
213 dim_control->set_size_request (40,40);
214 ARDOUR_UI::instance()->tooltips().set_tip (*dim_control, _("Gain reduction to use when dimming monitor outputs"));
216 dim_display = new ArdourDisplay ();
217 dim_display->set_name("monitor section cut");
218 dim_display->set_size_request(80,20);
219 dim_display->add_controllable_preset(_("0 dB"), 0.0);
220 dim_display->add_controllable_preset(_("-3 dB"), -3.0);
221 dim_display->add_controllable_preset(_("-6 dB"), -6.0);
222 dim_display->add_controllable_preset(_("-12 dB"), -12.0);
223 dim_display->add_controllable_preset(_("-20 dB"), -20.0);
225 HBox* dim_packer = manage (new HBox);
228 spin_label = manage (new Label (_("Dim")));
229 spin_packer = manage (new VBox);
230 spin_packer->show ();
231 spin_packer->set_spacing (3);
232 spin_packer->pack_start (*spin_label, false, false);
233 spin_packer->pack_start (*dim_control, false, false);
234 spin_packer->pack_start (*dim_display, false, false);
236 dim_packer->pack_start (*spin_packer, true, false);
238 exclusive_solo_button.set_text (_("Excl. Solo"));
239 exclusive_solo_button.set_name (X_("monitor solo exclusive"));
240 ARDOUR_UI::instance()->set_tip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time"));
242 act = ActionManager::get_action (X_("Monitor"), X_("toggle-exclusive-solo"));
244 exclusive_solo_button.set_related_action (act);
247 solo_mute_override_button.set_text (_("Solo » Mute"));
248 solo_mute_override_button.set_name (X_("monitor solo override"));
249 ARDOUR_UI::instance()->set_tip (&solo_mute_override_button, _("If enabled, solo will override mute\n(a soloed & muted track or bus will be audible)"));
251 act = ActionManager::get_action (X_("Monitor"), X_("toggle-mute-overrides-solo"));
253 solo_mute_override_button.set_related_action (act);
256 HBox* solo_opt_box = manage (new HBox);
257 solo_opt_box->set_spacing (12);
258 solo_opt_box->set_homogeneous (true);
259 solo_opt_box->pack_start (exclusive_solo_button);
260 solo_opt_box->pack_start (solo_mute_override_button);
261 solo_opt_box->show ();
263 upper_packer.set_spacing (6);
265 Gtk::HBox* rude_box = manage (new HBox);
266 rude_box->pack_start (rude_solo_button, true, true);
267 rude_box->pack_start (rude_iso_button, true, true);
269 upper_packer.pack_start (*rude_box, false, false);
270 upper_packer.pack_start (rude_audition_button, false, false);
271 upper_packer.pack_start (solo_model_box, false, false, 12);
272 upper_packer.pack_start (*solo_opt_box, false, false);
273 upper_packer.pack_start (*solo_packer, false, false, 12);
275 cut_all_button.set_text (_("Mute"));
276 cut_all_button.set_name ("monitor section cut");
277 cut_all_button.set_name (X_("monitor section cut"));
278 cut_all_button.set_size_request (-1,50);
279 cut_all_button.show ();
281 act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
283 cut_all_button.set_related_action (act);
286 dim_all_button.set_text (_("Dim"));
287 dim_all_button.set_name ("monitor section dim");
288 act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
290 dim_all_button.set_related_action (act);
293 mono_button.set_text (_("Mono"));
294 mono_button.set_name ("monitor section mono");
295 act = ActionManager::get_action (X_("Monitor"), X_("monitor-mono"));
297 mono_button.set_related_action (act);
300 HBox* bbox = manage (new HBox);
302 bbox->set_spacing (12);
303 bbox->pack_start (mono_button, true, true);
304 bbox->pack_start (dim_all_button, true, true);
306 lower_packer.set_spacing (12);
307 lower_packer.pack_start (*bbox, false, false);
308 lower_packer.pack_start (cut_all_button, false, false);
312 gain_control = new ArdourKnob ();
313 gain_control->set_name("monitor knob");
314 gain_control->set_size_request(80,80);
316 gain_display = new ArdourDisplay ();
317 gain_display->set_name("monitor section cut");
318 gain_display->set_size_request(40,20);
319 gain_display->add_controllable_preset(_("0 dB"), 0.0);
320 gain_display->add_controllable_preset(_("-3 dB"), -3.0);
321 gain_display->add_controllable_preset(_("-6 dB"), -6.0);
322 gain_display->add_controllable_preset(_("-12 dB"), -12.0);
323 gain_display->add_controllable_preset(_("-20 dB"), -20.0);
324 gain_display->add_controllable_preset(_("-30 dB"), -30.0);
326 Label* output_label = manage (new Label (_("Output")));
327 output_label->set_name (X_("MonitorSectionLabel"));
329 output_button = new ArdourButton ();
330 output_button->set_text (_("Output"));
331 output_button->set_name (X_("monitor section cut"));
332 output_button->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
333 VBox* out_packer = manage (new VBox);
334 out_packer->set_spacing (6);
335 out_packer->pack_start (*output_label, false, false);
336 out_packer->pack_start (*output_button, false, false);
338 spin_label = manage (new Label (_("Monitor")));
339 spin_packer = manage (new VBox);
340 spin_packer->show ();
341 spin_packer->set_spacing (3);
342 spin_packer->pack_start (*spin_label, false, false);
343 spin_packer->pack_start (*gain_control, false, false);
344 spin_packer->pack_start (*gain_display, false, false);
345 spin_packer->pack_start (*out_packer, false, false, 24);
347 lower_packer.pack_start (*spin_packer, true, true);
349 channel_table_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
350 channel_table_scroller.set_size_request (-1, 150);
351 channel_table_scroller.set_shadow_type (Gtk::SHADOW_NONE);
352 channel_table_scroller.show ();
353 channel_table_scroller.add (channel_table_viewport);
355 channel_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
356 channel_size_group->add_widget (channel_table_header);
357 channel_size_group->add_widget (channel_table);
359 channel_table_header.resize (1, 5);
361 Label* l1 = manage (new Label (X_(" ")));
362 l1->set_name (X_("MonitorSectionLabel"));
363 channel_table_header.attach (*l1, 0, 1, 0, 1, EXPAND|FILL);
365 l1 = manage (new Label (_("Mute")));
366 l1->set_name (X_("MonitorSectionLabel"));
367 channel_table_header.attach (*l1, 1, 2, 0, 1, EXPAND|FILL);
369 l1 = manage (new Label (_("Dim")));
370 l1->set_name (X_("MonitorSectionLabel"));
371 channel_table_header.attach (*l1, 2, 3, 0, 1, EXPAND|FILL);
373 l1 = manage (new Label (_("Solo")));
374 l1->set_name (X_("MonitorSectionLabel"));
375 channel_table_header.attach (*l1, 3, 4, 0, 1, EXPAND|FILL);
377 l1 = manage (new Label (_("Inv")));
378 l1->set_name (X_("MonitorSectionLabel"));
379 channel_table_header.attach (*l1, 4, 5, 0, 1, EXPAND|FILL);
381 channel_table_header.show ();
383 table_hpacker.pack_start (channel_table, true, true);
385 /* note that we don't pack the table_hpacker till later
388 vpacker.set_border_width (6);
389 vpacker.set_spacing (12);
390 vpacker.pack_start (upper_packer, false, false);
391 vpacker.pack_start (*dim_packer, false, false);
392 vpacker.pack_start (channel_table_header, false, false);
393 vpacker.pack_start (channel_table_packer, false, false);
394 vpacker.pack_start (lower_packer, false, false);
396 hpacker.pack_start (vpacker, true, true);
398 gain_control->show_all ();
399 gain_display->show_all ();
400 dim_control->show_all ();
401 dim_display->show_all();
402 solo_boost_control->show_all ();
403 solo_boost_display->show_all();
405 channel_table.show ();
407 upper_packer.show ();
408 lower_packer.show ();
413 assign_controllables ();
415 output_button->signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::output_press), false);
416 output_button->signal_button_release_event().connect (sigc::mem_fun(*this, &MonitorSection::output_release), false);
417 output_button->signal_size_allocate().connect (sigc::mem_fun (*this, &MonitorSection::output_button_resized));
419 _tearoff = new TearOff (hpacker);
421 /* if torn off, make this a normal window */
422 _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
423 _tearoff->tearoff_window().set_title (X_("Monitor"));
424 _tearoff->tearoff_window().signal_key_press_event().connect (sigc::ptr_fun (forward_key_press), false);
426 update_output_display();
428 /* catch changes that affect us */
429 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
430 *this, invalidator (*this), boost::bind (&MonitorSection::port_connected_or_disconnected, this, _1, _3), gui_context ()
432 Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context());
435 MonitorSection::~MonitorSection ()
437 for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
441 _channel_buttons.clear ();
442 _output_changed_connection.disconnect ();
444 delete output_button;
449 delete solo_boost_control;
450 delete solo_boost_display;
451 delete solo_cut_control;
452 delete solo_cut_display;
454 delete _output_selector;
455 _output_selector = 0;
459 MonitorSection::set_session (Session* s)
461 AxisView::set_session (s);
465 _route = _session->monitor_out ();
468 /* session with monitor section */
469 _monitor = _route->monitor_control ();
470 assign_controllables ();
471 _route->output()->changed.connect (_output_changed_connection, invalidator (*this),
472 boost::bind (&MonitorSection::update_output_display, this),
475 /* session with no monitor section */
476 _output_changed_connection.disconnect();
479 delete _output_selector;
480 _output_selector = 0;
483 if (channel_table_scroller.get_parent()) {
484 /* scroller is packed, so remove it */
485 channel_table_packer.remove (channel_table_scroller);
488 if (table_hpacker.get_parent () == &channel_table_packer) {
489 /* this occurs when the table hpacker is directly
490 packed, so remove it.
492 channel_table_packer.remove (table_hpacker);
493 } else if (table_hpacker.get_parent()) {
494 channel_table_viewport.remove ();
497 if (_monitor->output_streams().n_audio() > 7) {
498 /* put the table into a scrolled window, and then put
499 * that into the channel vpacker, after the table header
501 channel_table_viewport.add (table_hpacker);
502 channel_table_packer.pack_start (channel_table_scroller, true, true);
503 channel_table_viewport.show ();
504 channel_table_scroller.show ();
507 /* just put the channel table itself into the channel
508 * vpacker, after the table header
511 channel_table_packer.pack_start (table_hpacker, true, true);
512 channel_table_scroller.hide ();
515 table_hpacker.show ();
516 channel_table.show ();
521 _output_changed_connection.disconnect();
524 control_connections.drop_connections ();
525 rude_iso_button.unset_active_state ();
526 rude_solo_button.unset_active_state ();
527 delete _output_selector;
528 _output_selector = 0;
530 assign_controllables ();
534 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
536 cut.set_name (X_("monitor section cut"));
537 dim.set_name (X_("monitor section dim"));
538 solo.set_name (X_("monitor section solo"));
539 invert.set_name (X_("monitor section invert"));
541 cut.unset_flags (Gtk::CAN_FOCUS);
542 dim.unset_flags (Gtk::CAN_FOCUS);
543 solo.unset_flags (Gtk::CAN_FOCUS);
544 invert.unset_flags (Gtk::CAN_FOCUS);
548 MonitorSection::populate_buttons ()
554 Glib::RefPtr<Action> act;
555 uint32_t nchans = _monitor->output_streams().n_audio();
557 channel_table.resize (nchans, 5);
558 channel_table.set_col_spacings (6);
559 channel_table.set_row_spacings (6);
560 channel_table.set_homogeneous (true);
562 const uint32_t row_offset = 0;
564 for (uint32_t i = 0; i < nchans; ++i) {
577 snprintf (buf, sizeof (buf), "%d", i+1);
581 Label* label = manage (new Label (l));
582 channel_table.attach (*label, 0, 1, i+row_offset, i+row_offset+1, EXPAND|FILL);
584 ChannelButtonSet* cbs = new ChannelButtonSet;
586 _channel_buttons.push_back (cbs);
588 channel_table.attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, EXPAND|FILL);
589 channel_table.attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, EXPAND|FILL);
590 channel_table.attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, EXPAND|FILL);
591 channel_table.attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, EXPAND|FILL);
593 snprintf (buf, sizeof (buf), "monitor-cut-%u", i+1);
594 act = ActionManager::get_action (X_("Monitor"), buf);
596 cbs->cut.set_related_action (act);
599 snprintf (buf, sizeof (buf), "monitor-dim-%u", i+1);
600 act = ActionManager::get_action (X_("Monitor"), buf);
602 cbs->dim.set_related_action (act);
605 snprintf (buf, sizeof (buf), "monitor-solo-%u", i+1);
606 act = ActionManager::get_action (X_("Monitor"), buf);
608 cbs->solo.set_related_action (act);
611 snprintf (buf, sizeof (buf), "monitor-invert-%u", i+1);
612 act = ActionManager::get_action (X_("Monitor"), buf);
614 cbs->invert.set_related_action (act);
618 channel_table.show_all ();
622 MonitorSection::toggle_exclusive_solo ()
628 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo");
630 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
631 Config->set_exclusive_solo (tact->get_active());
637 MonitorSection::toggle_mute_overrides_solo ()
643 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo");
645 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
646 Config->set_solo_mute_override (tact->get_active());
651 MonitorSection::dim_all ()
657 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
659 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
660 _monitor->set_dim_all (tact->get_active());
666 MonitorSection::cut_all ()
672 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
674 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
675 _monitor->set_cut_all (tact->get_active());
680 MonitorSection::mono ()
686 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
688 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
689 _monitor->set_mono (tact->get_active());
694 MonitorSection::cut_channel (uint32_t chn)
701 snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
703 --chn; // 0-based in backend
705 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
707 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
708 _monitor->set_cut (chn, tact->get_active());
713 MonitorSection::dim_channel (uint32_t chn)
720 snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
722 --chn; // 0-based in backend
724 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
726 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
727 _monitor->set_dim (chn, tact->get_active());
733 MonitorSection::solo_channel (uint32_t chn)
740 snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
742 --chn; // 0-based in backend
744 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
746 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
747 _monitor->set_solo (chn, tact->get_active());
753 MonitorSection::invert_channel (uint32_t chn)
760 snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
762 --chn; // 0-based in backend
764 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
766 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
767 _monitor->set_polarity (chn, tact->get_active());
772 MonitorSection::register_actions ()
776 Glib::RefPtr<Action> act;
778 monitor_actions = ActionGroup::create (X_("Monitor"));
779 ActionManager::add_action_group (monitor_actions);
781 ActionManager::register_toggle_action (monitor_actions, "monitor-mono", "", _("Switch monitor to mono"),
782 sigc::mem_fun (*this, &MonitorSection::mono));
784 ActionManager::register_toggle_action (monitor_actions, "monitor-cut-all", "", _("Cut monitor"),
785 sigc::mem_fun (*this, &MonitorSection::cut_all));
787 ActionManager::register_toggle_action (monitor_actions, "monitor-dim-all", "", _("Dim monitor"),
788 sigc::mem_fun (*this, &MonitorSection::dim_all));
790 act = ActionManager::register_toggle_action (monitor_actions, "toggle-exclusive-solo", "", _("Toggle exclusive solo mode"),
791 sigc::mem_fun (*this, &MonitorSection::toggle_exclusive_solo));
793 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
794 tact->set_active (Config->get_exclusive_solo());
796 act = ActionManager::register_toggle_action (monitor_actions, "toggle-mute-overrides-solo", "", _("Toggle mute overrides solo mode"),
797 sigc::mem_fun (*this, &MonitorSection::toggle_mute_overrides_solo));
799 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
800 tact->set_active (Config->get_solo_mute_override());
803 /* note the 1-based counting (for naming - backend uses 0-based) */
805 for (uint32_t chn = 1; chn <= 16; ++chn) {
807 action_name = string_compose (X_("monitor-cut-%1"), chn);
808 action_descr = string_compose (_("Cut monitor channel %1"), chn);
809 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
810 sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
812 action_name = string_compose (X_("monitor-dim-%1"), chn);
813 action_descr = string_compose (_("Dim monitor channel %1"), chn);
814 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
815 sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
817 action_name = string_compose (X_("monitor-solo-%1"), chn);
818 action_descr = string_compose (_("Solo monitor channel %1"), chn);
819 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
820 sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
822 action_name = string_compose (X_("monitor-invert-%1"), chn);
823 action_descr = string_compose (_("Invert monitor channel %1"), chn);
824 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
825 sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
830 Glib::RefPtr<ActionGroup> solo_actions = ActionGroup::create (X_("Solo"));
831 RadioAction::Group solo_group;
833 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-in-place", "", _("In-place solo"),
834 sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
835 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-afl", "", _("After Fade Listen (AFL) solo"),
836 sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
837 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-pfl", "", _("Pre Fade Listen (PFL) solo"),
838 sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
840 ActionManager::add_action_group (solo_actions);
844 MonitorSection::solo_use_in_place ()
846 /* this is driven by a toggle on a radio group, and so is invoked twice,
847 once for the item that became inactive and once for the one that became
851 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
854 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
856 if (!ract->get_active ()) {
857 /* We are turning SiP off, which means that AFL or PFL will be turned on
858 shortly; don't update the solo model in the mean time, as if the currently
859 configured listen position is not the one that is about to be turned on,
860 things will go wrong.
862 _inhibit_solo_model_update = true;
864 Config->set_solo_control_is_listen_control (!ract->get_active());
865 _inhibit_solo_model_update = false;
871 MonitorSection::solo_use_afl ()
873 /* this is driven by a toggle on a radio group, and so is invoked twice,
874 once for the item that became inactive and once for the one that became
878 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
880 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
882 if (ract->get_active()) {
883 Config->set_solo_control_is_listen_control (true);
884 Config->set_listen_position (AfterFaderListen);
891 MonitorSection::solo_use_pfl ()
893 /* this is driven by a toggle on a radio group, and so is invoked twice,
894 once for the item that became inactive and once for the one that became
898 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
900 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
902 if (ract->get_active()) {
903 Config->set_solo_control_is_listen_control (true);
904 Config->set_listen_position (PreFaderListen);
911 MonitorSection::update_solo_model ()
913 if (_inhibit_solo_model_update) {
917 const char* action_name = 0;
918 Glib::RefPtr<Action> act;
920 if (Config->get_solo_control_is_listen_control()) {
921 switch (Config->get_listen_position()) {
922 case AfterFaderListen:
923 action_name = X_("solo-use-afl");
926 action_name = X_("solo-use-pfl");
930 action_name = X_("solo-use-in-place");
933 act = ActionManager::get_action (X_("Solo"), action_name);
936 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
938 /* because these are radio buttons, one of them will be
939 active no matter what. to trigger a change in the
940 action so that the view picks it up, toggle it.
942 if (ract->get_active()) {
943 ract->set_active (false);
945 ract->set_active (true);
952 MonitorSection::map_state ()
954 if (!_route || !_monitor) {
958 Glib::RefPtr<Action> act;
960 update_solo_model ();
962 act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
964 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
966 tact->set_active (_monitor->cut_all());
970 act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
972 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
974 tact->set_active (_monitor->dim_all());
978 act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
980 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
982 tact->set_active (_monitor->mono());
986 uint32_t nchans = _monitor->output_streams().n_audio();
988 assert (nchans == _channel_buttons.size ());
990 for (uint32_t n = 0; n < nchans; ++n) {
992 char action_name[32];
994 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n);
995 act = ActionManager::get_action (X_("Monitor"), action_name);
997 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
999 tact->set_active (_monitor->cut (n));
1003 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n);
1004 act = ActionManager::get_action (X_("Monitor"), action_name);
1006 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1008 tact->set_active (_monitor->dimmed (n));
1012 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n);
1013 act = ActionManager::get_action (X_("Monitor"), action_name);
1015 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1017 tact->set_active (_monitor->soloed (n));
1021 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n);
1022 act = ActionManager::get_action (X_("Monitor"), action_name);
1024 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1026 tact->set_active (_monitor->inverted (n));
1033 MonitorSection::do_blink (bool onoff)
1036 audition_blink (onoff);
1040 MonitorSection::audition_blink (bool onoff)
1042 if (_session == 0) {
1046 if (_session->is_auditioning()) {
1047 rude_audition_button.set_active (onoff);
1049 rude_audition_button.set_active (false);
1054 MonitorSection::solo_blink (bool onoff)
1056 if (_session == 0) {
1060 if (_session->soloing() || _session->listening()) {
1061 rude_solo_button.set_active (onoff);
1063 if (_session->soloing()) {
1064 if (_session->solo_isolated()) {
1065 rude_iso_button.set_active (onoff);
1067 rude_iso_button.set_active (false);
1072 rude_solo_button.set_active (false);
1073 rude_iso_button.set_active (false);
1078 MonitorSection::cancel_solo (GdkEventButton*)
1081 if (_session->soloing()) {
1082 _session->set_solo (_session->get_routes(), false);
1083 } else if (_session->listening()) {
1084 _session->set_listen (_session->get_routes(), false);
1092 MonitorSection::cancel_isolate (GdkEventButton*)
1095 boost::shared_ptr<RouteList> rl (_session->get_routes ());
1096 _session->set_solo_isolated (rl, false, Session::rt_cleanup, true);
1103 MonitorSection::cancel_audition (GdkEventButton*)
1106 _session->cancel_audition();
1111 #define SYNCHRONIZE_TOGGLE_ACTION(action, value) \
1113 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(action); \
1114 if (tact && tact->get_active() != value) { \
1115 tact->set_active(value); \
1120 MonitorSection::parameter_changed (std::string name)
1122 if (name == "solo-control-is-listen-control") {
1123 update_solo_model ();
1124 } else if (name == "listen-position") {
1125 update_solo_model ();
1126 } else if (name == "solo-mute-override") {
1127 SYNCHRONIZE_TOGGLE_ACTION(
1128 ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo"),
1129 Config->get_solo_mute_override ())
1130 } else if (name == "exclusive-solo") {
1131 SYNCHRONIZE_TOGGLE_ACTION(
1132 ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo"),
1133 Config->get_exclusive_solo ())
1138 MonitorSection::assign_controllables ()
1140 boost::shared_ptr<Controllable> none;
1142 if (!gain_control) {
1143 /* too early - GUI controls not set up yet */
1148 solo_cut_control->set_controllable (_session->solo_cut_control());
1149 solo_cut_display->set_controllable (_session->solo_cut_control());
1151 solo_cut_control->set_controllable (none);
1152 solo_cut_display->set_controllable (none);
1156 gain_control->set_controllable (_route->gain_control());
1157 gain_display->set_controllable (_route->gain_control());
1159 gain_control->set_controllable (none);
1164 cut_all_button.set_controllable (_monitor->cut_control());
1165 cut_all_button.watch ();
1166 dim_all_button.set_controllable (_monitor->dim_control());
1167 dim_all_button.watch ();
1168 mono_button.set_controllable (_monitor->mono_control());
1169 mono_button.watch ();
1171 dim_control->set_controllable (_monitor->dim_level_control ());
1172 dim_display->set_controllable (_monitor->dim_level_control ());
1173 solo_boost_control->set_controllable (_monitor->solo_boost_control ());
1174 solo_boost_display->set_controllable (_monitor->solo_boost_control ());
1178 cut_all_button.set_controllable (none);
1179 dim_all_button.set_controllable (none);
1180 mono_button.set_controllable (none);
1182 dim_control->set_controllable (none);
1183 dim_display->set_controllable (none);
1184 solo_boost_control->set_controllable (none);
1185 solo_boost_display->set_controllable (none);
1190 MonitorSection::state_id() const
1192 return "monitor-section";
1196 MonitorSection::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1198 using namespace Menu_Helpers;
1200 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1204 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1205 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1209 if (i != output_menu_bundles.end()) {
1213 output_menu_bundles.push_back (b);
1215 MenuList& citems = output_menu.items();
1217 std::string n = b->name ();
1218 replace_all (n, "_", " ");
1220 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MonitorSection::bundle_output_chosen), b)));
1224 MonitorSection::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1227 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1229 if (std::find (current.begin(), current.end(), c) == current.end()) {
1230 _route->output()->connect_ports_to_bundle (c, true, this);
1232 _route->output()->disconnect_ports_from_bundle (c, this);
1237 MonitorSection::output_release (GdkEventButton *ev)
1239 switch (ev->button) {
1241 edit_output_configuration ();
1248 struct RouteCompareByName {
1249 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1250 return a->name().compare (b->name()) < 0;
1255 MonitorSection::output_press (GdkEventButton *ev)
1257 using namespace Menu_Helpers;
1259 MessageDialog msg (_("No session - no I/O changes are possible"));
1264 MenuList& citems = output_menu.items();
1265 switch (ev->button) {
1268 return false; //wait for the mouse-up to pop the dialog
1272 output_menu.set_name ("ArdourContextMenu");
1274 output_menu_bundles.clear ();
1276 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(this), &MonitorSection::disconnect_output)));
1278 citems.push_back (SeparatorElem());
1279 uint32_t const n_with_separator = citems.size ();
1281 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1283 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1285 /* give user bundles first chance at being in the menu */
1287 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1288 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1289 maybe_add_bundle_to_output_menu (*i, current);
1293 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1294 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1295 maybe_add_bundle_to_output_menu (*i, current);
1299 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1300 RouteList copy = *routes;
1301 copy.sort (RouteCompareByName ());
1302 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1303 maybe_add_bundle_to_output_menu ((*i)->output()->bundle(), current);
1306 if (citems.size() == n_with_separator) {
1307 /* no routes added; remove the separator */
1311 citems.push_back (SeparatorElem());
1312 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(this), &MonitorSection::edit_output_configuration)));
1314 output_menu.popup (1, ev->time);
1325 MonitorSection::output_button_resized (Gtk::Allocation& alloc)
1327 output_button->set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1331 MonitorSection::update_output_display ()
1333 if (!_route || !_monitor) {
1339 boost::shared_ptr<Port> port;
1340 vector<string> port_connections;
1342 uint32_t total_connection_count = 0;
1343 uint32_t io_connection_count = 0;
1344 uint32_t ardour_connection_count = 0;
1345 uint32_t system_connection_count = 0;
1346 uint32_t other_connection_count = 0;
1348 ostringstream label;
1350 bool have_label = false;
1351 bool each_io_has_one_connection = true;
1353 string connection_name;
1354 string ardour_track_name;
1355 string other_connection_type;
1356 string system_ports;
1359 ostringstream tooltip;
1360 char * tooltip_cstr;
1362 io_count = _route->n_outputs().n_total();
1363 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(_route->name()));
1366 for (io_index = 0; io_index < io_count; ++io_index) {
1368 port = _route->output()->nth (io_index);
1370 //ignore any port connections that don't match our DataType
1371 if (port->type() != DataType::AUDIO) {
1375 port_connections.clear ();
1376 port->get_connections(port_connections);
1377 io_connection_count = 0;
1379 if (!port_connections.empty()) {
1380 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1382 string& connection_name (*i);
1384 if (connection_name.find("system:") == 0) {
1385 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1388 if (io_connection_count == 0) {
1389 tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1))
1391 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1394 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1397 if (connection_name.find("ardour:") == 0) {
1398 if (ardour_track_name.empty()) {
1399 // "ardour:Master/in 1" -> "ardour:Master/"
1400 string::size_type slash = connection_name.find("/");
1401 if (slash != string::npos) {
1402 ardour_track_name = connection_name.substr(0, slash + 1);
1406 if (connection_name.find(ardour_track_name) == 0) {
1407 ++ardour_connection_count;
1409 } else if (!pn.empty()) {
1410 if (system_ports.empty()) {
1413 system_ports += "/" + pn;
1415 if (connection_name.find("system:") == 0) {
1416 ++system_connection_count;
1418 } else if (connection_name.find("system:") == 0) {
1419 // "system:playback_123" -> "123"
1420 system_port = connection_name.substr(16);
1421 if (system_ports.empty()) {
1422 system_ports += system_port;
1424 system_ports += "/" + system_port;
1427 ++system_connection_count;
1429 if (other_connection_type.empty()) {
1430 // "jamin:in 1" -> "jamin:"
1431 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1434 if (connection_name.find(other_connection_type) == 0) {
1435 ++other_connection_count;
1439 ++total_connection_count;
1440 ++io_connection_count;
1444 if (io_connection_count != 1) {
1445 each_io_has_one_connection = false;
1449 if (total_connection_count == 0) {
1450 tooltip << endl << _("Disconnected");
1453 tooltip_cstr = new char[tooltip.str().size() + 1];
1454 strcpy(tooltip_cstr, tooltip.str().c_str());
1456 ARDOUR_UI::instance()->set_tip (output_button, tooltip_cstr, "");
1458 if (each_io_has_one_connection) {
1459 if (total_connection_count == ardour_connection_count) {
1460 // all connections are to the same track in ardour
1461 // "ardour:Master/" -> "Master"
1462 string::size_type slash = ardour_track_name.find("/");
1463 if (slash != string::npos) {
1464 label << ardour_track_name.substr(7, slash - 7);
1467 } else if (total_connection_count == system_connection_count) {
1468 // all connections are to system ports
1469 label << system_ports;
1471 } else if (total_connection_count == other_connection_count) {
1472 // all connections are to the same external program eg jamin
1473 // "jamin:" -> "jamin"
1474 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1480 if (total_connection_count == 0) {
1484 // Odd configuration
1485 label << "*" << total_connection_count << "*";
1489 output_button->set_text (label.str());
1493 MonitorSection::disconnect_output ()
1496 _route->output()->disconnect(this);
1501 MonitorSection::edit_output_configuration ()
1503 if (_output_selector == 0) {
1504 _output_selector = new MonitorSelectorWindow (_session, _route->output());
1506 _output_selector->present ();
1510 MonitorSection::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1515 boost::shared_ptr<Port> a = wa.lock ();
1516 boost::shared_ptr<Port> b = wb.lock ();
1517 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1518 update_output_display ();