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"
30 #include "gtkmm2ext/utils.h"
32 #include <gtkmm/menu.h>
33 #include <gtkmm/menuitem.h>
35 #include "ardour/audioengine.h"
36 #include "ardour/monitor_processor.h"
37 #include "ardour/port.h"
38 #include "ardour/route.h"
39 #include "ardour/user_bundle.h"
41 #include "gui_thread.h"
42 #include "monitor_section.h"
43 #include "public_editor.h"
46 #include "volume_controller.h"
47 #include "ui_config.h"
52 using namespace ARDOUR;
53 using namespace ARDOUR_UI_UTILS;
55 using namespace Gtkmm2ext;
59 Glib::RefPtr<ActionGroup> MonitorSection::monitor_actions;
61 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
63 MonitorSection::MonitorSection (Session* s)
67 , channel_table_viewport (*channel_table_scroller.get_hadjustment()
68 , *channel_table_scroller.get_vadjustment ())
71 , solo_boost_control (0)
72 , solo_cut_control (0)
75 , solo_boost_display (0)
76 , solo_cut_display (0)
77 , _output_selector (0)
78 , solo_in_place_button (_("SiP"), ArdourButton::led_default_elements)
79 , afl_button (_("AFL"), ArdourButton::led_default_elements)
80 , pfl_button (_("PFL"), ArdourButton::led_default_elements)
81 , exclusive_solo_button (ArdourButton::led_default_elements)
82 , solo_mute_override_button (ArdourButton::led_default_elements)
83 , _inhibit_solo_model_update (false)
86 using namespace Menu_Helpers;
88 Glib::RefPtr<Action> act;
90 if (!monitor_actions) {
92 /* do some static stuff */
105 rude_solo_button.set_text (_("Soloing"));
106 rude_solo_button.set_name ("rude solo");
107 rude_solo_button.show ();
109 rude_iso_button.set_text (_("Isolated"));
110 rude_iso_button.set_name ("rude isolate");
111 rude_iso_button.show ();
113 rude_audition_button.set_text (_("Auditioning"));
114 rude_audition_button.set_name ("rude audition");
115 rude_audition_button.show ();
117 Timers::blink_connect (sigc::mem_fun (*this, &MonitorSection::do_blink));
119 act = ActionManager::get_action (X_("Main"), X_("cancel-solo"));
120 rude_solo_button.set_related_action (act);
121 UI::instance()->set_tip (rude_solo_button, _("When active, something is soloed.\nClick to de-solo everything"));
123 rude_iso_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_isolate), false);
124 UI::instance()->set_tip (rude_iso_button, _("When active, something is solo-isolated.\nClick to de-isolate everything"));
126 rude_audition_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_audition), false);
127 UI::instance()->set_tip (rude_audition_button, _("When active, auditioning is active.\nClick to stop the audition"));
129 solo_in_place_button.set_name ("monitor section solo model");
130 afl_button.set_name ("monitor section solo model");
131 pfl_button.set_name ("monitor section solo model");
133 solo_model_box.set_spacing (6);
134 solo_model_box.pack_start (solo_in_place_button, true, false);
135 solo_model_box.pack_start (afl_button, true, false);
136 solo_model_box.pack_start (pfl_button, true, false);
138 solo_in_place_button.show ();
141 solo_model_box.show ();
143 act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
144 set_tooltip (solo_in_place_button, _("Solo controls affect solo-in-place"));
146 solo_in_place_button.set_related_action (act);
149 act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
150 set_tooltip (afl_button, _("Solo controls toggle after-fader-listen"));
152 afl_button.set_related_action (act);
155 act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
156 set_tooltip (pfl_button, _("Solo controls toggle pre-fader-listen"));
158 pfl_button.set_related_action (act);
163 solo_boost_control = new ArdourKnob ();
164 solo_boost_control->set_name("monitor knob");
165 solo_boost_control->set_size_request (PX_SCALE(40), PX_SCALE(40));
166 set_tooltip (*solo_boost_control, _("Gain increase for soloed signals (0dB is normal)"));
168 solo_boost_display = new ArdourDisplay ();
169 solo_boost_display->set_name("monitor section cut");
170 solo_boost_display->set_size_request (PX_SCALE(80), PX_SCALE(20));
171 solo_boost_display->add_controllable_preset(_("0 dB"), 0.0);
172 solo_boost_display->add_controllable_preset(_("3 dB"), 3.0);
173 solo_boost_display->add_controllable_preset(_("6 dB"), 6.0);
174 solo_boost_display->add_controllable_preset(_("10 dB"), 10.0);
176 HBox* solo_packer = manage (new HBox);
177 solo_packer->set_spacing (6);
178 solo_packer->show ();
180 spin_label = manage (new Label (_("Solo Boost")));
181 spin_packer = manage (new VBox);
182 spin_packer->show ();
183 spin_packer->set_spacing (3);
184 spin_packer->pack_start (*spin_label, false, false);
185 spin_packer->pack_start (*solo_boost_control, false, false);
186 spin_packer->pack_start (*solo_boost_display, false, false);
188 solo_packer->pack_start (*spin_packer, true, false);
192 solo_cut_control = new ArdourKnob ();
193 solo_cut_control->set_name ("monitor knob");
194 solo_cut_control->set_size_request (PX_SCALE(40), PX_SCALE(40));
195 set_tooltip (*solo_cut_control, _("Gain reduction non-soloed signals\nA value above -inf dB causes \"solo-in-front\""));
197 solo_cut_display = new ArdourDisplay ();
198 solo_cut_display->set_name("monitor section cut");
199 solo_cut_display->set_size_request (PX_SCALE(80), PX_SCALE(20));
200 solo_cut_display->add_controllable_preset(_("0 dB"), 0.0);
201 solo_cut_display->add_controllable_preset(_("-6 dB"), -6.0);
202 solo_cut_display->add_controllable_preset(_("-12 dB"), -12.0);
203 solo_cut_display->add_controllable_preset(_("-20 dB"), -20.0);
204 solo_cut_display->add_controllable_preset(_("OFF"), -1200.0);
206 spin_label = manage (new Label (_("SiP Cut")));
207 spin_packer = manage (new VBox);
208 spin_packer->show ();
209 spin_packer->set_spacing (3);
210 spin_packer->pack_start (*spin_label, false, false);
211 spin_packer->pack_start (*solo_cut_control, false, false);
212 spin_packer->pack_start (*solo_cut_display, false, false);
214 solo_packer->pack_start (*spin_packer, true, false);
218 dim_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
219 dim_control->set_name ("monitor knob");
220 dim_control->set_size_request (PX_SCALE(40), PX_SCALE(40));
221 set_tooltip (*dim_control, _("Gain reduction to use when dimming monitor outputs"));
223 dim_display = new ArdourDisplay ();
224 dim_display->set_name("monitor section cut");
225 dim_display->set_size_request (PX_SCALE(80), PX_SCALE(20));
226 dim_display->add_controllable_preset(_("0 dB"), 0.0);
227 dim_display->add_controllable_preset(_("-3 dB"), -3.0);
228 dim_display->add_controllable_preset(_("-6 dB"), -6.0);
229 dim_display->add_controllable_preset(_("-12 dB"), -12.0);
230 dim_display->add_controllable_preset(_("-20 dB"), -20.0);
232 HBox* dim_packer = manage (new HBox);
235 spin_label = manage (new Label (_("Dim")));
236 spin_packer = manage (new VBox);
237 spin_packer->show ();
238 spin_packer->set_spacing (3);
239 spin_packer->pack_start (*spin_label, false, false);
240 spin_packer->pack_start (*dim_control, false, false);
241 spin_packer->pack_start (*dim_display, false, false);
243 dim_packer->pack_start (*spin_packer, true, false);
245 exclusive_solo_button.set_text (_("Excl. Solo"));
246 exclusive_solo_button.set_name (X_("monitor solo exclusive"));
247 set_tooltip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time"));
249 act = ActionManager::get_action (X_("Monitor"), X_("toggle-exclusive-solo"));
251 exclusive_solo_button.set_related_action (act);
254 solo_mute_override_button.set_text (_("Solo » Mute"));
255 solo_mute_override_button.set_name (X_("monitor solo override"));
256 set_tooltip (&solo_mute_override_button, _("If enabled, solo will override mute\n(a soloed & muted track or bus will be audible)"));
258 act = ActionManager::get_action (X_("Monitor"), X_("toggle-mute-overrides-solo"));
260 solo_mute_override_button.set_related_action (act);
263 HBox* solo_opt_box = manage (new HBox);
264 solo_opt_box->set_spacing (12);
265 solo_opt_box->set_homogeneous (true);
266 solo_opt_box->pack_start (exclusive_solo_button);
267 solo_opt_box->pack_start (solo_mute_override_button);
268 solo_opt_box->show ();
270 upper_packer.set_spacing (6);
272 Gtk::HBox* rude_box = manage (new HBox);
273 rude_box->pack_start (rude_solo_button, true, true);
274 rude_box->pack_start (rude_iso_button, true, true);
276 upper_packer.pack_start (*rude_box, false, false);
277 upper_packer.pack_start (rude_audition_button, false, false);
278 upper_packer.pack_start (solo_model_box, false, false, 12);
279 upper_packer.pack_start (*solo_opt_box, false, false);
280 upper_packer.pack_start (*solo_packer, false, false, 12);
282 cut_all_button.set_text (_("Mute"));
283 cut_all_button.set_name ("monitor section cut");
284 cut_all_button.set_name (X_("monitor section cut"));
285 cut_all_button.set_size_request (-1, PX_SCALE(50));
286 cut_all_button.show ();
288 act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
290 cut_all_button.set_related_action (act);
293 dim_all_button.set_text (_("Dim"));
294 dim_all_button.set_name ("monitor section dim");
295 act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
297 dim_all_button.set_related_action (act);
300 mono_button.set_text (_("Mono"));
301 mono_button.set_name ("monitor section mono");
302 act = ActionManager::get_action (X_("Monitor"), X_("monitor-mono"));
304 mono_button.set_related_action (act);
307 HBox* bbox = manage (new HBox);
309 bbox->set_spacing (12);
310 bbox->pack_start (mono_button, true, true);
311 bbox->pack_start (dim_all_button, true, true);
313 lower_packer.set_spacing (12);
314 lower_packer.pack_start (*bbox, false, false);
315 lower_packer.pack_start (cut_all_button, false, false);
319 gain_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
320 gain_control->set_name("monitor knob");
321 gain_control->set_size_request (PX_SCALE(80), PX_SCALE(80));
323 gain_display = new ArdourDisplay ();
324 gain_display->set_name("monitor section cut");
325 gain_display->set_size_request (PX_SCALE(40), PX_SCALE(20));
326 gain_display->add_controllable_preset(_("0 dB"), 0.0);
327 gain_display->add_controllable_preset(_("-3 dB"), -3.0);
328 gain_display->add_controllable_preset(_("-6 dB"), -6.0);
329 gain_display->add_controllable_preset(_("-12 dB"), -12.0);
330 gain_display->add_controllable_preset(_("-20 dB"), -20.0);
331 gain_display->add_controllable_preset(_("-30 dB"), -30.0);
333 Label* output_label = manage (new Label (_("Output")));
334 output_label->set_name (X_("MonitorSectionLabel"));
336 output_button = new ArdourButton ();
337 output_button->set_text (_("Output"));
338 output_button->set_name (X_("monitor section cut"));
339 output_button->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
340 VBox* out_packer = manage (new VBox);
341 out_packer->set_spacing (6);
342 out_packer->pack_start (*output_label, false, false);
343 out_packer->pack_start (*output_button, false, false);
345 spin_label = manage (new Label (_("Monitor")));
346 spin_packer = manage (new VBox);
347 spin_packer->show ();
348 spin_packer->set_spacing (3);
349 spin_packer->pack_start (*spin_label, false, false);
350 spin_packer->pack_start (*gain_control, false, false);
351 spin_packer->pack_start (*gain_display, false, false);
352 spin_packer->pack_start (*out_packer, false, false, 24);
354 lower_packer.pack_start (*spin_packer, true, true);
356 channel_table_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
357 channel_table_scroller.set_size_request (-1, PX_SCALE(150));
358 channel_table_scroller.set_shadow_type (Gtk::SHADOW_NONE);
359 channel_table_scroller.show ();
360 channel_table_scroller.add (channel_table_viewport);
362 channel_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
363 channel_size_group->add_widget (channel_table_header);
364 channel_size_group->add_widget (channel_table);
366 channel_table_header.resize (1, 5);
368 Label* l1 = manage (new Label (X_(" ")));
369 l1->set_name (X_("MonitorSectionLabel"));
370 channel_table_header.attach (*l1, 0, 1, 0, 1, EXPAND|FILL);
372 l1 = manage (new Label (_("Mute")));
373 l1->set_name (X_("MonitorSectionLabel"));
374 channel_table_header.attach (*l1, 1, 2, 0, 1, EXPAND|FILL);
376 l1 = manage (new Label (_("Dim")));
377 l1->set_name (X_("MonitorSectionLabel"));
378 channel_table_header.attach (*l1, 2, 3, 0, 1, EXPAND|FILL);
380 l1 = manage (new Label (_("Solo")));
381 l1->set_name (X_("MonitorSectionLabel"));
382 channel_table_header.attach (*l1, 3, 4, 0, 1, EXPAND|FILL);
384 l1 = manage (new Label (_("Inv")));
385 l1->set_name (X_("MonitorSectionLabel"));
386 channel_table_header.attach (*l1, 4, 5, 0, 1, EXPAND|FILL);
388 channel_table_header.show ();
390 table_hpacker.pack_start (channel_table, true, true);
392 /* note that we don't pack the table_hpacker till later
395 vpacker.set_border_width (6);
396 vpacker.set_spacing (12);
397 vpacker.pack_start (upper_packer, false, false);
398 vpacker.pack_start (*dim_packer, false, false);
399 vpacker.pack_start (channel_table_header, false, false);
400 vpacker.pack_start (channel_table_packer, false, false);
401 vpacker.pack_start (lower_packer, false, false);
403 hpacker.pack_start (vpacker, true, true);
405 gain_control->show_all ();
406 gain_display->show_all ();
407 dim_control->show_all ();
408 dim_display->show_all();
409 solo_boost_control->show_all ();
410 solo_boost_display->show_all();
412 channel_table.show ();
414 upper_packer.show ();
415 lower_packer.show ();
420 assign_controllables ();
422 output_button->signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::output_press), false);
423 output_button->signal_button_release_event().connect (sigc::mem_fun(*this, &MonitorSection::output_release), false);
424 output_button->signal_size_allocate().connect (sigc::mem_fun (*this, &MonitorSection::output_button_resized));
426 _tearoff = new TearOff (hpacker);
428 /* if torn off, make this a normal window */
429 _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
430 _tearoff->tearoff_window().set_title (X_("Monitor"));
431 _tearoff->tearoff_window().signal_key_press_event().connect (sigc::ptr_fun (forward_key_press), false);
433 update_output_display();
435 /* catch changes that affect us */
436 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
437 *this, invalidator (*this), boost::bind (&MonitorSection::port_connected_or_disconnected, this, _1, _3), gui_context ()
439 Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context());
442 MonitorSection::~MonitorSection ()
444 for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
448 _channel_buttons.clear ();
449 _output_changed_connection.disconnect ();
451 delete output_button;
456 delete solo_boost_control;
457 delete solo_boost_display;
458 delete solo_cut_control;
459 delete solo_cut_display;
461 delete _output_selector;
462 _output_selector = 0;
466 MonitorSection::set_session (Session* s)
468 AxisView::set_session (s);
472 _route = _session->monitor_out ();
475 /* session with monitor section */
476 _monitor = _route->monitor_control ();
477 assign_controllables ();
478 _route->output()->changed.connect (_output_changed_connection, invalidator (*this),
479 boost::bind (&MonitorSection::update_output_display, this),
482 /* session with no monitor section */
483 _output_changed_connection.disconnect();
486 delete _output_selector;
487 _output_selector = 0;
490 if (channel_table_scroller.get_parent()) {
491 /* scroller is packed, so remove it */
492 channel_table_packer.remove (channel_table_scroller);
495 if (table_hpacker.get_parent () == &channel_table_packer) {
496 /* this occurs when the table hpacker is directly
497 packed, so remove it.
499 channel_table_packer.remove (table_hpacker);
500 } else if (table_hpacker.get_parent()) {
501 channel_table_viewport.remove ();
504 if (_monitor->output_streams().n_audio() > 7) {
505 /* put the table into a scrolled window, and then put
506 * that into the channel vpacker, after the table header
508 channel_table_viewport.add (table_hpacker);
509 channel_table_packer.pack_start (channel_table_scroller, true, true);
510 channel_table_viewport.show ();
511 channel_table_scroller.show ();
514 /* just put the channel table itself into the channel
515 * vpacker, after the table header
518 channel_table_packer.pack_start (table_hpacker, true, true);
519 channel_table_scroller.hide ();
522 table_hpacker.show ();
523 channel_table.show ();
528 _output_changed_connection.disconnect();
531 control_connections.drop_connections ();
532 rude_iso_button.unset_active_state ();
533 rude_solo_button.unset_active_state ();
534 delete _output_selector;
535 _output_selector = 0;
537 assign_controllables ();
541 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
543 cut.set_name (X_("monitor section cut"));
544 dim.set_name (X_("monitor section dim"));
545 solo.set_name (X_("monitor section solo"));
546 invert.set_name (X_("monitor section invert"));
548 cut.unset_flags (Gtk::CAN_FOCUS);
549 dim.unset_flags (Gtk::CAN_FOCUS);
550 solo.unset_flags (Gtk::CAN_FOCUS);
551 invert.unset_flags (Gtk::CAN_FOCUS);
555 MonitorSection::populate_buttons ()
561 Glib::RefPtr<Action> act;
562 uint32_t nchans = _monitor->output_streams().n_audio();
564 channel_table.resize (nchans, 5);
565 channel_table.set_col_spacings (6);
566 channel_table.set_row_spacings (6);
567 channel_table.set_homogeneous (true);
569 const uint32_t row_offset = 0;
571 for (uint32_t i = 0; i < nchans; ++i) {
584 snprintf (buf, sizeof (buf), "%d", i+1);
588 Label* label = manage (new Label (l));
589 channel_table.attach (*label, 0, 1, i+row_offset, i+row_offset+1, EXPAND|FILL);
591 ChannelButtonSet* cbs = new ChannelButtonSet;
593 _channel_buttons.push_back (cbs);
595 channel_table.attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, EXPAND|FILL);
596 channel_table.attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, EXPAND|FILL);
597 channel_table.attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, EXPAND|FILL);
598 channel_table.attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, EXPAND|FILL);
600 snprintf (buf, sizeof (buf), "monitor-cut-%u", i+1);
601 act = ActionManager::get_action (X_("Monitor"), buf);
603 cbs->cut.set_related_action (act);
606 snprintf (buf, sizeof (buf), "monitor-dim-%u", i+1);
607 act = ActionManager::get_action (X_("Monitor"), buf);
609 cbs->dim.set_related_action (act);
612 snprintf (buf, sizeof (buf), "monitor-solo-%u", i+1);
613 act = ActionManager::get_action (X_("Monitor"), buf);
615 cbs->solo.set_related_action (act);
618 snprintf (buf, sizeof (buf), "monitor-invert-%u", i+1);
619 act = ActionManager::get_action (X_("Monitor"), buf);
621 cbs->invert.set_related_action (act);
625 channel_table.show_all ();
629 MonitorSection::toggle_exclusive_solo ()
635 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo");
637 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
638 Config->set_exclusive_solo (tact->get_active());
644 MonitorSection::toggle_mute_overrides_solo ()
650 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo");
652 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
653 Config->set_solo_mute_override (tact->get_active());
658 MonitorSection::dim_all ()
664 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
666 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
667 _monitor->set_dim_all (tact->get_active());
673 MonitorSection::cut_all ()
679 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
681 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
682 _monitor->set_cut_all (tact->get_active());
687 MonitorSection::mono ()
693 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
695 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
696 _monitor->set_mono (tact->get_active());
701 MonitorSection::cut_channel (uint32_t chn)
708 snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
710 --chn; // 0-based in backend
712 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
714 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
715 _monitor->set_cut (chn, tact->get_active());
720 MonitorSection::dim_channel (uint32_t chn)
727 snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
729 --chn; // 0-based in backend
731 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
733 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
734 _monitor->set_dim (chn, tact->get_active());
740 MonitorSection::solo_channel (uint32_t chn)
747 snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
749 --chn; // 0-based in backend
751 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
753 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
754 _monitor->set_solo (chn, tact->get_active());
760 MonitorSection::invert_channel (uint32_t chn)
767 snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
769 --chn; // 0-based in backend
771 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
773 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
774 _monitor->set_polarity (chn, tact->get_active());
779 MonitorSection::register_actions ()
783 Glib::RefPtr<Action> act;
785 monitor_actions = ActionGroup::create (X_("Monitor"));
786 ActionManager::add_action_group (monitor_actions);
788 ActionManager::register_toggle_action (monitor_actions, "monitor-mono", "", _("Switch monitor to mono"),
789 sigc::mem_fun (*this, &MonitorSection::mono));
791 ActionManager::register_toggle_action (monitor_actions, "monitor-cut-all", "", _("Cut monitor"),
792 sigc::mem_fun (*this, &MonitorSection::cut_all));
794 ActionManager::register_toggle_action (monitor_actions, "monitor-dim-all", "", _("Dim monitor"),
795 sigc::mem_fun (*this, &MonitorSection::dim_all));
797 act = ActionManager::register_toggle_action (monitor_actions, "toggle-exclusive-solo", "", _("Toggle exclusive solo mode"),
798 sigc::mem_fun (*this, &MonitorSection::toggle_exclusive_solo));
800 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
801 tact->set_active (Config->get_exclusive_solo());
803 act = ActionManager::register_toggle_action (monitor_actions, "toggle-mute-overrides-solo", "", _("Toggle mute overrides solo mode"),
804 sigc::mem_fun (*this, &MonitorSection::toggle_mute_overrides_solo));
806 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
807 tact->set_active (Config->get_solo_mute_override());
810 /* note the 1-based counting (for naming - backend uses 0-based) */
812 for (uint32_t chn = 1; chn <= 16; ++chn) {
814 action_name = string_compose (X_("monitor-cut-%1"), chn);
815 action_descr = string_compose (_("Cut monitor channel %1"), chn);
816 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
817 sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
819 action_name = string_compose (X_("monitor-dim-%1"), chn);
820 action_descr = string_compose (_("Dim monitor channel %1"), chn);
821 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
822 sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
824 action_name = string_compose (X_("monitor-solo-%1"), chn);
825 action_descr = string_compose (_("Solo monitor channel %1"), chn);
826 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
827 sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
829 action_name = string_compose (X_("monitor-invert-%1"), chn);
830 action_descr = string_compose (_("Invert monitor channel %1"), chn);
831 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
832 sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
837 Glib::RefPtr<ActionGroup> solo_actions = ActionGroup::create (X_("Solo"));
838 RadioAction::Group solo_group;
840 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-in-place", "", _("In-place solo"),
841 sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
842 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-afl", "", _("After Fade Listen (AFL) solo"),
843 sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
844 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-pfl", "", _("Pre Fade Listen (PFL) solo"),
845 sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
847 ActionManager::add_action_group (solo_actions);
851 MonitorSection::solo_use_in_place ()
853 /* this is driven by a toggle on a radio group, and so is invoked twice,
854 once for the item that became inactive and once for the one that became
858 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
861 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
863 if (!ract->get_active ()) {
864 /* We are turning SiP off, which means that AFL or PFL will be turned on
865 shortly; don't update the solo model in the mean time, as if the currently
866 configured listen position is not the one that is about to be turned on,
867 things will go wrong.
869 _inhibit_solo_model_update = true;
871 Config->set_solo_control_is_listen_control (!ract->get_active());
872 _inhibit_solo_model_update = false;
878 MonitorSection::solo_use_afl ()
880 /* this is driven by a toggle on a radio group, and so is invoked twice,
881 once for the item that became inactive and once for the one that became
885 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
887 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
889 if (ract->get_active()) {
890 Config->set_solo_control_is_listen_control (true);
891 Config->set_listen_position (AfterFaderListen);
898 MonitorSection::solo_use_pfl ()
900 /* this is driven by a toggle on a radio group, and so is invoked twice,
901 once for the item that became inactive and once for the one that became
905 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
907 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
909 if (ract->get_active()) {
910 Config->set_solo_control_is_listen_control (true);
911 Config->set_listen_position (PreFaderListen);
918 MonitorSection::update_solo_model ()
920 if (_inhibit_solo_model_update) {
924 const char* action_name = 0;
925 Glib::RefPtr<Action> act;
927 if (Config->get_solo_control_is_listen_control()) {
928 switch (Config->get_listen_position()) {
929 case AfterFaderListen:
930 action_name = X_("solo-use-afl");
933 action_name = X_("solo-use-pfl");
937 action_name = X_("solo-use-in-place");
940 act = ActionManager::get_action (X_("Solo"), action_name);
943 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
945 /* because these are radio buttons, one of them will be
946 active no matter what. to trigger a change in the
947 action so that the view picks it up, toggle it.
949 if (ract->get_active()) {
950 ract->set_active (false);
952 ract->set_active (true);
959 MonitorSection::map_state ()
961 if (!_route || !_monitor) {
965 Glib::RefPtr<Action> act;
967 update_solo_model ();
969 act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
971 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
973 tact->set_active (_monitor->cut_all());
977 act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
979 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
981 tact->set_active (_monitor->dim_all());
985 act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
987 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
989 tact->set_active (_monitor->mono());
993 uint32_t nchans = _monitor->output_streams().n_audio();
995 assert (nchans == _channel_buttons.size ());
997 for (uint32_t n = 0; n < nchans; ++n) {
999 char action_name[32];
1001 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n);
1002 act = ActionManager::get_action (X_("Monitor"), action_name);
1004 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1006 tact->set_active (_monitor->cut (n));
1010 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n);
1011 act = ActionManager::get_action (X_("Monitor"), action_name);
1013 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1015 tact->set_active (_monitor->dimmed (n));
1019 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n);
1020 act = ActionManager::get_action (X_("Monitor"), action_name);
1022 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1024 tact->set_active (_monitor->soloed (n));
1028 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n);
1029 act = ActionManager::get_action (X_("Monitor"), action_name);
1031 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1033 tact->set_active (_monitor->inverted (n));
1040 MonitorSection::do_blink (bool onoff)
1043 audition_blink (onoff);
1047 MonitorSection::audition_blink (bool onoff)
1049 if (_session == 0) {
1053 if (_session->is_auditioning()) {
1054 rude_audition_button.set_active (onoff);
1056 rude_audition_button.set_active (false);
1061 MonitorSection::solo_blink (bool onoff)
1063 if (_session == 0) {
1067 if (_session->soloing() || _session->listening()) {
1068 rude_solo_button.set_active (onoff);
1070 if (_session->soloing()) {
1071 if (_session->solo_isolated()) {
1072 rude_iso_button.set_active (onoff);
1074 rude_iso_button.set_active (false);
1079 rude_solo_button.set_active (false);
1080 rude_iso_button.set_active (false);
1085 MonitorSection::cancel_isolate (GdkEventButton*)
1088 boost::shared_ptr<RouteList> rl (_session->get_routes ());
1089 _session->set_solo_isolated (rl, false, Session::rt_cleanup, true);
1096 MonitorSection::cancel_audition (GdkEventButton*)
1099 _session->cancel_audition();
1104 #define SYNCHRONIZE_TOGGLE_ACTION(action, value) \
1106 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(action); \
1107 if (tact && tact->get_active() != value) { \
1108 tact->set_active(value); \
1113 MonitorSection::parameter_changed (std::string name)
1115 if (name == "solo-control-is-listen-control") {
1116 update_solo_model ();
1117 } else if (name == "listen-position") {
1118 update_solo_model ();
1119 } else if (name == "solo-mute-override") {
1120 SYNCHRONIZE_TOGGLE_ACTION(
1121 ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo"),
1122 Config->get_solo_mute_override ())
1123 } else if (name == "exclusive-solo") {
1124 SYNCHRONIZE_TOGGLE_ACTION(
1125 ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo"),
1126 Config->get_exclusive_solo ())
1131 MonitorSection::assign_controllables ()
1133 boost::shared_ptr<Controllable> none;
1135 if (!gain_control) {
1136 /* too early - GUI controls not set up yet */
1141 solo_cut_control->set_controllable (_session->solo_cut_control());
1142 solo_cut_display->set_controllable (_session->solo_cut_control());
1144 solo_cut_control->set_controllable (none);
1145 solo_cut_display->set_controllable (none);
1149 gain_control->set_controllable (_route->gain_control());
1150 gain_display->set_controllable (_route->gain_control());
1152 gain_control->set_controllable (none);
1157 cut_all_button.set_controllable (_monitor->cut_control());
1158 cut_all_button.watch ();
1159 dim_all_button.set_controllable (_monitor->dim_control());
1160 dim_all_button.watch ();
1161 mono_button.set_controllable (_monitor->mono_control());
1162 mono_button.watch ();
1164 dim_control->set_controllable (_monitor->dim_level_control ());
1165 dim_display->set_controllable (_monitor->dim_level_control ());
1166 solo_boost_control->set_controllable (_monitor->solo_boost_control ());
1167 solo_boost_display->set_controllable (_monitor->solo_boost_control ());
1171 cut_all_button.set_controllable (none);
1172 dim_all_button.set_controllable (none);
1173 mono_button.set_controllable (none);
1175 dim_control->set_controllable (none);
1176 dim_display->set_controllable (none);
1177 solo_boost_control->set_controllable (none);
1178 solo_boost_display->set_controllable (none);
1183 MonitorSection::state_id() const
1185 return "monitor-section";
1189 MonitorSection::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1191 using namespace Menu_Helpers;
1193 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1197 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1198 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1202 if (i != output_menu_bundles.end()) {
1206 output_menu_bundles.push_back (b);
1208 MenuList& citems = output_menu.items();
1210 std::string n = b->name ();
1211 replace_all (n, "_", " ");
1213 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MonitorSection::bundle_output_chosen), b)));
1217 MonitorSection::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1220 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1222 if (std::find (current.begin(), current.end(), c) == current.end()) {
1223 _route->output()->connect_ports_to_bundle (c, true, this);
1225 _route->output()->disconnect_ports_from_bundle (c, this);
1230 MonitorSection::output_release (GdkEventButton *ev)
1232 switch (ev->button) {
1234 edit_output_configuration ();
1241 struct RouteCompareByName {
1242 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1243 return a->name().compare (b->name()) < 0;
1248 MonitorSection::output_press (GdkEventButton *ev)
1250 using namespace Menu_Helpers;
1252 MessageDialog msg (_("No session - no I/O changes are possible"));
1257 MenuList& citems = output_menu.items();
1258 switch (ev->button) {
1261 return false; //wait for the mouse-up to pop the dialog
1265 output_menu.set_name ("ArdourContextMenu");
1267 output_menu_bundles.clear ();
1269 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(this), &MonitorSection::disconnect_output)));
1271 citems.push_back (SeparatorElem());
1272 uint32_t const n_with_separator = citems.size ();
1274 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1276 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1278 /* give user bundles first chance at being in the menu */
1280 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1281 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1282 maybe_add_bundle_to_output_menu (*i, current);
1286 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1287 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1288 maybe_add_bundle_to_output_menu (*i, current);
1292 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1293 RouteList copy = *routes;
1294 copy.sort (RouteCompareByName ());
1295 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1296 maybe_add_bundle_to_output_menu ((*i)->output()->bundle(), current);
1299 if (citems.size() == n_with_separator) {
1300 /* no routes added; remove the separator */
1304 citems.push_back (SeparatorElem());
1305 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(this), &MonitorSection::edit_output_configuration)));
1307 output_menu.popup (1, ev->time);
1318 MonitorSection::output_button_resized (Gtk::Allocation& alloc)
1320 output_button->set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1324 MonitorSection::update_output_display ()
1326 if (!_route || !_monitor || _session->deletion_in_progress()) {
1332 boost::shared_ptr<Port> port;
1333 vector<string> port_connections;
1335 uint32_t total_connection_count = 0;
1336 uint32_t io_connection_count = 0;
1337 uint32_t ardour_connection_count = 0;
1338 uint32_t system_connection_count = 0;
1339 uint32_t other_connection_count = 0;
1341 ostringstream label;
1343 bool have_label = false;
1344 bool each_io_has_one_connection = true;
1346 string connection_name;
1347 string ardour_track_name;
1348 string other_connection_type;
1349 string system_ports;
1352 ostringstream tooltip;
1353 char * tooltip_cstr;
1355 io_count = _route->n_outputs().n_total();
1356 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (_route->name()));
1359 for (io_index = 0; io_index < io_count; ++io_index) {
1361 port = _route->output()->nth (io_index);
1363 //ignore any port connections that don't match our DataType
1364 if (port->type() != DataType::AUDIO) {
1368 port_connections.clear ();
1369 port->get_connections(port_connections);
1370 io_connection_count = 0;
1372 if (!port_connections.empty()) {
1373 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1375 string& connection_name (*i);
1377 if (connection_name.find("system:") == 0) {
1378 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1381 if (io_connection_count == 0) {
1382 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1384 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1387 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1390 if (connection_name.find("ardour:") == 0) {
1391 if (ardour_track_name.empty()) {
1392 // "ardour:Master/in 1" -> "ardour:Master/"
1393 string::size_type slash = connection_name.find("/");
1394 if (slash != string::npos) {
1395 ardour_track_name = connection_name.substr(0, slash + 1);
1399 if (connection_name.find(ardour_track_name) == 0) {
1400 ++ardour_connection_count;
1402 } else if (!pn.empty()) {
1403 if (system_ports.empty()) {
1406 system_ports += "/" + pn;
1408 if (connection_name.find("system:") == 0) {
1409 ++system_connection_count;
1411 } else if (connection_name.find("system:") == 0) {
1412 // "system:playback_123" -> "123"
1413 system_port = connection_name.substr(16);
1414 if (system_ports.empty()) {
1415 system_ports += system_port;
1417 system_ports += "/" + system_port;
1420 ++system_connection_count;
1422 if (other_connection_type.empty()) {
1423 // "jamin:in 1" -> "jamin:"
1424 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1427 if (connection_name.find(other_connection_type) == 0) {
1428 ++other_connection_count;
1432 ++total_connection_count;
1433 ++io_connection_count;
1437 if (io_connection_count != 1) {
1438 each_io_has_one_connection = false;
1442 if (total_connection_count == 0) {
1443 tooltip << endl << _("Disconnected");
1446 tooltip_cstr = new char[tooltip.str().size() + 1];
1447 strcpy(tooltip_cstr, tooltip.str().c_str());
1449 set_tooltip (output_button, tooltip_cstr, "");
1451 if (each_io_has_one_connection) {
1452 if (total_connection_count == ardour_connection_count) {
1453 // all connections are to the same track in ardour
1454 // "ardour:Master/" -> "Master"
1455 string::size_type slash = ardour_track_name.find("/");
1456 if (slash != string::npos) {
1457 label << ardour_track_name.substr(7, slash - 7);
1460 } else if (total_connection_count == system_connection_count) {
1461 // all connections are to system ports
1462 label << system_ports;
1464 } else if (total_connection_count == other_connection_count) {
1465 // all connections are to the same external program eg jamin
1466 // "jamin:" -> "jamin"
1467 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1473 if (total_connection_count == 0) {
1477 // Odd configuration
1478 label << "*" << total_connection_count << "*";
1482 output_button->set_text (label.str());
1486 MonitorSection::disconnect_output ()
1489 _route->output()->disconnect(this);
1494 MonitorSection::edit_output_configuration ()
1496 if (_output_selector == 0) {
1497 _output_selector = new MonitorSelectorWindow (_session, _route->output());
1499 _output_selector->present ();
1503 MonitorSection::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1508 boost::shared_ptr<Port> a = wa.lock ();
1509 boost::shared_ptr<Port> b = wb.lock ();
1510 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1511 update_output_display ();