X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fio.cc;h=0343545936fcdf07e348c8601cb4427bcc1e5e50;hb=b5ec66ae6cb60fa43c343d3d29340b2370d0b9d1;hp=29cc94ffe70f2ba9772070503822991d9d3aa9a5;hpb=a1052b0eca7bdc8ec1e3ac2996cd16bb48e2a6d2;p=ardour.git diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 29cc94ffe7..0343545936 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -24,17 +24,20 @@ #include +#include #include #include #include +#include #include #include +#include #include #include #include -#include +#include #include #include #include @@ -70,7 +73,7 @@ sigc::signal IO::Meter; sigc::signal IO::ConnectingLegal; sigc::signal IO::PortsLegal; sigc::signal IO::PannersLegal; -sigc::signal IO::MoreChannels; +sigc::signal IO::PortCountChanged; sigc::signal IO::PortsCreated; Glib::StaticMutex IO::m_meter_signal_lock = GLIBMM_STATIC_MUTEX_INIT; @@ -99,10 +102,13 @@ static double direct_gain_to_control (gain_t gain) { */ IO::IO (Session& s, const string& name, int input_min, int input_max, int output_min, int output_max, - DataType default_type) - : Automatable (s, name), - _output_buffers (new BufferSet()), + DataType default_type, bool public_ports) + : SessionObject(s, name), + AutomatableControls (s), + _output_buffers (new BufferSet()), + _active(true), _default_type (default_type), + _public_ports (public_ports), _input_minimum (ChanCount::ZERO), _input_maximum (ChanCount::INFINITE), _output_minimum (ChanCount::ZERO), @@ -126,15 +132,13 @@ IO::IO (Session& s, const string& name, _gain = 1.0; _desired_gain = 1.0; - _input_bundle = 0; - _output_bundle = 0; pending_state_node = 0; no_panner_reset = false; _phase_invert = false; deferred_state = 0; boost::shared_ptr gl( - new AutomationList(Parameter(GainAutomation), 0.0, 2.0, 1.0)); + new AutomationList(Evoral::Parameter(GainAutomation))); _gain_control = boost::shared_ptr( new GainControl(X_("gaincontrol"), *this, gl)); @@ -150,31 +154,33 @@ IO::IO (Session& s, const string& name, m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter)); } - // Connect to our own MoreChannels signal to connect output buffers - IO::MoreChannels.connect (mem_fun (*this, &IO::attach_buffers)); + // Connect to our own PortCountChanged signal to connect output buffers + IO::PortCountChanged.connect (mem_fun (*this, &IO::attach_buffers)); _session.add_controllable (_gain_control); + + create_bundles_for_inputs_and_outputs (); } IO::IO (Session& s, const XMLNode& node, DataType dt) - : Automatable (s, "unnamed io"), - _output_buffers (new BufferSet()), + : SessionObject(s, "unnamed io"), + AutomatableControls (s), + _output_buffers (new BufferSet()), + _active(true), _default_type (dt) { _meter = new PeakMeter (_session); - + _public_ports = true; // XXX get this from node _panner = 0; deferred_state = 0; no_panner_reset = false; _desired_gain = 1.0; _gain = 1.0; - _input_bundle = 0; - _output_bundle = 0; apply_gain_automation = false; boost::shared_ptr gl( - new AutomationList(Parameter(GainAutomation), 0.0, 2.0, 1.0)); + new AutomationList(Evoral::Parameter(GainAutomation))); _gain_control = boost::shared_ptr( new GainControl(X_("gaincontrol"), *this, gl)); @@ -190,18 +196,21 @@ IO::IO (Session& s, const XMLNode& node, DataType dt) m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter)); } - // Connect to our own MoreChannels signal to connect output buffers - IO::MoreChannels.connect (mem_fun (*this, &IO::attach_buffers)); + // Connect to our own PortCountChanged signal to connect output buffers + IO::PortCountChanged.connect (mem_fun (*this, &IO::attach_buffers)); _session.add_controllable (_gain_control); + + create_bundles_for_inputs_and_outputs (); } IO::~IO () { Glib::Mutex::Lock guard (m_meter_signal_lock); - Glib::Mutex::Lock lm (io_lock); + BLOCK_PROCESS_CALLBACK (); + for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) { _session.engine().unregister_port (*i); } @@ -227,7 +236,7 @@ IO::silence (nframes_t nframes, nframes_t offset) } } -/** Deliver bufs to the IO's Jack outputs. +/** Deliver bufs to the IO's output ports * * This function should automatically do whatever it necessary to correctly deliver bufs * to the outputs, eg applying gain or pan or whatever else needs to be done. @@ -305,16 +314,17 @@ void IO::collect_input (BufferSet& outs, nframes_t nframes, nframes_t offset) { assert(outs.available() >= n_inputs()); - - outs.set_count(n_inputs()); - if (outs.count() == ChanCount::ZERO) + if (n_inputs() == ChanCount::ZERO) return; + outs.set_count(n_inputs()); + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { BufferSet::iterator o = outs.begin(*t); for (PortSet::iterator i = _inputs.begin(*t); i != _inputs.end(*t); ++i, ++o) { + (*i).cycle_start (nframes, offset); o->read_from(i->get_buffer(), nframes, offset); } @@ -329,27 +339,67 @@ IO::just_meter_input (nframes_t start_frame, nframes_t end_frame, collect_input (bufs, nframes, offset); - _meter->run(bufs, start_frame, end_frame, nframes, offset); + _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset); } + void -IO::drop_input_bundle () +IO::check_bundles_connected_to_inputs () { - _input_bundle = 0; - input_bundle_configuration_connection.disconnect(); - input_bundle_connection_connection.disconnect(); - _session.set_dirty (); + check_bundles (_bundles_connected_to_inputs, inputs()); } void -IO::drop_output_bundle () +IO::check_bundles_connected_to_outputs () { - _output_bundle = 0; - output_bundle_configuration_connection.disconnect(); - output_bundle_connection_connection.disconnect(); - _session.set_dirty (); + check_bundles (_bundles_connected_to_outputs, outputs()); +} + +void +IO::check_bundles (std::vector& list, const PortSet& ports) +{ + std::vector new_list; + + for (std::vector::iterator i = list.begin(); i != list.end(); ++i) { + + ChanCount const N = i->bundle->nchannels (); + + if (ports.num_ports (default_type()) < N.get (default_type())) { + continue; + } + + bool ok = true; + uint32_t n = N.get (default_type()); + + for (uint32_t j = 0; j < n; ++j) { + /* Every port on bundle channel j must be connected to our input j */ + PortList const pl = i->bundle->channel_ports (j); + for (uint32_t k = 0; k < pl.size(); ++k) { + if (ports.port(j)->connected_to (pl[k]) == false) { + ok = false; + break; + } + } + + if (ok == false) { + break; + } + } + + if (ok) { + new_list.push_back (*i); + } else { + i->configuration_will_change.disconnect (); + i->configuration_has_changed.disconnect (); + i->ports_will_change.disconnect (); + i->ports_have_changed.disconnect (); + } + } + + list = new_list; } + int IO::disconnect_input (Port* our_port, string other_port, void* src) { @@ -376,7 +426,7 @@ IO::disconnect_input (Port* our_port, string other_port, void* src) return -1; } - drop_input_bundle (); + check_bundles_connected_to_inputs (); } } @@ -410,8 +460,6 @@ IO::connect_input (Port* our_port, string other_port, void* src) if (_session.engine().connect (other_port, our_port->name())) { return -1; } - - drop_input_bundle (); } } @@ -446,7 +494,7 @@ IO::disconnect_output (Port* our_port, string other_port, void* src) return -1; } - drop_output_bundle (); + check_bundles_connected_to_outputs (); } } @@ -480,8 +528,6 @@ IO::connect_output (Port* our_port, string other_port, void* src) if (_session.engine().connect (our_port->name(), other_port)) { return -1; } - - drop_output_bundle (); } } @@ -542,20 +588,26 @@ IO::remove_output_port (Port* port, void* src) } _session.engine().unregister_port (*port); - drop_output_bundle (); + check_bundles_connected_to_outputs (); setup_peak_meters (); reset_panner (); } } + + PortCountChanged (n_outputs()); /* EMIT SIGNAL */ + } + + if (change == ConnectionsChanged) { + setup_bundles_for_inputs_and_outputs (); } if (change != NoChange) { output_changed (change, src); _session.set_dirty (); return 0; - } - + } + return -1; } @@ -569,7 +621,6 @@ int IO::add_output_port (string destination, void* src, DataType type) { Port* our_port; - char name[64]; if (type == DataType::NIL) type = _default_type; @@ -587,25 +638,19 @@ IO::add_output_port (string destination, void* src, DataType type) /* Create a new output port */ - // FIXME: naming scheme for differently typed ports? - if (_output_maximum.get(type) == 1) { - snprintf (name, sizeof (name), _("%s/out"), _name.c_str()); - } else { - snprintf (name, sizeof (name), _("%s/out %u"), _name.c_str(), find_output_port_hole()); - } + string portname = build_legal_port_name (type, false); - if ((our_port = _session.engine().register_output_port (type, name)) == 0) { - error << string_compose(_("IO: cannot register output port %1"), name) << endmsg; + if ((our_port = _session.engine().register_output_port (type, portname, _public_ports)) == 0) { + error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg; return -1; } _outputs.add (our_port); - drop_output_bundle (); setup_peak_meters (); reset_panner (); } - MoreChannels (n_outputs()); /* EMIT SIGNAL */ + PortCountChanged (n_outputs()); /* EMIT SIGNAL */ } if (destination.length()) { @@ -616,8 +661,9 @@ IO::add_output_port (string destination, void* src, DataType type) // pan_changed (src); /* EMIT SIGNAL */ output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ + setup_bundles_for_inputs_and_outputs (); _session.set_dirty (); - + return 0; } @@ -646,12 +692,18 @@ IO::remove_input_port (Port* port, void* src) } _session.engine().unregister_port (*port); - drop_input_bundle (); + check_bundles_connected_to_inputs (); setup_peak_meters (); reset_panner (); } } + + PortCountChanged (n_inputs ()); /* EMIT SIGNAL */ + } + + if (change == ConfigurationChanged) { + setup_bundles_for_inputs_and_outputs (); } if (change != NoChange) { @@ -666,7 +718,7 @@ IO::remove_input_port (Port* port, void* src) /** Add an input port. * - * @param type Data type of port. The appropriate Jack port type, and @ref Port will be created. + * @param type Data type of port. The appropriate port type, and @ref Port will be created. * @param destination Name of input port to connect new port to. * @param src Source for emitted ConfigurationChanged signal. */ @@ -674,7 +726,6 @@ int IO::add_input_port (string source, void* src, DataType type) { Port* our_port; - char name[64]; if (type == DataType::NIL) type = _default_type; @@ -684,32 +735,26 @@ IO::add_input_port (string source, void* src, DataType type) { Glib::Mutex::Lock lm (io_lock); - - if (n_inputs() >= _input_maximum) { + + if (_input_maximum.get(type) >= 0 && n_inputs().get (type) >= _input_maximum.get (type)) { return -1; } /* Create a new input port */ - // FIXME: naming scheme for differently typed ports? - if (_input_maximum.get(type) == 1) { - snprintf (name, sizeof (name), _("%s/in"), _name.c_str()); - } else { - snprintf (name, sizeof (name), _("%s/in %u"), _name.c_str(), find_input_port_hole()); - } - - if ((our_port = _session.engine().register_input_port (type, name)) == 0) { - error << string_compose(_("IO: cannot register input port %1"), name) << endmsg; + string portname = build_legal_port_name (type, true); + + if ((our_port = _session.engine().register_input_port (type, portname, _public_ports)) == 0) { + error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg; return -1; } _inputs.add (our_port); - drop_input_bundle (); setup_peak_meters (); reset_panner (); } - MoreChannels (n_inputs()); /* EMIT SIGNAL */ + PortCountChanged (n_inputs()); /* EMIT SIGNAL */ } if (source.length()) { @@ -721,6 +766,7 @@ IO::add_input_port (string source, void* src, DataType type) // pan_changed (src); /* EMIT SIGNAL */ input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ + setup_bundles_for_inputs_and_outputs (); _session.set_dirty (); return 0; @@ -739,7 +785,7 @@ IO::disconnect_inputs (void* src) _session.engine().disconnect (*i); } - drop_input_bundle (); + check_bundles_connected_to_inputs (); } } @@ -761,7 +807,7 @@ IO::disconnect_outputs (void* src) _session.engine().disconnect (*i); } - drop_output_bundle (); + check_bundles_connected_to_outputs (); } } @@ -796,18 +842,12 @@ IO::ensure_inputs_locked (ChanCount count, bool clear, void* src) /* create any necessary new ports */ while (n_inputs().get(*t) < n) { - char buf[64]; - - if (_input_maximum.get(*t) == 1) { - snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str()); - } else { - snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole()); - } + string portname = build_legal_port_name (*t, true); try { - if ((input_port = _session.engine().register_input_port (*t, buf)) == 0) { - error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg; + if ((input_port = _session.engine().register_input_port (*t, portname, _public_ports)) == 0) { + error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg; return -1; } } @@ -825,10 +865,10 @@ IO::ensure_inputs_locked (ChanCount count, bool clear, void* src) } if (changed) { - drop_input_bundle (); + check_bundles_connected_to_inputs (); setup_peak_meters (); reset_panner (); - MoreChannels (n_inputs()); /* EMIT SIGNAL */ + PortCountChanged (n_inputs()); /* EMIT SIGNAL */ _session.set_dirty (); } @@ -844,7 +884,7 @@ IO::ensure_inputs_locked (ChanCount count, bool clear, void* src) /** Attach output_buffers to port buffers. * - * Connected to IO's own MoreChannels signal. + * Connected to IO's own PortCountChanged signal. */ void IO::attach_buffers(ChanCount ignored) @@ -911,19 +951,11 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) while (n_inputs().get(*t) < nin) { - char buf[64]; - - /* Create a new input port */ - - if (_input_maximum.get(*t) == 1) { - snprintf (buf, sizeof (buf), _("%s/in"), _name.c_str()); - } else { - snprintf (buf, sizeof (buf), _("%s/in %u"), _name.c_str(), find_input_port_hole()); - } + string portname = build_legal_port_name (*t, true); try { - if ((port = _session.engine().register_input_port (*t, buf)) == 0) { - error << string_compose(_("IO: cannot register input port %1"), buf) << endmsg; + if ((port = _session.engine().register_input_port (*t, portname, _public_ports)) == 0) { + error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg; return -1; } } @@ -943,19 +975,11 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) while (n_outputs().get(*t) < nout) { - char buf[64]; - - /* Create a new output port */ - - if (_output_maximum.get(*t) == 1) { - snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str()); - } else { - snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole()); - } - + string portname = build_legal_port_name (*t, false); + try { - if ((port = _session.engine().register_output_port (*t, buf)) == 0) { - error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg; + if ((port = _session.engine().register_output_port (*t, portname, _public_ports)) == 0) { + error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg; return -1; } } @@ -992,17 +1016,18 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) } if (out_changed) { - drop_output_bundle (); + check_bundles_connected_to_outputs (); output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ } if (in_changed) { - drop_input_bundle (); + check_bundles_connected_to_inputs (); input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ } if (in_changed || out_changed) { - MoreChannels (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */ + PortCountChanged (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */ + setup_bundles_for_inputs_and_outputs (); _session.set_dirty (); } @@ -1030,6 +1055,7 @@ IO::ensure_inputs (ChanCount count, bool clear, bool lockit, void* src) if (changed) { input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ + setup_bundles_for_inputs_and_outputs (); _session.set_dirty (); } return 0; @@ -1064,16 +1090,10 @@ IO::ensure_outputs_locked (ChanCount count, bool clear, void* src) /* create any necessary new ports */ while (n_outputs().get(*t) < n) { - char buf[64]; - - if (_output_maximum.get(*t) == 1) { - snprintf (buf, sizeof (buf), _("%s/out"), _name.c_str()); - } else { - snprintf (buf, sizeof (buf), _("%s/out %u"), _name.c_str(), find_output_port_hole()); - } + string portname = build_legal_port_name (*t, false); - if ((output_port = _session.engine().register_output_port (*t, buf)) == 0) { - error << string_compose(_("IO: cannot register output port %1"), buf) << endmsg; + if ((output_port = _session.engine().register_output_port (*t, portname, _public_ports)) == 0) { + error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg; return -1; } @@ -1088,8 +1108,8 @@ IO::ensure_outputs_locked (ChanCount count, bool clear, void* src) } if (changed) { - drop_output_bundle (); - MoreChannels (n_outputs()); /* EMIT SIGNAL */ + check_bundles_connected_to_outputs (); + PortCountChanged (n_outputs()); /* EMIT SIGNAL */ _session.set_dirty (); } @@ -1127,6 +1147,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src) if (changed) { output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ + setup_bundles_for_inputs_and_outputs (); } return 0; @@ -1135,7 +1156,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src) gain_t IO::effective_gain () const { - if (_gain_control->list()->automation_playback()) { + if (_gain_control->automation_playback()) { return _gain_control->get_value(); } else { return _desired_gain; @@ -1190,8 +1211,8 @@ IO::state (bool full_state) XMLNode* node = new XMLNode (state_node_name); char buf[64]; string str; - bool need_ins = true; - bool need_outs = true; + vector::iterator ci; + int n; LocaleGuard lg (X_("POSIX")); Glib::Mutex::Lock lm (io_lock); @@ -1199,83 +1220,88 @@ IO::state (bool full_state) id().print (buf, sizeof (buf)); node->add_property("id", buf); - str = ""; - - if (_input_bundle) { - node->add_property ("input-connection", _input_bundle->name()); - need_ins = false; + for ( + std::vector::iterator i = _bundles_connected_to_inputs.begin(); + i != _bundles_connected_to_inputs.end(); + ++i + ) + { + XMLNode* n = new XMLNode ("InputBundle"); + n->add_property ("name", i->bundle->name ()); + node->add_child_nocopy (*n); } - if (_output_bundle) { - node->add_property ("output-connection", _output_bundle->name()); - need_outs = false; + for ( + std::vector::iterator i = _bundles_connected_to_outputs.begin(); + i != _bundles_connected_to_outputs.end(); + ++i + ) + { + XMLNode* n = new XMLNode ("OutputBundle"); + n->add_property ("name", i->bundle->name ()); + node->add_child_nocopy (*n); } + + str = ""; - if (need_ins) { - for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) { + for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) { - const char **connections = i->get_connections(); + vector connections; + + if (i->get_connections (connections)) { + + str += '{'; - if (connections && connections[0]) { - str += '{'; + for (n = 0, ci = connections.begin(); ci != connections.end(); ++ci, ++n) { + if (n) { + str += ','; + } - for (int n = 0; connections && connections[n]; ++n) { - if (n) { - str += ','; - } - - /* if its a connection to our own port, - return only the port name, not the - whole thing. this allows connections - to be re-established even when our - client name is different. - */ - - str += _session.engine().make_port_name_relative (connections[n]); - } - - str += '}'; + /* if its a connection to our own port, + return only the port name, not the + whole thing. this allows connections + to be re-established even when our + client name is different. + */ - free (connections); - } - else { - str += "{}"; - } + str += _session.engine().make_port_name_relative (*ci); + } + + str += '}'; + + } else { + str += "{}"; } - - node->add_property ("inputs", str); } + + node->add_property ("inputs", str); - if (need_outs) { - str = ""; + str = ""; + + for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) { - for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) { + vector connections; + + if (i->get_connections (connections)) { - const char **connections = i->get_connections(); + str += '{'; - if (connections && connections[0]) { - - str += '{'; - - for (int n = 0; connections[n]; ++n) { - if (n) { - str += ','; - } - - str += _session.engine().make_port_name_relative (connections[n]); + for (n = 0, ci = connections.begin(); ci != connections.end(); ++ci, ++n) { + if (n) { + str += ','; } - - str += '}'; - free (connections); - } - else { - str += "{}"; + str += _session.engine().make_port_name_relative (*ci); } + + str += '}'; + + } else { + str += "{}"; } - - node->add_property ("outputs", str); } + + node->add_property ("outputs", str); node->add_child_nocopy (_panner->state (full_state)); node->add_child_nocopy (_gain_control->get_state ()); @@ -1283,13 +1309,14 @@ IO::state (bool full_state) snprintf (buf, sizeof(buf), "%2.12f", gain()); node->add_property ("gain", buf); - // FIXME: this is NOT sufficient! - const int in_min = (_input_minimum == ChanCount::ZERO) ? -1 : _input_minimum.get(_default_type); - const int in_max = (_input_maximum == ChanCount::INFINITE) ? -1 : _input_maximum.get(_default_type); - const int out_min = (_output_minimum == ChanCount::ZERO) ? -1 : _output_minimum.get(_default_type); - const int out_max = (_output_maximum == ChanCount::INFINITE) ? -1 : _output_maximum.get(_default_type); + /* To make backwards compatibility a bit easier, write ChanCount::INFINITE to the session file + as -1. + */ + + int const in_max = _input_maximum == ChanCount::INFINITE ? -1 : _input_maximum.get(_default_type); + int const out_max = _output_maximum == ChanCount::INFINITE ? -1 : _output_maximum.get(_default_type); - snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", in_min, in_max, out_min, out_max); + snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", _input_minimum.get(_default_type), in_max, _output_minimum.get(_default_type), out_max); node->add_property ("iolimits", buf); @@ -1326,18 +1353,42 @@ IO::set_state (const XMLNode& node) _id = prop->value (); } - size_t in_min = -1; - size_t in_max = -1; - size_t out_min = -1; - size_t out_max = -1; + int in_min = -1; + int in_max = -1; + int out_min = -1; + int out_max = -1; if ((prop = node.property ("iolimits")) != 0) { - sscanf (prop->value().c_str(), "%zd,%zd,%zd,%zd", + sscanf (prop->value().c_str(), "%d,%d,%d,%d", &in_min, &in_max, &out_min, &out_max); - _input_minimum = ChanCount(_default_type, in_min); - _input_maximum = ChanCount(_default_type, in_max); - _output_minimum = ChanCount(_default_type, out_min); - _output_maximum = ChanCount(_default_type, out_max); + + /* Correct for the difference between the way we write things to session files and the + way things are described by ChanCount; see comments in io.h about what the different + ChanCount values mean. */ + + if (in_min < 0) { + _input_minimum = ChanCount::ZERO; + } else { + _input_minimum = ChanCount (_default_type, in_min); + } + + if (in_max < 0) { + _input_maximum = ChanCount::INFINITE; + } else { + _input_maximum = ChanCount (_default_type, in_max); + } + + if (out_min < 0) { + _output_minimum = ChanCount::ZERO; + } else { + _output_minimum = ChanCount (_default_type, out_min); + } + + if (out_max < 0) { + _output_maximum = ChanCount::INFINITE; + } else { + _output_maximum = ChanCount (_default_type, out_max); + } } if ((prop = node.property ("gain")) != 0) { @@ -1360,7 +1411,7 @@ IO::set_state (const XMLNode& node) if ((*iter)->name() == X_("Automation")) { - set_automation_state (*(*iter), Parameter(GainAutomation)); + set_automation_state (*(*iter), Evoral::Parameter(GainAutomation)); } if ((*iter)->name() == X_("controllable")) { @@ -1415,16 +1466,12 @@ IO::load_automation (string path) float version; LocaleGuard lg (X_("POSIX")); - fullpath = _session.automation_dir(); - fullpath += path; + fullpath = Glib::build_filename(_session.automation_dir(), path); in.open (fullpath.c_str()); if (!in) { - fullpath = _session.automation_dir(); - fullpath += _session.snap_name(); - fullpath += '-'; - fullpath += path; + fullpath = Glib::build_filename(_session.automation_dir(), _session.snap_name() + '-' + path); in.open (fullpath.c_str()); @@ -1528,66 +1575,138 @@ IO::ports_became_legal () return ret; } +boost::shared_ptr +IO::find_possible_bundle (const string &desired_name, const string &default_name, const string &bundle_type_name) +{ + static const string digits = "0123456789"; + + boost::shared_ptr c = _session.bundle_by_name (desired_name); + + if (!c) { + int bundle_number, mask; + string possible_name; + bool stereo = false; + string::size_type last_non_digit_pos; + + error << string_compose(_("Unknown bundle \"%1\" listed for %2 of %3"), desired_name, bundle_type_name, _name) + << endmsg; + + // find numeric suffix of desired name + bundle_number = 0; + + last_non_digit_pos = desired_name.find_last_not_of(digits); + + if (last_non_digit_pos != string::npos) { + stringstream s; + s << desired_name.substr(last_non_digit_pos); + s >> bundle_number; + } + + // see if it's a stereo connection e.g. "in 3+4" + + if (last_non_digit_pos > 1 && desired_name[last_non_digit_pos] == '+') { + int left_bundle_number = 0; + string::size_type left_last_non_digit_pos; + + left_last_non_digit_pos = desired_name.find_last_not_of(digits, last_non_digit_pos-1); + + if (left_last_non_digit_pos != string::npos) { + stringstream s; + s << desired_name.substr(left_last_non_digit_pos, last_non_digit_pos-1); + s >> left_bundle_number; + + if (left_bundle_number > 0 && left_bundle_number + 1 == bundle_number) { + bundle_number--; + stereo = true; + } + } + } + + // make 0-based + if (bundle_number) + bundle_number--; + + // find highest set bit + mask = 1; + while ((mask <= bundle_number) && (mask <<= 1)); + + // "wrap" bundle number into largest possible power of 2 + // that works... + + while (mask) { + + if (bundle_number & mask) { + bundle_number &= ~mask; + + stringstream s; + s << default_name << " " << bundle_number + 1; + + if (stereo) { + s << "+" << bundle_number + 2; + } + + possible_name = s.str(); + + if ((c = _session.bundle_by_name (possible_name)) != 0) { + break; + } + } + mask >>= 1; + } + if (c) { + info << string_compose (_("Bundle %1 was not available - \"%2\" used instead"), desired_name, possible_name) + << endmsg; + } else { + error << string_compose(_("No %1 bundles available as a replacement"), bundle_type_name) + << endmsg; + } + + } + + return c; + +} + int IO::create_ports (const XMLNode& node) { - const XMLProperty* prop; - int num_inputs = 0; - int num_outputs = 0; + XMLProperty const * prop; + ChanCount num_inputs; + ChanCount num_outputs; - /* XXX: we could change *-connection to *-bundle, but it seems a bit silly to - * break the session file format. - */ if ((prop = node.property ("input-connection")) != 0) { - Bundle* c = _session.bundle_by_name (prop->value()); + boost::shared_ptr c = find_possible_bundle (prop->value(), _("in"), _("input")); - if (c == 0) { - error << string_compose(_("Unknown bundle \"%1\" listed for input of %2"), prop->value(), _name) << endmsg; - - if ((c = _session.bundle_by_name (_("in 1"))) == 0) { - error << _("No input bundles available as a replacement") - << endmsg; - return -1; - } else { - info << string_compose (_("Bundle %1 was not available - \"in 1\" used instead"), prop->value()) - << endmsg; - } + if (!c) { + return -1; } num_inputs = c->nchannels(); } else if ((prop = node.property ("inputs")) != 0) { - num_inputs = count (prop->value().begin(), prop->value().end(), '{'); + num_inputs.set (default_type(), count (prop->value().begin(), prop->value().end(), '{')); } if ((prop = node.property ("output-connection")) != 0) { - Bundle* c = _session.bundle_by_name (prop->value()); - if (c == 0) { - error << string_compose(_("Unknown bundle \"%1\" listed for output of %2"), prop->value(), _name) << endmsg; + boost::shared_ptr c = find_possible_bundle(prop->value(), _("out"), _("output")); - if ((c = _session.bundle_by_name (_("out 1"))) == 0) { - error << _("No output bundles available as a replacement") - << endmsg; - return -1; - } else { - info << string_compose (_("Bundle %1 was not available - \"out 1\" used instead"), prop->value()) - << endmsg; - } + if (!c) { + return -1; } num_outputs = c->nchannels (); } else if ((prop = node.property ("outputs")) != 0) { - num_outputs = count (prop->value().begin(), prop->value().end(), '{'); + + num_outputs.set (default_type(), count (prop->value().begin(), prop->value().end(), '{')); } no_panner_reset = true; - // FIXME: audio-only - if (ensure_io (ChanCount(DataType::AUDIO, num_inputs), ChanCount(DataType::AUDIO, num_outputs), true, this)) { + if (ensure_io (num_inputs, num_outputs, true, this)) { error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg; return -1; } @@ -1604,25 +1723,17 @@ IO::create_ports (const XMLNode& node) int IO::make_connections (const XMLNode& node) { + const XMLProperty* prop; if ((prop = node.property ("input-connection")) != 0) { - Bundle* c = _session.bundle_by_name (prop->value()); + boost::shared_ptr c = find_possible_bundle (prop->value(), _("in"), _("input")); - if (c == 0) { - error << string_compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg; - - if ((c = _session.bundle_by_name (_("in 1"))) == 0) { - error << _("No input connections available as a replacement") - << endmsg; - return -1; - } else { - info << string_compose (_("Bundle %1 was not available - \"in 1\" used instead"), prop->value()) - << endmsg; - } + if (!c) { + return -1; } - use_input_bundle (*c, this); + connect_input_ports_to_bundle (c, this); } else if ((prop = node.property ("inputs")) != 0) { if (set_inputs (prop->value())) { @@ -1630,24 +1741,15 @@ IO::make_connections (const XMLNode& node) return -1; } } - - if ((prop = node.property ("output-bundle")) != 0) { - Bundle* c = _session.bundle_by_name (prop->value()); - - if (c == 0) { - error << string_compose(_("Unknown bundle \"%1\" listed for output of %2"), prop->value(), _name) << endmsg; - if ((c = _session.bundle_by_name (_("out 1"))) == 0) { - error << _("No output bundles available as a replacement") - << endmsg; - return -1; - } else { - info << string_compose (_("Bundle %1 was not available - \"out 1\" used instead"), prop->value()) - << endmsg; - } + if ((prop = node.property ("output-connection")) != 0) { + boost::shared_ptr c = find_possible_bundle (prop->value(), _("out"), _("output")); + + if (!c) { + return -1; } - - use_output_bundle (*c, this); + + connect_output_ports_to_bundle (c, this); } else if ((prop = node.property ("outputs")) != 0) { if (set_outputs (prop->value())) { @@ -1655,6 +1757,28 @@ IO::make_connections (const XMLNode& node) return -1; } } + + for (XMLNodeConstIterator i = node.children().begin(); i != node.children().end(); ++i) { + + if ((*i)->name() == "InputBundle") { + XMLProperty const * prop = (*i)->property ("name"); + if (prop) { + boost::shared_ptr b = find_possible_bundle (prop->value(), _("in"), _("input")); + if (b) { + connect_input_ports_to_bundle (b, this); + } + } + + } else if ((*i)->name() == "OutputBundle") { + XMLProperty const * prop = (*i)->property ("name"); + if (prop) { + boost::shared_ptr b = find_possible_bundle (prop->value(), _("out"), _("output")); + if (b) { + connect_output_ports_to_bundle (b, this); + } + } + } + } return 0; } @@ -1809,14 +1933,22 @@ IO::parse_gain_string (const string& str, vector& ports) } bool -IO::set_name (const string& str) +IO::set_name (const string& requested_name) { - if (str == _name) { + if (requested_name == _name) { return true; } + string name; + Route *rt; + if ( (rt = dynamic_cast(this))) { + name = Route::ensure_track_or_route_name(requested_name, _session); + } else { + name = requested_name; + } + + /* replace all colons in the name. i wish we didn't have to do this */ - string name = str; if (replace_all (name, ":", "-")) { warning << _("you cannot use colons to name objects with I/O connections") << endmsg; @@ -1834,7 +1966,11 @@ IO::set_name (const string& str) i->set_name (current_name); } - return SessionObject::set_name(name); + bool const r = SessionObject::set_name(name); + + setup_bundles_for_inputs_and_outputs (); + + return r; } void @@ -1910,63 +2046,24 @@ IO::input_latency () const } int -IO::use_input_bundle (Bundle& c, void* src) +IO::connect_input_ports_to_bundle (boost::shared_ptr c, void* src) { - uint32_t limit; - { BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock lm2 (io_lock); - - limit = c.nchannels(); - - drop_input_bundle (); - - // FIXME bundles only work for audio-only - if (ensure_inputs (ChanCount(DataType::AUDIO, limit), false, false, src)) { - return -1; - } - /* first pass: check the current state to see what's correctly - connected, and drop anything that we don't want. - */ - - for (uint32_t n = 0; n < limit; ++n) { - const Bundle::PortList& pl = c.channel_ports (n); - - for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { - - if (!_inputs.port(n)->connected_to ((*i))) { - - /* clear any existing connections */ - - _session.engine().disconnect (*_inputs.port(n)); - - } else if (_inputs.port(n)->connected() > 1) { - - /* OK, it is connected to the port we want, - but its also connected to other ports. - Change that situation. - */ - - /* XXX could be optimized to not drop - the one we want. - */ - - _session.engine().disconnect (*_inputs.port(n)); - - } - } - } - - /* second pass: connect all requested ports where necessary */ - - for (uint32_t n = 0; n < limit; ++n) { - const Bundle::PortList& pl = c.channel_ports (n); - - for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { - - if (!_inputs.port(n)->connected_to ((*i))) { + /* Connect to the bundle, not worrying about any connections + that are already made. */ + + ChanCount const channels = c->nchannels (); + uint32_t cnt = channels.get (default_type()); + + for (uint32_t n = 0; n < cnt; ++n) { + const PortList& pl = c->channel_ports (n); + + for (PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { + + if (!_inputs.port(n)->connected_to (*i)) { if (_session.engine().connect (*i, _inputs.port(n)->name())) { return -1; @@ -1975,13 +2072,23 @@ IO::use_input_bundle (Bundle& c, void* src) } } - - _input_bundle = &c; - - input_bundle_configuration_connection = c.ConfigurationChanged.connect - (mem_fun (*this, &IO::input_bundle_configuration_changed)); - input_bundle_connection_connection = c.PortsChanged.connect - (mem_fun (*this, &IO::input_bundle_connection_changed)); + + /* If this is a UserBundle, make a note of what we've done */ + + boost::shared_ptr ub = boost::dynamic_pointer_cast (c); + if (ub) { + + /* See if we already know about this one */ + std::vector::iterator i = _bundles_connected_to_inputs.begin(); + while (i != _bundles_connected_to_inputs.end() && i->bundle != ub) { + ++i; + } + + if (i == _bundles_connected_to_inputs.end()) { + /* We don't, so make a note */ + _bundles_connected_to_inputs.push_back (UserBundleInfo (this, ub)); + } + } } input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */ @@ -1989,64 +2096,25 @@ IO::use_input_bundle (Bundle& c, void* src) } int -IO::use_output_bundle (Bundle& c, void* src) +IO::connect_output_ports_to_bundle (boost::shared_ptr c, void* src) { - uint32_t limit; - { BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock lm2 (io_lock); - limit = c.nchannels(); - - drop_output_bundle (); - - // FIXME: audio-only - if (ensure_outputs (ChanCount(DataType::AUDIO, limit), false, false, src)) { - return -1; - } - - /* first pass: check the current state to see what's correctly - connected, and drop anything that we don't want. - */ - - for (uint32_t n = 0; n < limit; ++n) { - - const Bundle::PortList& pl = c.channel_ports (n); - - for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { - - if (!_outputs.port(n)->connected_to ((*i))) { - - /* clear any existing connections */ - - _session.engine().disconnect (*_outputs.port(n)); - - } else if (_outputs.port(n)->connected() > 1) { + /* Connect to the bundle, not worrying about any connections + that are already made. */ - /* OK, it is connected to the port we want, - but its also connected to other ports. - Change that situation. - */ + ChanCount const channels = c->nchannels (); + uint32_t cnt = channels.get (default_type()); - /* XXX could be optimized to not drop - the one we want. - */ - - _session.engine().disconnect (*_outputs.port(n)); - } - } - } + for (uint32_t n = 0; n < cnt; ++n) { - /* second pass: connect all requested ports where necessary */ + const PortList& pl = c->channel_ports (n); - for (uint32_t n = 0; n < limit; ++n) { + for (PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { - const Bundle::PortList& pl = c.channel_ports (n); - - for (Bundle::PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { - - if (!_outputs.port(n)->connected_to ((*i))) { + if (!_outputs.port(n)->connected_to (*i)) { if (_session.engine().connect (_outputs.port(n)->name(), *i)) { return -1; @@ -2055,12 +2123,22 @@ IO::use_output_bundle (Bundle& c, void* src) } } - _output_bundle = &c; + /* If this is a UserBundle, make a note of what we've done */ + + boost::shared_ptr ub = boost::dynamic_pointer_cast (c); + if (ub) { + + /* See if we already know about this one */ + std::vector::iterator i = _bundles_connected_to_outputs.begin(); + while (i != _bundles_connected_to_outputs.end() && i->bundle != ub) { + ++i; + } - output_bundle_configuration_connection = c.ConfigurationChanged.connect - (mem_fun (*this, &IO::output_bundle_configuration_changed)); - output_bundle_connection_connection = c.PortsChanged.connect - (mem_fun (*this, &IO::output_bundle_connection_changed)); + if (i == _bundles_connected_to_outputs.end()) { + /* We don't, so make a note */ + _bundles_connected_to_outputs.push_back (UserBundleInfo (this, ub)); + } + } } output_changed (IOChange (ConnectionsChanged|ConfigurationChanged), src); /* EMIT SIGNAL */ @@ -2111,27 +2189,31 @@ IO::reset_panners () } void -IO::input_bundle_connection_changed (int ignored) +IO::bundle_configuration_will_change () { - use_input_bundle (*_input_bundle, this); + //XXX +// connect_input_ports_to_bundle (_input_bundle, this); } void -IO::input_bundle_configuration_changed () +IO::bundle_configuration_has_changed () { - use_input_bundle (*_input_bundle, this); + //XXX +// connect_input_ports_to_bundle (_input_bundle, this); } void -IO::output_bundle_connection_changed (int ignored) +IO::bundle_ports_will_change (int ignored) { - use_output_bundle (*_output_bundle, this); +//XXX +// connect_output_ports_to_bundle (_output_bundle, this); } void -IO::output_bundle_configuration_changed () +IO::bundle_ports_have_changed (int ignored) { - use_output_bundle (*_output_bundle, this); + //XXX +// connect_output_ports_to_bundle (_output_bundle, this); } void @@ -2156,8 +2238,8 @@ IO::GainControl::get_value (void) const void IO::setup_peak_meters() { - ChanCount max_streams = std::max(_inputs.count(), _outputs.count()); - _meter->configure_io(max_streams, max_streams); + ChanCount max_streams = std::max (_inputs.count(), _outputs.count()); + _meter->configure_io (max_streams, max_streams); } /** @@ -2171,9 +2253,8 @@ IO::setup_peak_meters() void IO::update_meters() { - Glib::Mutex::Lock guard (m_meter_signal_lock); - - Meter(); /* EMIT SIGNAL */ + Glib::Mutex::Lock guard (m_meter_signal_lock); + Meter(); /* EMIT SIGNAL */ } void @@ -2188,12 +2269,12 @@ IO::meter () void IO::clear_automation () { - Automatable::clear_automation (); // clears gain automation + data().clear (); // clears gain automation _panner->clear_automation (); } void -IO::set_parameter_automation_state (Parameter param, AutoState state) +IO::set_parameter_automation_state (Evoral::Parameter param, AutoState state) { // XXX: would be nice to get rid of this special hack @@ -2202,9 +2283,10 @@ IO::set_parameter_automation_state (Parameter param, AutoState state) bool changed = false; { - Glib::Mutex::Lock lm (_automation_lock); + Glib::Mutex::Lock lm (control_lock()); - boost::shared_ptr gain_auto = _gain_control->list(); + boost::shared_ptr gain_auto + = boost::dynamic_pointer_cast(_gain_control->list()); if (state != gain_auto->automation_state()) { changed = true; @@ -2223,7 +2305,7 @@ IO::set_parameter_automation_state (Parameter param, AutoState state) } } else { - Automatable::set_parameter_automation_state(param, state); + AutomatableControls::set_parameter_automation_state(param, state); } } @@ -2259,7 +2341,7 @@ IO::set_gain (gain_t val, void *src) _gain = val; } - if (_session.transport_stopped() && src != 0 && src != this && _gain_control->list()->automation_write()) { + if (_session.transport_stopped() && src != 0 && src != this && _gain_control->automation_write()) { _gain_control->list()->add (_session.transport_frame(), val); } @@ -2271,7 +2353,7 @@ void IO::start_pan_touch (uint32_t which) { if (which < _panner->size()) { - (*_panner)[which]->pan_control()->list()->start_touch(); + (*_panner)[which]->pan_control()->start_touch(); } } @@ -2279,19 +2361,22 @@ void IO::end_pan_touch (uint32_t which) { if (which < _panner->size()) { - (*_panner)[which]->pan_control()->list()->stop_touch(); + (*_panner)[which]->pan_control()->stop_touch(); } } void -IO::automation_snapshot (nframes_t now) +IO::automation_snapshot (nframes_t now, bool force) { - Automatable::automation_snapshot (now); + AutomatableControls::automation_snapshot (now, force); - if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _session.automation_interval()) { + if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) { _panner->snapshot (now); } + + _panner->snapshot (now); + _last_automation_snapshot = now; } void @@ -2299,7 +2384,7 @@ IO::transport_stopped (nframes_t frame) { _gain_control->list()->reposition_for_rt_add (frame); - if (_gain_control->list()->automation_state() != Off) { + if (_gain_control->automation_state() != Off) { /* the src=0 condition is a special signal to not propagate automation gain changes into the mix group when locating. @@ -2312,8 +2397,62 @@ IO::transport_stopped (nframes_t frame) _panner->transport_stopped (frame); } +string +IO::build_legal_port_name (DataType type, bool in) +{ + const int name_size = jack_port_name_size(); + int limit; + string suffix; + int maxports; + + if (type == DataType::AUDIO) { + suffix = _("audio"); + } else if (type == DataType::MIDI) { + suffix = _("midi"); + } else { + throw unknown_type(); + } + + if (in) { + suffix += _("_in"); + maxports = _input_maximum.get(type); + } else { + suffix += _("_out"); + maxports = _output_maximum.get(type); + } + + if (maxports == 1) { + // allow space for the slash + the suffix + limit = name_size - _session.engine().client_name().length() - (suffix.length() + 1); + char buf[name_size+1]; + snprintf (buf, name_size+1, ("%.*s/%s"), limit, _name.c_str(), suffix.c_str()); + return string (buf); + } + + // allow up to 4 digits for the output port number, plus the slash, suffix and extra space + + limit = name_size - _session.engine().client_name().length() - (suffix.length() + 5); + + char buf1[name_size+1]; + char buf2[name_size+1]; + + snprintf (buf1, name_size+1, ("%.*s/%s"), limit, _name.c_str(), suffix.c_str()); + + int port_number; + + if (in) { + port_number = find_input_port_hole (buf1); + } else { + port_number = find_output_port_hole (buf1); + } + + snprintf (buf2, name_size+1, "%s %d", buf1, port_number); + + return string (buf2); +} + int32_t -IO::find_input_port_hole () +IO::find_input_port_hole (const char* base) { /* CALLER MUST HOLD IO LOCK */ @@ -2323,11 +2462,14 @@ IO::find_input_port_hole () return 1; } - for (n = 1; n < UINT_MAX; ++n) { + /* we only allow up to 4 characters for the port number + */ + + for (n = 1; n < 9999; ++n) { char buf[jack_port_name_size()]; PortSet::iterator i = _inputs.begin(); - snprintf (buf, jack_port_name_size(), _("%s/in %u"), _name.c_str(), n); + snprintf (buf, jack_port_name_size(), _("%s %u"), base, n); for ( ; i != _inputs.end(); ++i) { if (i->short_name() == buf) { @@ -2343,7 +2485,7 @@ IO::find_input_port_hole () } int32_t -IO::find_output_port_hole () +IO::find_output_port_hole (const char* base) { /* CALLER MUST HOLD IO LOCK */ @@ -2353,11 +2495,14 @@ IO::find_output_port_hole () return 1; } - for (n = 1; n < UINT_MAX; ++n) { + /* we only allow up to 4 characters for the port number + */ + + for (n = 1; n < 9999; ++n) { char buf[jack_port_name_size()]; PortSet::iterator i = _outputs.begin(); - snprintf (buf, jack_port_name_size(), _("%s/out %u"), _name.c_str(), n); + snprintf (buf, jack_port_name_size(), _("%s %u"), base, n); for ( ; i != _outputs.end(); ++i) { if (i->short_name() == buf) { @@ -2373,6 +2518,14 @@ IO::find_output_port_hole () return n; } +void +IO::set_active (bool yn) +{ + _active = yn; + active_changed(); /* EMIT SIGNAL */ +} + + AudioPort* IO::audio_input(uint32_t n) const { @@ -2428,3 +2581,176 @@ IO::update_port_total_latencies () _session.engine().update_total_latency (*i); } } + + +/** + * Setup bundles that describe our inputs and outputs. + */ + +void +IO::setup_bundles_for_inputs_and_outputs () +{ + char buf[32]; + + snprintf(buf, sizeof (buf), _("%s in"), _name.c_str()); + _bundle_for_inputs->set_name (buf); + uint32_t const ni = inputs().num_ports(); + _bundle_for_inputs->set_channels (ni); + for (uint32_t i = 0; i < ni; ++i) { + _bundle_for_inputs->set_port (i, inputs().port(i)->name()); + } + + snprintf(buf, sizeof (buf), _("%s out"), _name.c_str()); + _bundle_for_outputs->set_name (buf); + uint32_t const no = outputs().num_ports(); + _bundle_for_outputs->set_channels (no); + for (uint32_t i = 0; i < no; ++i) { + _bundle_for_outputs->set_port (i, outputs().port(i)->name()); + } +} + + +/** + * Create and setup bundles that describe our inputs and outputs. + */ + +void +IO::create_bundles_for_inputs_and_outputs () +{ + _bundle_for_inputs = boost::shared_ptr (new AutoBundle (true)); + _bundle_for_outputs = boost::shared_ptr (new AutoBundle (false)); + setup_bundles_for_inputs_and_outputs (); +} + +/** Add a bundle to a list if is connected to our inputs. + * @param b Bundle to check. + * @param bundles List to add to. + */ +void +IO::maybe_add_input_bundle_to_list (boost::shared_ptr b, std::vector >* bundles) +{ + boost::shared_ptr ab = boost::dynamic_pointer_cast (b); + + if (ab == 0 || ab->ports_are_outputs() == false) { + return; + } + + if (ab->nchannels().get (default_type()) != n_inputs().n_total ()) { + return; + } + + for (uint32_t i = 0; i < n_inputs().n_total (); ++i) { + + PortList const & pl = b->channel_ports (i); + + if (pl.empty()) { + return; + } + + if (!input(i)->connected_to (pl[0])) { + return; + } + } + + bundles->push_back (b); +} + +/** @return Bundles connected to our inputs */ +std::vector > +IO::bundles_connected_to_inputs () +{ + std::vector > bundles; + + /* User bundles */ + for (std::vector::iterator i = _bundles_connected_to_inputs.begin(); i != _bundles_connected_to_inputs.end(); ++i) { + bundles.push_back (i->bundle); + } + + /* Auto bundles */ + _session.foreach_bundle ( + sigc::bind (sigc::mem_fun (*this, &IO::maybe_add_input_bundle_to_list), &bundles) + ); + + return bundles; +} + + +/** Add a bundle to a list if is connected to our outputs. + * @param b Bundle to check. + * @param bundles List to add to. + */ +void +IO::maybe_add_output_bundle_to_list (boost::shared_ptr b, std::vector >* bundles) +{ + boost::shared_ptr ab = boost::dynamic_pointer_cast (b); + if (ab == 0 || ab->ports_are_inputs() == false) { + return; + } + + if (ab->nchannels ().get (default_type()) != n_outputs().n_total ()) { + return; + } + + for (uint32_t i = 0; i < n_outputs().n_total (); ++i) { + + PortList const & pl = b->channel_ports (i); + + if (pl.empty()) { + return; + } + + if (!output(i)->connected_to (pl[0])) { + return; + } + } + + bundles->push_back (b); +} + + +/* @return Bundles connected to our outputs */ +std::vector > +IO::bundles_connected_to_outputs () +{ + std::vector > bundles; + + /* User bundles */ + for (std::vector::iterator i = _bundles_connected_to_outputs.begin(); i != _bundles_connected_to_outputs.end(); ++i) { + bundles.push_back (i->bundle); + } + + /* Auto bundles */ + _session.foreach_bundle ( + sigc::bind (sigc::mem_fun (*this, &IO::maybe_add_output_bundle_to_list), &bundles) + ); + + return bundles; +} + + +IO::UserBundleInfo::UserBundleInfo (IO* io, boost::shared_ptr b) +{ + bundle = b; + configuration_will_change = b->ConfigurationWillChange.connect ( + sigc::mem_fun (*io, &IO::bundle_configuration_will_change) + ); + configuration_has_changed = b->ConfigurationHasChanged.connect ( + sigc::mem_fun (*io, &IO::bundle_configuration_has_changed) + ); + ports_will_change = b->PortsWillChange.connect ( + sigc::mem_fun (*io, &IO::bundle_ports_will_change) + ); + ports_have_changed = b->PortsHaveChanged.connect ( + sigc::mem_fun (*io, &IO::bundle_ports_have_changed) + ); +} + +void +IO::prepare_inputs (nframes_t nframes, nframes_t offset) +{ + /* io_lock, not taken: function must be called from Session::process() calltree */ + + for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) { + (*i).cycle_start (nframes, offset); + } +}