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 "gui_thread.h"
41 #include "monitor_section.h"
42 #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 act = ActionManager::get_action (X_("Main"), X_("cancel-solo"));
119 rude_solo_button.set_related_action (act);
120 UI::instance()->set_tip (rude_solo_button, _("When active, something is soloed.\nClick to de-solo everything"));
122 rude_iso_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_isolate), false);
123 UI::instance()->set_tip (rude_iso_button, _("When active, something is solo-isolated.\nClick to de-isolate everything"));
125 rude_audition_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_audition), false);
126 UI::instance()->set_tip (rude_audition_button, _("When active, auditioning is active.\nClick to stop the audition"));
128 solo_in_place_button.set_name ("monitor section solo model");
129 afl_button.set_name ("monitor section solo model");
130 pfl_button.set_name ("monitor section solo model");
132 solo_model_box.set_spacing (6);
133 solo_model_box.pack_start (solo_in_place_button, true, false);
134 solo_model_box.pack_start (afl_button, true, false);
135 solo_model_box.pack_start (pfl_button, true, false);
137 solo_in_place_button.show ();
140 solo_model_box.show ();
142 act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
143 set_tooltip (solo_in_place_button, _("Solo controls affect solo-in-place"));
145 solo_in_place_button.set_related_action (act);
148 act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
149 set_tooltip (afl_button, _("Solo controls toggle after-fader-listen"));
151 afl_button.set_related_action (act);
154 act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
155 set_tooltip (pfl_button, _("Solo controls toggle pre-fader-listen"));
157 pfl_button.set_related_action (act);
162 solo_boost_control = new ArdourKnob ();
163 solo_boost_control->set_name("monitor knob");
164 solo_boost_control->set_size_request (PX_SCALE(40), PX_SCALE(40));
165 set_tooltip (*solo_boost_control, _("Gain increase for soloed signals (0dB is normal)"));
167 solo_boost_display = new ArdourDisplay ();
168 solo_boost_display->set_name("monitor section cut");
169 solo_boost_display->set_size_request (PX_SCALE(80), PX_SCALE(20));
170 solo_boost_display->add_controllable_preset(_("0 dB"), 0.0);
171 solo_boost_display->add_controllable_preset(_("3 dB"), 3.0);
172 solo_boost_display->add_controllable_preset(_("6 dB"), 6.0);
173 solo_boost_display->add_controllable_preset(_("10 dB"), 10.0);
175 HBox* solo_packer = manage (new HBox);
176 solo_packer->set_spacing (6);
177 solo_packer->show ();
179 spin_label = manage (new Label (_("Solo Boost")));
180 spin_packer = manage (new VBox);
181 spin_packer->show ();
182 spin_packer->set_spacing (3);
183 spin_packer->pack_start (*spin_label, false, false);
184 spin_packer->pack_start (*solo_boost_control, false, false);
185 spin_packer->pack_start (*solo_boost_display, false, false);
187 solo_packer->pack_start (*spin_packer, true, false);
191 solo_cut_control = new ArdourKnob ();
192 solo_cut_control->set_name ("monitor knob");
193 solo_cut_control->set_size_request (PX_SCALE(40), PX_SCALE(40));
194 set_tooltip (*solo_cut_control, _("Gain reduction non-soloed signals\nA value above -inf dB causes \"solo-in-front\""));
196 solo_cut_display = new ArdourDisplay ();
197 solo_cut_display->set_name("monitor section cut");
198 solo_cut_display->set_size_request (PX_SCALE(80), PX_SCALE(20));
199 solo_cut_display->add_controllable_preset(_("0 dB"), 0.0);
200 solo_cut_display->add_controllable_preset(_("-6 dB"), -6.0);
201 solo_cut_display->add_controllable_preset(_("-12 dB"), -12.0);
202 solo_cut_display->add_controllable_preset(_("-20 dB"), -20.0);
203 solo_cut_display->add_controllable_preset(_("OFF"), -1200.0);
205 spin_label = manage (new Label (_("SiP Cut")));
206 spin_packer = manage (new VBox);
207 spin_packer->show ();
208 spin_packer->set_spacing (3);
209 spin_packer->pack_start (*spin_label, false, false);
210 spin_packer->pack_start (*solo_cut_control, false, false);
211 spin_packer->pack_start (*solo_cut_display, false, false);
213 solo_packer->pack_start (*spin_packer, true, false);
217 dim_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
218 dim_control->set_name ("monitor knob");
219 dim_control->set_size_request (PX_SCALE(40), PX_SCALE(40));
220 set_tooltip (*dim_control, _("Gain reduction to use when dimming monitor outputs"));
222 dim_display = new ArdourDisplay ();
223 dim_display->set_name("monitor section cut");
224 dim_display->set_size_request (PX_SCALE(80), PX_SCALE(20));
225 dim_display->add_controllable_preset(_("0 dB"), 0.0);
226 dim_display->add_controllable_preset(_("-3 dB"), -3.0);
227 dim_display->add_controllable_preset(_("-6 dB"), -6.0);
228 dim_display->add_controllable_preset(_("-12 dB"), -12.0);
229 dim_display->add_controllable_preset(_("-20 dB"), -20.0);
231 HBox* dim_packer = manage (new HBox);
234 spin_label = manage (new Label (_("Dim")));
235 spin_packer = manage (new VBox);
236 spin_packer->show ();
237 spin_packer->set_spacing (3);
238 spin_packer->pack_start (*spin_label, false, false);
239 spin_packer->pack_start (*dim_control, false, false);
240 spin_packer->pack_start (*dim_display, false, false);
242 dim_packer->pack_start (*spin_packer, true, false);
244 exclusive_solo_button.set_text (_("Excl. Solo"));
245 exclusive_solo_button.set_name (X_("monitor solo exclusive"));
246 set_tooltip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time"));
248 act = ActionManager::get_action (X_("Monitor"), X_("toggle-exclusive-solo"));
250 exclusive_solo_button.set_related_action (act);
253 solo_mute_override_button.set_text (_("Solo » Mute"));
254 solo_mute_override_button.set_name (X_("monitor solo override"));
255 set_tooltip (&solo_mute_override_button, _("If enabled, solo will override mute\n(a soloed & muted track or bus will be audible)"));
257 act = ActionManager::get_action (X_("Monitor"), X_("toggle-mute-overrides-solo"));
259 solo_mute_override_button.set_related_action (act);
262 HBox* solo_opt_box = manage (new HBox);
263 solo_opt_box->set_spacing (12);
264 solo_opt_box->set_homogeneous (true);
265 solo_opt_box->pack_start (exclusive_solo_button);
266 solo_opt_box->pack_start (solo_mute_override_button);
267 solo_opt_box->show ();
269 upper_packer.set_spacing (6);
271 Gtk::HBox* rude_box = manage (new HBox);
272 rude_box->pack_start (rude_solo_button, true, true);
273 rude_box->pack_start (rude_iso_button, true, true);
275 upper_packer.pack_start (*rude_box, false, false);
276 upper_packer.pack_start (rude_audition_button, false, false);
277 upper_packer.pack_start (solo_model_box, false, false, 12);
278 upper_packer.pack_start (*solo_opt_box, false, false);
279 upper_packer.pack_start (*solo_packer, false, false, 12);
281 cut_all_button.set_text (_("Mute"));
282 cut_all_button.set_name ("monitor section cut");
283 cut_all_button.set_name (X_("monitor section cut"));
284 cut_all_button.set_size_request (-1, PX_SCALE(50));
285 cut_all_button.show ();
287 act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
289 cut_all_button.set_related_action (act);
292 dim_all_button.set_text (_("Dim"));
293 dim_all_button.set_name ("monitor section dim");
294 act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
296 dim_all_button.set_related_action (act);
299 mono_button.set_text (_("Mono"));
300 mono_button.set_name ("monitor section mono");
301 act = ActionManager::get_action (X_("Monitor"), X_("monitor-mono"));
303 mono_button.set_related_action (act);
306 HBox* bbox = manage (new HBox);
308 bbox->set_spacing (12);
309 bbox->pack_start (mono_button, true, true);
310 bbox->pack_start (dim_all_button, true, true);
312 lower_packer.set_spacing (12);
313 lower_packer.pack_start (*bbox, false, false);
314 lower_packer.pack_start (cut_all_button, false, false);
318 gain_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
319 gain_control->set_name("monitor knob");
320 gain_control->set_size_request (PX_SCALE(80), PX_SCALE(80));
322 gain_display = new ArdourDisplay ();
323 gain_display->set_name("monitor section cut");
324 gain_display->set_size_request (PX_SCALE(40), PX_SCALE(20));
325 gain_display->add_controllable_preset(_("0 dB"), 0.0);
326 gain_display->add_controllable_preset(_("-3 dB"), -3.0);
327 gain_display->add_controllable_preset(_("-6 dB"), -6.0);
328 gain_display->add_controllable_preset(_("-12 dB"), -12.0);
329 gain_display->add_controllable_preset(_("-20 dB"), -20.0);
330 gain_display->add_controllable_preset(_("-30 dB"), -30.0);
332 Label* output_label = manage (new Label (_("Output")));
333 output_label->set_name (X_("MonitorSectionLabel"));
335 output_button = new ArdourButton ();
336 output_button->set_text (_("Output"));
337 output_button->set_name (X_("monitor section cut"));
338 output_button->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
339 VBox* out_packer = manage (new VBox);
340 out_packer->set_spacing (6);
341 out_packer->pack_start (*output_label, false, false);
342 out_packer->pack_start (*output_button, false, false);
344 spin_label = manage (new Label (_("Monitor")));
345 spin_packer = manage (new VBox);
346 spin_packer->show ();
347 spin_packer->set_spacing (3);
348 spin_packer->pack_start (*spin_label, false, false);
349 spin_packer->pack_start (*gain_control, false, false);
350 spin_packer->pack_start (*gain_display, false, false);
351 spin_packer->pack_start (*out_packer, false, false, 24);
353 lower_packer.pack_start (*spin_packer, true, true);
355 channel_table_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
356 channel_table_scroller.set_size_request (-1, PX_SCALE(150));
357 channel_table_scroller.set_shadow_type (Gtk::SHADOW_NONE);
358 channel_table_scroller.show ();
359 channel_table_scroller.add (channel_table_viewport);
361 channel_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
362 channel_size_group->add_widget (channel_table_header);
363 channel_size_group->add_widget (channel_table);
365 channel_table_header.resize (1, 5);
367 Label* l1 = manage (new Label (X_(" ")));
368 l1->set_name (X_("MonitorSectionLabel"));
369 channel_table_header.attach (*l1, 0, 1, 0, 1, EXPAND|FILL);
371 l1 = manage (new Label (_("Mute")));
372 l1->set_name (X_("MonitorSectionLabel"));
373 channel_table_header.attach (*l1, 1, 2, 0, 1, EXPAND|FILL);
375 l1 = manage (new Label (_("Dim")));
376 l1->set_name (X_("MonitorSectionLabel"));
377 channel_table_header.attach (*l1, 2, 3, 0, 1, EXPAND|FILL);
379 l1 = manage (new Label (_("Solo")));
380 l1->set_name (X_("MonitorSectionLabel"));
381 channel_table_header.attach (*l1, 3, 4, 0, 1, EXPAND|FILL);
383 l1 = manage (new Label (_("Inv")));
384 l1->set_name (X_("MonitorSectionLabel"));
385 channel_table_header.attach (*l1, 4, 5, 0, 1, EXPAND|FILL);
387 channel_table_header.show ();
389 table_hpacker.pack_start (channel_table, true, true);
391 /* note that we don't pack the table_hpacker till later
394 vpacker.set_border_width (6);
395 vpacker.set_spacing (12);
396 vpacker.pack_start (upper_packer, false, false);
397 vpacker.pack_start (*dim_packer, false, false);
398 vpacker.pack_start (channel_table_header, false, false);
399 vpacker.pack_start (channel_table_packer, false, false);
400 vpacker.pack_start (lower_packer, false, false);
402 hpacker.pack_start (vpacker, true, true);
404 gain_control->show_all ();
405 gain_display->show_all ();
406 dim_control->show_all ();
407 dim_display->show_all();
408 solo_boost_control->show_all ();
409 solo_boost_display->show_all();
411 channel_table.show ();
413 upper_packer.show ();
414 lower_packer.show ();
419 assign_controllables ();
421 output_button->signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::output_press), false);
422 output_button->signal_button_release_event().connect (sigc::mem_fun(*this, &MonitorSection::output_release), false);
423 output_button->signal_size_allocate().connect (sigc::mem_fun (*this, &MonitorSection::output_button_resized));
425 _tearoff = new TearOff (hpacker);
427 /* if torn off, make this a normal window */
428 _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
429 _tearoff->tearoff_window().set_title (X_("Monitor"));
430 _tearoff->tearoff_window().signal_key_press_event().connect (sigc::ptr_fun (forward_key_press), false);
432 update_output_display();
434 /* catch changes that affect us */
435 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
436 *this, invalidator (*this), boost::bind (&MonitorSection::port_connected_or_disconnected, this, _1, _3), gui_context ()
438 Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context());
441 MonitorSection::~MonitorSection ()
443 for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
447 _channel_buttons.clear ();
448 _output_changed_connection.disconnect ();
450 delete output_button;
455 delete solo_boost_control;
456 delete solo_boost_display;
457 delete solo_cut_control;
458 delete solo_cut_display;
460 delete _output_selector;
461 _output_selector = 0;
465 MonitorSection::set_session (Session* s)
467 AxisView::set_session (s);
471 _route = _session->monitor_out ();
474 /* session with monitor section */
475 _monitor = _route->monitor_control ();
476 assign_controllables ();
477 _route->output()->changed.connect (_output_changed_connection, invalidator (*this),
478 boost::bind (&MonitorSection::update_output_display, this),
481 /* session with no monitor section */
482 _output_changed_connection.disconnect();
485 delete _output_selector;
486 _output_selector = 0;
489 if (channel_table_scroller.get_parent()) {
490 /* scroller is packed, so remove it */
491 channel_table_packer.remove (channel_table_scroller);
494 if (table_hpacker.get_parent () == &channel_table_packer) {
495 /* this occurs when the table hpacker is directly
496 packed, so remove it.
498 channel_table_packer.remove (table_hpacker);
499 } else if (table_hpacker.get_parent()) {
500 channel_table_viewport.remove ();
503 if (_monitor->output_streams().n_audio() > 7) {
504 /* put the table into a scrolled window, and then put
505 * that into the channel vpacker, after the table header
507 channel_table_viewport.add (table_hpacker);
508 channel_table_packer.pack_start (channel_table_scroller, true, true);
509 channel_table_viewport.show ();
510 channel_table_scroller.show ();
513 /* just put the channel table itself into the channel
514 * vpacker, after the table header
517 channel_table_packer.pack_start (table_hpacker, true, true);
518 channel_table_scroller.hide ();
521 table_hpacker.show ();
522 channel_table.show ();
527 _output_changed_connection.disconnect();
530 control_connections.drop_connections ();
531 rude_iso_button.unset_active_state ();
532 rude_solo_button.unset_active_state ();
533 delete _output_selector;
534 _output_selector = 0;
536 assign_controllables ();
540 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
542 cut.set_name (X_("monitor section cut"));
543 dim.set_name (X_("monitor section dim"));
544 solo.set_name (X_("monitor section solo"));
545 invert.set_name (X_("monitor section invert"));
547 cut.unset_flags (Gtk::CAN_FOCUS);
548 dim.unset_flags (Gtk::CAN_FOCUS);
549 solo.unset_flags (Gtk::CAN_FOCUS);
550 invert.unset_flags (Gtk::CAN_FOCUS);
554 MonitorSection::populate_buttons ()
560 Glib::RefPtr<Action> act;
561 uint32_t nchans = _monitor->output_streams().n_audio();
563 channel_table.resize (nchans, 5);
564 channel_table.set_col_spacings (6);
565 channel_table.set_row_spacings (6);
566 channel_table.set_homogeneous (true);
568 const uint32_t row_offset = 0;
570 for (uint32_t i = 0; i < nchans; ++i) {
583 snprintf (buf, sizeof (buf), "%d", i+1);
587 Label* label = manage (new Label (l));
588 channel_table.attach (*label, 0, 1, i+row_offset, i+row_offset+1, EXPAND|FILL);
590 ChannelButtonSet* cbs = new ChannelButtonSet;
592 _channel_buttons.push_back (cbs);
594 channel_table.attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, EXPAND|FILL);
595 channel_table.attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, EXPAND|FILL);
596 channel_table.attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, EXPAND|FILL);
597 channel_table.attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, EXPAND|FILL);
599 snprintf (buf, sizeof (buf), "monitor-cut-%u", i+1);
600 act = ActionManager::get_action (X_("Monitor"), buf);
602 cbs->cut.set_related_action (act);
605 snprintf (buf, sizeof (buf), "monitor-dim-%u", i+1);
606 act = ActionManager::get_action (X_("Monitor"), buf);
608 cbs->dim.set_related_action (act);
611 snprintf (buf, sizeof (buf), "monitor-solo-%u", i+1);
612 act = ActionManager::get_action (X_("Monitor"), buf);
614 cbs->solo.set_related_action (act);
617 snprintf (buf, sizeof (buf), "monitor-invert-%u", i+1);
618 act = ActionManager::get_action (X_("Monitor"), buf);
620 cbs->invert.set_related_action (act);
624 channel_table.show_all ();
628 MonitorSection::toggle_exclusive_solo ()
634 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo");
636 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
637 Config->set_exclusive_solo (tact->get_active());
643 MonitorSection::toggle_mute_overrides_solo ()
649 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo");
651 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
652 Config->set_solo_mute_override (tact->get_active());
657 MonitorSection::dim_all ()
663 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
665 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
666 _monitor->set_dim_all (tact->get_active());
672 MonitorSection::cut_all ()
678 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
680 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
681 _monitor->set_cut_all (tact->get_active());
686 MonitorSection::mono ()
692 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
694 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
695 _monitor->set_mono (tact->get_active());
700 MonitorSection::cut_channel (uint32_t chn)
707 snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
709 --chn; // 0-based in backend
711 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
713 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
714 _monitor->set_cut (chn, tact->get_active());
719 MonitorSection::dim_channel (uint32_t chn)
726 snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
728 --chn; // 0-based in backend
730 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
732 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
733 _monitor->set_dim (chn, tact->get_active());
739 MonitorSection::solo_channel (uint32_t chn)
746 snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
748 --chn; // 0-based in backend
750 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
752 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
753 _monitor->set_solo (chn, tact->get_active());
759 MonitorSection::invert_channel (uint32_t chn)
766 snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
768 --chn; // 0-based in backend
770 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
772 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
773 _monitor->set_polarity (chn, tact->get_active());
778 MonitorSection::register_actions ()
782 Glib::RefPtr<Action> act;
784 monitor_actions = ActionGroup::create (X_("Monitor"));
785 ActionManager::add_action_group (monitor_actions);
787 ActionManager::register_toggle_action (monitor_actions, "monitor-mono", "", _("Switch monitor to mono"),
788 sigc::mem_fun (*this, &MonitorSection::mono));
790 ActionManager::register_toggle_action (monitor_actions, "monitor-cut-all", "", _("Cut monitor"),
791 sigc::mem_fun (*this, &MonitorSection::cut_all));
793 ActionManager::register_toggle_action (monitor_actions, "monitor-dim-all", "", _("Dim monitor"),
794 sigc::mem_fun (*this, &MonitorSection::dim_all));
796 act = ActionManager::register_toggle_action (monitor_actions, "toggle-exclusive-solo", "", _("Toggle exclusive solo mode"),
797 sigc::mem_fun (*this, &MonitorSection::toggle_exclusive_solo));
799 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
800 tact->set_active (Config->get_exclusive_solo());
802 act = ActionManager::register_toggle_action (monitor_actions, "toggle-mute-overrides-solo", "", _("Toggle mute overrides solo mode"),
803 sigc::mem_fun (*this, &MonitorSection::toggle_mute_overrides_solo));
805 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
806 tact->set_active (Config->get_solo_mute_override());
809 /* note the 1-based counting (for naming - backend uses 0-based) */
811 for (uint32_t chn = 1; chn <= 16; ++chn) {
813 action_name = string_compose (X_("monitor-cut-%1"), chn);
814 action_descr = string_compose (_("Cut monitor channel %1"), chn);
815 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
816 sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
818 action_name = string_compose (X_("monitor-dim-%1"), chn);
819 action_descr = string_compose (_("Dim monitor channel %1"), chn);
820 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
821 sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
823 action_name = string_compose (X_("monitor-solo-%1"), chn);
824 action_descr = string_compose (_("Solo monitor channel %1"), chn);
825 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
826 sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
828 action_name = string_compose (X_("monitor-invert-%1"), chn);
829 action_descr = string_compose (_("Invert monitor channel %1"), chn);
830 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
831 sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
836 Glib::RefPtr<ActionGroup> solo_actions = ActionGroup::create (X_("Solo"));
837 RadioAction::Group solo_group;
839 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-in-place", "", _("In-place solo"),
840 sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
841 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-afl", "", _("After Fade Listen (AFL) solo"),
842 sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
843 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-pfl", "", _("Pre Fade Listen (PFL) solo"),
844 sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
846 ActionManager::add_action_group (solo_actions);
850 MonitorSection::solo_use_in_place ()
852 /* this is driven by a toggle on a radio group, and so is invoked twice,
853 once for the item that became inactive and once for the one that became
857 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
860 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
862 if (!ract->get_active ()) {
863 /* We are turning SiP off, which means that AFL or PFL will be turned on
864 shortly; don't update the solo model in the mean time, as if the currently
865 configured listen position is not the one that is about to be turned on,
866 things will go wrong.
868 _inhibit_solo_model_update = true;
870 Config->set_solo_control_is_listen_control (!ract->get_active());
871 _inhibit_solo_model_update = false;
877 MonitorSection::solo_use_afl ()
879 /* this is driven by a toggle on a radio group, and so is invoked twice,
880 once for the item that became inactive and once for the one that became
884 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
886 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
888 if (ract->get_active()) {
889 Config->set_solo_control_is_listen_control (true);
890 Config->set_listen_position (AfterFaderListen);
897 MonitorSection::solo_use_pfl ()
899 /* this is driven by a toggle on a radio group, and so is invoked twice,
900 once for the item that became inactive and once for the one that became
904 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
906 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
908 if (ract->get_active()) {
909 Config->set_solo_control_is_listen_control (true);
910 Config->set_listen_position (PreFaderListen);
917 MonitorSection::update_solo_model ()
919 if (_inhibit_solo_model_update) {
923 const char* action_name = 0;
924 Glib::RefPtr<Action> act;
926 if (Config->get_solo_control_is_listen_control()) {
927 switch (Config->get_listen_position()) {
928 case AfterFaderListen:
929 action_name = X_("solo-use-afl");
932 action_name = X_("solo-use-pfl");
936 action_name = X_("solo-use-in-place");
939 act = ActionManager::get_action (X_("Solo"), action_name);
942 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
944 /* because these are radio buttons, one of them will be
945 active no matter what. to trigger a change in the
946 action so that the view picks it up, toggle it.
948 if (ract->get_active()) {
949 ract->set_active (false);
951 ract->set_active (true);
958 MonitorSection::map_state ()
960 if (!_route || !_monitor) {
964 Glib::RefPtr<Action> act;
966 update_solo_model ();
968 act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
970 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
972 tact->set_active (_monitor->cut_all());
976 act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
978 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
980 tact->set_active (_monitor->dim_all());
984 act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
986 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
988 tact->set_active (_monitor->mono());
992 uint32_t nchans = _monitor->output_streams().n_audio();
994 assert (nchans == _channel_buttons.size ());
996 for (uint32_t n = 0; n < nchans; ++n) {
998 char action_name[32];
1000 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n);
1001 act = ActionManager::get_action (X_("Monitor"), action_name);
1003 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1005 tact->set_active (_monitor->cut (n));
1009 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n);
1010 act = ActionManager::get_action (X_("Monitor"), action_name);
1012 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1014 tact->set_active (_monitor->dimmed (n));
1018 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n);
1019 act = ActionManager::get_action (X_("Monitor"), action_name);
1021 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1023 tact->set_active (_monitor->soloed (n));
1027 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n);
1028 act = ActionManager::get_action (X_("Monitor"), action_name);
1030 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1032 tact->set_active (_monitor->inverted (n));
1039 MonitorSection::do_blink (bool onoff)
1042 audition_blink (onoff);
1046 MonitorSection::audition_blink (bool onoff)
1048 if (_session == 0) {
1052 if (_session->is_auditioning()) {
1053 rude_audition_button.set_active (onoff);
1055 rude_audition_button.set_active (false);
1060 MonitorSection::solo_blink (bool onoff)
1062 if (_session == 0) {
1066 if (_session->soloing() || _session->listening()) {
1067 rude_solo_button.set_active (onoff);
1069 if (_session->soloing()) {
1070 if (_session->solo_isolated()) {
1071 rude_iso_button.set_active (onoff);
1073 rude_iso_button.set_active (false);
1078 rude_solo_button.set_active (false);
1079 rude_iso_button.set_active (false);
1084 MonitorSection::cancel_isolate (GdkEventButton*)
1087 boost::shared_ptr<RouteList> rl (_session->get_routes ());
1088 _session->set_solo_isolated (rl, false, Session::rt_cleanup, true);
1095 MonitorSection::cancel_audition (GdkEventButton*)
1098 _session->cancel_audition();
1103 #define SYNCHRONIZE_TOGGLE_ACTION(action, value) \
1105 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(action); \
1106 if (tact && tact->get_active() != value) { \
1107 tact->set_active(value); \
1112 MonitorSection::parameter_changed (std::string name)
1114 if (name == "solo-control-is-listen-control") {
1115 update_solo_model ();
1116 } else if (name == "listen-position") {
1117 update_solo_model ();
1118 } else if (name == "solo-mute-override") {
1119 SYNCHRONIZE_TOGGLE_ACTION(
1120 ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo"),
1121 Config->get_solo_mute_override ())
1122 } else if (name == "exclusive-solo") {
1123 SYNCHRONIZE_TOGGLE_ACTION(
1124 ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo"),
1125 Config->get_exclusive_solo ())
1130 MonitorSection::assign_controllables ()
1132 boost::shared_ptr<Controllable> none;
1134 if (!gain_control) {
1135 /* too early - GUI controls not set up yet */
1140 solo_cut_control->set_controllable (_session->solo_cut_control());
1141 solo_cut_display->set_controllable (_session->solo_cut_control());
1143 solo_cut_control->set_controllable (none);
1144 solo_cut_display->set_controllable (none);
1148 gain_control->set_controllable (_route->gain_control());
1149 gain_display->set_controllable (_route->gain_control());
1151 gain_control->set_controllable (none);
1156 cut_all_button.set_controllable (_monitor->cut_control());
1157 cut_all_button.watch ();
1158 dim_all_button.set_controllable (_monitor->dim_control());
1159 dim_all_button.watch ();
1160 mono_button.set_controllable (_monitor->mono_control());
1161 mono_button.watch ();
1163 dim_control->set_controllable (_monitor->dim_level_control ());
1164 dim_display->set_controllable (_monitor->dim_level_control ());
1165 solo_boost_control->set_controllable (_monitor->solo_boost_control ());
1166 solo_boost_display->set_controllable (_monitor->solo_boost_control ());
1170 cut_all_button.set_controllable (none);
1171 dim_all_button.set_controllable (none);
1172 mono_button.set_controllable (none);
1174 dim_control->set_controllable (none);
1175 dim_display->set_controllable (none);
1176 solo_boost_control->set_controllable (none);
1177 solo_boost_display->set_controllable (none);
1182 MonitorSection::state_id() const
1184 return "monitor-section";
1188 MonitorSection::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1190 using namespace Menu_Helpers;
1192 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1196 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1197 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1201 if (i != output_menu_bundles.end()) {
1205 output_menu_bundles.push_back (b);
1207 MenuList& citems = output_menu.items();
1209 std::string n = b->name ();
1210 replace_all (n, "_", " ");
1212 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MonitorSection::bundle_output_chosen), b)));
1216 MonitorSection::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1219 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1221 if (std::find (current.begin(), current.end(), c) == current.end()) {
1222 _route->output()->connect_ports_to_bundle (c, true, this);
1224 _route->output()->disconnect_ports_from_bundle (c, this);
1229 MonitorSection::output_release (GdkEventButton *ev)
1231 switch (ev->button) {
1233 edit_output_configuration ();
1240 struct RouteCompareByName {
1241 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1242 return a->name().compare (b->name()) < 0;
1247 MonitorSection::output_press (GdkEventButton *ev)
1249 using namespace Menu_Helpers;
1251 MessageDialog msg (_("No session - no I/O changes are possible"));
1256 MenuList& citems = output_menu.items();
1257 switch (ev->button) {
1260 return false; //wait for the mouse-up to pop the dialog
1264 output_menu.set_name ("ArdourContextMenu");
1266 output_menu_bundles.clear ();
1268 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(this), &MonitorSection::disconnect_output)));
1270 citems.push_back (SeparatorElem());
1271 uint32_t const n_with_separator = citems.size ();
1273 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1275 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1277 /* give user bundles first chance at being in the menu */
1279 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1280 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1281 maybe_add_bundle_to_output_menu (*i, current);
1285 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1286 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1287 maybe_add_bundle_to_output_menu (*i, current);
1291 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1292 RouteList copy = *routes;
1293 copy.sort (RouteCompareByName ());
1294 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1295 maybe_add_bundle_to_output_menu ((*i)->output()->bundle(), current);
1298 if (citems.size() == n_with_separator) {
1299 /* no routes added; remove the separator */
1303 citems.push_back (SeparatorElem());
1304 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(this), &MonitorSection::edit_output_configuration)));
1306 output_menu.popup (1, ev->time);
1317 MonitorSection::output_button_resized (Gtk::Allocation& alloc)
1319 output_button->set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1323 MonitorSection::update_output_display ()
1325 if (!_route || !_monitor || _session->deletion_in_progress()) {
1331 boost::shared_ptr<Port> port;
1332 vector<string> port_connections;
1334 uint32_t total_connection_count = 0;
1335 uint32_t io_connection_count = 0;
1336 uint32_t ardour_connection_count = 0;
1337 uint32_t system_connection_count = 0;
1338 uint32_t other_connection_count = 0;
1340 ostringstream label;
1342 bool have_label = false;
1343 bool each_io_has_one_connection = true;
1345 string connection_name;
1346 string ardour_track_name;
1347 string other_connection_type;
1348 string system_ports;
1351 ostringstream tooltip;
1352 char * tooltip_cstr;
1354 io_count = _route->n_outputs().n_total();
1355 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(_route->name()));
1358 for (io_index = 0; io_index < io_count; ++io_index) {
1360 port = _route->output()->nth (io_index);
1362 //ignore any port connections that don't match our DataType
1363 if (port->type() != DataType::AUDIO) {
1367 port_connections.clear ();
1368 port->get_connections(port_connections);
1369 io_connection_count = 0;
1371 if (!port_connections.empty()) {
1372 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1374 string& connection_name (*i);
1376 if (connection_name.find("system:") == 0) {
1377 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1380 if (io_connection_count == 0) {
1381 tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1))
1383 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1386 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1389 if (connection_name.find("ardour:") == 0) {
1390 if (ardour_track_name.empty()) {
1391 // "ardour:Master/in 1" -> "ardour:Master/"
1392 string::size_type slash = connection_name.find("/");
1393 if (slash != string::npos) {
1394 ardour_track_name = connection_name.substr(0, slash + 1);
1398 if (connection_name.find(ardour_track_name) == 0) {
1399 ++ardour_connection_count;
1401 } else if (!pn.empty()) {
1402 if (system_ports.empty()) {
1405 system_ports += "/" + pn;
1407 if (connection_name.find("system:") == 0) {
1408 ++system_connection_count;
1410 } else if (connection_name.find("system:") == 0) {
1411 // "system:playback_123" -> "123"
1412 system_port = connection_name.substr(16);
1413 if (system_ports.empty()) {
1414 system_ports += system_port;
1416 system_ports += "/" + system_port;
1419 ++system_connection_count;
1421 if (other_connection_type.empty()) {
1422 // "jamin:in 1" -> "jamin:"
1423 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1426 if (connection_name.find(other_connection_type) == 0) {
1427 ++other_connection_count;
1431 ++total_connection_count;
1432 ++io_connection_count;
1436 if (io_connection_count != 1) {
1437 each_io_has_one_connection = false;
1441 if (total_connection_count == 0) {
1442 tooltip << endl << _("Disconnected");
1445 tooltip_cstr = new char[tooltip.str().size() + 1];
1446 strcpy(tooltip_cstr, tooltip.str().c_str());
1448 set_tooltip (output_button, tooltip_cstr, "");
1450 if (each_io_has_one_connection) {
1451 if (total_connection_count == ardour_connection_count) {
1452 // all connections are to the same track in ardour
1453 // "ardour:Master/" -> "Master"
1454 string::size_type slash = ardour_track_name.find("/");
1455 if (slash != string::npos) {
1456 label << ardour_track_name.substr(7, slash - 7);
1459 } else if (total_connection_count == system_connection_count) {
1460 // all connections are to system ports
1461 label << system_ports;
1463 } else if (total_connection_count == other_connection_count) {
1464 // all connections are to the same external program eg jamin
1465 // "jamin:" -> "jamin"
1466 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1472 if (total_connection_count == 0) {
1476 // Odd configuration
1477 label << "*" << total_connection_count << "*";
1481 output_button->set_text (label.str());
1485 MonitorSection::disconnect_output ()
1488 _route->output()->disconnect(this);
1493 MonitorSection::edit_output_configuration ()
1495 if (_output_selector == 0) {
1496 _output_selector = new MonitorSelectorWindow (_session, _route->output());
1498 _output_selector->present ();
1502 MonitorSection::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1507 boost::shared_ptr<Port> a = wa.lock ();
1508 boost::shared_ptr<Port> b = wb.lock ();
1509 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1510 update_output_display ();