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 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
60 MonitorSection::MonitorSection (Session* s)
64 , channel_table_viewport (*channel_table_scroller.get_hadjustment()
65 , *channel_table_scroller.get_vadjustment ())
68 , solo_boost_control (0)
69 , solo_cut_control (0)
72 , solo_boost_display (0)
73 , solo_cut_display (0)
74 , _output_selector (0)
75 , solo_in_place_button (_("SiP"), ArdourButton::led_default_elements)
76 , afl_button (_("AFL"), ArdourButton::led_default_elements)
77 , pfl_button (_("PFL"), ArdourButton::led_default_elements)
78 , exclusive_solo_button (ArdourButton::led_default_elements)
79 , solo_mute_override_button (ArdourButton::led_default_elements)
80 , _inhibit_solo_model_update (false)
83 using namespace Menu_Helpers;
85 Glib::RefPtr<Action> act;
87 if (!monitor_actions) {
89 /* do some static stuff */
102 rude_solo_button.set_text (_("Soloing"));
103 rude_solo_button.set_name ("rude solo");
104 rude_solo_button.show ();
106 rude_iso_button.set_text (_("Isolated"));
107 rude_iso_button.set_name ("rude isolate");
108 rude_iso_button.show ();
110 rude_audition_button.set_text (_("Auditioning"));
111 rude_audition_button.set_name ("rude audition");
112 rude_audition_button.show ();
114 Timers::blink_connect (sigc::mem_fun (*this, &MonitorSection::do_blink));
116 rude_solo_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_solo), false);
117 UI::instance()->set_tip (rude_solo_button, _("When active, something is soloed.\nClick to de-solo everything"));
119 rude_iso_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_isolate), false);
120 UI::instance()->set_tip (rude_iso_button, _("When active, something is solo-isolated.\nClick to de-isolate everything"));
122 rude_audition_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_audition), false);
123 UI::instance()->set_tip (rude_audition_button, _("When active, auditioning is active.\nClick to stop the audition"));
125 solo_in_place_button.set_name ("monitor section solo model");
126 afl_button.set_name ("monitor section solo model");
127 pfl_button.set_name ("monitor section solo model");
129 solo_model_box.set_spacing (6);
130 solo_model_box.pack_start (solo_in_place_button, true, false);
131 solo_model_box.pack_start (afl_button, true, false);
132 solo_model_box.pack_start (pfl_button, true, false);
134 solo_in_place_button.show ();
137 solo_model_box.show ();
139 act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
140 ARDOUR_UI::instance()->tooltips().set_tip (solo_in_place_button, _("Solo controls affect solo-in-place"));
142 solo_in_place_button.set_related_action (act);
145 act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
146 ARDOUR_UI::instance()->tooltips().set_tip (afl_button, _("Solo controls toggle after-fader-listen"));
148 afl_button.set_related_action (act);
151 act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
152 ARDOUR_UI::instance()->tooltips().set_tip (pfl_button, _("Solo controls toggle pre-fader-listen"));
154 pfl_button.set_related_action (act);
159 solo_boost_control = new ArdourKnob ();
160 solo_boost_control->set_name("monitor knob");
161 solo_boost_control->set_size_request (PX_SCALE(40), PX_SCALE(40));
162 ARDOUR_UI::instance()->tooltips().set_tip (*solo_boost_control, _("Gain increase for soloed signals (0dB is normal)"));
164 solo_boost_display = new ArdourDisplay ();
165 solo_boost_display->set_name("monitor section cut");
166 solo_boost_display->set_size_request (PX_SCALE(80), PX_SCALE(20));
167 solo_boost_display->add_controllable_preset(_("0 dB"), 0.0);
168 solo_boost_display->add_controllable_preset(_("3 dB"), 3.0);
169 solo_boost_display->add_controllable_preset(_("6 dB"), 6.0);
170 solo_boost_display->add_controllable_preset(_("10 dB"), 10.0);
172 HBox* solo_packer = manage (new HBox);
173 solo_packer->set_spacing (6);
174 solo_packer->show ();
176 spin_label = manage (new Label (_("Solo Boost")));
177 spin_packer = manage (new VBox);
178 spin_packer->show ();
179 spin_packer->set_spacing (3);
180 spin_packer->pack_start (*spin_label, false, false);
181 spin_packer->pack_start (*solo_boost_control, false, false);
182 spin_packer->pack_start (*solo_boost_display, false, false);
184 solo_packer->pack_start (*spin_packer, true, false);
188 solo_cut_control = new ArdourKnob ();
189 solo_cut_control->set_name ("monitor knob");
190 solo_cut_control->set_size_request (PX_SCALE(40), PX_SCALE(40));
191 ARDOUR_UI::instance()->tooltips().set_tip (*solo_cut_control, _("Gain reduction non-soloed signals\nA value above -inf dB causes \"solo-in-front\""));
193 solo_cut_display = new ArdourDisplay ();
194 solo_cut_display->set_name("monitor section cut");
195 solo_cut_display->set_size_request (PX_SCALE(80), PX_SCALE(20));
196 solo_cut_display->add_controllable_preset(_("0 dB"), 0.0);
197 solo_cut_display->add_controllable_preset(_("-6 dB"), -6.0);
198 solo_cut_display->add_controllable_preset(_("-12 dB"), -12.0);
199 solo_cut_display->add_controllable_preset(_("-20 dB"), -20.0);
200 solo_cut_display->add_controllable_preset(_("OFF"), -1200.0);
202 spin_label = manage (new Label (_("SiP Cut")));
203 spin_packer = manage (new VBox);
204 spin_packer->show ();
205 spin_packer->set_spacing (3);
206 spin_packer->pack_start (*spin_label, false, false);
207 spin_packer->pack_start (*solo_cut_control, false, false);
208 spin_packer->pack_start (*solo_cut_display, false, false);
210 solo_packer->pack_start (*spin_packer, true, false);
214 dim_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
215 dim_control->set_name ("monitor knob");
216 dim_control->set_size_request (PX_SCALE(40), PX_SCALE(40));
217 ARDOUR_UI::instance()->tooltips().set_tip (*dim_control, _("Gain reduction to use when dimming monitor outputs"));
219 dim_display = new ArdourDisplay ();
220 dim_display->set_name("monitor section cut");
221 dim_display->set_size_request (PX_SCALE(80), PX_SCALE(20));
222 dim_display->add_controllable_preset(_("0 dB"), 0.0);
223 dim_display->add_controllable_preset(_("-3 dB"), -3.0);
224 dim_display->add_controllable_preset(_("-6 dB"), -6.0);
225 dim_display->add_controllable_preset(_("-12 dB"), -12.0);
226 dim_display->add_controllable_preset(_("-20 dB"), -20.0);
228 HBox* dim_packer = manage (new HBox);
231 spin_label = manage (new Label (_("Dim")));
232 spin_packer = manage (new VBox);
233 spin_packer->show ();
234 spin_packer->set_spacing (3);
235 spin_packer->pack_start (*spin_label, false, false);
236 spin_packer->pack_start (*dim_control, false, false);
237 spin_packer->pack_start (*dim_display, false, false);
239 dim_packer->pack_start (*spin_packer, true, false);
241 exclusive_solo_button.set_text (_("Excl. Solo"));
242 exclusive_solo_button.set_name (X_("monitor solo exclusive"));
243 ARDOUR_UI::instance()->set_tip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time"));
245 act = ActionManager::get_action (X_("Monitor"), X_("toggle-exclusive-solo"));
247 exclusive_solo_button.set_related_action (act);
250 solo_mute_override_button.set_text (_("Solo » Mute"));
251 solo_mute_override_button.set_name (X_("monitor solo override"));
252 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)"));
254 act = ActionManager::get_action (X_("Monitor"), X_("toggle-mute-overrides-solo"));
256 solo_mute_override_button.set_related_action (act);
259 HBox* solo_opt_box = manage (new HBox);
260 solo_opt_box->set_spacing (12);
261 solo_opt_box->set_homogeneous (true);
262 solo_opt_box->pack_start (exclusive_solo_button);
263 solo_opt_box->pack_start (solo_mute_override_button);
264 solo_opt_box->show ();
266 upper_packer.set_spacing (6);
268 Gtk::HBox* rude_box = manage (new HBox);
269 rude_box->pack_start (rude_solo_button, true, true);
270 rude_box->pack_start (rude_iso_button, true, true);
272 upper_packer.pack_start (*rude_box, false, false);
273 upper_packer.pack_start (rude_audition_button, false, false);
274 upper_packer.pack_start (solo_model_box, false, false, 12);
275 upper_packer.pack_start (*solo_opt_box, false, false);
276 upper_packer.pack_start (*solo_packer, false, false, 12);
278 cut_all_button.set_text (_("Mute"));
279 cut_all_button.set_name ("monitor section cut");
280 cut_all_button.set_name (X_("monitor section cut"));
281 cut_all_button.set_size_request (-1, PX_SCALE(50));
282 cut_all_button.show ();
284 act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
286 cut_all_button.set_related_action (act);
289 dim_all_button.set_text (_("Dim"));
290 dim_all_button.set_name ("monitor section dim");
291 act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
293 dim_all_button.set_related_action (act);
296 mono_button.set_text (_("Mono"));
297 mono_button.set_name ("monitor section mono");
298 act = ActionManager::get_action (X_("Monitor"), X_("monitor-mono"));
300 mono_button.set_related_action (act);
303 HBox* bbox = manage (new HBox);
305 bbox->set_spacing (12);
306 bbox->pack_start (mono_button, true, true);
307 bbox->pack_start (dim_all_button, true, true);
309 lower_packer.set_spacing (12);
310 lower_packer.pack_start (*bbox, false, false);
311 lower_packer.pack_start (cut_all_button, false, false);
315 gain_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
316 gain_control->set_name("monitor knob");
317 gain_control->set_size_request (PX_SCALE(80), PX_SCALE(80));
319 gain_display = new ArdourDisplay ();
320 gain_display->set_name("monitor section cut");
321 gain_display->set_size_request (PX_SCALE(40), PX_SCALE(20));
322 gain_display->add_controllable_preset(_("0 dB"), 0.0);
323 gain_display->add_controllable_preset(_("-3 dB"), -3.0);
324 gain_display->add_controllable_preset(_("-6 dB"), -6.0);
325 gain_display->add_controllable_preset(_("-12 dB"), -12.0);
326 gain_display->add_controllable_preset(_("-20 dB"), -20.0);
327 gain_display->add_controllable_preset(_("-30 dB"), -30.0);
329 Label* output_label = manage (new Label (_("Output")));
330 output_label->set_name (X_("MonitorSectionLabel"));
332 output_button = new ArdourButton ();
333 output_button->set_text (_("Output"));
334 output_button->set_name (X_("monitor section cut"));
335 output_button->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
336 VBox* out_packer = manage (new VBox);
337 out_packer->set_spacing (6);
338 out_packer->pack_start (*output_label, false, false);
339 out_packer->pack_start (*output_button, false, false);
341 spin_label = manage (new Label (_("Monitor")));
342 spin_packer = manage (new VBox);
343 spin_packer->show ();
344 spin_packer->set_spacing (3);
345 spin_packer->pack_start (*spin_label, false, false);
346 spin_packer->pack_start (*gain_control, false, false);
347 spin_packer->pack_start (*gain_display, false, false);
348 spin_packer->pack_start (*out_packer, false, false, 24);
350 lower_packer.pack_start (*spin_packer, true, true);
352 channel_table_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
353 channel_table_scroller.set_size_request (-1, PX_SCALE(150));
354 channel_table_scroller.set_shadow_type (Gtk::SHADOW_NONE);
355 channel_table_scroller.show ();
356 channel_table_scroller.add (channel_table_viewport);
358 channel_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
359 channel_size_group->add_widget (channel_table_header);
360 channel_size_group->add_widget (channel_table);
362 channel_table_header.resize (1, 5);
364 Label* l1 = manage (new Label (X_(" ")));
365 l1->set_name (X_("MonitorSectionLabel"));
366 channel_table_header.attach (*l1, 0, 1, 0, 1, EXPAND|FILL);
368 l1 = manage (new Label (_("Mute")));
369 l1->set_name (X_("MonitorSectionLabel"));
370 channel_table_header.attach (*l1, 1, 2, 0, 1, EXPAND|FILL);
372 l1 = manage (new Label (_("Dim")));
373 l1->set_name (X_("MonitorSectionLabel"));
374 channel_table_header.attach (*l1, 2, 3, 0, 1, EXPAND|FILL);
376 l1 = manage (new Label (_("Solo")));
377 l1->set_name (X_("MonitorSectionLabel"));
378 channel_table_header.attach (*l1, 3, 4, 0, 1, EXPAND|FILL);
380 l1 = manage (new Label (_("Inv")));
381 l1->set_name (X_("MonitorSectionLabel"));
382 channel_table_header.attach (*l1, 4, 5, 0, 1, EXPAND|FILL);
384 channel_table_header.show ();
386 table_hpacker.pack_start (channel_table, true, true);
388 /* note that we don't pack the table_hpacker till later
391 vpacker.set_border_width (6);
392 vpacker.set_spacing (12);
393 vpacker.pack_start (upper_packer, false, false);
394 vpacker.pack_start (*dim_packer, false, false);
395 vpacker.pack_start (channel_table_header, false, false);
396 vpacker.pack_start (channel_table_packer, false, false);
397 vpacker.pack_start (lower_packer, false, false);
399 hpacker.pack_start (vpacker, true, true);
401 gain_control->show_all ();
402 gain_display->show_all ();
403 dim_control->show_all ();
404 dim_display->show_all();
405 solo_boost_control->show_all ();
406 solo_boost_display->show_all();
408 channel_table.show ();
410 upper_packer.show ();
411 lower_packer.show ();
416 assign_controllables ();
418 output_button->signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::output_press), false);
419 output_button->signal_button_release_event().connect (sigc::mem_fun(*this, &MonitorSection::output_release), false);
420 output_button->signal_size_allocate().connect (sigc::mem_fun (*this, &MonitorSection::output_button_resized));
422 _tearoff = new TearOff (hpacker);
424 /* if torn off, make this a normal window */
425 _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
426 _tearoff->tearoff_window().set_title (X_("Monitor"));
427 _tearoff->tearoff_window().signal_key_press_event().connect (sigc::ptr_fun (forward_key_press), false);
429 update_output_display();
431 /* catch changes that affect us */
432 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
433 *this, invalidator (*this), boost::bind (&MonitorSection::port_connected_or_disconnected, this, _1, _3), gui_context ()
435 Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context());
438 MonitorSection::~MonitorSection ()
440 for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
444 _channel_buttons.clear ();
445 _output_changed_connection.disconnect ();
447 delete output_button;
452 delete solo_boost_control;
453 delete solo_boost_display;
454 delete solo_cut_control;
455 delete solo_cut_display;
457 delete _output_selector;
458 _output_selector = 0;
462 MonitorSection::set_session (Session* s)
464 AxisView::set_session (s);
468 _route = _session->monitor_out ();
471 /* session with monitor section */
472 _monitor = _route->monitor_control ();
473 assign_controllables ();
474 _route->output()->changed.connect (_output_changed_connection, invalidator (*this),
475 boost::bind (&MonitorSection::update_output_display, this),
478 /* session with no monitor section */
479 _output_changed_connection.disconnect();
482 delete _output_selector;
483 _output_selector = 0;
486 if (channel_table_scroller.get_parent()) {
487 /* scroller is packed, so remove it */
488 channel_table_packer.remove (channel_table_scroller);
491 if (table_hpacker.get_parent () == &channel_table_packer) {
492 /* this occurs when the table hpacker is directly
493 packed, so remove it.
495 channel_table_packer.remove (table_hpacker);
496 } else if (table_hpacker.get_parent()) {
497 channel_table_viewport.remove ();
500 if (_monitor->output_streams().n_audio() > 7) {
501 /* put the table into a scrolled window, and then put
502 * that into the channel vpacker, after the table header
504 channel_table_viewport.add (table_hpacker);
505 channel_table_packer.pack_start (channel_table_scroller, true, true);
506 channel_table_viewport.show ();
507 channel_table_scroller.show ();
510 /* just put the channel table itself into the channel
511 * vpacker, after the table header
514 channel_table_packer.pack_start (table_hpacker, true, true);
515 channel_table_scroller.hide ();
518 table_hpacker.show ();
519 channel_table.show ();
524 _output_changed_connection.disconnect();
527 control_connections.drop_connections ();
528 rude_iso_button.unset_active_state ();
529 rude_solo_button.unset_active_state ();
530 delete _output_selector;
531 _output_selector = 0;
533 assign_controllables ();
537 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
539 cut.set_name (X_("monitor section cut"));
540 dim.set_name (X_("monitor section dim"));
541 solo.set_name (X_("monitor section solo"));
542 invert.set_name (X_("monitor section invert"));
544 cut.unset_flags (Gtk::CAN_FOCUS);
545 dim.unset_flags (Gtk::CAN_FOCUS);
546 solo.unset_flags (Gtk::CAN_FOCUS);
547 invert.unset_flags (Gtk::CAN_FOCUS);
551 MonitorSection::populate_buttons ()
557 Glib::RefPtr<Action> act;
558 uint32_t nchans = _monitor->output_streams().n_audio();
560 channel_table.resize (nchans, 5);
561 channel_table.set_col_spacings (6);
562 channel_table.set_row_spacings (6);
563 channel_table.set_homogeneous (true);
565 const uint32_t row_offset = 0;
567 for (uint32_t i = 0; i < nchans; ++i) {
580 snprintf (buf, sizeof (buf), "%d", i+1);
584 Label* label = manage (new Label (l));
585 channel_table.attach (*label, 0, 1, i+row_offset, i+row_offset+1, EXPAND|FILL);
587 ChannelButtonSet* cbs = new ChannelButtonSet;
589 _channel_buttons.push_back (cbs);
591 channel_table.attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, EXPAND|FILL);
592 channel_table.attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, EXPAND|FILL);
593 channel_table.attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, EXPAND|FILL);
594 channel_table.attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, EXPAND|FILL);
596 snprintf (buf, sizeof (buf), "monitor-cut-%u", i+1);
597 act = ActionManager::get_action (X_("Monitor"), buf);
599 cbs->cut.set_related_action (act);
602 snprintf (buf, sizeof (buf), "monitor-dim-%u", i+1);
603 act = ActionManager::get_action (X_("Monitor"), buf);
605 cbs->dim.set_related_action (act);
608 snprintf (buf, sizeof (buf), "monitor-solo-%u", i+1);
609 act = ActionManager::get_action (X_("Monitor"), buf);
611 cbs->solo.set_related_action (act);
614 snprintf (buf, sizeof (buf), "monitor-invert-%u", i+1);
615 act = ActionManager::get_action (X_("Monitor"), buf);
617 cbs->invert.set_related_action (act);
621 channel_table.show_all ();
625 MonitorSection::toggle_exclusive_solo ()
631 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo");
633 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
634 Config->set_exclusive_solo (tact->get_active());
640 MonitorSection::toggle_mute_overrides_solo ()
646 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo");
648 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
649 Config->set_solo_mute_override (tact->get_active());
654 MonitorSection::dim_all ()
660 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
662 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
663 _monitor->set_dim_all (tact->get_active());
669 MonitorSection::cut_all ()
675 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
677 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
678 _monitor->set_cut_all (tact->get_active());
683 MonitorSection::mono ()
689 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
691 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
692 _monitor->set_mono (tact->get_active());
697 MonitorSection::cut_channel (uint32_t chn)
704 snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
706 --chn; // 0-based in backend
708 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
710 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
711 _monitor->set_cut (chn, tact->get_active());
716 MonitorSection::dim_channel (uint32_t chn)
723 snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
725 --chn; // 0-based in backend
727 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
729 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
730 _monitor->set_dim (chn, tact->get_active());
736 MonitorSection::solo_channel (uint32_t chn)
743 snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
745 --chn; // 0-based in backend
747 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
749 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
750 _monitor->set_solo (chn, tact->get_active());
756 MonitorSection::invert_channel (uint32_t chn)
763 snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
765 --chn; // 0-based in backend
767 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
769 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
770 _monitor->set_polarity (chn, tact->get_active());
775 MonitorSection::register_actions ()
779 Glib::RefPtr<Action> act;
781 monitor_actions = ActionGroup::create (X_("Monitor"));
782 ActionManager::add_action_group (monitor_actions);
784 ActionManager::register_toggle_action (monitor_actions, "monitor-mono", "", _("Switch monitor to mono"),
785 sigc::mem_fun (*this, &MonitorSection::mono));
787 ActionManager::register_toggle_action (monitor_actions, "monitor-cut-all", "", _("Cut monitor"),
788 sigc::mem_fun (*this, &MonitorSection::cut_all));
790 ActionManager::register_toggle_action (monitor_actions, "monitor-dim-all", "", _("Dim monitor"),
791 sigc::mem_fun (*this, &MonitorSection::dim_all));
793 act = ActionManager::register_toggle_action (monitor_actions, "toggle-exclusive-solo", "", _("Toggle exclusive solo mode"),
794 sigc::mem_fun (*this, &MonitorSection::toggle_exclusive_solo));
796 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
797 tact->set_active (Config->get_exclusive_solo());
799 act = ActionManager::register_toggle_action (monitor_actions, "toggle-mute-overrides-solo", "", _("Toggle mute overrides solo mode"),
800 sigc::mem_fun (*this, &MonitorSection::toggle_mute_overrides_solo));
802 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
803 tact->set_active (Config->get_solo_mute_override());
806 /* note the 1-based counting (for naming - backend uses 0-based) */
808 for (uint32_t chn = 1; chn <= 16; ++chn) {
810 action_name = string_compose (X_("monitor-cut-%1"), chn);
811 action_descr = string_compose (_("Cut monitor channel %1"), chn);
812 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
813 sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
815 action_name = string_compose (X_("monitor-dim-%1"), chn);
816 action_descr = string_compose (_("Dim monitor channel %1"), chn);
817 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
818 sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
820 action_name = string_compose (X_("monitor-solo-%1"), chn);
821 action_descr = string_compose (_("Solo monitor channel %1"), chn);
822 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
823 sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
825 action_name = string_compose (X_("monitor-invert-%1"), chn);
826 action_descr = string_compose (_("Invert monitor channel %1"), chn);
827 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
828 sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
833 Glib::RefPtr<ActionGroup> solo_actions = ActionGroup::create (X_("Solo"));
834 RadioAction::Group solo_group;
836 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-in-place", "", _("In-place solo"),
837 sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
838 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-afl", "", _("After Fade Listen (AFL) solo"),
839 sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
840 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-pfl", "", _("Pre Fade Listen (PFL) solo"),
841 sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
843 ActionManager::add_action_group (solo_actions);
847 MonitorSection::solo_use_in_place ()
849 /* this is driven by a toggle on a radio group, and so is invoked twice,
850 once for the item that became inactive and once for the one that became
854 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
857 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
859 if (!ract->get_active ()) {
860 /* We are turning SiP off, which means that AFL or PFL will be turned on
861 shortly; don't update the solo model in the mean time, as if the currently
862 configured listen position is not the one that is about to be turned on,
863 things will go wrong.
865 _inhibit_solo_model_update = true;
867 Config->set_solo_control_is_listen_control (!ract->get_active());
868 _inhibit_solo_model_update = false;
874 MonitorSection::solo_use_afl ()
876 /* this is driven by a toggle on a radio group, and so is invoked twice,
877 once for the item that became inactive and once for the one that became
881 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
883 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
885 if (ract->get_active()) {
886 Config->set_solo_control_is_listen_control (true);
887 Config->set_listen_position (AfterFaderListen);
894 MonitorSection::solo_use_pfl ()
896 /* this is driven by a toggle on a radio group, and so is invoked twice,
897 once for the item that became inactive and once for the one that became
901 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
903 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
905 if (ract->get_active()) {
906 Config->set_solo_control_is_listen_control (true);
907 Config->set_listen_position (PreFaderListen);
914 MonitorSection::update_solo_model ()
916 if (_inhibit_solo_model_update) {
920 const char* action_name = 0;
921 Glib::RefPtr<Action> act;
923 if (Config->get_solo_control_is_listen_control()) {
924 switch (Config->get_listen_position()) {
925 case AfterFaderListen:
926 action_name = X_("solo-use-afl");
929 action_name = X_("solo-use-pfl");
933 action_name = X_("solo-use-in-place");
936 act = ActionManager::get_action (X_("Solo"), action_name);
939 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
941 /* because these are radio buttons, one of them will be
942 active no matter what. to trigger a change in the
943 action so that the view picks it up, toggle it.
945 if (ract->get_active()) {
946 ract->set_active (false);
948 ract->set_active (true);
955 MonitorSection::map_state ()
957 if (!_route || !_monitor) {
961 Glib::RefPtr<Action> act;
963 update_solo_model ();
965 act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
967 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
969 tact->set_active (_monitor->cut_all());
973 act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
975 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
977 tact->set_active (_monitor->dim_all());
981 act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
983 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
985 tact->set_active (_monitor->mono());
989 uint32_t nchans = _monitor->output_streams().n_audio();
991 assert (nchans == _channel_buttons.size ());
993 for (uint32_t n = 0; n < nchans; ++n) {
995 char action_name[32];
997 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n);
998 act = ActionManager::get_action (X_("Monitor"), action_name);
1000 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1002 tact->set_active (_monitor->cut (n));
1006 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n);
1007 act = ActionManager::get_action (X_("Monitor"), action_name);
1009 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1011 tact->set_active (_monitor->dimmed (n));
1015 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n);
1016 act = ActionManager::get_action (X_("Monitor"), action_name);
1018 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1020 tact->set_active (_monitor->soloed (n));
1024 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n);
1025 act = ActionManager::get_action (X_("Monitor"), action_name);
1027 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1029 tact->set_active (_monitor->inverted (n));
1036 MonitorSection::do_blink (bool onoff)
1039 audition_blink (onoff);
1043 MonitorSection::audition_blink (bool onoff)
1045 if (_session == 0) {
1049 if (_session->is_auditioning()) {
1050 rude_audition_button.set_active (onoff);
1052 rude_audition_button.set_active (false);
1057 MonitorSection::solo_blink (bool onoff)
1059 if (_session == 0) {
1063 if (_session->soloing() || _session->listening()) {
1064 rude_solo_button.set_active (onoff);
1066 if (_session->soloing()) {
1067 if (_session->solo_isolated()) {
1068 rude_iso_button.set_active (onoff);
1070 rude_iso_button.set_active (false);
1075 rude_solo_button.set_active (false);
1076 rude_iso_button.set_active (false);
1081 MonitorSection::cancel_solo (GdkEventButton*)
1084 if (_session->soloing()) {
1085 _session->set_solo (_session->get_routes(), false);
1086 } else if (_session->listening()) {
1087 _session->set_listen (_session->get_routes(), false);
1095 MonitorSection::cancel_isolate (GdkEventButton*)
1098 boost::shared_ptr<RouteList> rl (_session->get_routes ());
1099 _session->set_solo_isolated (rl, false, Session::rt_cleanup, true);
1106 MonitorSection::cancel_audition (GdkEventButton*)
1109 _session->cancel_audition();
1114 #define SYNCHRONIZE_TOGGLE_ACTION(action, value) \
1116 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(action); \
1117 if (tact && tact->get_active() != value) { \
1118 tact->set_active(value); \
1123 MonitorSection::parameter_changed (std::string name)
1125 if (name == "solo-control-is-listen-control") {
1126 update_solo_model ();
1127 } else if (name == "listen-position") {
1128 update_solo_model ();
1129 } else if (name == "solo-mute-override") {
1130 SYNCHRONIZE_TOGGLE_ACTION(
1131 ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo"),
1132 Config->get_solo_mute_override ())
1133 } else if (name == "exclusive-solo") {
1134 SYNCHRONIZE_TOGGLE_ACTION(
1135 ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo"),
1136 Config->get_exclusive_solo ())
1141 MonitorSection::assign_controllables ()
1143 boost::shared_ptr<Controllable> none;
1145 if (!gain_control) {
1146 /* too early - GUI controls not set up yet */
1151 solo_cut_control->set_controllable (_session->solo_cut_control());
1152 solo_cut_display->set_controllable (_session->solo_cut_control());
1154 solo_cut_control->set_controllable (none);
1155 solo_cut_display->set_controllable (none);
1159 gain_control->set_controllable (_route->gain_control());
1160 gain_display->set_controllable (_route->gain_control());
1162 gain_control->set_controllable (none);
1167 cut_all_button.set_controllable (_monitor->cut_control());
1168 cut_all_button.watch ();
1169 dim_all_button.set_controllable (_monitor->dim_control());
1170 dim_all_button.watch ();
1171 mono_button.set_controllable (_monitor->mono_control());
1172 mono_button.watch ();
1174 dim_control->set_controllable (_monitor->dim_level_control ());
1175 dim_display->set_controllable (_monitor->dim_level_control ());
1176 solo_boost_control->set_controllable (_monitor->solo_boost_control ());
1177 solo_boost_display->set_controllable (_monitor->solo_boost_control ());
1181 cut_all_button.set_controllable (none);
1182 dim_all_button.set_controllable (none);
1183 mono_button.set_controllable (none);
1185 dim_control->set_controllable (none);
1186 dim_display->set_controllable (none);
1187 solo_boost_control->set_controllable (none);
1188 solo_boost_display->set_controllable (none);
1193 MonitorSection::state_id() const
1195 return "monitor-section";
1199 MonitorSection::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1201 using namespace Menu_Helpers;
1203 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1207 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1208 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1212 if (i != output_menu_bundles.end()) {
1216 output_menu_bundles.push_back (b);
1218 MenuList& citems = output_menu.items();
1220 std::string n = b->name ();
1221 replace_all (n, "_", " ");
1223 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MonitorSection::bundle_output_chosen), b)));
1227 MonitorSection::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1230 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1232 if (std::find (current.begin(), current.end(), c) == current.end()) {
1233 _route->output()->connect_ports_to_bundle (c, true, this);
1235 _route->output()->disconnect_ports_from_bundle (c, this);
1240 MonitorSection::output_release (GdkEventButton *ev)
1242 switch (ev->button) {
1244 edit_output_configuration ();
1251 struct RouteCompareByName {
1252 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1253 return a->name().compare (b->name()) < 0;
1258 MonitorSection::output_press (GdkEventButton *ev)
1260 using namespace Menu_Helpers;
1262 MessageDialog msg (_("No session - no I/O changes are possible"));
1267 MenuList& citems = output_menu.items();
1268 switch (ev->button) {
1271 return false; //wait for the mouse-up to pop the dialog
1275 output_menu.set_name ("ArdourContextMenu");
1277 output_menu_bundles.clear ();
1279 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(this), &MonitorSection::disconnect_output)));
1281 citems.push_back (SeparatorElem());
1282 uint32_t const n_with_separator = citems.size ();
1284 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1286 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1288 /* give user bundles first chance at being in the menu */
1290 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1291 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1292 maybe_add_bundle_to_output_menu (*i, current);
1296 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1297 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1298 maybe_add_bundle_to_output_menu (*i, current);
1302 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1303 RouteList copy = *routes;
1304 copy.sort (RouteCompareByName ());
1305 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1306 maybe_add_bundle_to_output_menu ((*i)->output()->bundle(), current);
1309 if (citems.size() == n_with_separator) {
1310 /* no routes added; remove the separator */
1314 citems.push_back (SeparatorElem());
1315 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(this), &MonitorSection::edit_output_configuration)));
1317 output_menu.popup (1, ev->time);
1328 MonitorSection::output_button_resized (Gtk::Allocation& alloc)
1330 output_button->set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1334 MonitorSection::update_output_display ()
1336 if (!_route || !_monitor || _session->deletion_in_progress()) {
1342 boost::shared_ptr<Port> port;
1343 vector<string> port_connections;
1345 uint32_t total_connection_count = 0;
1346 uint32_t io_connection_count = 0;
1347 uint32_t ardour_connection_count = 0;
1348 uint32_t system_connection_count = 0;
1349 uint32_t other_connection_count = 0;
1351 ostringstream label;
1353 bool have_label = false;
1354 bool each_io_has_one_connection = true;
1356 string connection_name;
1357 string ardour_track_name;
1358 string other_connection_type;
1359 string system_ports;
1362 ostringstream tooltip;
1363 char * tooltip_cstr;
1365 io_count = _route->n_outputs().n_total();
1366 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(_route->name()));
1369 for (io_index = 0; io_index < io_count; ++io_index) {
1371 port = _route->output()->nth (io_index);
1373 //ignore any port connections that don't match our DataType
1374 if (port->type() != DataType::AUDIO) {
1378 port_connections.clear ();
1379 port->get_connections(port_connections);
1380 io_connection_count = 0;
1382 if (!port_connections.empty()) {
1383 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1385 string& connection_name (*i);
1387 if (connection_name.find("system:") == 0) {
1388 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1391 if (io_connection_count == 0) {
1392 tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1))
1394 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1397 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1400 if (connection_name.find("ardour:") == 0) {
1401 if (ardour_track_name.empty()) {
1402 // "ardour:Master/in 1" -> "ardour:Master/"
1403 string::size_type slash = connection_name.find("/");
1404 if (slash != string::npos) {
1405 ardour_track_name = connection_name.substr(0, slash + 1);
1409 if (connection_name.find(ardour_track_name) == 0) {
1410 ++ardour_connection_count;
1412 } else if (!pn.empty()) {
1413 if (system_ports.empty()) {
1416 system_ports += "/" + pn;
1418 if (connection_name.find("system:") == 0) {
1419 ++system_connection_count;
1421 } else if (connection_name.find("system:") == 0) {
1422 // "system:playback_123" -> "123"
1423 system_port = connection_name.substr(16);
1424 if (system_ports.empty()) {
1425 system_ports += system_port;
1427 system_ports += "/" + system_port;
1430 ++system_connection_count;
1432 if (other_connection_type.empty()) {
1433 // "jamin:in 1" -> "jamin:"
1434 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1437 if (connection_name.find(other_connection_type) == 0) {
1438 ++other_connection_count;
1442 ++total_connection_count;
1443 ++io_connection_count;
1447 if (io_connection_count != 1) {
1448 each_io_has_one_connection = false;
1452 if (total_connection_count == 0) {
1453 tooltip << endl << _("Disconnected");
1456 tooltip_cstr = new char[tooltip.str().size() + 1];
1457 strcpy(tooltip_cstr, tooltip.str().c_str());
1459 ARDOUR_UI::instance()->set_tip (output_button, tooltip_cstr, "");
1461 if (each_io_has_one_connection) {
1462 if (total_connection_count == ardour_connection_count) {
1463 // all connections are to the same track in ardour
1464 // "ardour:Master/" -> "Master"
1465 string::size_type slash = ardour_track_name.find("/");
1466 if (slash != string::npos) {
1467 label << ardour_track_name.substr(7, slash - 7);
1470 } else if (total_connection_count == system_connection_count) {
1471 // all connections are to system ports
1472 label << system_ports;
1474 } else if (total_connection_count == other_connection_count) {
1475 // all connections are to the same external program eg jamin
1476 // "jamin:" -> "jamin"
1477 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1483 if (total_connection_count == 0) {
1487 // Odd configuration
1488 label << "*" << total_connection_count << "*";
1492 output_button->set_text (label.str());
1496 MonitorSection::disconnect_output ()
1499 _route->output()->disconnect(this);
1504 MonitorSection::edit_output_configuration ()
1506 if (_output_selector == 0) {
1507 _output_selector = new MonitorSelectorWindow (_session, _route->output());
1509 _output_selector->present ();
1513 MonitorSection::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1518 boost::shared_ptr<Port> a = wa.lock ();
1519 boost::shared_ptr<Port> b = wb.lock ();
1520 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1521 update_output_display ();