From f44e2e55fdd6e4d265f3eff715b071a27da8b5eb Mon Sep 17 00:00:00 2001 From: Ben Loftis Date: Tue, 21 Apr 2015 08:18:10 -0500 Subject: merge monitor_section branch --- gtk2_ardour/monitor_section.cc | 366 +++++++++++++++++++++++++++++++++++++++- gtk2_ardour/monitor_section.h | 17 ++ gtk2_ardour/monitor_selector.cc | 251 +++++++++++++++++++++++++++ gtk2_ardour/monitor_selector.h | 91 ++++++++++ gtk2_ardour/wscript | 1 + libs/ardour/ardour/session.h | 1 + libs/ardour/route.cc | 5 + libs/ardour/session.cc | 115 +++++++++++++ 8 files changed, 846 insertions(+), 1 deletion(-) create mode 100644 gtk2_ardour/monitor_selector.cc create mode 100644 gtk2_ardour/monitor_selector.h diff --git a/gtk2_ardour/monitor_section.cc b/gtk2_ardour/monitor_section.cc index 4bfe24ae90..7ccf2857a7 100644 --- a/gtk2_ardour/monitor_section.cc +++ b/gtk2_ardour/monitor_section.cc @@ -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 #include +#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 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 >::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 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 a, boost::shared_ptr 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 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 (*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 (*i) == 0) { + maybe_add_bundle_to_output_menu (*i, current); + } + } + + boost::shared_ptr 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; + vector 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 (_("OUTPUT 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::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 wa, boost::weak_ptr wb) +{ + if (!_route) { + return; + } + boost::shared_ptr a = wa.lock (); + boost::shared_ptr b = wb.lock (); + if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) { + update_output_display (); + } +} diff --git a/gtk2_ardour/monitor_section.h b/gtk2_ardour/monitor_section.h index 65023aa667..31d7374810 100644 --- a/gtk2_ardour/monitor_section.h +++ b/gtk2_ardour/monitor_section.h @@ -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 > output_menu_bundles; + Gtk::Menu output_menu; + MonitorSelectorWindow *_output_selector; + ArdourButton* output_button; + + void maybe_add_bundle_to_output_menu (boost::shared_ptr, ARDOUR::BundleList const &); + void bundle_output_chosen (boost::shared_ptr); + 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, boost::weak_ptr); }; diff --git a/gtk2_ardour/monitor_selector.cc b/gtk2_ardour/monitor_selector.cc new file mode 100644 index 0000000000..ad5b799627 --- /dev/null +++ b/gtk2_ardour/monitor_selector.cc @@ -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 + +#include + +#include + +#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 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 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 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 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 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 index 0000000000..8c642b5dfe --- /dev/null +++ b/gtk2_ardour/monitor_selector.h @@ -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); + + 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 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) const { return false; } + bool can_remove_channels (boost::shared_ptr) const { return false; } + bool can_rename_channels (boost::shared_ptr) const { return false; } + + private: + + void io_changed (); + void io_changed_proxy (); + + int _other; + int _ours; + boost::shared_ptr _io; + boost::shared_ptr _port_group; + bool _find_inputs_for_io_outputs; + PBD::ScopedConnection _io_connection; +}; + +class MonitorSelectorWindow : public ArdourWindow +{ + public: + MonitorSelectorWindow (ARDOUR::Session *, boost::shared_ptr, 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__ */ diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 6f094daa73..84db23987f 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -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', diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 75b3bd2006..30afd00fdb 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -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 monitor_out() const { return _monitor_out; } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 36adbfa148..e22acd8d4d 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -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 */ } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index a45175cef3..31f2635ac3 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -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 p = _monitor_out->input()->ports().nth_audio_port (n); + boost::shared_ptr 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 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 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 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 rls = routes.reader (); + + PBD::Unwinder 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 () { -- cgit v1.2.3