2 Copyright (C) 2012 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <gdkmm/pixbuf.h>
22 #include "pbd/compose.h"
23 #include "pbd/error.h"
24 #include "pbd/replace_all.h"
26 #include "gtkmm2ext/bindable_button.h"
27 #include "gtkmm2ext/tearoff.h"
28 #include "gtkmm2ext/actions.h"
29 #include "gtkmm2ext/motionfeedback.h"
30 #include "gtkmm2ext/utils.h"
32 #include <gtkmm/menu.h>
33 #include <gtkmm/menuitem.h>
35 #include "ardour/audioengine.h"
36 #include "ardour/monitor_processor.h"
37 #include "ardour/port.h"
38 #include "ardour/route.h"
39 #include "ardour/user_bundle.h"
41 #include "gui_thread.h"
42 #include "monitor_section.h"
43 #include "public_editor.h"
46 #include "volume_controller.h"
47 #include "ui_config.h"
52 using namespace ARDOUR;
53 using namespace ARDOUR_UI_UTILS;
55 using namespace Gtkmm2ext;
59 Glib::RefPtr<ActionGroup> MonitorSection::monitor_actions;
61 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
63 MonitorSection::MonitorSection (Session* s)
67 , channel_table_viewport (*channel_table_scroller.get_hadjustment()
68 , *channel_table_scroller.get_vadjustment ())
71 , solo_boost_control (0)
72 , solo_cut_control (0)
75 , solo_boost_display (0)
76 , solo_cut_display (0)
77 , _output_selector (0)
78 , solo_in_place_button (_("SiP"), ArdourButton::led_default_elements)
79 , afl_button (_("AFL"), ArdourButton::led_default_elements)
80 , pfl_button (_("PFL"), ArdourButton::led_default_elements)
81 , exclusive_solo_button (ArdourButton::led_default_elements)
82 , solo_mute_override_button (ArdourButton::led_default_elements)
83 , _inhibit_solo_model_update (false)
86 using namespace Menu_Helpers;
88 Glib::RefPtr<Action> act;
90 if (!monitor_actions) {
92 /* do some static stuff */
100 /* Rude Solo & Solo Isolated */
101 rude_solo_button.set_text (_("Soloing"));
102 rude_solo_button.set_name ("rude solo");
103 rude_solo_button.show ();
105 rude_iso_button.set_text (_("Isolated"));
106 rude_iso_button.set_name ("rude isolate");
107 rude_iso_button.show ();
109 rude_audition_button.set_text (_("Auditioning"));
110 rude_audition_button.set_name ("rude audition");
111 rude_audition_button.show ();
113 Timers::blink_connect (sigc::mem_fun (*this, &MonitorSection::do_blink));
115 act = ActionManager::get_action (X_("Main"), X_("cancel-solo"));
116 rude_solo_button.set_related_action (act);
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 /* SIP, AFL, PFL radio */
127 solo_in_place_button.set_name ("monitor section solo model");
128 afl_button.set_name ("monitor section solo model");
129 pfl_button.set_name ("monitor section solo model");
131 solo_in_place_button.set_led_left (true);
132 afl_button.set_led_left (true);
133 pfl_button.set_led_left (true);
135 solo_in_place_button.show ();
139 act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
140 set_tooltip (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 set_tooltip (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 set_tooltip (pfl_button, _("Solo controls toggle pre-fader-listen"));
154 pfl_button.set_related_action (act);
157 /* Solo option buttons */
158 exclusive_solo_button.set_text (_("Excl. Solo"));
159 exclusive_solo_button.set_name (X_("monitor solo exclusive"));
160 set_tooltip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time"));
162 act = ActionManager::get_action (X_("Monitor"), X_("toggle-exclusive-solo"));
164 exclusive_solo_button.set_related_action (act);
167 solo_mute_override_button.set_text (_("Solo ยป Mute"));
168 solo_mute_override_button.set_name (X_("monitor solo override"));
169 set_tooltip (&solo_mute_override_button, _("If enabled, solo will override mute\n(a soloed & muted track or bus will be audible)"));
171 act = ActionManager::get_action (X_("Monitor"), X_("toggle-mute-overrides-solo"));
173 solo_mute_override_button.set_related_action (act);
178 Label* solo_boost_label;
179 Label* solo_cut_label;
182 /* Solo Boost Knob */
184 solo_boost_control = new ArdourKnob ();
185 solo_boost_control->set_name("monitor knob");
186 solo_boost_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
187 set_tooltip (*solo_boost_control, _("Gain increase for soloed signals (0dB is normal)"));
189 solo_boost_display = new ArdourDisplay ();
190 solo_boost_display->set_name("monitor section cut");
191 solo_boost_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
192 solo_boost_display->add_controllable_preset(_("0 dB"), 0.0);
193 solo_boost_display->add_controllable_preset(_("3 dB"), 3.0);
194 solo_boost_display->add_controllable_preset(_("6 dB"), 6.0);
195 solo_boost_display->add_controllable_preset(_("10 dB"), 10.0);
197 solo_boost_label = manage (new Label (_("Solo Boost")));
201 solo_cut_control = new ArdourKnob ();
202 solo_cut_control->set_name ("monitor knob");
203 solo_cut_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
204 set_tooltip (*solo_cut_control, _("Gain reduction non-soloed signals\nA value above -inf dB causes \"solo-in-front\""));
206 solo_cut_display = new ArdourDisplay ();
207 solo_cut_display->set_name("monitor section cut");
208 solo_cut_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
209 solo_cut_display->add_controllable_preset(_("0 dB"), 0.0);
210 solo_cut_display->add_controllable_preset(_("-6 dB"), -6.0);
211 solo_cut_display->add_controllable_preset(_("-12 dB"), -12.0);
212 solo_cut_display->add_controllable_preset(_("-20 dB"), -20.0);
213 solo_cut_display->add_controllable_preset(_("OFF"), -1200.0);
215 solo_cut_label = manage (new Label (_("SiP Cut")));
219 dim_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
220 dim_control->set_name ("monitor knob");
221 dim_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
222 set_tooltip (*dim_control, _("Gain reduction to use when dimming monitor outputs"));
224 dim_display = new ArdourDisplay ();
225 dim_display->set_name("monitor section cut");
226 dim_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
227 dim_display->add_controllable_preset(_("0 dB"), 0.0);
228 dim_display->add_controllable_preset(_("-3 dB"), -3.0);
229 dim_display->add_controllable_preset(_("-6 dB"), -6.0);
230 dim_display->add_controllable_preset(_("-12 dB"), -12.0);
231 dim_display->add_controllable_preset(_("-20 dB"), -20.0);
233 dim_label = manage (new Label (_("Dim")));
236 cut_all_button.set_text (_("Mute"));
237 cut_all_button.set_name ("monitor section cut");
238 cut_all_button.set_name (X_("monitor section cut"));
239 cut_all_button.set_size_request (-1, PX_SCALE(30));
240 cut_all_button.show ();
242 act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
244 cut_all_button.set_related_action (act);
248 dim_all_button.set_text (_("Dim"));
249 dim_all_button.set_name ("monitor section dim");
250 dim_all_button.set_size_request (-1, PX_SCALE(30));
251 act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
253 dim_all_button.set_related_action (act);
257 mono_button.set_text (_("Mono"));
258 mono_button.set_name ("monitor section mono");
259 mono_button.set_size_request (-1, PX_SCALE(30));
260 act = ActionManager::get_action (X_("Monitor"), X_("monitor-mono"));
262 mono_button.set_related_action (act);
267 gain_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
268 gain_control->set_name("monitor knob");
269 gain_control->set_size_request (PX_SCALE(70), PX_SCALE(70));
271 gain_display = new ArdourDisplay ();
272 gain_display->set_name("monitor section cut");
273 gain_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
274 gain_display->add_controllable_preset(_("0 dB"), 0.0);
275 gain_display->add_controllable_preset(_("-3 dB"), -3.0);
276 gain_display->add_controllable_preset(_("-6 dB"), -6.0);
277 gain_display->add_controllable_preset(_("-12 dB"), -12.0);
278 gain_display->add_controllable_preset(_("-20 dB"), -20.0);
279 gain_display->add_controllable_preset(_("-30 dB"), -30.0);
281 Label* output_label = manage (new Label (_("Output")));
282 output_label->set_name (X_("MonitorSectionLabel"));
284 output_button = new ArdourButton ();
285 output_button->set_text (_("Output"));
286 output_button->set_name (X_("monitor section cut"));
287 output_button->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
288 output_button->set_layout_ellipsize_width (PX_SCALE(128) * PANGO_SCALE);
290 channel_table_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
291 channel_table_scroller.set_size_request (-1, PX_SCALE(150));
292 channel_table_scroller.set_shadow_type (Gtk::SHADOW_NONE);
293 channel_table_scroller.show ();
294 channel_table_scroller.add (channel_table_viewport);
296 channel_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
297 channel_size_group->add_widget (channel_table_header);
298 channel_size_group->add_widget (channel_table);
300 channel_table_header.resize (1, 5);
302 Label* l1 = manage (new Label (X_(" ")));
303 l1->set_name (X_("MonitorSectionLabel"));
304 channel_table_header.attach (*l1, 0, 1, 0, 1, EXPAND|FILL);
306 l1 = manage (new Label (_("Mute")));
307 l1->set_name (X_("MonitorSectionLabel"));
308 channel_table_header.attach (*l1, 1, 2, 0, 1, EXPAND|FILL);
310 l1 = manage (new Label (_("Dim")));
311 l1->set_name (X_("MonitorSectionLabel"));
312 channel_table_header.attach (*l1, 2, 3, 0, 1, EXPAND|FILL);
314 l1 = manage (new Label (_("Solo")));
315 l1->set_name (X_("MonitorSectionLabel"));
316 channel_table_header.attach (*l1, 3, 4, 0, 1, EXPAND|FILL);
318 l1 = manage (new Label (_("Inv")));
319 l1->set_name (X_("MonitorSectionLabel"));
320 channel_table_header.attach (*l1, 4, 5, 0, 1, EXPAND|FILL);
322 channel_table_header.show ();
325 /****************************************************************************
326 * LAYOUT top to bottom
329 // solo, iso information
330 HBox* rude_box = manage (new HBox);
331 rude_box->set_spacing (PX_SCALE(4));
332 rude_box->set_homogeneous (true);
333 rude_box->pack_start (rude_solo_button, true, true);
334 rude_box->pack_start (rude_iso_button, true, true);
336 // solo mode (SiP, AFL, PFL)
337 VBox* solo_mode_box = manage (new VBox);
338 solo_mode_box->set_spacing (PX_SCALE(3));
339 solo_mode_box->pack_start (solo_in_place_button, true, false);
340 solo_mode_box->pack_start (afl_button, true, false);
341 solo_mode_box->pack_start (pfl_button, true, false);
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 VBox* solo_opt_box = manage (new VBox);
351 solo_opt_box->set_spacing (PX_SCALE(3));
352 solo_opt_box->pack_start (*tbx1, true, false);
353 solo_opt_box->pack_start (*tbx2, true, false);
355 // combined solo mode & options
356 HBox* solo_box = manage (new HBox);
357 solo_box->set_spacing (PX_SCALE(4));
358 solo_box->pack_start (*solo_mode_box, true, false);
359 solo_box->pack_end (*solo_opt_box, true, true);
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(16));
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_box, false, false);
420 vpacker->pack_start (*level_tbl, false, false, PX_SCALE(16));
421 vpacker->pack_start (*lower_packer, true, false); // expand, center
422 vpacker->pack_end (*out_packer, false, false, PX_SCALE(16));
424 hpacker.set_spacing (0);
425 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_box->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 torn off, make this a normal window */
459 _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
460 _tearoff->tearoff_window().set_title (X_("Monitor"));
461 _tearoff->tearoff_window().signal_key_press_event().connect (sigc::ptr_fun (forward_key_press), false);
463 update_output_display();
465 /* catch changes that affect us */
466 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
467 *this, invalidator (*this), boost::bind (&MonitorSection::port_connected_or_disconnected, this, _1, _3), gui_context ()
469 Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context());
472 MonitorSection::~MonitorSection ()
474 for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
478 _channel_buttons.clear ();
479 _output_changed_connection.disconnect ();
481 delete output_button;
486 delete solo_boost_control;
487 delete solo_boost_display;
488 delete solo_cut_control;
489 delete solo_cut_display;
491 delete _output_selector;
492 _output_selector = 0;
496 MonitorSection::set_session (Session* s)
498 AxisView::set_session (s);
502 _route = _session->monitor_out ();
505 /* session with monitor section */
506 _monitor = _route->monitor_control ();
507 assign_controllables ();
508 _route->output()->changed.connect (_output_changed_connection, invalidator (*this),
509 boost::bind (&MonitorSection::update_output_display, this),
512 /* session with no monitor section */
513 _output_changed_connection.disconnect();
516 delete _output_selector;
517 _output_selector = 0;
520 if (channel_table_scroller.get_parent()) {
521 /* scroller is packed, so remove it */
522 channel_table_packer.remove (channel_table_scroller);
525 if (table_hpacker.get_parent () == &channel_table_packer) {
526 /* this occurs when the table hpacker is directly
527 packed, so remove it.
529 channel_table_packer.remove (table_hpacker);
530 } else if (table_hpacker.get_parent()) {
531 channel_table_viewport.remove ();
534 if (_monitor->output_streams().n_audio() > 7) {
535 /* put the table into a scrolled window, and then put
536 * that into the channel vpacker, after the table header
538 channel_table_viewport.add (table_hpacker);
539 channel_table_packer.pack_start (channel_table_scroller, true, true);
540 channel_table_viewport.show ();
541 channel_table_scroller.show ();
544 /* just put the channel table itself into the channel
545 * vpacker, after the table header
548 channel_table_packer.pack_start (table_hpacker, true, true);
549 channel_table_scroller.hide ();
552 table_hpacker.show ();
553 channel_table.show ();
558 _output_changed_connection.disconnect();
561 control_connections.drop_connections ();
562 rude_iso_button.unset_active_state ();
563 rude_solo_button.unset_active_state ();
564 delete _output_selector;
565 _output_selector = 0;
567 assign_controllables ();
571 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
573 cut.set_name (X_("monitor section cut"));
574 dim.set_name (X_("monitor section dim"));
575 solo.set_name (X_("monitor section solo"));
576 invert.set_name (X_("monitor section invert"));
578 cut.unset_flags (Gtk::CAN_FOCUS);
579 dim.unset_flags (Gtk::CAN_FOCUS);
580 solo.unset_flags (Gtk::CAN_FOCUS);
581 invert.unset_flags (Gtk::CAN_FOCUS);
585 MonitorSection::populate_buttons ()
591 Glib::RefPtr<Action> act;
592 uint32_t nchans = _monitor->output_streams().n_audio();
594 channel_table.resize (nchans, 5);
595 channel_table.set_col_spacings (6);
596 channel_table.set_row_spacings (6);
597 channel_table.set_homogeneous (true);
599 const uint32_t row_offset = 0;
601 for (uint32_t i = 0; i < nchans; ++i) {
614 snprintf (buf, sizeof (buf), "%d", i+1);
618 Label* label = manage (new Label (l));
619 channel_table.attach (*label, 0, 1, i+row_offset, i+row_offset+1, EXPAND|FILL);
621 ChannelButtonSet* cbs = new ChannelButtonSet;
623 _channel_buttons.push_back (cbs);
625 channel_table.attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, EXPAND|FILL);
626 channel_table.attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, EXPAND|FILL);
627 channel_table.attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, EXPAND|FILL);
628 channel_table.attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, EXPAND|FILL);
630 snprintf (buf, sizeof (buf), "monitor-cut-%u", i+1);
631 act = ActionManager::get_action (X_("Monitor"), buf);
633 cbs->cut.set_related_action (act);
636 snprintf (buf, sizeof (buf), "monitor-dim-%u", i+1);
637 act = ActionManager::get_action (X_("Monitor"), buf);
639 cbs->dim.set_related_action (act);
642 snprintf (buf, sizeof (buf), "monitor-solo-%u", i+1);
643 act = ActionManager::get_action (X_("Monitor"), buf);
645 cbs->solo.set_related_action (act);
648 snprintf (buf, sizeof (buf), "monitor-invert-%u", i+1);
649 act = ActionManager::get_action (X_("Monitor"), buf);
651 cbs->invert.set_related_action (act);
655 channel_table.show_all ();
659 MonitorSection::toggle_exclusive_solo ()
665 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo");
667 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
668 Config->set_exclusive_solo (tact->get_active());
674 MonitorSection::toggle_mute_overrides_solo ()
680 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo");
682 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
683 Config->set_solo_mute_override (tact->get_active());
688 MonitorSection::dim_all ()
694 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
696 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
697 _monitor->set_dim_all (tact->get_active());
703 MonitorSection::cut_all ()
709 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
711 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
712 _monitor->set_cut_all (tact->get_active());
717 MonitorSection::mono ()
723 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
725 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
726 _monitor->set_mono (tact->get_active());
731 MonitorSection::cut_channel (uint32_t chn)
738 snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
740 --chn; // 0-based in backend
742 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
744 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
745 _monitor->set_cut (chn, tact->get_active());
750 MonitorSection::dim_channel (uint32_t chn)
757 snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
759 --chn; // 0-based in backend
761 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
763 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
764 _monitor->set_dim (chn, tact->get_active());
770 MonitorSection::solo_channel (uint32_t chn)
777 snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
779 --chn; // 0-based in backend
781 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
783 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
784 _monitor->set_solo (chn, tact->get_active());
790 MonitorSection::invert_channel (uint32_t chn)
797 snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
799 --chn; // 0-based in backend
801 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
803 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
804 _monitor->set_polarity (chn, tact->get_active());
809 MonitorSection::register_actions ()
813 Glib::RefPtr<Action> act;
815 monitor_actions = ActionGroup::create (X_("Monitor"));
816 ActionManager::add_action_group (monitor_actions);
818 ActionManager::register_toggle_action (monitor_actions, "monitor-mono", "", _("Switch monitor to mono"),
819 sigc::mem_fun (*this, &MonitorSection::mono));
821 ActionManager::register_toggle_action (monitor_actions, "monitor-cut-all", "", _("Cut monitor"),
822 sigc::mem_fun (*this, &MonitorSection::cut_all));
824 ActionManager::register_toggle_action (monitor_actions, "monitor-dim-all", "", _("Dim monitor"),
825 sigc::mem_fun (*this, &MonitorSection::dim_all));
827 act = ActionManager::register_toggle_action (monitor_actions, "toggle-exclusive-solo", "", _("Toggle exclusive solo mode"),
828 sigc::mem_fun (*this, &MonitorSection::toggle_exclusive_solo));
830 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
831 tact->set_active (Config->get_exclusive_solo());
833 act = ActionManager::register_toggle_action (monitor_actions, "toggle-mute-overrides-solo", "", _("Toggle mute overrides solo mode"),
834 sigc::mem_fun (*this, &MonitorSection::toggle_mute_overrides_solo));
836 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
837 tact->set_active (Config->get_solo_mute_override());
840 /* note the 1-based counting (for naming - backend uses 0-based) */
842 for (uint32_t chn = 1; chn <= 16; ++chn) {
844 action_name = string_compose (X_("monitor-cut-%1"), chn);
845 action_descr = string_compose (_("Cut monitor channel %1"), chn);
846 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
847 sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
849 action_name = string_compose (X_("monitor-dim-%1"), chn);
850 action_descr = string_compose (_("Dim monitor channel %1"), chn);
851 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
852 sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
854 action_name = string_compose (X_("monitor-solo-%1"), chn);
855 action_descr = string_compose (_("Solo monitor channel %1"), chn);
856 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
857 sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
859 action_name = string_compose (X_("monitor-invert-%1"), chn);
860 action_descr = string_compose (_("Invert monitor channel %1"), chn);
861 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
862 sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
867 Glib::RefPtr<ActionGroup> solo_actions = ActionGroup::create (X_("Solo"));
868 RadioAction::Group solo_group;
870 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-in-place", "", _("In-place solo"),
871 sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
872 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-afl", "", _("After Fade Listen (AFL) solo"),
873 sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
874 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-pfl", "", _("Pre Fade Listen (PFL) solo"),
875 sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
877 ActionManager::add_action_group (solo_actions);
881 MonitorSection::solo_use_in_place ()
883 /* this is driven by a toggle on a radio group, and so is invoked twice,
884 once for the item that became inactive and once for the one that became
888 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
891 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
893 if (!ract->get_active ()) {
894 /* We are turning SiP off, which means that AFL or PFL will be turned on
895 shortly; don't update the solo model in the mean time, as if the currently
896 configured listen position is not the one that is about to be turned on,
897 things will go wrong.
899 _inhibit_solo_model_update = true;
901 Config->set_solo_control_is_listen_control (!ract->get_active());
902 _inhibit_solo_model_update = false;
908 MonitorSection::solo_use_afl ()
910 /* this is driven by a toggle on a radio group, and so is invoked twice,
911 once for the item that became inactive and once for the one that became
915 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
917 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
919 if (ract->get_active()) {
920 Config->set_solo_control_is_listen_control (true);
921 Config->set_listen_position (AfterFaderListen);
928 MonitorSection::solo_use_pfl ()
930 /* this is driven by a toggle on a radio group, and so is invoked twice,
931 once for the item that became inactive and once for the one that became
935 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
937 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
939 if (ract->get_active()) {
940 Config->set_solo_control_is_listen_control (true);
941 Config->set_listen_position (PreFaderListen);
948 MonitorSection::update_solo_model ()
950 if (_inhibit_solo_model_update) {
954 const char* action_name = 0;
955 Glib::RefPtr<Action> act;
957 if (Config->get_solo_control_is_listen_control()) {
958 switch (Config->get_listen_position()) {
959 case AfterFaderListen:
960 action_name = X_("solo-use-afl");
963 action_name = X_("solo-use-pfl");
967 action_name = X_("solo-use-in-place");
970 act = ActionManager::get_action (X_("Solo"), action_name);
973 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
975 /* because these are radio buttons, one of them will be
976 active no matter what. to trigger a change in the
977 action so that the view picks it up, toggle it.
979 if (ract->get_active()) {
980 ract->set_active (false);
982 ract->set_active (true);
989 MonitorSection::map_state ()
991 if (!_route || !_monitor) {
995 Glib::RefPtr<Action> act;
997 update_solo_model ();
999 act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
1001 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1003 tact->set_active (_monitor->cut_all());
1007 act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
1009 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1011 tact->set_active (_monitor->dim_all());
1015 act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
1017 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1019 tact->set_active (_monitor->mono());
1023 uint32_t nchans = _monitor->output_streams().n_audio();
1025 assert (nchans == _channel_buttons.size ());
1027 for (uint32_t n = 0; n < nchans; ++n) {
1029 char action_name[32];
1031 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n);
1032 act = ActionManager::get_action (X_("Monitor"), action_name);
1034 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1036 tact->set_active (_monitor->cut (n));
1040 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n);
1041 act = ActionManager::get_action (X_("Monitor"), action_name);
1043 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1045 tact->set_active (_monitor->dimmed (n));
1049 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n);
1050 act = ActionManager::get_action (X_("Monitor"), action_name);
1052 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1054 tact->set_active (_monitor->soloed (n));
1058 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n);
1059 act = ActionManager::get_action (X_("Monitor"), action_name);
1061 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1063 tact->set_active (_monitor->inverted (n));
1070 MonitorSection::do_blink (bool onoff)
1073 audition_blink (onoff);
1077 MonitorSection::audition_blink (bool onoff)
1079 if (_session == 0) {
1083 if (_session->is_auditioning()) {
1084 rude_audition_button.set_active (onoff);
1086 rude_audition_button.set_active (false);
1091 MonitorSection::solo_blink (bool onoff)
1093 if (_session == 0) {
1097 if (_session->soloing() || _session->listening()) {
1098 rude_solo_button.set_active (onoff);
1100 if (_session->soloing()) {
1101 if (_session->solo_isolated()) {
1102 rude_iso_button.set_active (onoff);
1104 rude_iso_button.set_active (false);
1109 rude_solo_button.set_active (false);
1110 rude_iso_button.set_active (false);
1115 MonitorSection::cancel_isolate (GdkEventButton*)
1118 boost::shared_ptr<RouteList> rl (_session->get_routes ());
1119 _session->set_solo_isolated (rl, false, Session::rt_cleanup, true);
1126 MonitorSection::cancel_audition (GdkEventButton*)
1129 _session->cancel_audition();
1134 #define SYNCHRONIZE_TOGGLE_ACTION(action, value) \
1136 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(action); \
1137 if (tact && tact->get_active() != value) { \
1138 tact->set_active(value); \
1143 MonitorSection::parameter_changed (std::string name)
1145 if (name == "solo-control-is-listen-control") {
1146 update_solo_model ();
1147 } else if (name == "listen-position") {
1148 update_solo_model ();
1149 } else if (name == "solo-mute-override") {
1150 SYNCHRONIZE_TOGGLE_ACTION(
1151 ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo"),
1152 Config->get_solo_mute_override ())
1153 } else if (name == "exclusive-solo") {
1154 SYNCHRONIZE_TOGGLE_ACTION(
1155 ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo"),
1156 Config->get_exclusive_solo ())
1161 MonitorSection::assign_controllables ()
1163 boost::shared_ptr<Controllable> none;
1165 if (!gain_control) {
1166 /* too early - GUI controls not set up yet */
1171 solo_cut_control->set_controllable (_session->solo_cut_control());
1172 solo_cut_display->set_controllable (_session->solo_cut_control());
1174 solo_cut_control->set_controllable (none);
1175 solo_cut_display->set_controllable (none);
1179 gain_control->set_controllable (_route->gain_control());
1180 gain_display->set_controllable (_route->gain_control());
1182 gain_control->set_controllable (none);
1187 cut_all_button.set_controllable (_monitor->cut_control());
1188 cut_all_button.watch ();
1189 dim_all_button.set_controllable (_monitor->dim_control());
1190 dim_all_button.watch ();
1191 mono_button.set_controllable (_monitor->mono_control());
1192 mono_button.watch ();
1194 dim_control->set_controllable (_monitor->dim_level_control ());
1195 dim_display->set_controllable (_monitor->dim_level_control ());
1196 solo_boost_control->set_controllable (_monitor->solo_boost_control ());
1197 solo_boost_display->set_controllable (_monitor->solo_boost_control ());
1201 cut_all_button.set_controllable (none);
1202 dim_all_button.set_controllable (none);
1203 mono_button.set_controllable (none);
1205 dim_control->set_controllable (none);
1206 dim_display->set_controllable (none);
1207 solo_boost_control->set_controllable (none);
1208 solo_boost_display->set_controllable (none);
1213 MonitorSection::state_id() const
1215 return "monitor-section";
1219 MonitorSection::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1221 using namespace Menu_Helpers;
1223 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1227 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1228 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1232 if (i != output_menu_bundles.end()) {
1236 output_menu_bundles.push_back (b);
1238 MenuList& citems = output_menu.items();
1240 std::string n = b->name ();
1241 replace_all (n, "_", " ");
1243 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MonitorSection::bundle_output_chosen), b)));
1247 MonitorSection::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1250 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1252 if (std::find (current.begin(), current.end(), c) == current.end()) {
1253 _route->output()->connect_ports_to_bundle (c, true, this);
1255 _route->output()->disconnect_ports_from_bundle (c, this);
1260 MonitorSection::output_release (GdkEventButton *ev)
1262 switch (ev->button) {
1264 edit_output_configuration ();
1271 struct RouteCompareByName {
1272 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1273 return a->name().compare (b->name()) < 0;
1278 MonitorSection::output_press (GdkEventButton *ev)
1280 using namespace Menu_Helpers;
1282 MessageDialog msg (_("No session - no I/O changes are possible"));
1287 MenuList& citems = output_menu.items();
1288 switch (ev->button) {
1291 return false; //wait for the mouse-up to pop the dialog
1295 output_menu.set_name ("ArdourContextMenu");
1297 output_menu_bundles.clear ();
1299 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(this), &MonitorSection::disconnect_output)));
1301 citems.push_back (SeparatorElem());
1302 uint32_t const n_with_separator = citems.size ();
1304 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1306 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1308 /* give user bundles first chance at being in the menu */
1310 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1311 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1312 maybe_add_bundle_to_output_menu (*i, current);
1316 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1317 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1318 maybe_add_bundle_to_output_menu (*i, current);
1322 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1323 RouteList copy = *routes;
1324 copy.sort (RouteCompareByName ());
1325 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1326 maybe_add_bundle_to_output_menu ((*i)->output()->bundle(), current);
1329 if (citems.size() == n_with_separator) {
1330 /* no routes added; remove the separator */
1334 citems.push_back (SeparatorElem());
1335 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(this), &MonitorSection::edit_output_configuration)));
1337 output_menu.popup (1, ev->time);
1348 MonitorSection::update_output_display ()
1350 if (!_route || !_monitor || _session->deletion_in_progress()) {
1356 boost::shared_ptr<Port> port;
1357 vector<string> port_connections;
1359 uint32_t total_connection_count = 0;
1360 uint32_t io_connection_count = 0;
1361 uint32_t ardour_connection_count = 0;
1362 uint32_t system_connection_count = 0;
1363 uint32_t other_connection_count = 0;
1365 ostringstream label;
1367 bool have_label = false;
1368 bool each_io_has_one_connection = true;
1370 string connection_name;
1371 string ardour_track_name;
1372 string other_connection_type;
1373 string system_ports;
1376 ostringstream tooltip;
1377 char * tooltip_cstr;
1379 io_count = _route->n_outputs().n_total();
1380 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (_route->name()));
1383 for (io_index = 0; io_index < io_count; ++io_index) {
1385 port = _route->output()->nth (io_index);
1387 //ignore any port connections that don't match our DataType
1388 if (port->type() != DataType::AUDIO) {
1392 port_connections.clear ();
1393 port->get_connections(port_connections);
1394 io_connection_count = 0;
1396 if (!port_connections.empty()) {
1397 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1399 string& connection_name (*i);
1401 if (connection_name.find("system:") == 0) {
1402 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1405 if (io_connection_count == 0) {
1406 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1408 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1411 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1414 if (connection_name.find("ardour:") == 0) {
1415 if (ardour_track_name.empty()) {
1416 // "ardour:Master/in 1" -> "ardour:Master/"
1417 string::size_type slash = connection_name.find("/");
1418 if (slash != string::npos) {
1419 ardour_track_name = connection_name.substr(0, slash + 1);
1423 if (connection_name.find(ardour_track_name) == 0) {
1424 ++ardour_connection_count;
1426 } else if (!pn.empty()) {
1427 if (system_ports.empty()) {
1430 system_ports += "/" + pn;
1432 if (connection_name.find("system:") == 0) {
1433 ++system_connection_count;
1435 } else if (connection_name.find("system:") == 0) {
1436 // "system:playback_123" -> "123"
1437 system_port = connection_name.substr(16);
1438 if (system_ports.empty()) {
1439 system_ports += system_port;
1441 system_ports += "/" + system_port;
1444 ++system_connection_count;
1446 if (other_connection_type.empty()) {
1447 // "jamin:in 1" -> "jamin:"
1448 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1451 if (connection_name.find(other_connection_type) == 0) {
1452 ++other_connection_count;
1456 ++total_connection_count;
1457 ++io_connection_count;
1461 if (io_connection_count != 1) {
1462 each_io_has_one_connection = false;
1466 if (total_connection_count == 0) {
1467 tooltip << endl << _("Disconnected");
1470 tooltip_cstr = new char[tooltip.str().size() + 1];
1471 strcpy(tooltip_cstr, tooltip.str().c_str());
1473 set_tooltip (output_button, tooltip_cstr, "");
1475 if (each_io_has_one_connection) {
1476 if (total_connection_count == ardour_connection_count) {
1477 // all connections are to the same track in ardour
1478 // "ardour:Master/" -> "Master"
1479 string::size_type slash = ardour_track_name.find("/");
1480 if (slash != string::npos) {
1481 label << ardour_track_name.substr(7, slash - 7);
1484 } else if (total_connection_count == system_connection_count) {
1485 // all connections are to system ports
1486 label << system_ports;
1488 } else if (total_connection_count == other_connection_count) {
1489 // all connections are to the same external program eg jamin
1490 // "jamin:" -> "jamin"
1491 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1497 if (total_connection_count == 0) {
1501 // Odd configuration
1502 label << "*" << total_connection_count << "*";
1506 output_button->set_text (label.str());
1510 MonitorSection::disconnect_output ()
1513 _route->output()->disconnect(this);
1518 MonitorSection::edit_output_configuration ()
1520 if (_output_selector == 0) {
1521 _output_selector = new MonitorSelectorWindow (_session, _route->output());
1523 _output_selector->present ();
1527 MonitorSection::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1532 boost::shared_ptr<Port> a = wa.lock ();
1533 boost::shared_ptr<Port> b = wb.lock ();
1534 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1535 update_output_display ();