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"
38 #include "ardour/user_bundle.h"
40 #include "ardour_ui.h"
41 #include "gui_thread.h"
42 #include "monitor_section.h"
43 #include "public_editor.h"
45 #include "volume_controller.h"
46 #include "ui_config.h"
51 using namespace ARDOUR;
52 using namespace ARDOUR_UI_UTILS;
54 using namespace Gtkmm2ext;
58 Glib::RefPtr<ActionGroup> MonitorSection::monitor_actions;
60 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
62 MonitorSection::MonitorSection (Session* s)
66 , channel_table_viewport (*channel_table_scroller.get_hadjustment()
67 , *channel_table_scroller.get_vadjustment ())
70 , solo_boost_control (0)
71 , solo_cut_control (0)
74 , solo_boost_display (0)
75 , solo_cut_display (0)
76 , _output_selector (0)
77 , solo_in_place_button (_("SiP"), ArdourButton::led_default_elements)
78 , afl_button (_("AFL"), ArdourButton::led_default_elements)
79 , pfl_button (_("PFL"), ArdourButton::led_default_elements)
80 , exclusive_solo_button (ArdourButton::led_default_elements)
81 , solo_mute_override_button (ArdourButton::led_default_elements)
82 , _inhibit_solo_model_update (false)
85 using namespace Menu_Helpers;
87 Glib::RefPtr<Action> act;
89 if (!monitor_actions) {
91 /* do some static stuff */
104 rude_solo_button.set_text (_("Soloing"));
105 rude_solo_button.set_name ("rude solo");
106 rude_solo_button.show ();
108 rude_iso_button.set_text (_("Isolated"));
109 rude_iso_button.set_name ("rude isolate");
110 rude_iso_button.show ();
112 rude_audition_button.set_text (_("Auditioning"));
113 rude_audition_button.set_name ("rude audition");
114 rude_audition_button.show ();
116 Timers::blink_connect (sigc::mem_fun (*this, &MonitorSection::do_blink));
118 rude_solo_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_solo), false);
119 UI::instance()->set_tip (rude_solo_button, _("When active, something is soloed.\nClick to de-solo everything"));
121 rude_iso_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_isolate), false);
122 UI::instance()->set_tip (rude_iso_button, _("When active, something is solo-isolated.\nClick to de-isolate everything"));
124 rude_audition_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_audition), false);
125 UI::instance()->set_tip (rude_audition_button, _("When active, auditioning is active.\nClick to stop the audition"));
127 solo_in_place_button.set_name ("monitor section solo model");
128 afl_button.set_name ("monitor section solo model");
129 pfl_button.set_name ("monitor section solo model");
131 solo_model_box.set_spacing (6);
132 solo_model_box.pack_start (solo_in_place_button, true, false);
133 solo_model_box.pack_start (afl_button, true, false);
134 solo_model_box.pack_start (pfl_button, true, false);
136 solo_in_place_button.show ();
139 solo_model_box.show ();
141 act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
142 ARDOUR_UI::instance()->set_tip (solo_in_place_button, _("Solo controls affect solo-in-place"));
144 solo_in_place_button.set_related_action (act);
147 act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
148 ARDOUR_UI::instance()->set_tip (afl_button, _("Solo controls toggle after-fader-listen"));
150 afl_button.set_related_action (act);
153 act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
154 ARDOUR_UI::instance()->set_tip (pfl_button, _("Solo controls toggle pre-fader-listen"));
156 pfl_button.set_related_action (act);
161 solo_boost_control = new ArdourKnob ();
162 solo_boost_control->set_name("monitor knob");
163 solo_boost_control->set_size_request (PX_SCALE(40), PX_SCALE(40));
164 ARDOUR_UI::instance()->set_tip (*solo_boost_control, _("Gain increase for soloed signals (0dB is normal)"));
166 solo_boost_display = new ArdourDisplay ();
167 solo_boost_display->set_name("monitor section cut");
168 solo_boost_display->set_size_request (PX_SCALE(80), PX_SCALE(20));
169 solo_boost_display->add_controllable_preset(_("0 dB"), 0.0);
170 solo_boost_display->add_controllable_preset(_("3 dB"), 3.0);
171 solo_boost_display->add_controllable_preset(_("6 dB"), 6.0);
172 solo_boost_display->add_controllable_preset(_("10 dB"), 10.0);
174 HBox* solo_packer = manage (new HBox);
175 solo_packer->set_spacing (6);
176 solo_packer->show ();
178 spin_label = manage (new Label (_("Solo Boost")));
179 spin_packer = manage (new VBox);
180 spin_packer->show ();
181 spin_packer->set_spacing (3);
182 spin_packer->pack_start (*spin_label, false, false);
183 spin_packer->pack_start (*solo_boost_control, false, false);
184 spin_packer->pack_start (*solo_boost_display, false, false);
186 solo_packer->pack_start (*spin_packer, true, false);
190 solo_cut_control = new ArdourKnob ();
191 solo_cut_control->set_name ("monitor knob");
192 solo_cut_control->set_size_request (PX_SCALE(40), PX_SCALE(40));
193 ARDOUR_UI::instance()->set_tip (*solo_cut_control, _("Gain reduction non-soloed signals\nA value above -inf dB causes \"solo-in-front\""));
195 solo_cut_display = new ArdourDisplay ();
196 solo_cut_display->set_name("monitor section cut");
197 solo_cut_display->set_size_request (PX_SCALE(80), PX_SCALE(20));
198 solo_cut_display->add_controllable_preset(_("0 dB"), 0.0);
199 solo_cut_display->add_controllable_preset(_("-6 dB"), -6.0);
200 solo_cut_display->add_controllable_preset(_("-12 dB"), -12.0);
201 solo_cut_display->add_controllable_preset(_("-20 dB"), -20.0);
202 solo_cut_display->add_controllable_preset(_("OFF"), -1200.0);
204 spin_label = manage (new Label (_("SiP Cut")));
205 spin_packer = manage (new VBox);
206 spin_packer->show ();
207 spin_packer->set_spacing (3);
208 spin_packer->pack_start (*spin_label, false, false);
209 spin_packer->pack_start (*solo_cut_control, false, false);
210 spin_packer->pack_start (*solo_cut_display, false, false);
212 solo_packer->pack_start (*spin_packer, true, false);
216 dim_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
217 dim_control->set_name ("monitor knob");
218 dim_control->set_size_request (PX_SCALE(40), PX_SCALE(40));
219 ARDOUR_UI::instance()->set_tip (*dim_control, _("Gain reduction to use when dimming monitor outputs"));
221 dim_display = new ArdourDisplay ();
222 dim_display->set_name("monitor section cut");
223 dim_display->set_size_request (PX_SCALE(80), PX_SCALE(20));
224 dim_display->add_controllable_preset(_("0 dB"), 0.0);
225 dim_display->add_controllable_preset(_("-3 dB"), -3.0);
226 dim_display->add_controllable_preset(_("-6 dB"), -6.0);
227 dim_display->add_controllable_preset(_("-12 dB"), -12.0);
228 dim_display->add_controllable_preset(_("-20 dB"), -20.0);
230 HBox* dim_packer = manage (new HBox);
233 spin_label = manage (new Label (_("Dim")));
234 spin_packer = manage (new VBox);
235 spin_packer->show ();
236 spin_packer->set_spacing (3);
237 spin_packer->pack_start (*spin_label, false, false);
238 spin_packer->pack_start (*dim_control, false, false);
239 spin_packer->pack_start (*dim_display, false, false);
241 dim_packer->pack_start (*spin_packer, true, false);
243 exclusive_solo_button.set_text (_("Excl. Solo"));
244 exclusive_solo_button.set_name (X_("monitor solo exclusive"));
245 ARDOUR_UI::instance()->set_tip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time"));
247 act = ActionManager::get_action (X_("Monitor"), X_("toggle-exclusive-solo"));
249 exclusive_solo_button.set_related_action (act);
252 solo_mute_override_button.set_text (_("Solo » Mute"));
253 solo_mute_override_button.set_name (X_("monitor solo override"));
254 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)"));
256 act = ActionManager::get_action (X_("Monitor"), X_("toggle-mute-overrides-solo"));
258 solo_mute_override_button.set_related_action (act);
261 HBox* solo_opt_box = manage (new HBox);
262 solo_opt_box->set_spacing (12);
263 solo_opt_box->set_homogeneous (true);
264 solo_opt_box->pack_start (exclusive_solo_button);
265 solo_opt_box->pack_start (solo_mute_override_button);
266 solo_opt_box->show ();
268 upper_packer.set_spacing (6);
270 Gtk::HBox* rude_box = manage (new HBox);
271 rude_box->pack_start (rude_solo_button, true, true);
272 rude_box->pack_start (rude_iso_button, true, true);
274 upper_packer.pack_start (*rude_box, false, false);
275 upper_packer.pack_start (rude_audition_button, false, false);
276 upper_packer.pack_start (solo_model_box, false, false, 12);
277 upper_packer.pack_start (*solo_opt_box, false, false);
278 upper_packer.pack_start (*solo_packer, false, false, 12);
280 cut_all_button.set_text (_("Mute"));
281 cut_all_button.set_name ("monitor section cut");
282 cut_all_button.set_name (X_("monitor section cut"));
283 cut_all_button.set_size_request (-1, PX_SCALE(50));
284 cut_all_button.show ();
286 act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
288 cut_all_button.set_related_action (act);
291 dim_all_button.set_text (_("Dim"));
292 dim_all_button.set_name ("monitor section dim");
293 act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
295 dim_all_button.set_related_action (act);
298 mono_button.set_text (_("Mono"));
299 mono_button.set_name ("monitor section mono");
300 act = ActionManager::get_action (X_("Monitor"), X_("monitor-mono"));
302 mono_button.set_related_action (act);
305 HBox* bbox = manage (new HBox);
307 bbox->set_spacing (12);
308 bbox->pack_start (mono_button, true, true);
309 bbox->pack_start (dim_all_button, true, true);
311 lower_packer.set_spacing (12);
312 lower_packer.pack_start (*bbox, false, false);
313 lower_packer.pack_start (cut_all_button, false, false);
317 gain_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
318 gain_control->set_name("monitor knob");
319 gain_control->set_size_request (PX_SCALE(80), PX_SCALE(80));
321 gain_display = new ArdourDisplay ();
322 gain_display->set_name("monitor section cut");
323 gain_display->set_size_request (PX_SCALE(40), PX_SCALE(20));
324 gain_display->add_controllable_preset(_("0 dB"), 0.0);
325 gain_display->add_controllable_preset(_("-3 dB"), -3.0);
326 gain_display->add_controllable_preset(_("-6 dB"), -6.0);
327 gain_display->add_controllable_preset(_("-12 dB"), -12.0);
328 gain_display->add_controllable_preset(_("-20 dB"), -20.0);
329 gain_display->add_controllable_preset(_("-30 dB"), -30.0);
331 Label* output_label = manage (new Label (_("Output")));
332 output_label->set_name (X_("MonitorSectionLabel"));
334 output_button = new ArdourButton ();
335 output_button->set_text (_("Output"));
336 output_button->set_name (X_("monitor section cut"));
337 output_button->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
338 VBox* out_packer = manage (new VBox);
339 out_packer->set_spacing (6);
340 out_packer->pack_start (*output_label, false, false);
341 out_packer->pack_start (*output_button, false, false);
343 spin_label = manage (new Label (_("Monitor")));
344 spin_packer = manage (new VBox);
345 spin_packer->show ();
346 spin_packer->set_spacing (3);
347 spin_packer->pack_start (*spin_label, false, false);
348 spin_packer->pack_start (*gain_control, false, false);
349 spin_packer->pack_start (*gain_display, false, false);
350 spin_packer->pack_start (*out_packer, false, false, 24);
352 lower_packer.pack_start (*spin_packer, true, true);
354 channel_table_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
355 channel_table_scroller.set_size_request (-1, PX_SCALE(150));
356 channel_table_scroller.set_shadow_type (Gtk::SHADOW_NONE);
357 channel_table_scroller.show ();
358 channel_table_scroller.add (channel_table_viewport);
360 channel_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
361 channel_size_group->add_widget (channel_table_header);
362 channel_size_group->add_widget (channel_table);
364 channel_table_header.resize (1, 5);
366 Label* l1 = manage (new Label (X_(" ")));
367 l1->set_name (X_("MonitorSectionLabel"));
368 channel_table_header.attach (*l1, 0, 1, 0, 1, EXPAND|FILL);
370 l1 = manage (new Label (_("Mute")));
371 l1->set_name (X_("MonitorSectionLabel"));
372 channel_table_header.attach (*l1, 1, 2, 0, 1, EXPAND|FILL);
374 l1 = manage (new Label (_("Dim")));
375 l1->set_name (X_("MonitorSectionLabel"));
376 channel_table_header.attach (*l1, 2, 3, 0, 1, EXPAND|FILL);
378 l1 = manage (new Label (_("Solo")));
379 l1->set_name (X_("MonitorSectionLabel"));
380 channel_table_header.attach (*l1, 3, 4, 0, 1, EXPAND|FILL);
382 l1 = manage (new Label (_("Inv")));
383 l1->set_name (X_("MonitorSectionLabel"));
384 channel_table_header.attach (*l1, 4, 5, 0, 1, EXPAND|FILL);
386 channel_table_header.show ();
388 table_hpacker.pack_start (channel_table, true, true);
390 /* note that we don't pack the table_hpacker till later
393 vpacker.set_border_width (6);
394 vpacker.set_spacing (12);
395 vpacker.pack_start (upper_packer, false, false);
396 vpacker.pack_start (*dim_packer, false, false);
397 vpacker.pack_start (channel_table_header, false, false);
398 vpacker.pack_start (channel_table_packer, false, false);
399 vpacker.pack_start (lower_packer, false, false);
401 hpacker.pack_start (vpacker, true, true);
403 gain_control->show_all ();
404 gain_display->show_all ();
405 dim_control->show_all ();
406 dim_display->show_all();
407 solo_boost_control->show_all ();
408 solo_boost_display->show_all();
410 channel_table.show ();
412 upper_packer.show ();
413 lower_packer.show ();
418 assign_controllables ();
420 output_button->signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::output_press), false);
421 output_button->signal_button_release_event().connect (sigc::mem_fun(*this, &MonitorSection::output_release), false);
422 output_button->signal_size_allocate().connect (sigc::mem_fun (*this, &MonitorSection::output_button_resized));
424 _tearoff = new TearOff (hpacker);
426 /* if torn off, make this a normal window */
427 _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
428 _tearoff->tearoff_window().set_title (X_("Monitor"));
429 _tearoff->tearoff_window().signal_key_press_event().connect (sigc::ptr_fun (forward_key_press), false);
431 update_output_display();
433 /* catch changes that affect us */
434 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
435 *this, invalidator (*this), boost::bind (&MonitorSection::port_connected_or_disconnected, this, _1, _3), gui_context ()
437 Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context());
440 MonitorSection::~MonitorSection ()
442 for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
446 _channel_buttons.clear ();
447 _output_changed_connection.disconnect ();
449 delete output_button;
454 delete solo_boost_control;
455 delete solo_boost_display;
456 delete solo_cut_control;
457 delete solo_cut_display;
459 delete _output_selector;
460 _output_selector = 0;
464 MonitorSection::set_session (Session* s)
466 AxisView::set_session (s);
470 _route = _session->monitor_out ();
473 /* session with monitor section */
474 _monitor = _route->monitor_control ();
475 assign_controllables ();
476 _route->output()->changed.connect (_output_changed_connection, invalidator (*this),
477 boost::bind (&MonitorSection::update_output_display, this),
480 /* session with no monitor section */
481 _output_changed_connection.disconnect();
484 delete _output_selector;
485 _output_selector = 0;
488 if (channel_table_scroller.get_parent()) {
489 /* scroller is packed, so remove it */
490 channel_table_packer.remove (channel_table_scroller);
493 if (table_hpacker.get_parent () == &channel_table_packer) {
494 /* this occurs when the table hpacker is directly
495 packed, so remove it.
497 channel_table_packer.remove (table_hpacker);
498 } else if (table_hpacker.get_parent()) {
499 channel_table_viewport.remove ();
502 if (_monitor->output_streams().n_audio() > 7) {
503 /* put the table into a scrolled window, and then put
504 * that into the channel vpacker, after the table header
506 channel_table_viewport.add (table_hpacker);
507 channel_table_packer.pack_start (channel_table_scroller, true, true);
508 channel_table_viewport.show ();
509 channel_table_scroller.show ();
512 /* just put the channel table itself into the channel
513 * vpacker, after the table header
516 channel_table_packer.pack_start (table_hpacker, true, true);
517 channel_table_scroller.hide ();
520 table_hpacker.show ();
521 channel_table.show ();
526 _output_changed_connection.disconnect();
529 control_connections.drop_connections ();
530 rude_iso_button.unset_active_state ();
531 rude_solo_button.unset_active_state ();
532 delete _output_selector;
533 _output_selector = 0;
535 assign_controllables ();
539 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
541 cut.set_name (X_("monitor section cut"));
542 dim.set_name (X_("monitor section dim"));
543 solo.set_name (X_("monitor section solo"));
544 invert.set_name (X_("monitor section invert"));
546 cut.unset_flags (Gtk::CAN_FOCUS);
547 dim.unset_flags (Gtk::CAN_FOCUS);
548 solo.unset_flags (Gtk::CAN_FOCUS);
549 invert.unset_flags (Gtk::CAN_FOCUS);
553 MonitorSection::populate_buttons ()
559 Glib::RefPtr<Action> act;
560 uint32_t nchans = _monitor->output_streams().n_audio();
562 channel_table.resize (nchans, 5);
563 channel_table.set_col_spacings (6);
564 channel_table.set_row_spacings (6);
565 channel_table.set_homogeneous (true);
567 const uint32_t row_offset = 0;
569 for (uint32_t i = 0; i < nchans; ++i) {
582 snprintf (buf, sizeof (buf), "%d", i+1);
586 Label* label = manage (new Label (l));
587 channel_table.attach (*label, 0, 1, i+row_offset, i+row_offset+1, EXPAND|FILL);
589 ChannelButtonSet* cbs = new ChannelButtonSet;
591 _channel_buttons.push_back (cbs);
593 channel_table.attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, EXPAND|FILL);
594 channel_table.attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, EXPAND|FILL);
595 channel_table.attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, EXPAND|FILL);
596 channel_table.attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, EXPAND|FILL);
598 snprintf (buf, sizeof (buf), "monitor-cut-%u", i+1);
599 act = ActionManager::get_action (X_("Monitor"), buf);
601 cbs->cut.set_related_action (act);
604 snprintf (buf, sizeof (buf), "monitor-dim-%u", i+1);
605 act = ActionManager::get_action (X_("Monitor"), buf);
607 cbs->dim.set_related_action (act);
610 snprintf (buf, sizeof (buf), "monitor-solo-%u", i+1);
611 act = ActionManager::get_action (X_("Monitor"), buf);
613 cbs->solo.set_related_action (act);
616 snprintf (buf, sizeof (buf), "monitor-invert-%u", i+1);
617 act = ActionManager::get_action (X_("Monitor"), buf);
619 cbs->invert.set_related_action (act);
623 channel_table.show_all ();
627 MonitorSection::toggle_exclusive_solo ()
633 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo");
635 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
636 Config->set_exclusive_solo (tact->get_active());
642 MonitorSection::toggle_mute_overrides_solo ()
648 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo");
650 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
651 Config->set_solo_mute_override (tact->get_active());
656 MonitorSection::dim_all ()
662 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
664 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
665 _monitor->set_dim_all (tact->get_active());
671 MonitorSection::cut_all ()
677 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
679 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
680 _monitor->set_cut_all (tact->get_active());
685 MonitorSection::mono ()
691 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
693 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
694 _monitor->set_mono (tact->get_active());
699 MonitorSection::cut_channel (uint32_t chn)
706 snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
708 --chn; // 0-based in backend
710 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
712 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
713 _monitor->set_cut (chn, tact->get_active());
718 MonitorSection::dim_channel (uint32_t chn)
725 snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
727 --chn; // 0-based in backend
729 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
731 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
732 _monitor->set_dim (chn, tact->get_active());
738 MonitorSection::solo_channel (uint32_t chn)
745 snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
747 --chn; // 0-based in backend
749 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
751 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
752 _monitor->set_solo (chn, tact->get_active());
758 MonitorSection::invert_channel (uint32_t chn)
765 snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
767 --chn; // 0-based in backend
769 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
771 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
772 _monitor->set_polarity (chn, tact->get_active());
777 MonitorSection::register_actions ()
781 Glib::RefPtr<Action> act;
783 monitor_actions = ActionGroup::create (X_("Monitor"));
784 ActionManager::add_action_group (monitor_actions);
786 ActionManager::register_toggle_action (monitor_actions, "monitor-mono", "", _("Switch monitor to mono"),
787 sigc::mem_fun (*this, &MonitorSection::mono));
789 ActionManager::register_toggle_action (monitor_actions, "monitor-cut-all", "", _("Cut monitor"),
790 sigc::mem_fun (*this, &MonitorSection::cut_all));
792 ActionManager::register_toggle_action (monitor_actions, "monitor-dim-all", "", _("Dim monitor"),
793 sigc::mem_fun (*this, &MonitorSection::dim_all));
795 act = ActionManager::register_toggle_action (monitor_actions, "toggle-exclusive-solo", "", _("Toggle exclusive solo mode"),
796 sigc::mem_fun (*this, &MonitorSection::toggle_exclusive_solo));
798 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
799 tact->set_active (Config->get_exclusive_solo());
801 act = ActionManager::register_toggle_action (monitor_actions, "toggle-mute-overrides-solo", "", _("Toggle mute overrides solo mode"),
802 sigc::mem_fun (*this, &MonitorSection::toggle_mute_overrides_solo));
804 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
805 tact->set_active (Config->get_solo_mute_override());
808 /* note the 1-based counting (for naming - backend uses 0-based) */
810 for (uint32_t chn = 1; chn <= 16; ++chn) {
812 action_name = string_compose (X_("monitor-cut-%1"), chn);
813 action_descr = string_compose (_("Cut 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::cut_channel), chn));
817 action_name = string_compose (X_("monitor-dim-%1"), chn);
818 action_descr = string_compose (_("Dim 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::dim_channel), chn));
822 action_name = string_compose (X_("monitor-solo-%1"), chn);
823 action_descr = string_compose (_("Solo 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::solo_channel), chn));
827 action_name = string_compose (X_("monitor-invert-%1"), chn);
828 action_descr = string_compose (_("Invert monitor channel %1"), chn);
829 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
830 sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
835 Glib::RefPtr<ActionGroup> solo_actions = ActionGroup::create (X_("Solo"));
836 RadioAction::Group solo_group;
838 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-in-place", "", _("In-place solo"),
839 sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
840 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-afl", "", _("After Fade Listen (AFL) solo"),
841 sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
842 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-pfl", "", _("Pre Fade Listen (PFL) solo"),
843 sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
845 ActionManager::add_action_group (solo_actions);
849 MonitorSection::solo_use_in_place ()
851 /* this is driven by a toggle on a radio group, and so is invoked twice,
852 once for the item that became inactive and once for the one that became
856 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
859 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
861 if (!ract->get_active ()) {
862 /* We are turning SiP off, which means that AFL or PFL will be turned on
863 shortly; don't update the solo model in the mean time, as if the currently
864 configured listen position is not the one that is about to be turned on,
865 things will go wrong.
867 _inhibit_solo_model_update = true;
869 Config->set_solo_control_is_listen_control (!ract->get_active());
870 _inhibit_solo_model_update = false;
876 MonitorSection::solo_use_afl ()
878 /* this is driven by a toggle on a radio group, and so is invoked twice,
879 once for the item that became inactive and once for the one that became
883 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
885 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
887 if (ract->get_active()) {
888 Config->set_solo_control_is_listen_control (true);
889 Config->set_listen_position (AfterFaderListen);
896 MonitorSection::solo_use_pfl ()
898 /* this is driven by a toggle on a radio group, and so is invoked twice,
899 once for the item that became inactive and once for the one that became
903 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
905 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
907 if (ract->get_active()) {
908 Config->set_solo_control_is_listen_control (true);
909 Config->set_listen_position (PreFaderListen);
916 MonitorSection::update_solo_model ()
918 if (_inhibit_solo_model_update) {
922 const char* action_name = 0;
923 Glib::RefPtr<Action> act;
925 if (Config->get_solo_control_is_listen_control()) {
926 switch (Config->get_listen_position()) {
927 case AfterFaderListen:
928 action_name = X_("solo-use-afl");
931 action_name = X_("solo-use-pfl");
935 action_name = X_("solo-use-in-place");
938 act = ActionManager::get_action (X_("Solo"), action_name);
941 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
943 /* because these are radio buttons, one of them will be
944 active no matter what. to trigger a change in the
945 action so that the view picks it up, toggle it.
947 if (ract->get_active()) {
948 ract->set_active (false);
950 ract->set_active (true);
957 MonitorSection::map_state ()
959 if (!_route || !_monitor) {
963 Glib::RefPtr<Action> act;
965 update_solo_model ();
967 act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
969 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
971 tact->set_active (_monitor->cut_all());
975 act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
977 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
979 tact->set_active (_monitor->dim_all());
983 act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
985 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
987 tact->set_active (_monitor->mono());
991 uint32_t nchans = _monitor->output_streams().n_audio();
993 assert (nchans == _channel_buttons.size ());
995 for (uint32_t n = 0; n < nchans; ++n) {
997 char action_name[32];
999 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n);
1000 act = ActionManager::get_action (X_("Monitor"), action_name);
1002 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1004 tact->set_active (_monitor->cut (n));
1008 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n);
1009 act = ActionManager::get_action (X_("Monitor"), action_name);
1011 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1013 tact->set_active (_monitor->dimmed (n));
1017 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n);
1018 act = ActionManager::get_action (X_("Monitor"), action_name);
1020 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1022 tact->set_active (_monitor->soloed (n));
1026 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n);
1027 act = ActionManager::get_action (X_("Monitor"), action_name);
1029 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1031 tact->set_active (_monitor->inverted (n));
1038 MonitorSection::do_blink (bool onoff)
1041 audition_blink (onoff);
1045 MonitorSection::audition_blink (bool onoff)
1047 if (_session == 0) {
1051 if (_session->is_auditioning()) {
1052 rude_audition_button.set_active (onoff);
1054 rude_audition_button.set_active (false);
1059 MonitorSection::solo_blink (bool onoff)
1061 if (_session == 0) {
1065 if (_session->soloing() || _session->listening()) {
1066 rude_solo_button.set_active (onoff);
1068 if (_session->soloing()) {
1069 if (_session->solo_isolated()) {
1070 rude_iso_button.set_active (onoff);
1072 rude_iso_button.set_active (false);
1077 rude_solo_button.set_active (false);
1078 rude_iso_button.set_active (false);
1083 MonitorSection::cancel_solo (GdkEventButton*)
1086 if (_session->soloing()) {
1087 _session->set_solo (_session->get_routes(), false);
1088 } else if (_session->listening()) {
1089 _session->set_listen (_session->get_routes(), false);
1097 MonitorSection::cancel_isolate (GdkEventButton*)
1100 boost::shared_ptr<RouteList> rl (_session->get_routes ());
1101 _session->set_solo_isolated (rl, false, Session::rt_cleanup, true);
1108 MonitorSection::cancel_audition (GdkEventButton*)
1111 _session->cancel_audition();
1116 #define SYNCHRONIZE_TOGGLE_ACTION(action, value) \
1118 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(action); \
1119 if (tact && tact->get_active() != value) { \
1120 tact->set_active(value); \
1125 MonitorSection::parameter_changed (std::string name)
1127 if (name == "solo-control-is-listen-control") {
1128 update_solo_model ();
1129 } else if (name == "listen-position") {
1130 update_solo_model ();
1131 } else if (name == "solo-mute-override") {
1132 SYNCHRONIZE_TOGGLE_ACTION(
1133 ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo"),
1134 Config->get_solo_mute_override ())
1135 } else if (name == "exclusive-solo") {
1136 SYNCHRONIZE_TOGGLE_ACTION(
1137 ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo"),
1138 Config->get_exclusive_solo ())
1143 MonitorSection::assign_controllables ()
1145 boost::shared_ptr<Controllable> none;
1147 if (!gain_control) {
1148 /* too early - GUI controls not set up yet */
1153 solo_cut_control->set_controllable (_session->solo_cut_control());
1154 solo_cut_display->set_controllable (_session->solo_cut_control());
1156 solo_cut_control->set_controllable (none);
1157 solo_cut_display->set_controllable (none);
1161 gain_control->set_controllable (_route->gain_control());
1162 gain_display->set_controllable (_route->gain_control());
1164 gain_control->set_controllable (none);
1169 cut_all_button.set_controllable (_monitor->cut_control());
1170 cut_all_button.watch ();
1171 dim_all_button.set_controllable (_monitor->dim_control());
1172 dim_all_button.watch ();
1173 mono_button.set_controllable (_monitor->mono_control());
1174 mono_button.watch ();
1176 dim_control->set_controllable (_monitor->dim_level_control ());
1177 dim_display->set_controllable (_monitor->dim_level_control ());
1178 solo_boost_control->set_controllable (_monitor->solo_boost_control ());
1179 solo_boost_display->set_controllable (_monitor->solo_boost_control ());
1183 cut_all_button.set_controllable (none);
1184 dim_all_button.set_controllable (none);
1185 mono_button.set_controllable (none);
1187 dim_control->set_controllable (none);
1188 dim_display->set_controllable (none);
1189 solo_boost_control->set_controllable (none);
1190 solo_boost_display->set_controllable (none);
1195 MonitorSection::state_id() const
1197 return "monitor-section";
1201 MonitorSection::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1203 using namespace Menu_Helpers;
1205 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1209 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1210 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1214 if (i != output_menu_bundles.end()) {
1218 output_menu_bundles.push_back (b);
1220 MenuList& citems = output_menu.items();
1222 std::string n = b->name ();
1223 replace_all (n, "_", " ");
1225 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MonitorSection::bundle_output_chosen), b)));
1229 MonitorSection::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1232 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1234 if (std::find (current.begin(), current.end(), c) == current.end()) {
1235 _route->output()->connect_ports_to_bundle (c, true, this);
1237 _route->output()->disconnect_ports_from_bundle (c, this);
1242 MonitorSection::output_release (GdkEventButton *ev)
1244 switch (ev->button) {
1246 edit_output_configuration ();
1253 struct RouteCompareByName {
1254 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1255 return a->name().compare (b->name()) < 0;
1260 MonitorSection::output_press (GdkEventButton *ev)
1262 using namespace Menu_Helpers;
1264 MessageDialog msg (_("No session - no I/O changes are possible"));
1269 MenuList& citems = output_menu.items();
1270 switch (ev->button) {
1273 return false; //wait for the mouse-up to pop the dialog
1277 output_menu.set_name ("ArdourContextMenu");
1279 output_menu_bundles.clear ();
1281 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(this), &MonitorSection::disconnect_output)));
1283 citems.push_back (SeparatorElem());
1284 uint32_t const n_with_separator = citems.size ();
1286 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1288 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1290 /* give user bundles first chance at being in the menu */
1292 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1293 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1294 maybe_add_bundle_to_output_menu (*i, current);
1298 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1299 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1300 maybe_add_bundle_to_output_menu (*i, current);
1304 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1305 RouteList copy = *routes;
1306 copy.sort (RouteCompareByName ());
1307 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1308 maybe_add_bundle_to_output_menu ((*i)->output()->bundle(), current);
1311 if (citems.size() == n_with_separator) {
1312 /* no routes added; remove the separator */
1316 citems.push_back (SeparatorElem());
1317 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(this), &MonitorSection::edit_output_configuration)));
1319 output_menu.popup (1, ev->time);
1330 MonitorSection::output_button_resized (Gtk::Allocation& alloc)
1332 output_button->set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1336 MonitorSection::update_output_display ()
1338 if (!_route || !_monitor || _session->deletion_in_progress()) {
1344 boost::shared_ptr<Port> port;
1345 vector<string> port_connections;
1347 uint32_t total_connection_count = 0;
1348 uint32_t io_connection_count = 0;
1349 uint32_t ardour_connection_count = 0;
1350 uint32_t system_connection_count = 0;
1351 uint32_t other_connection_count = 0;
1353 ostringstream label;
1355 bool have_label = false;
1356 bool each_io_has_one_connection = true;
1358 string connection_name;
1359 string ardour_track_name;
1360 string other_connection_type;
1361 string system_ports;
1364 ostringstream tooltip;
1365 char * tooltip_cstr;
1367 io_count = _route->n_outputs().n_total();
1368 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(_route->name()));
1371 for (io_index = 0; io_index < io_count; ++io_index) {
1373 port = _route->output()->nth (io_index);
1375 //ignore any port connections that don't match our DataType
1376 if (port->type() != DataType::AUDIO) {
1380 port_connections.clear ();
1381 port->get_connections(port_connections);
1382 io_connection_count = 0;
1384 if (!port_connections.empty()) {
1385 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1387 string& connection_name (*i);
1389 if (connection_name.find("system:") == 0) {
1390 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1393 if (io_connection_count == 0) {
1394 tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1))
1396 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1399 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1402 if (connection_name.find("ardour:") == 0) {
1403 if (ardour_track_name.empty()) {
1404 // "ardour:Master/in 1" -> "ardour:Master/"
1405 string::size_type slash = connection_name.find("/");
1406 if (slash != string::npos) {
1407 ardour_track_name = connection_name.substr(0, slash + 1);
1411 if (connection_name.find(ardour_track_name) == 0) {
1412 ++ardour_connection_count;
1414 } else if (!pn.empty()) {
1415 if (system_ports.empty()) {
1418 system_ports += "/" + pn;
1420 if (connection_name.find("system:") == 0) {
1421 ++system_connection_count;
1423 } else if (connection_name.find("system:") == 0) {
1424 // "system:playback_123" -> "123"
1425 system_port = connection_name.substr(16);
1426 if (system_ports.empty()) {
1427 system_ports += system_port;
1429 system_ports += "/" + system_port;
1432 ++system_connection_count;
1434 if (other_connection_type.empty()) {
1435 // "jamin:in 1" -> "jamin:"
1436 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1439 if (connection_name.find(other_connection_type) == 0) {
1440 ++other_connection_count;
1444 ++total_connection_count;
1445 ++io_connection_count;
1449 if (io_connection_count != 1) {
1450 each_io_has_one_connection = false;
1454 if (total_connection_count == 0) {
1455 tooltip << endl << _("Disconnected");
1458 tooltip_cstr = new char[tooltip.str().size() + 1];
1459 strcpy(tooltip_cstr, tooltip.str().c_str());
1461 ARDOUR_UI::instance()->set_tip (output_button, tooltip_cstr, "");
1463 if (each_io_has_one_connection) {
1464 if (total_connection_count == ardour_connection_count) {
1465 // all connections are to the same track in ardour
1466 // "ardour:Master/" -> "Master"
1467 string::size_type slash = ardour_track_name.find("/");
1468 if (slash != string::npos) {
1469 label << ardour_track_name.substr(7, slash - 7);
1472 } else if (total_connection_count == system_connection_count) {
1473 // all connections are to system ports
1474 label << system_ports;
1476 } else if (total_connection_count == other_connection_count) {
1477 // all connections are to the same external program eg jamin
1478 // "jamin:" -> "jamin"
1479 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1485 if (total_connection_count == 0) {
1489 // Odd configuration
1490 label << "*" << total_connection_count << "*";
1494 output_button->set_text (label.str());
1498 MonitorSection::disconnect_output ()
1501 _route->output()->disconnect(this);
1506 MonitorSection::edit_output_configuration ()
1508 if (_output_selector == 0) {
1509 _output_selector = new MonitorSelectorWindow (_session, _route->output());
1511 _output_selector->present ();
1515 MonitorSection::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1520 boost::shared_ptr<Port> a = wa.lock ();
1521 boost::shared_ptr<Port> b = wb.lock ();
1522 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1523 update_output_display ();