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"
40 #include "ardour/plugin_manager.h"
42 #include "gui_thread.h"
43 #include "monitor_section.h"
44 #include "public_editor.h"
47 #include "volume_controller.h"
48 #include "ui_config.h"
53 using namespace ARDOUR;
54 using namespace ARDOUR_UI_UTILS;
56 using namespace Gtkmm2ext;
60 Glib::RefPtr<ActionGroup> MonitorSection::monitor_actions;
62 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
64 MonitorSection::MonitorSection (Session* s)
68 , channel_table_viewport (*channel_table_scroller.get_hadjustment()
69 , *channel_table_scroller.get_vadjustment ())
72 , solo_boost_control (0)
73 , solo_cut_control (0)
76 , solo_boost_display (0)
77 , solo_cut_display (0)
78 , _output_selector (0)
79 , solo_in_place_button (_("SiP"), ArdourButton::led_default_elements)
80 , afl_button (_("AFL"), ArdourButton::led_default_elements)
81 , pfl_button (_("PFL"), ArdourButton::led_default_elements)
82 , exclusive_solo_button (ArdourButton::led_default_elements)
83 , solo_mute_override_button (ArdourButton::led_default_elements)
84 , _inhibit_solo_model_update (false)
87 using namespace Menu_Helpers;
89 Glib::RefPtr<Action> act;
91 if (!monitor_actions) {
93 /* do some static stuff */
99 _plugin_selector = new PluginSelector (PluginManager::instance());
100 insert_box = new ProcessorBox (_session, boost::bind (&MonitorSection::plugin_selector, this), _rr_selection, 0);
101 insert_box->set_no_show_all ();
103 // TODO allow keyboard shortcuts in ProcessorBox
107 /* Rude Solo & Solo Isolated */
108 rude_solo_button.set_text (_("Soloing"));
109 rude_solo_button.set_name ("rude solo");
110 rude_solo_button.show ();
112 rude_iso_button.set_text (_("Isolated"));
113 rude_iso_button.set_name ("rude isolate");
114 rude_iso_button.show ();
116 rude_audition_button.set_text (_("Auditioning"));
117 rude_audition_button.set_name ("rude audition");
118 rude_audition_button.show ();
120 Timers::blink_connect (sigc::mem_fun (*this, &MonitorSection::do_blink));
122 act = ActionManager::get_action (X_("Main"), X_("cancel-solo"));
123 rude_solo_button.set_related_action (act);
124 UI::instance()->set_tip (rude_solo_button, _("When active, something is soloed.\nClick to de-solo everything"));
126 rude_iso_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_isolate), false);
127 UI::instance()->set_tip (rude_iso_button, _("When active, something is solo-isolated.\nClick to de-isolate everything"));
129 rude_audition_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_audition), false);
130 UI::instance()->set_tip (rude_audition_button, _("When active, auditioning is active.\nClick to stop the audition"));
132 /* SIP, AFL, PFL radio */
134 solo_in_place_button.set_name ("monitor section solo model");
135 afl_button.set_name ("monitor section solo model");
136 pfl_button.set_name ("monitor section solo model");
138 solo_in_place_button.set_led_left (true);
139 afl_button.set_led_left (true);
140 pfl_button.set_led_left (true);
142 solo_in_place_button.show ();
146 act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
147 set_tooltip (solo_in_place_button, _("Solo controls affect solo-in-place"));
149 solo_in_place_button.set_related_action (act);
152 act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
153 set_tooltip (afl_button, _("Solo controls toggle after-fader-listen"));
155 afl_button.set_related_action (act);
158 act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
159 set_tooltip (pfl_button, _("Solo controls toggle pre-fader-listen"));
161 pfl_button.set_related_action (act);
164 /* Solo option buttons */
165 exclusive_solo_button.set_text (_("Excl. Solo"));
166 exclusive_solo_button.set_name (X_("monitor solo exclusive"));
167 set_tooltip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time"));
169 act = ActionManager::get_action (X_("Monitor"), X_("toggle-exclusive-solo"));
171 exclusive_solo_button.set_related_action (act);
174 solo_mute_override_button.set_text (_("Solo ยป Mute"));
175 solo_mute_override_button.set_name (X_("monitor solo override"));
176 set_tooltip (&solo_mute_override_button, _("If enabled, solo will override mute\n(a soloed & muted track or bus will be audible)"));
178 act = ActionManager::get_action (X_("Monitor"), X_("toggle-mute-overrides-solo"));
180 solo_mute_override_button.set_related_action (act);
185 Label* solo_boost_label;
186 Label* solo_cut_label;
189 /* Solo Boost Knob */
191 solo_boost_control = new ArdourKnob ();
192 solo_boost_control->set_name("monitor knob");
193 solo_boost_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
194 set_tooltip (*solo_boost_control, _("Gain increase for soloed signals (0dB is normal)"));
196 solo_boost_display = new ArdourDisplay ();
197 solo_boost_display->set_name("monitor section cut");
198 solo_boost_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
199 solo_boost_display->add_controllable_preset(_("0 dB"), 0.0);
200 solo_boost_display->add_controllable_preset(_("3 dB"), 3.0);
201 solo_boost_display->add_controllable_preset(_("6 dB"), 6.0);
202 solo_boost_display->add_controllable_preset(_("10 dB"), 10.0);
204 solo_boost_label = manage (new Label (_("Solo Boost")));
208 solo_cut_control = new ArdourKnob ();
209 solo_cut_control->set_name ("monitor knob");
210 solo_cut_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
211 set_tooltip (*solo_cut_control, _("Gain reduction non-soloed signals\nA value above -inf dB causes \"solo-in-front\""));
213 solo_cut_display = new ArdourDisplay ();
214 solo_cut_display->set_name("monitor section cut");
215 solo_cut_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
216 solo_cut_display->add_controllable_preset(_("0 dB"), 0.0);
217 solo_cut_display->add_controllable_preset(_("-6 dB"), -6.0);
218 solo_cut_display->add_controllable_preset(_("-12 dB"), -12.0);
219 solo_cut_display->add_controllable_preset(_("-20 dB"), -20.0);
220 solo_cut_display->add_controllable_preset(_("OFF"), -1200.0);
222 solo_cut_label = manage (new Label (_("SiP Cut")));
226 dim_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
227 dim_control->set_name ("monitor knob");
228 dim_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
229 set_tooltip (*dim_control, _("Gain reduction to use when dimming monitor outputs"));
231 dim_display = new ArdourDisplay ();
232 dim_display->set_name("monitor section cut");
233 dim_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
234 dim_display->add_controllable_preset(_("0 dB"), 0.0);
235 dim_display->add_controllable_preset(_("-3 dB"), -3.0);
236 dim_display->add_controllable_preset(_("-6 dB"), -6.0);
237 dim_display->add_controllable_preset(_("-12 dB"), -12.0);
238 dim_display->add_controllable_preset(_("-20 dB"), -20.0);
240 dim_label = manage (new Label (_("Dim")));
243 cut_all_button.set_text (_("Mute"));
244 cut_all_button.set_name ("monitor section cut");
245 cut_all_button.set_name (X_("monitor section cut"));
246 cut_all_button.set_size_request (-1, PX_SCALE(30));
247 cut_all_button.show ();
249 act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
251 cut_all_button.set_related_action (act);
255 dim_all_button.set_text (_("Dim"));
256 dim_all_button.set_name ("monitor section dim");
257 dim_all_button.set_size_request (-1, PX_SCALE(30));
258 act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
260 dim_all_button.set_related_action (act);
264 mono_button.set_text (_("Mono"));
265 mono_button.set_name ("monitor section mono");
266 mono_button.set_size_request (-1, PX_SCALE(30));
267 act = ActionManager::get_action (X_("Monitor"), X_("monitor-mono"));
269 mono_button.set_related_action (act);
274 gain_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
275 gain_control->set_name("monitor knob");
276 gain_control->set_size_request (PX_SCALE(60), PX_SCALE(60));
278 gain_display = new ArdourDisplay ();
279 gain_display->set_name("monitor section cut");
280 gain_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
281 gain_display->add_controllable_preset(_("0 dB"), 0.0);
282 gain_display->add_controllable_preset(_("-3 dB"), -3.0);
283 gain_display->add_controllable_preset(_("-6 dB"), -6.0);
284 gain_display->add_controllable_preset(_("-12 dB"), -12.0);
285 gain_display->add_controllable_preset(_("-20 dB"), -20.0);
286 gain_display->add_controllable_preset(_("-30 dB"), -30.0);
288 Label* output_label = manage (new Label (_("Output")));
289 output_label->set_name (X_("MonitorSectionLabel"));
291 output_button = new ArdourButton ();
292 output_button->set_text (_("Output"));
293 output_button->set_name (X_("monitor section cut"));
294 output_button->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
295 output_button->set_layout_ellipsize_width (PX_SCALE(128) * PANGO_SCALE);
297 channel_table_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
298 channel_table_scroller.set_size_request (-1, PX_SCALE(150));
299 channel_table_scroller.set_shadow_type (Gtk::SHADOW_NONE);
300 channel_table_scroller.show ();
301 channel_table_scroller.add (channel_table_viewport);
303 channel_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
304 channel_size_group->add_widget (channel_table_header);
305 channel_size_group->add_widget (channel_table);
307 channel_table_header.resize (1, 5);
309 Label* l1 = manage (new Label (X_(" ")));
310 l1->set_name (X_("MonitorSectionLabel"));
311 channel_table_header.attach (*l1, 0, 1, 0, 1, EXPAND|FILL);
313 l1 = manage (new Label (_("Mute")));
314 l1->set_name (X_("MonitorSectionLabel"));
315 channel_table_header.attach (*l1, 1, 2, 0, 1, EXPAND|FILL);
317 l1 = manage (new Label (_("Dim")));
318 l1->set_name (X_("MonitorSectionLabel"));
319 channel_table_header.attach (*l1, 2, 3, 0, 1, EXPAND|FILL);
321 l1 = manage (new Label (_("Solo")));
322 l1->set_name (X_("MonitorSectionLabel"));
323 channel_table_header.attach (*l1, 3, 4, 0, 1, EXPAND|FILL);
325 l1 = manage (new Label (_("Inv")));
326 l1->set_name (X_("MonitorSectionLabel"));
327 channel_table_header.attach (*l1, 4, 5, 0, 1, EXPAND|FILL);
329 channel_table_header.show ();
332 /****************************************************************************
333 * LAYOUT top to bottom
336 // solo, iso information
337 HBox* rude_box = manage (new HBox);
338 rude_box->set_spacing (PX_SCALE(4));
339 rude_box->set_homogeneous (true);
340 rude_box->pack_start (rude_solo_button, true, true);
341 rude_box->pack_start (rude_iso_button, true, true);
343 // solo options (right align)
344 HBox* tbx1 = manage (new HBox);
345 tbx1->pack_end (exclusive_solo_button, false, false);
347 HBox* tbx2 = manage (new HBox);
348 tbx2->pack_end (solo_mute_override_button, false, false);
350 HBox* tbx0 = manage (new HBox); // space
352 // combined solo mode (Sip, AFL, PFL) & solo options
353 Table *solo_tbl = manage (new Table);
354 solo_tbl->attach (solo_in_place_button, 0, 1, 0, 1, EXPAND|FILL, SHRINK, 0, 2);
355 solo_tbl->attach (pfl_button, 0, 1, 1, 2, EXPAND|FILL, SHRINK, 0, 2);
356 solo_tbl->attach (afl_button, 0, 1, 2, 3, EXPAND|FILL, SHRINK, 0, 2);
357 solo_tbl->attach (*tbx0, 1, 2, 0, 3, EXPAND|FILL, SHRINK, 2, 2);
358 solo_tbl->attach (*tbx1, 2, 3, 1, 2, EXPAND|FILL, SHRINK, 0, 2);
359 solo_tbl->attach (*tbx2, 2, 3, 2, 3, EXPAND|FILL, SHRINK, 0, 2);
361 // boost, cut, dim volume control
362 Table *level_tbl = manage (new Table);
363 level_tbl->attach (*solo_boost_label, 0, 2, 0, 1, EXPAND|FILL, SHRINK, 1, 2);
364 level_tbl->attach (*solo_boost_control, 0, 2, 1, 2, EXPAND|FILL, SHRINK, 1, 2);
365 level_tbl->attach (*solo_boost_display, 0, 2, 2, 3, EXPAND , SHRINK, 1, 2);
367 level_tbl->attach (*solo_cut_label, 2, 4, 0, 1, EXPAND|FILL, SHRINK, 1, 2);
368 level_tbl->attach (*solo_cut_control, 2, 4, 1, 2, EXPAND|FILL, SHRINK, 1, 2);
369 level_tbl->attach (*solo_cut_display, 2, 4, 2, 3, EXPAND , SHRINK, 1, 2);
371 level_tbl->attach (*dim_label, 1, 3, 3, 4, EXPAND|FILL, SHRINK, 1, 2);
372 level_tbl->attach (*dim_control, 1, 3, 4, 5, EXPAND|FILL, SHRINK, 1, 2);
373 level_tbl->attach (*dim_display, 1, 3, 5, 6, EXPAND , SHRINK, 1, 2);
375 /* note that we don't pack the table_hpacker till later
376 * -> top level channel_table_packer */
377 table_hpacker.pack_start (channel_table, true, true);
380 HBox* mono_dim_box = manage (new HBox);
381 mono_dim_box->set_spacing (PX_SCALE(4));
382 mono_dim_box->set_homogeneous (true);
383 mono_dim_box->pack_start (mono_button, true, true);
384 mono_dim_box->pack_end (dim_all_button, true, true);
387 Label* spin_label = manage (new Label (_("Monitor")));
388 VBox* spin_packer = manage (new VBox);
389 spin_packer->set_spacing (PX_SCALE(2));
390 spin_packer->pack_start (*spin_label, false, false);
391 spin_packer->pack_start (*gain_control, false, false);
392 spin_packer->pack_start (*gain_display, false, false);
394 HBox* master_box = manage (new HBox);
395 master_box->pack_start (*spin_packer, true, false);
397 // combined gain section (channels, mute, dim, master)
398 VBox* lower_packer = manage (new VBox);
399 lower_packer->set_spacing (PX_SCALE(8));
400 lower_packer->pack_start (channel_table_header, false, false);
401 lower_packer->pack_start (channel_table_packer, false, false);
402 lower_packer->pack_start (*mono_dim_box, false, false);
403 lower_packer->pack_start (cut_all_button, false, false);
404 lower_packer->pack_start (*master_box, false, false, PX_SCALE(10));
406 // output port select
407 VBox* out_packer = manage (new VBox);
408 out_packer->set_spacing (PX_SCALE(2));
409 out_packer->pack_start (*output_label, false, false);
410 out_packer->pack_start (*output_button, false, false);
412 /****************************************************************************
415 VBox* vpacker = manage (new VBox);
416 vpacker->set_border_width (PX_SCALE(3));
417 vpacker->set_spacing (PX_SCALE(10));
418 vpacker->pack_start (*rude_box, false, false, PX_SCALE(3));
419 vpacker->pack_start (*solo_tbl, false, false);
420 vpacker->pack_start (*level_tbl, false, false);
421 vpacker->pack_start (*lower_packer, false, false);
422 vpacker->pack_start (*insert_box, true, true);
423 vpacker->pack_end (*out_packer, false, false);
425 hpacker.set_spacing (0);
426 hpacker.pack_start (*vpacker, true, true);
428 gain_control->show_all ();
429 gain_display->show_all ();
430 dim_control->show_all ();
431 dim_display->show_all();
432 solo_boost_control->show_all ();
433 solo_boost_display->show_all();
435 mono_dim_box->show ();
436 spin_packer->show ();
438 channel_table.show ();
441 solo_tbl->show_all();
443 lower_packer->show ();
451 assign_controllables ();
453 output_button->signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::output_press), false);
454 output_button->signal_button_release_event().connect (sigc::mem_fun(*this, &MonitorSection::output_release), false);
456 _tearoff = new TearOff (hpacker);
458 if (!UIConfiguration::instance().get_floating_monitor_section()) {
459 /* if torn off, make this a normal window
460 * (default is WINDOW_TYPE_HINT_UTILITY in libs/gtkmm2ext/tearoff.cc)
462 _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
464 _tearoff->tearoff_window().set_title (X_("Monitor"));
465 _tearoff->tearoff_window().signal_key_press_event().connect (sigc::ptr_fun (forward_key_press), false);
467 update_output_display();
469 /* catch changes that affect us */
470 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
471 *this, invalidator (*this), boost::bind (&MonitorSection::port_connected_or_disconnected, this, _1, _3), gui_context ()
473 Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context());
476 MonitorSection::~MonitorSection ()
478 for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
482 _channel_buttons.clear ();
483 _output_changed_connection.disconnect ();
486 delete output_button;
491 delete solo_boost_control;
492 delete solo_boost_display;
493 delete solo_cut_control;
494 delete solo_cut_display;
496 delete _output_selector;
497 _output_selector = 0;
501 MonitorSection::set_session (Session* s)
503 AxisView::set_session (s);
504 _plugin_selector->set_session (_session);
508 _route = _session->monitor_out ();
511 /* session with monitor section */
512 _monitor = _route->monitor_control ();
513 assign_controllables ();
514 _route->output()->changed.connect (_output_changed_connection, invalidator (*this),
515 boost::bind (&MonitorSection::update_output_display, this),
517 insert_box->set_route (_route);
519 /* session with no monitor section */
520 _output_changed_connection.disconnect();
523 delete _output_selector;
524 _output_selector = 0;
527 if (channel_table_scroller.get_parent()) {
528 /* scroller is packed, so remove it */
529 channel_table_packer.remove (channel_table_scroller);
532 if (table_hpacker.get_parent () == &channel_table_packer) {
533 /* this occurs when the table hpacker is directly
534 packed, so remove it.
536 channel_table_packer.remove (table_hpacker);
537 } else if (table_hpacker.get_parent()) {
538 channel_table_viewport.remove ();
541 if (_monitor->output_streams().n_audio() > 7) {
542 /* put the table into a scrolled window, and then put
543 * that into the channel vpacker, after the table header
545 channel_table_viewport.add (table_hpacker);
546 channel_table_packer.pack_start (channel_table_scroller, true, true);
547 channel_table_viewport.show ();
548 channel_table_scroller.show ();
551 /* just put the channel table itself into the channel
552 * vpacker, after the table header
555 channel_table_packer.pack_start (table_hpacker, true, true);
556 channel_table_scroller.hide ();
559 table_hpacker.show ();
560 channel_table.show ();
565 _output_changed_connection.disconnect();
568 control_connections.drop_connections ();
569 rude_iso_button.unset_active_state ();
570 rude_solo_button.unset_active_state ();
571 delete _output_selector;
572 _output_selector = 0;
574 assign_controllables ();
578 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
580 cut.set_name (X_("monitor section cut"));
581 dim.set_name (X_("monitor section dim"));
582 solo.set_name (X_("monitor section solo"));
583 invert.set_name (X_("monitor section invert"));
585 cut.unset_flags (Gtk::CAN_FOCUS);
586 dim.unset_flags (Gtk::CAN_FOCUS);
587 solo.unset_flags (Gtk::CAN_FOCUS);
588 invert.unset_flags (Gtk::CAN_FOCUS);
592 MonitorSection::populate_buttons ()
598 Glib::RefPtr<Action> act;
599 uint32_t nchans = _monitor->output_streams().n_audio();
601 channel_table.resize (nchans, 5);
602 channel_table.set_col_spacings (6);
603 channel_table.set_row_spacings (6);
604 channel_table.set_homogeneous (true);
606 const uint32_t row_offset = 0;
608 for (uint32_t i = 0; i < nchans; ++i) {
621 snprintf (buf, sizeof (buf), "%d", i+1);
625 Label* label = manage (new Label (l));
626 channel_table.attach (*label, 0, 1, i+row_offset, i+row_offset+1, EXPAND|FILL);
628 ChannelButtonSet* cbs = new ChannelButtonSet;
630 _channel_buttons.push_back (cbs);
632 channel_table.attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, EXPAND|FILL);
633 channel_table.attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, EXPAND|FILL);
634 channel_table.attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, EXPAND|FILL);
635 channel_table.attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, EXPAND|FILL);
637 snprintf (buf, sizeof (buf), "monitor-cut-%u", i+1);
638 act = ActionManager::get_action (X_("Monitor"), buf);
640 cbs->cut.set_related_action (act);
643 snprintf (buf, sizeof (buf), "monitor-dim-%u", i+1);
644 act = ActionManager::get_action (X_("Monitor"), buf);
646 cbs->dim.set_related_action (act);
649 snprintf (buf, sizeof (buf), "monitor-solo-%u", i+1);
650 act = ActionManager::get_action (X_("Monitor"), buf);
652 cbs->solo.set_related_action (act);
655 snprintf (buf, sizeof (buf), "monitor-invert-%u", i+1);
656 act = ActionManager::get_action (X_("Monitor"), buf);
658 cbs->invert.set_related_action (act);
662 channel_table.show_all ();
666 MonitorSection::toggle_exclusive_solo ()
672 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo");
674 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
675 Config->set_exclusive_solo (tact->get_active());
681 MonitorSection::toggle_mute_overrides_solo ()
687 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo");
689 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
690 Config->set_solo_mute_override (tact->get_active());
695 MonitorSection::dim_all ()
701 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
703 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
704 _monitor->set_dim_all (tact->get_active());
710 MonitorSection::cut_all ()
716 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
718 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
719 _monitor->set_cut_all (tact->get_active());
724 MonitorSection::mono ()
730 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
732 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
733 _monitor->set_mono (tact->get_active());
738 MonitorSection::cut_channel (uint32_t chn)
745 snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
747 --chn; // 0-based in backend
749 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
751 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
752 _monitor->set_cut (chn, tact->get_active());
757 MonitorSection::dim_channel (uint32_t chn)
764 snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
766 --chn; // 0-based in backend
768 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
770 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
771 _monitor->set_dim (chn, tact->get_active());
777 MonitorSection::solo_channel (uint32_t chn)
784 snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
786 --chn; // 0-based in backend
788 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
790 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
791 _monitor->set_solo (chn, tact->get_active());
797 MonitorSection::invert_channel (uint32_t chn)
804 snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
806 --chn; // 0-based in backend
808 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
810 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
811 _monitor->set_polarity (chn, tact->get_active());
816 MonitorSection::register_actions ()
820 Glib::RefPtr<Action> act;
822 monitor_actions = ActionGroup::create (X_("Monitor"));
823 ActionManager::add_action_group (monitor_actions);
825 ActionManager::register_toggle_action (monitor_actions, "monitor-mono", "", _("Switch monitor to mono"),
826 sigc::mem_fun (*this, &MonitorSection::mono));
828 ActionManager::register_toggle_action (monitor_actions, "monitor-cut-all", "", _("Cut monitor"),
829 sigc::mem_fun (*this, &MonitorSection::cut_all));
831 ActionManager::register_toggle_action (monitor_actions, "monitor-dim-all", "", _("Dim monitor"),
832 sigc::mem_fun (*this, &MonitorSection::dim_all));
834 act = ActionManager::register_toggle_action (monitor_actions, "toggle-exclusive-solo", "", _("Toggle exclusive solo mode"),
835 sigc::mem_fun (*this, &MonitorSection::toggle_exclusive_solo));
837 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
838 tact->set_active (Config->get_exclusive_solo());
840 act = ActionManager::register_toggle_action (monitor_actions, "toggle-mute-overrides-solo", "", _("Toggle mute overrides solo mode"),
841 sigc::mem_fun (*this, &MonitorSection::toggle_mute_overrides_solo));
843 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
844 tact->set_active (Config->get_solo_mute_override());
847 /* note the 1-based counting (for naming - backend uses 0-based) */
849 for (uint32_t chn = 1; chn <= 16; ++chn) {
851 action_name = string_compose (X_("monitor-cut-%1"), chn);
852 action_descr = string_compose (_("Cut monitor channel %1"), chn);
853 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
854 sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
856 action_name = string_compose (X_("monitor-dim-%1"), chn);
857 action_descr = string_compose (_("Dim monitor channel %1"), chn);
858 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
859 sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
861 action_name = string_compose (X_("monitor-solo-%1"), chn);
862 action_descr = string_compose (_("Solo monitor channel %1"), chn);
863 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
864 sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
866 action_name = string_compose (X_("monitor-invert-%1"), chn);
867 action_descr = string_compose (_("Invert monitor channel %1"), chn);
868 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
869 sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
874 Glib::RefPtr<ActionGroup> solo_actions = ActionGroup::create (X_("Solo"));
875 RadioAction::Group solo_group;
877 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-in-place", "", _("In-place solo"),
878 sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
879 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-afl", "", _("After Fade Listen (AFL) solo"),
880 sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
881 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-pfl", "", _("Pre Fade Listen (PFL) solo"),
882 sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
884 ActionManager::add_action_group (solo_actions);
888 MonitorSection::solo_use_in_place ()
890 /* this is driven by a toggle on a radio group, and so is invoked twice,
891 once for the item that became inactive and once for the one that became
895 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
898 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
900 if (!ract->get_active ()) {
901 /* We are turning SiP off, which means that AFL or PFL will be turned on
902 shortly; don't update the solo model in the mean time, as if the currently
903 configured listen position is not the one that is about to be turned on,
904 things will go wrong.
906 _inhibit_solo_model_update = true;
908 Config->set_solo_control_is_listen_control (!ract->get_active());
909 _inhibit_solo_model_update = false;
915 MonitorSection::solo_use_afl ()
917 /* this is driven by a toggle on a radio group, and so is invoked twice,
918 once for the item that became inactive and once for the one that became
922 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
924 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
926 if (ract->get_active()) {
927 Config->set_solo_control_is_listen_control (true);
928 Config->set_listen_position (AfterFaderListen);
935 MonitorSection::solo_use_pfl ()
937 /* this is driven by a toggle on a radio group, and so is invoked twice,
938 once for the item that became inactive and once for the one that became
942 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
944 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
946 if (ract->get_active()) {
947 Config->set_solo_control_is_listen_control (true);
948 Config->set_listen_position (PreFaderListen);
955 MonitorSection::update_solo_model ()
957 if (_inhibit_solo_model_update) {
961 const char* action_name = 0;
962 Glib::RefPtr<Action> act;
964 if (Config->get_solo_control_is_listen_control()) {
965 switch (Config->get_listen_position()) {
966 case AfterFaderListen:
967 action_name = X_("solo-use-afl");
970 action_name = X_("solo-use-pfl");
974 action_name = X_("solo-use-in-place");
977 act = ActionManager::get_action (X_("Solo"), action_name);
980 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
982 /* because these are radio buttons, one of them will be
983 active no matter what. to trigger a change in the
984 action so that the view picks it up, toggle it.
986 if (ract->get_active()) {
987 ract->set_active (false);
989 ract->set_active (true);
996 MonitorSection::map_state ()
998 if (!_route || !_monitor) {
1002 Glib::RefPtr<Action> act;
1004 update_solo_model ();
1006 act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
1008 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1010 tact->set_active (_monitor->cut_all());
1014 act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
1016 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1018 tact->set_active (_monitor->dim_all());
1022 act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
1024 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1026 tact->set_active (_monitor->mono());
1030 uint32_t nchans = _monitor->output_streams().n_audio();
1032 assert (nchans == _channel_buttons.size ());
1034 for (uint32_t n = 0; n < nchans; ++n) {
1036 char action_name[32];
1038 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n);
1039 act = ActionManager::get_action (X_("Monitor"), action_name);
1041 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1043 tact->set_active (_monitor->cut (n));
1047 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n);
1048 act = ActionManager::get_action (X_("Monitor"), action_name);
1050 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1052 tact->set_active (_monitor->dimmed (n));
1056 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n);
1057 act = ActionManager::get_action (X_("Monitor"), action_name);
1059 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1061 tact->set_active (_monitor->soloed (n));
1065 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n);
1066 act = ActionManager::get_action (X_("Monitor"), action_name);
1068 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1070 tact->set_active (_monitor->inverted (n));
1077 MonitorSection::do_blink (bool onoff)
1080 audition_blink (onoff);
1084 MonitorSection::audition_blink (bool onoff)
1086 if (_session == 0) {
1090 if (_session->is_auditioning()) {
1091 rude_audition_button.set_active (onoff);
1093 rude_audition_button.set_active (false);
1098 MonitorSection::solo_blink (bool onoff)
1100 if (_session == 0) {
1104 if (_session->soloing() || _session->listening()) {
1105 rude_solo_button.set_active (onoff);
1107 if (_session->soloing()) {
1108 if (_session->solo_isolated()) {
1109 rude_iso_button.set_active (onoff);
1111 rude_iso_button.set_active (false);
1116 rude_solo_button.set_active (false);
1117 rude_iso_button.set_active (false);
1122 MonitorSection::cancel_isolate (GdkEventButton*)
1125 boost::shared_ptr<RouteList> rl (_session->get_routes ());
1126 _session->set_solo_isolated (rl, false, Session::rt_cleanup, true);
1133 MonitorSection::cancel_audition (GdkEventButton*)
1136 _session->cancel_audition();
1141 #define SYNCHRONIZE_TOGGLE_ACTION(action, value) \
1143 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(action); \
1144 if (tact && tact->get_active() != value) { \
1145 tact->set_active(value); \
1150 MonitorSection::parameter_changed (std::string name)
1152 if (name == "solo-control-is-listen-control") {
1153 update_solo_model ();
1154 } else if (name == "listen-position") {
1155 update_solo_model ();
1156 } else if (name == "solo-mute-override") {
1157 SYNCHRONIZE_TOGGLE_ACTION(
1158 ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo"),
1159 Config->get_solo_mute_override ())
1160 } else if (name == "exclusive-solo") {
1161 SYNCHRONIZE_TOGGLE_ACTION(
1162 ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo"),
1163 Config->get_exclusive_solo ())
1168 MonitorSection::assign_controllables ()
1170 boost::shared_ptr<Controllable> none;
1172 if (!gain_control) {
1173 /* too early - GUI controls not set up yet */
1178 solo_cut_control->set_controllable (_session->solo_cut_control());
1179 solo_cut_display->set_controllable (_session->solo_cut_control());
1181 solo_cut_control->set_controllable (none);
1182 solo_cut_display->set_controllable (none);
1186 gain_control->set_controllable (_route->gain_control());
1187 gain_display->set_controllable (_route->gain_control());
1189 gain_control->set_controllable (none);
1194 cut_all_button.set_controllable (_monitor->cut_control());
1195 cut_all_button.watch ();
1196 dim_all_button.set_controllable (_monitor->dim_control());
1197 dim_all_button.watch ();
1198 mono_button.set_controllable (_monitor->mono_control());
1199 mono_button.watch ();
1201 dim_control->set_controllable (_monitor->dim_level_control ());
1202 dim_display->set_controllable (_monitor->dim_level_control ());
1203 solo_boost_control->set_controllable (_monitor->solo_boost_control ());
1204 solo_boost_display->set_controllable (_monitor->solo_boost_control ());
1208 cut_all_button.set_controllable (none);
1209 dim_all_button.set_controllable (none);
1210 mono_button.set_controllable (none);
1212 dim_control->set_controllable (none);
1213 dim_display->set_controllable (none);
1214 solo_boost_control->set_controllable (none);
1215 solo_boost_display->set_controllable (none);
1220 MonitorSection::state_id() const
1222 return "monitor-section";
1226 MonitorSection::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1228 using namespace Menu_Helpers;
1230 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1234 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1235 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1239 if (i != output_menu_bundles.end()) {
1243 output_menu_bundles.push_back (b);
1245 MenuList& citems = output_menu.items();
1247 std::string n = b->name ();
1248 replace_all (n, "_", " ");
1250 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MonitorSection::bundle_output_chosen), b)));
1254 MonitorSection::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1257 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1259 if (std::find (current.begin(), current.end(), c) == current.end()) {
1260 _route->output()->connect_ports_to_bundle (c, true, this);
1262 _route->output()->disconnect_ports_from_bundle (c, this);
1267 MonitorSection::output_release (GdkEventButton *ev)
1269 switch (ev->button) {
1271 edit_output_configuration ();
1278 struct RouteCompareByName {
1279 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1280 return a->name().compare (b->name()) < 0;
1285 MonitorSection::output_press (GdkEventButton *ev)
1287 using namespace Menu_Helpers;
1289 MessageDialog msg (_("No session - no I/O changes are possible"));
1294 MenuList& citems = output_menu.items();
1295 switch (ev->button) {
1298 return false; //wait for the mouse-up to pop the dialog
1302 output_menu.set_name ("ArdourContextMenu");
1304 output_menu_bundles.clear ();
1306 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(this), &MonitorSection::disconnect_output)));
1308 citems.push_back (SeparatorElem());
1309 uint32_t const n_with_separator = citems.size ();
1311 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1313 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1315 /* give user bundles first chance at being in the menu */
1317 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1318 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1319 maybe_add_bundle_to_output_menu (*i, current);
1323 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1324 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1325 maybe_add_bundle_to_output_menu (*i, current);
1329 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1330 RouteList copy = *routes;
1331 copy.sort (RouteCompareByName ());
1332 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1333 maybe_add_bundle_to_output_menu ((*i)->output()->bundle(), current);
1336 if (citems.size() == n_with_separator) {
1337 /* no routes added; remove the separator */
1341 citems.push_back (SeparatorElem());
1342 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(this), &MonitorSection::edit_output_configuration)));
1344 output_menu.popup (1, ev->time);
1355 MonitorSection::update_output_display ()
1357 if (!_route || !_monitor || _session->deletion_in_progress()) {
1363 boost::shared_ptr<Port> port;
1364 vector<string> port_connections;
1366 uint32_t total_connection_count = 0;
1367 uint32_t io_connection_count = 0;
1368 uint32_t ardour_connection_count = 0;
1369 uint32_t system_connection_count = 0;
1370 uint32_t other_connection_count = 0;
1372 ostringstream label;
1374 bool have_label = false;
1375 bool each_io_has_one_connection = true;
1377 string connection_name;
1378 string ardour_track_name;
1379 string other_connection_type;
1380 string system_ports;
1383 ostringstream tooltip;
1384 char * tooltip_cstr;
1386 io_count = _route->n_outputs().n_total();
1387 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (_route->name()));
1390 for (io_index = 0; io_index < io_count; ++io_index) {
1392 port = _route->output()->nth (io_index);
1394 //ignore any port connections that don't match our DataType
1395 if (port->type() != DataType::AUDIO) {
1399 port_connections.clear ();
1400 port->get_connections(port_connections);
1401 io_connection_count = 0;
1403 if (!port_connections.empty()) {
1404 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1406 string& connection_name (*i);
1408 if (connection_name.find("system:") == 0) {
1409 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1412 if (io_connection_count == 0) {
1413 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1415 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1418 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1421 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1422 if (ardour_track_name.empty()) {
1423 // "ardour:Master/in 1" -> "ardour:Master/"
1424 string::size_type slash = connection_name.find("/");
1425 if (slash != string::npos) {
1426 ardour_track_name = connection_name.substr(0, slash + 1);
1430 if (connection_name.find(ardour_track_name) == 0) {
1431 ++ardour_connection_count;
1433 } else if (!pn.empty()) {
1434 if (system_ports.empty()) {
1437 system_ports += "/" + pn;
1439 if (connection_name.find("system:") == 0) {
1440 ++system_connection_count;
1442 } else if (connection_name.find("system:") == 0) {
1443 // "system:playback_123" -> "123"
1444 system_port = connection_name.substr(16);
1445 if (system_ports.empty()) {
1446 system_ports += system_port;
1448 system_ports += "/" + system_port;
1451 ++system_connection_count;
1453 if (other_connection_type.empty()) {
1454 // "jamin:in 1" -> "jamin:"
1455 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1458 if (connection_name.find(other_connection_type) == 0) {
1459 ++other_connection_count;
1463 ++total_connection_count;
1464 ++io_connection_count;
1468 if (io_connection_count != 1) {
1469 each_io_has_one_connection = false;
1473 if (total_connection_count == 0) {
1474 tooltip << endl << _("Disconnected");
1477 tooltip_cstr = new char[tooltip.str().size() + 1];
1478 strcpy(tooltip_cstr, tooltip.str().c_str());
1480 set_tooltip (output_button, tooltip_cstr, "");
1482 if (each_io_has_one_connection) {
1483 if (total_connection_count == ardour_connection_count) {
1484 // all connections are to the same track in ardour
1485 // "ardour:Master/" -> "Master"
1486 string::size_type slash = ardour_track_name.find("/");
1487 if (slash != string::npos) {
1488 label << ardour_track_name.substr(7, slash - 7);
1491 } else if (total_connection_count == system_connection_count) {
1492 // all connections are to system ports
1493 label << system_ports;
1495 } else if (total_connection_count == other_connection_count) {
1496 // all connections are to the same external program eg jamin
1497 // "jamin:" -> "jamin"
1498 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1504 if (total_connection_count == 0) {
1508 // Odd configuration
1509 label << "*" << total_connection_count << "*";
1513 output_button->set_text (label.str());
1517 MonitorSection::disconnect_output ()
1520 _route->output()->disconnect(this);
1525 MonitorSection::edit_output_configuration ()
1527 if (_output_selector == 0) {
1528 _output_selector = new MonitorSelectorWindow (_session, _route->output());
1530 _output_selector->present ();
1534 MonitorSection::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1539 boost::shared_ptr<Port> a = wa.lock ();
1540 boost::shared_ptr<Port> b = wb.lock ();
1541 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1542 update_output_display ();