merge monitor_section branch
authorBen Loftis <ben@harrisonconsoles.com>
Tue, 21 Apr 2015 13:18:10 +0000 (08:18 -0500)
committerBen Loftis <ben@harrisonconsoles.com>
Tue, 21 Apr 2015 13:18:10 +0000 (08:18 -0500)
gtk2_ardour/monitor_section.cc
gtk2_ardour/monitor_section.h
gtk2_ardour/monitor_selector.cc [new file with mode: 0644]
gtk2_ardour/monitor_selector.h [new file with mode: 0644]
gtk2_ardour/wscript
libs/ardour/ardour/session.h
libs/ardour/route.cc
libs/ardour/session.cc

index 4bfe24ae901db2ccb8b53a6756071806f8b196e4..7ccf2857a729fcf4d890eeafe628357c09acdd8e 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "pbd/compose.h"
 #include "pbd/error.h"
+#include "pbd/replace_all.h"
 
 #include "gtkmm2ext/bindable_button.h"
 #include "gtkmm2ext/tearoff.h"
@@ -30,7 +31,9 @@
 #include <gtkmm/menu.h>
 #include <gtkmm/menuitem.h>
 
+#include "ardour/audioengine.h"
 #include "ardour/monitor_processor.h"
+#include "ardour/port.h"
 #include "ardour/route.h"
 
 #include "ardour_ui.h"
@@ -320,6 +323,18 @@ MonitorSection::MonitorSection (Session* s)
        gain_display->add_controllable_preset(_("-20 dB"), -20.0);
        gain_display->add_controllable_preset(_("-30 dB"), -30.0);
 
+       Label* output_label = manage (new Label (_("Output")));
+       output_label->set_name (X_("MonitorSectionLabel"));
+
+       output_button = new ArdourButton ();
+       output_button->set_text (_("Output"));
+       output_button->set_name (X_("monitor section cut"));
+       output_button->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
+       VBox* out_packer = manage (new VBox);
+       out_packer->set_spacing (6);
+       out_packer->pack_start (*output_label, false, false);
+       out_packer->pack_start (*output_button, false, false);
+
        spin_label = manage (new Label (_("Monitor")));
        spin_packer = manage (new VBox);
        spin_packer->show ();
@@ -327,6 +342,7 @@ MonitorSection::MonitorSection (Session* s)
        spin_packer->pack_start (*spin_label, false, false);
        spin_packer->pack_start (*gain_control, false, false);
        spin_packer->pack_start (*gain_display, false, false);
+       spin_packer->pack_start (*out_packer, false, false, 24);
 
        lower_packer.pack_start (*spin_packer, true, true);
 
@@ -396,6 +412,10 @@ MonitorSection::MonitorSection (Session* s)
        map_state ();
        assign_controllables ();
 
+       output_button->signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::output_press), false);
+       output_button->signal_button_release_event().connect (sigc::mem_fun(*this, &MonitorSection::output_release), false);
+       output_button->signal_size_allocate().connect (sigc::mem_fun (*this, &MonitorSection::output_button_resized));
+
        _tearoff = new TearOff (hpacker);
 
        /* if torn off, make this a normal window */
@@ -403,8 +423,12 @@ MonitorSection::MonitorSection (Session* s)
        _tearoff->tearoff_window().set_title (X_("Monitor"));
        _tearoff->tearoff_window().signal_key_press_event().connect (sigc::ptr_fun (forward_key_press), false);
 
-       /* catch changes that affect us */
+       update_output_display();
 
+       /* catch changes that affect us */
+       AudioEngine::instance()->PortConnectedOrDisconnected.connect (
+               *this, invalidator (*this), boost::bind (&MonitorSection::port_connected_or_disconnected, this, _1, _3), gui_context ()
+               );
        Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context());
 }
 
@@ -415,7 +439,9 @@ MonitorSection::~MonitorSection ()
        }
 
        _channel_buttons.clear ();
+       _output_changed_connection.disconnect ();
 
+       delete output_button;
        delete gain_control;
        delete gain_display;
        delete dim_control;
@@ -425,6 +451,8 @@ MonitorSection::~MonitorSection ()
        delete solo_cut_control;
        delete solo_cut_display;
        delete _tearoff;
+       delete _output_selector;
+       _output_selector = 0;
 }
 
 void
@@ -440,10 +468,16 @@ MonitorSection::set_session (Session* s)
                        /* session with monitor section */
                        _monitor = _route->monitor_control ();
                        assign_controllables ();
+                       _route->output()->changed.connect (_output_changed_connection, invalidator (*this),
+                                                                                       boost::bind (&MonitorSection::update_output_display, this),
+                                                                                       gui_context());
                } else {
                        /* session with no monitor section */
+                       _output_changed_connection.disconnect();
                        _monitor.reset ();
                        _route.reset ();
+                       delete _output_selector;
+                       _output_selector = 0;
                }
 
                if (channel_table_scroller.get_parent()) {
@@ -484,11 +518,14 @@ MonitorSection::set_session (Session* s)
        } else {
                /* no session */
 
+               _output_changed_connection.disconnect();
                _monitor.reset ();
                _route.reset ();
                control_connections.drop_connections ();
                rude_iso_button.unset_active_state ();
                rude_solo_button.unset_active_state ();
+               delete _output_selector;
+               _output_selector = 0;
 
                assign_controllables ();
        }
@@ -1154,3 +1191,330 @@ MonitorSection::state_id() const
 {
        return "monitor-section";
 }
+
+void
+MonitorSection::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
+{
+       using namespace Menu_Helpers;
+
+       if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
+               return;
+       }
+
+       list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
+       while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
+               ++i;
+       }
+
+       if (i != output_menu_bundles.end()) {
+               return;
+       }
+
+       output_menu_bundles.push_back (b);
+
+       MenuList& citems = output_menu.items();
+
+       std::string n = b->name ();
+       replace_all (n, "_", " ");
+
+       citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MonitorSection::bundle_output_chosen), b)));
+}
+
+void
+MonitorSection::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
+{
+
+       ARDOUR::BundleList current = _route->output()->bundles_connected ();
+
+       if (std::find (current.begin(), current.end(), c) == current.end()) {
+               _route->output()->connect_ports_to_bundle (c, true, this);
+       } else {
+               _route->output()->disconnect_ports_from_bundle (c, this);
+       }
+}
+
+gint
+MonitorSection::output_release (GdkEventButton *ev)
+{
+       switch (ev->button) {
+       case 3:
+               edit_output_configuration ();
+               break;
+       }
+
+       return false;
+}
+
+struct RouteCompareByName {
+       bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
+               return a->name().compare (b->name()) < 0;
+       }
+};
+
+gint
+MonitorSection::output_press (GdkEventButton *ev)
+{
+       using namespace Menu_Helpers;
+       if (!_session) {
+               MessageDialog msg (_("No session - no I/O changes are possible"));
+               msg.run ();
+               return true;
+       }
+
+       MenuList& citems = output_menu.items();
+       switch (ev->button) {
+
+       case 3:
+               return false;  //wait for the mouse-up to pop the dialog
+
+       case 1:
+       {
+               output_menu.set_name ("ArdourContextMenu");
+               citems.clear ();
+               output_menu_bundles.clear ();
+
+               citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(this), &MonitorSection::disconnect_output)));
+
+               citems.push_back (SeparatorElem());
+               uint32_t const n_with_separator = citems.size ();
+
+               ARDOUR::BundleList current = _route->output()->bundles_connected ();
+
+               boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
+
+               /* give user bundles first chance at being in the menu */
+
+               for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
+                       if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
+                               maybe_add_bundle_to_output_menu (*i, current);
+                       }
+               }
+
+               for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
+                       if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
+                               maybe_add_bundle_to_output_menu (*i, current);
+                       }
+               }
+
+               boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
+               RouteList copy = *routes;
+               copy.sort (RouteCompareByName ());
+               for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
+                       maybe_add_bundle_to_output_menu ((*i)->output()->bundle(), current);
+               }
+
+               if (citems.size() == n_with_separator) {
+                       /* no routes added; remove the separator */
+                       citems.pop_back ();
+               }
+
+               citems.push_back (SeparatorElem());
+               citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(this), &MonitorSection::edit_output_configuration)));
+
+               output_menu.popup (1, ev->time);
+               break;
+       }
+
+       default:
+               break;
+       }
+       return TRUE;
+}
+
+void
+MonitorSection::output_button_resized (Gtk::Allocation& alloc)
+{
+       output_button->set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
+}
+
+void
+MonitorSection::update_output_display ()
+{
+       if (!_route || !_monitor) {
+               return;
+       }
+
+       uint32_t io_count;
+       uint32_t io_index;
+       boost::shared_ptr<Port> port;
+       vector<string> port_connections;
+
+       uint32_t total_connection_count = 0;
+       uint32_t io_connection_count = 0;
+       uint32_t ardour_connection_count = 0;
+       uint32_t system_connection_count = 0;
+       uint32_t other_connection_count = 0;
+
+       ostringstream label;
+
+       bool have_label = false;
+       bool each_io_has_one_connection = true;
+
+       string connection_name;
+       string ardour_track_name;
+       string other_connection_type;
+       string system_ports;
+       string system_port;
+
+       ostringstream tooltip;
+       char * tooltip_cstr;
+
+       io_count = _route->n_outputs().n_total();
+       tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(_route->name()));
+
+
+       for (io_index = 0; io_index < io_count; ++io_index) {
+
+               port = _route->output()->nth (io_index);
+
+               //ignore any port connections that don't match our DataType
+               if (port->type() != DataType::AUDIO) {
+                       continue;
+               }
+
+               port_connections.clear ();
+               port->get_connections(port_connections);
+               io_connection_count = 0;
+
+               if (!port_connections.empty()) {
+                       for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
+                               string pn = "";
+                               string& connection_name (*i);
+
+                               if (connection_name.find("system:") == 0) {
+                                       pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
+                               }
+
+                               if (io_connection_count == 0) {
+                                       tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1))
+                                               << " -> "
+                                               << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
+                               } else {
+                                       tooltip << ", "
+                                               << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
+                               }
+
+                               if (connection_name.find("ardour:") == 0) {
+                                       if (ardour_track_name.empty()) {
+                                               // "ardour:Master/in 1" -> "ardour:Master/"
+                                               string::size_type slash = connection_name.find("/");
+                                               if (slash != string::npos) {
+                                                       ardour_track_name = connection_name.substr(0, slash + 1);
+                                               }
+                                       }
+
+                                       if (connection_name.find(ardour_track_name) == 0) {
+                                               ++ardour_connection_count;
+                                       }
+                               } else if (!pn.empty()) {
+                                       if (system_ports.empty()) {
+                                               system_ports += pn;
+                                       } else {
+                                               system_ports += "/" + pn;
+                                       }
+                                       if (connection_name.find("system:") == 0) {
+                                               ++system_connection_count;
+                                       }
+                               } else if (connection_name.find("system:") == 0) {
+                                       // "system:playback_123" -> "123"
+                                       system_port = connection_name.substr(16);
+                                       if (system_ports.empty()) {
+                                               system_ports += system_port;
+                                       } else {
+                                               system_ports += "/" + system_port;
+                                       }
+
+                                       ++system_connection_count;
+                               } else {
+                                       if (other_connection_type.empty()) {
+                                               // "jamin:in 1" -> "jamin:"
+                                               other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
+                                       }
+
+                                       if (connection_name.find(other_connection_type) == 0) {
+                                               ++other_connection_count;
+                                       }
+                               }
+
+                               ++total_connection_count;
+                               ++io_connection_count;
+                       }
+               }
+
+               if (io_connection_count != 1) {
+                       each_io_has_one_connection = false;
+               }
+       }
+
+       if (total_connection_count == 0) {
+               tooltip << endl << _("Disconnected");
+       }
+
+       tooltip_cstr = new char[tooltip.str().size() + 1];
+       strcpy(tooltip_cstr, tooltip.str().c_str());
+
+       ARDOUR_UI::instance()->set_tip (output_button, tooltip_cstr, "");
+
+       if (each_io_has_one_connection) {
+               if (total_connection_count == ardour_connection_count) {
+                       // all connections are to the same track in ardour
+                       // "ardour:Master/" -> "Master"
+                       string::size_type slash = ardour_track_name.find("/");
+                       if (slash != string::npos) {
+                               label << ardour_track_name.substr(7, slash - 7);
+                               have_label = true;
+                       }
+               } else if (total_connection_count == system_connection_count) {
+                       // all connections are to system ports
+                       label << system_ports;
+                       have_label = true;
+               } else if (total_connection_count == other_connection_count) {
+                       // all connections are to the same external program eg jamin
+                       // "jamin:" -> "jamin"
+                       label << other_connection_type.substr(0, other_connection_type.size() - 1);
+                       have_label = true;
+               }
+       }
+
+       if (!have_label) {
+               if (total_connection_count == 0) {
+                       // Disconnected
+                       label << "-";
+               } else {
+                       // Odd configuration
+                       label << "*" << total_connection_count << "*";
+               }
+       }
+
+       output_button->set_text (label.str());
+}
+
+void
+MonitorSection::disconnect_output ()
+{
+       if (_route) {
+               _route->output()->disconnect(this);
+       }
+}
+
+void
+MonitorSection::edit_output_configuration ()
+{
+       if (_output_selector == 0) {
+               _output_selector = new MonitorSelectorWindow (_session, _route->output());
+       }
+       _output_selector->present ();
+}
+
+void
+MonitorSection::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
+{
+       if (!_route) {
+               return;
+       }
+       boost::shared_ptr<Port> a = wa.lock ();
+       boost::shared_ptr<Port> b = wb.lock ();
+       if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
+               update_output_display ();
+       }
+}
index 65023aa667bce03100f3ba9c16085d62f4049682..31d73748106400d6b8c81a6df0cb570893f7ff4a 100644 (file)
@@ -28,6 +28,7 @@
 #include "axis_view.h"
 #include "level_meter.h"
 #include "route_ui.h"
+#include "monitor_selector.h"
 
 namespace Gtkmm2ext {
        class TearOff;
@@ -85,6 +86,18 @@ class MonitorSection : public RouteUI
        ArdourDisplay*  solo_boost_display;
        ArdourDisplay*  solo_cut_display;
 
+       std::list<boost::shared_ptr<ARDOUR::Bundle> > output_menu_bundles;
+       Gtk::Menu output_menu;
+       MonitorSelectorWindow *_output_selector;
+       ArdourButton* output_button;
+
+       void maybe_add_bundle_to_output_menu (boost::shared_ptr<ARDOUR::Bundle>, ARDOUR::BundleList const &);
+       void bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle>);
+       void output_button_resized (Gtk::Allocation&);
+       void update_output_display ();
+       void disconnect_output ();
+       void edit_output_configuration ();
+
        void populate_buttons ();
        void map_state ();
 
@@ -107,6 +120,8 @@ class MonitorSection : public RouteUI
        void dim_level_changed ();
        void solo_boost_changed ();
        void gain_value_changed ();
+       gint output_press (GdkEventButton *);
+       gint output_release (GdkEventButton *);
 
        ArdourButton solo_in_place_button;
        ArdourButton afl_button;
@@ -138,8 +153,10 @@ class MonitorSection : public RouteUI
 
        PBD::ScopedConnection config_connection;
        PBD::ScopedConnectionList control_connections;
+       PBD::ScopedConnection _output_changed_connection;
 
        bool _inhibit_solo_model_update;
 
        void assign_controllables ();
+       void port_connected_or_disconnected (boost::weak_ptr<ARDOUR::Port>, boost::weak_ptr<ARDOUR::Port>);
 };
diff --git a/gtk2_ardour/monitor_selector.cc b/gtk2_ardour/monitor_selector.cc
new file mode 100644 (file)
index 0000000..ad5b799
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+    Copyright (C) 2002-2007 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <stdint.h>
+
+#include <glibmm/objectbase.h>
+
+#include <gtkmm2ext/doi.h>
+
+#include "ardour/audioengine.h"
+#include "ardour/bundle.h"
+#include "ardour/data_type.h"
+#include "ardour/io.h"
+#include "ardour/port.h"
+#include "ardour/session.h"
+
+#include "monitor_selector.h"
+#include "utils.h"
+#include "gui_thread.h"
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
+using namespace Gtk;
+
+MonitorSelector::MonitorSelector (Gtk::Window* p, ARDOUR::Session* session, boost::shared_ptr<ARDOUR::IO> io)
+       : PortMatrix (p, session, DataType::AUDIO)
+       , _io (io)
+{
+       set_type (DataType::AUDIO);
+
+       /* signal flow from 0 to 1 */
+
+       _find_inputs_for_io_outputs = (_io->direction() == IO::Output);
+
+       if (_find_inputs_for_io_outputs) {
+               _other = 1;
+               _ours = 0;
+       } else {
+               _other = 0;
+               _ours = 1;
+       }
+
+       _port_group.reset (new PortGroup (io->name()));
+       _ports[_ours].add_group (_port_group);
+
+       io->changed.connect (_io_connection, invalidator (*this), boost::bind (&MonitorSelector::io_changed_proxy, this), gui_context ());
+
+       setup_all_ports ();
+       init ();
+}
+
+void
+MonitorSelector::io_changed_proxy ()
+{
+       /* The IO's changed signal is emitted from code that holds its route's processor lock,
+          so we can't call setup_all_ports (which results in a call to Route::foreach_processor)
+          without a deadlock unless we break things up with this idle handler.
+       */
+
+       Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MonitorSelector::io_changed));
+}
+
+void
+MonitorSelector::io_changed ()
+{
+       setup_all_ports ();
+}
+
+void
+MonitorSelector::setup_ports (int dim)
+{
+       if (!_session) {
+               return;
+       }
+
+       _ports[dim].suspend_signals ();
+
+       if (dim == _other) {
+
+               _ports[_other].gather (_session, type(), _find_inputs_for_io_outputs, false, show_only_bundles ());
+
+       } else {
+
+               _port_group->clear ();
+               _port_group->add_bundle (_io->bundle (), _io);
+       }
+
+       _ports[dim].resume_signals ();
+}
+
+void
+MonitorSelector::set_state (ARDOUR::BundleChannel c[2], bool s)
+{
+       ARDOUR::Bundle::PortList const & our_ports = c[_ours].bundle->channel_ports (c[_ours].channel);
+       ARDOUR::Bundle::PortList const & other_ports = c[_other].bundle->channel_ports (c[_other].channel);
+
+       for (ARDOUR::Bundle::PortList::const_iterator i = our_ports.begin(); i != our_ports.end(); ++i) {
+               for (ARDOUR::Bundle::PortList::const_iterator j = other_ports.begin(); j != other_ports.end(); ++j) {
+
+                       boost::shared_ptr<Port> f = _session->engine().get_port_by_name (*i);
+                       if (!f) {
+                               return;
+                       }
+
+                        if (s) {
+                               if (!f->connected_to (*j)) {
+                                       _io->connect (f, *j, 0);
+                               }
+                        } else {
+                               if (f->connected_to (*j)) {
+                                       _io->disconnect (f, *j, 0);
+                               }
+                        }
+               }
+       }
+}
+
+PortMatrixNode::State
+MonitorSelector::get_state (ARDOUR::BundleChannel c[2]) const
+{
+       if (c[0].bundle->nchannels() == ChanCount::ZERO || c[1].bundle->nchannels() == ChanCount::ZERO) {
+               return PortMatrixNode::NOT_ASSOCIATED;
+       }
+
+       ARDOUR::Bundle::PortList const & our_ports = c[_ours].bundle->channel_ports (c[_ours].channel);
+       ARDOUR::Bundle::PortList const & other_ports = c[_other].bundle->channel_ports (c[_other].channel);
+
+       if (!_session || our_ports.empty() || other_ports.empty()) {
+               /* we're looking at a bundle with no parts associated with this channel,
+                  so nothing to connect */
+               return PortMatrixNode::NOT_ASSOCIATED;
+       }
+
+       for (ARDOUR::Bundle::PortList::const_iterator i = our_ports.begin(); i != our_ports.end(); ++i) {
+               for (ARDOUR::Bundle::PortList::const_iterator j = other_ports.begin(); j != other_ports.end(); ++j) {
+
+                       boost::shared_ptr<Port> f = _session->engine().get_port_by_name (*i);
+
+                       /* since we are talking about an IO, our ports should all have an associated Port *,
+                          so the above call should never fail */
+                       assert (f);
+
+                       if (!f->connected_to (*j)) {
+                               /* if any one thing is not connected, all bets are off */
+                               return PortMatrixNode::NOT_ASSOCIATED;
+                       }
+               }
+       }
+
+       return PortMatrixNode::ASSOCIATED;
+}
+
+uint32_t
+MonitorSelector::n_io_ports () const
+{
+       if (!_find_inputs_for_io_outputs) {
+               return _io->n_ports().get (_io->default_type());
+       } else {
+               return _io->n_ports().get (_io->default_type());
+       }
+}
+
+bool
+MonitorSelector::list_is_global (int dim) const
+{
+       return (dim == _other);
+}
+
+std::string
+MonitorSelector::disassociation_verb () const
+{
+       return _("Disconnect");
+}
+
+std::string
+MonitorSelector::channel_noun () const
+{
+       return _("port");
+}
+
+MonitorSelectorWindow::MonitorSelectorWindow (ARDOUR::Session* session, boost::shared_ptr<ARDOUR::IO> io, bool /*can_cancel*/)
+       : ArdourWindow (_("Monitor output selector"))
+       , _selector (this, session, io)
+{
+       set_name ("IOSelectorWindow2");
+
+       add (_selector);
+
+       io_name_changed (this);
+
+       show_all ();
+
+       signal_delete_event().connect (sigc::mem_fun (*this, &MonitorSelectorWindow::wm_delete));
+}
+
+bool
+MonitorSelectorWindow::wm_delete (GdkEventAny* /*event*/)
+{
+       _selector.Finished (MonitorSelector::Accepted);
+       return false;
+}
+
+
+void
+MonitorSelectorWindow::on_map ()
+{
+       _selector.setup_all_ports ();
+       Window::on_map ();
+}
+
+void
+MonitorSelectorWindow::on_show ()
+{
+       Gtk::Window::on_show ();
+       std::pair<uint32_t, uint32_t> const pm_max = _selector.max_size ();
+       resize_window_to_proportion_of_monitor (this, pm_max.first, pm_max.second);
+}
+
+void
+MonitorSelectorWindow::io_name_changed (void*)
+{
+       ENSURE_GUI_THREAD (*this, &MonitorSelectorWindow::io_name_changed, src)
+               
+       std::string title;
+
+       if (!_selector.find_inputs_for_io_outputs()) {
+               title = string_compose(_("%1 input"), _selector.io()->name());
+       } else {
+               title = string_compose(_("%1 output"), _selector.io()->name());
+       }
+
+       set_title (title);
+}
+
diff --git a/gtk2_ardour/monitor_selector.h b/gtk2_ardour/monitor_selector.h
new file mode 100644 (file)
index 0000000..8c642b5
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+    Copyright (C) 2002-2007 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __gtkardour_monitor_output_selector_h__
+#define __gtkardour_monitor_output_selector_h__
+
+#include "port_matrix.h"
+#include "ardour_window.h"
+
+class MonitorSelector : public PortMatrix
+{
+  public:
+       MonitorSelector (Gtk::Window*, ARDOUR::Session *, boost::shared_ptr<ARDOUR::IO>);
+
+       void set_state (ARDOUR::BundleChannel c[2], bool);
+       PortMatrixNode::State get_state (ARDOUR::BundleChannel c[2]) const;
+
+       std::string disassociation_verb () const;
+       std::string channel_noun () const;
+
+        ARDOUR::Session* session() const { return _session; }
+
+       uint32_t n_io_ports () const;
+       boost::shared_ptr<ARDOUR::IO> const io () { return _io; }
+       void setup_ports (int);
+       bool list_is_global (int) const;
+
+       bool find_inputs_for_io_outputs () const {
+               return _find_inputs_for_io_outputs;
+       }
+
+       int ours () const {
+               return _ours;
+       }
+
+       int other () const {
+               return _other;
+       }
+
+       bool can_add_channels (boost::shared_ptr<ARDOUR::Bundle>) const { return false; }
+       bool can_remove_channels (boost::shared_ptr<ARDOUR::Bundle>) const { return false; }
+       bool can_rename_channels (boost::shared_ptr<ARDOUR::Bundle>) const { return false; }
+
+  private:
+
+       void io_changed ();
+       void io_changed_proxy ();
+
+       int _other;
+       int _ours;
+       boost::shared_ptr<ARDOUR::IO> _io;
+       boost::shared_ptr<PortGroup> _port_group;
+       bool _find_inputs_for_io_outputs;
+       PBD::ScopedConnection _io_connection;
+};
+
+class MonitorSelectorWindow : public ArdourWindow
+{
+  public:
+       MonitorSelectorWindow (ARDOUR::Session *, boost::shared_ptr<ARDOUR::IO>, bool can_cancel = false);
+
+       MonitorSelector& selector() { return _selector; }
+
+  protected:
+       void on_map ();
+       void on_show ();
+
+  private:
+       MonitorSelector _selector;
+
+       void io_name_changed (void *src);
+       bool wm_delete (GdkEventAny*);
+};
+
+#endif /* __gtkardour_monitor_output_selector_h__ */
index 6f094daa73cb58723e293f98e193d9a12c9234c8..84db23987fd41a200d9144d46cc1e7a213fb473e 100644 (file)
@@ -144,6 +144,7 @@ gtk2_ardour_sources = [
         'meter_strip.cc',
         'meter_patterns.cc',
         'monitor_section.cc',
+        'monitor_selector.cc',
         'mono_panner.cc',
         'mono_panner_editor.cc',
         'mouse_cursors.cc',
index 75b3bd2006c69e1035b859d7c12d5762b03a3f5a..30afd00fdb5489f16a896d7d70279c8e7d7f59e2 100644 (file)
@@ -702,6 +702,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
        /* monitor/master out */
 
        void add_monitor_section ();
+       void reset_monitor_section ();
        void remove_monitor_section ();
 
        boost::shared_ptr<Route> monitor_out() const { return _monitor_out; }
index 36adbfa148f6b4e9442a5bb5495a80e78703e3f3..e22acd8d4d83e4f9f3d7de4f43d2ba9f1cb80154 100644 (file)
@@ -3096,6 +3096,11 @@ Route::output_change_handler (IOChange change, void * /*src*/)
                */
                need_to_queue_solo_change = false;
                configure_processors (0);
+
+               if (is_master()) {
+                       _session.reset_monitor_section();
+               }
+
                io_changed (); /* EMIT SIGNAL */
        }
 
index a45175cef3e94d29031e973aad4e4e7928f3136a..31f2635ac3dcc007ca14ac844802d1aa73f35a29 100644 (file)
@@ -1060,6 +1060,121 @@ Session::add_monitor_section ()
        }
 }
 
+void
+Session::reset_monitor_section ()
+{
+       /* Process lock should be held by the caller.*/
+
+       if (!_monitor_out) {
+               return;
+       }
+
+       uint32_t limit = _master_out->n_outputs().n_audio();
+
+       /* connect the inputs to the master bus outputs. this
+        * represents a separate data feed from the internal sends from
+        * each route. as of jan 2011, it allows the monitor section to
+        * conditionally ignore either the internal sends or the normal
+        * input feed, but we should really find a better way to do
+        * this, i think.
+        */
+
+       _master_out->output()->disconnect (this);
+       _monitor_out->output()->disconnect (this);
+
+       _monitor_out->input()->ensure_io (_master_out->output()->n_ports(), false, this);
+       _monitor_out->output()->ensure_io (_master_out->output()->n_ports(), false, this);
+
+       for (uint32_t n = 0; n < limit; ++n) {
+               boost::shared_ptr<AudioPort> p = _monitor_out->input()->ports().nth_audio_port (n);
+               boost::shared_ptr<AudioPort> o = _master_out->output()->ports().nth_audio_port (n);
+
+               if (o) {
+                       string connect_to = o->name();
+                       if (_monitor_out->input()->connect (p, connect_to, this)) {
+                               error << string_compose (_("cannot connect control input %1 to %2"), n, connect_to)
+                                     << endmsg;
+                               break;
+                       }
+               }
+       }
+
+       /* connect monitor section to physical outs
+        */
+
+       if (Config->get_auto_connect_standard_busses()) {
+
+               if (!Config->get_monitor_bus_preferred_bundle().empty()) {
+
+                       boost::shared_ptr<Bundle> b = bundle_by_name (Config->get_monitor_bus_preferred_bundle());
+
+                       if (b) {
+                               _monitor_out->output()->connect_ports_to_bundle (b, true, this);
+                       } else {
+                               warning << string_compose (_("The preferred I/O for the monitor bus (%1) cannot be found"),
+                                                          Config->get_monitor_bus_preferred_bundle())
+                                       << endmsg;
+                       }
+
+               } else {
+
+                       /* Monitor bus is audio only */
+
+                       vector<string> outputs[DataType::num_types];
+
+                       for (uint32_t i = 0; i < DataType::num_types; ++i) {
+                               _engine.get_physical_outputs (DataType (DataType::Symbol (i)), outputs[i]);
+                       }
+
+                       uint32_t mod = outputs[DataType::AUDIO].size();
+                       uint32_t limit = _monitor_out->n_outputs().get (DataType::AUDIO);
+
+                       if (mod != 0) {
+
+                               for (uint32_t n = 0; n < limit; ++n) {
+
+                                       boost::shared_ptr<Port> p = _monitor_out->output()->ports().port(DataType::AUDIO, n);
+                                       string connect_to;
+                                       if (outputs[DataType::AUDIO].size() > (n % mod)) {
+                                               connect_to = outputs[DataType::AUDIO][n % mod];
+                                       }
+
+                                       if (!connect_to.empty()) {
+                                               if (_monitor_out->output()->connect (p, connect_to, this)) {
+                                                       error << string_compose (
+                                                               _("cannot connect control output %1 to %2"),
+                                                               n, connect_to)
+                                                             << endmsg;
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /* Connect tracks to monitor section. Note that in an
+          existing session, the internal sends will already exist, but we want the
+          routes to notice that they connect to the control out specifically.
+       */
+
+
+       boost::shared_ptr<RouteList> rls = routes.reader ();
+
+       PBD::Unwinder<bool> uw (ignore_route_processor_changes, true);
+
+       for (RouteList::iterator x = rls->begin(); x != rls->end(); ++x) {
+
+               if ((*x)->is_monitor()) {
+                       /* relax */
+               } else if ((*x)->is_master()) {
+                       /* relax */
+               } else {
+                       (*x)->enable_monitor_send ();
+               }
+       }
+}
+
 void
 Session::hookup_io ()
 {