diff options
author | Carl Hetherington <carl@carlh.net> | 2007-10-19 13:30:07 +0000 |
---|---|---|
committer | Carl Hetherington <carl@carlh.net> | 2007-10-19 13:30:07 +0000 |
commit | 77f16522e0b396262bc272c1637753faa9da0ba7 (patch) | |
tree | 54520a9d20bc61e72cfdf8c162eb9ed07b95e99d /libs/ardour | |
parent | 239ec39da6583e6e00cd03fa3bde8f1e27016b4d (diff) |
Various work on bundles. We now have a Bundle Manager dialogue, and hopefully things are a bit cleaner internally. This commit changes the session file format with respect to bundles (or Connections as they used to be called).
git-svn-id: svn://localhost/ardour2/trunk@2561 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour')
-rw-r--r-- | libs/ardour/SConscript | 5 | ||||
-rw-r--r-- | libs/ardour/ardour/audioengine.h | 4 | ||||
-rw-r--r-- | libs/ardour/ardour/auto_bundle.h | 50 | ||||
-rw-r--r-- | libs/ardour/ardour/bundle.h | 120 | ||||
-rw-r--r-- | libs/ardour/ardour/data_type.h | 1 | ||||
-rw-r--r-- | libs/ardour/ardour/io.h | 58 | ||||
-rw-r--r-- | libs/ardour/ardour/session.h | 7 | ||||
-rw-r--r-- | libs/ardour/ardour/user_bundle.h | 72 | ||||
-rw-r--r-- | libs/ardour/audioengine.cc | 2 | ||||
-rw-r--r-- | libs/ardour/auto_bundle.cc | 47 | ||||
-rw-r--r-- | libs/ardour/io.cc | 705 | ||||
-rw-r--r-- | libs/ardour/session.cc | 89 | ||||
-rw-r--r-- | libs/ardour/session_state.cc | 56 | ||||
-rw-r--r-- | libs/ardour/user_bundle.cc | 198 |
14 files changed, 853 insertions, 561 deletions
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 4d68dc7725..af08d4569c 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -12,7 +12,7 @@ ardour = env.Copy() # this defines the version number of libardour # -domain = 'libardour2' +domain = 'libardour' ardour.Append(DOMAIN = domain, MAJOR = 2, MINOR = 0, MICRO = 0) ardour.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"") @@ -29,6 +29,8 @@ ardour.Append(CPPPATH = '#libs/surfaces/control_protocol') ardour_files=Split(""" amp.cc audio_buffer.cc +auto_bundle.cc +user_bundle.cc audio_diskstream.cc audio_library.cc audio_playlist.cc @@ -45,7 +47,6 @@ automation_control.cc automation_event.cc buffer.cc buffer_set.cc -bundle.cc chan_count.cc configuration.cc control_protocol_manager.cc diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index 34f7eb8c22..503b8166f0 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -144,7 +144,7 @@ class AudioEngine : public sigc::trackable /** Caller may not delete the object pointed to by the return value */ - Port *get_port_by_name (const std::string& name, bool keep = true); + Port *get_port_by_name (const std::string& name, bool keep = true) const; enum TransportState { TransportStopped = JackTransportStopped, @@ -199,7 +199,7 @@ class AudioEngine : public sigc::trackable ARDOUR::Session *session; jack_client_t *_jack; std::string jack_client_name; - Glib::Mutex _process_lock; + mutable Glib::Mutex _process_lock; Glib::Cond session_removed; bool session_remove_pending; bool _running; diff --git a/libs/ardour/ardour/auto_bundle.h b/libs/ardour/ardour/auto_bundle.h new file mode 100644 index 0000000000..685a083e8d --- /dev/null +++ b/libs/ardour/ardour/auto_bundle.h @@ -0,0 +1,50 @@ +/* + Copyright (C) 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 __ardour_auto_bundle_h__ +#define __ardour_auto_bundle_h__ + +#include <vector> +#include <glibmm/thread.h> +#include "ardour/bundle.h" + +namespace ARDOUR { + +class AutoBundle : public Bundle { + + public: + AutoBundle (bool i = true); + AutoBundle (std::string const &, bool i = true); + + uint32_t nchannels () const; + const PortList& channel_ports (uint32_t) const; + + void set_channels (uint32_t); + void set_port (uint32_t, std::string const &); + + private: + /// mutex for _ports; + /// XXX: is this necessary? + mutable Glib::Mutex _ports_mutex; + std::vector<PortList> _ports; +}; + +} + +#endif /* __ardour_auto_bundle_h__ */ diff --git a/libs/ardour/ardour/bundle.h b/libs/ardour/ardour/bundle.h index 9c5f3cb21a..ba92063b30 100644 --- a/libs/ardour/ardour/bundle.h +++ b/libs/ardour/ardour/bundle.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2002 Paul Davis + 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 @@ -20,118 +20,54 @@ #ifndef __ardour_bundle_h__ #define __ardour_bundle_h__ -#include <vector> #include <string> #include <sigc++/signal.h> -#include <glibmm/thread.h> -#include <pbd/stateful.h> - -using std::vector; -using std::string; +#include "ardour/data_type.h" namespace ARDOUR { +typedef std::vector<std::string> PortList; + /** - * A set of `channels', each of which is associated with 0 or more - * JACK ports. + * A set of `channels', each of which is associated with 0 or more JACK ports. */ -class Bundle : public PBD::Stateful, public sigc::trackable { +class Bundle { public: - /** - * Bundle constructor. - * @param name Name for this Bundle. - * @param dy true if this Bundle is `dynamic', ie it is created on-the-fly - * and should not be written to the session file. - */ - Bundle (string name, bool dy = false) : _name (name), _dynamic(dy) {} - ~Bundle() {} - - /// A vector of JACK port names - typedef vector<string> PortList; - - void set_name (string name, void *src); - - /** - * @return name of this Bundle. - */ - string name() const { return _name; } - - /** - * @return true if this Bundle is marked as `dynamic', meaning - * that it won't be written to the session file. - */ - bool dynamic() const { return _dynamic; } + Bundle () : _type (DataType::AUDIO) {} + Bundle (bool i) : _type (DataType::AUDIO), _ports_are_inputs (i) {} + Bundle (std::string const & n, bool i = true) : _name (n), _type (DataType::AUDIO), _ports_are_inputs (i) {} + virtual ~Bundle() {} /** * @return Number of channels that this Bundle has. */ - uint32_t nchannels () const { return _channels.size(); } - const PortList& channel_ports (int ch) const; + virtual uint32_t nchannels () const = 0; + virtual const PortList& channel_ports (uint32_t) const = 0; - void set_nchannels (int n); + void set_name (std::string const & n) { + _name = n; + NameChanged (); + } + + std::string name () const { return _name; } - void add_port_to_channel (int ch, string portname); - void remove_port_from_channel (int ch, string portname); + sigc::signal<void> NameChanged; - /// Our name changed - sigc::signal<void, void*> NameChanged; - /// The number of channels changed - sigc::signal<void> ConfigurationChanged; - /// The ports associated with one of our channels changed - sigc::signal<void, int> PortsChanged; + void set_type (DataType t) { _type = t; } + DataType type () const { return _type; } - bool operator==(const Bundle& other) const; - - XMLNode& get_state (void); - int set_state (const XMLNode&); - - protected: - Bundle (const XMLNode&); + void set_ports_are_inputs () { _ports_are_inputs = true; } + void set_ports_are_outputs () { _ports_are_inputs = false; } + bool ports_are_inputs () const { return _ports_are_inputs; } + bool ports_are_outputs () const { return !_ports_are_inputs; } private: - mutable Glib::Mutex channels_lock; ///< mutex for _channels - vector<PortList> _channels; ///< list of JACK ports associated with each of our channels - string _name; ///< name - bool _dynamic; ///< true if `dynamic', ie not to be written to the session file - - int set_channels (const string& str); - int parse_io_string (const string& str, vector<string>& ports); -}; - -/** - * Bundle in which the JACK ports are inputs. - */ - -class InputBundle : public Bundle { - public: - /** - * InputBundle constructor. - * \param name Name. - * \param dy true if this Bundle is `dynamic'; ie it is created on-the-fly - * and should not be written to the session file. - */ - InputBundle (string name, bool dy = false) : Bundle (name, dy) {} - InputBundle (const XMLNode&); -}; - -/** - * Bundle in which the JACK ports are outputs. - */ - -class OutputBundle : public Bundle { - public: - /** - * OutputBundle constructor. - * \param name Name. - * \param dy true if this Bundle is `dynamic'; ie it is created on-the-fly - * and should not be written to the session file. - */ - OutputBundle (string name, bool dy = false) : Bundle (name, dy) {} - OutputBundle (const XMLNode&); + std::string _name; + ARDOUR::DataType _type; + bool _ports_are_inputs; }; } #endif /* __ardour_bundle_h__ */ - diff --git a/libs/ardour/ardour/data_type.h b/libs/ardour/ardour/data_type.h index 68d9554904..854f52acba 100644 --- a/libs/ardour/ardour/data_type.h +++ b/libs/ardour/ardour/data_type.h @@ -21,7 +21,6 @@ #define __ardour_data_type_h__ #include <string> -#include <ardour/data_type.h> #include <jack/jack.h> namespace ARDOUR { diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 6e68c01d8c..888b770250 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -43,6 +43,7 @@ #include <ardour/chan_count.h> #include <ardour/latent.h> #include <ardour/automation_control.h> +#include <ardour/user_bundle.h> using std::string; using std::vector; @@ -54,6 +55,7 @@ namespace ARDOUR { class Session; class AudioEngine; class Bundle; +class AutoBundle; class Panner; class PeakMeter; class Port; @@ -123,9 +125,12 @@ class IO : public Automatable, public Latent int connect_input_ports_to_bundle (boost::shared_ptr<Bundle>, void *src); int connect_output_ports_to_bundle (boost::shared_ptr<Bundle>, void *src); - boost::shared_ptr<Bundle> input_bundle(); - boost::shared_ptr<Bundle> output_bundle(); + std::vector<boost::shared_ptr<Bundle> > bundles_connected_to_inputs (); + std::vector<boost::shared_ptr<Bundle> > bundles_connected_to_outputs (); + boost::shared_ptr<AutoBundle> bundle_for_inputs () { return _bundle_for_inputs; } + boost::shared_ptr<AutoBundle> bundle_for_outputs () { return _bundle_for_outputs; } + int add_input_port (string source, void *src, DataType type = DataType::NIL); int add_output_port (string destination, void *src, DataType type = DataType::NIL); @@ -179,9 +184,6 @@ class IO : public Automatable, public Latent void attach_buffers(ChanCount ignored); - boost::shared_ptr<Bundle> bundle_for_inputs () const { return _bundle_for_inputs; } - boost::shared_ptr<Bundle> bundle_for_outputs () const { return _bundle_for_outputs; } - sigc::signal<void,IOChange,void*> input_changed; sigc::signal<void,IOChange,void*> output_changed; @@ -272,8 +274,6 @@ class IO : public Automatable, public Latent PortSet _outputs; PortSet _inputs; PeakMeter* _meter; - boost::shared_ptr<Bundle> _input_bundle; ///< bundle connected to our inputs - boost::shared_ptr<Bundle> _output_bundle; ///< bundle connected to our outputs bool no_panner_reset; bool _phase_invert; bool _denormal_protection; @@ -310,13 +310,6 @@ class IO : public Automatable, public Latent friend class Send; - /* are these the best variable names ever, or what? */ - - sigc::connection input_bundle_configuration_connection; - sigc::connection output_bundle_configuration_connection; - sigc::connection input_bundle_connection_connection; - sigc::connection output_bundle_connection_connection; - static bool panners_legal; int connecting_became_legal (); @@ -330,8 +323,21 @@ class IO : public Automatable, public Latent ChanCount _output_minimum; ///< minimum number of output channels (0 for no minimum) ChanCount _output_maximum; ///< maximum number of output channels (ChanCount::INFINITE for no maximum) - boost::shared_ptr<Bundle> _bundle_for_inputs; - boost::shared_ptr<Bundle> _bundle_for_outputs; + boost::shared_ptr<AutoBundle> _bundle_for_inputs; ///< a bundle representing our inputs + boost::shared_ptr<AutoBundle> _bundle_for_outputs; ///< a bundle representing our outputs + + struct UserBundleInfo { + UserBundleInfo (IO*, boost::shared_ptr<UserBundle> b); + + boost::shared_ptr<UserBundle> bundle; + sigc::connection configuration_will_change; + sigc::connection configuration_has_changed; + sigc::connection ports_will_change; + sigc::connection ports_have_changed; + }; + + std::vector<UserBundleInfo> _bundles_connected_to_outputs; ///< user bundles connected to our outputs + std::vector<UserBundleInfo> _bundles_connected_to_inputs; ///< user bundles connected to our inputs static int parse_io_string (const string&, vector<string>& chns); @@ -343,13 +349,14 @@ class IO : public Automatable, public Latent int ensure_inputs (ChanCount, bool clear, bool lockit, void *src); int ensure_outputs (ChanCount, bool clear, bool lockit, void *src); - void drop_input_bundle (); - void drop_output_bundle (); + void check_bundles_connected_to_inputs (); + void check_bundles_connected_to_outputs (); + void check_bundles (std::vector<UserBundleInfo>&, const PortSet&); - void input_bundle_configuration_changed (); - void input_bundle_connection_changed (int); - void output_bundle_configuration_changed (); - void output_bundle_connection_changed (int); + void bundle_configuration_will_change (); + void bundle_configuration_has_changed (); + void bundle_ports_will_change (int); + void bundle_ports_have_changed (int); int create_ports (const XMLNode&); int make_connections (const XMLNode&); @@ -363,8 +370,11 @@ class IO : public Automatable, public Latent int32_t find_input_port_hole (); int32_t find_output_port_hole (); - void create_bundles (); - void setup_bundles (); + void create_bundles_for_inputs_and_outputs (); + void setup_bundles_for_inputs_and_outputs (); + + void maybe_add_input_bundle_to_list (boost::shared_ptr<Bundle>, std::vector<boost::shared_ptr<Bundle> >*); + void maybe_add_output_bundle_to_list (boost::shared_ptr<Bundle>, std::vector<boost::shared_ptr<Bundle> >*); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index bbcae6e91d..e153914a09 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -489,7 +489,8 @@ class Session : public PBD::StatefulDestructible void set_remote_control_ids(); - AudioEngine &engine() { return _engine; }; + AudioEngine & engine() { return _engine; } + AudioEngine const & engine () const { return _engine; } int32_t max_level; int32_t min_level; @@ -716,7 +717,6 @@ class Session : public PBD::StatefulDestructible void add_bundle (boost::shared_ptr<Bundle>); void remove_bundle (boost::shared_ptr<Bundle>); boost::shared_ptr<Bundle> bundle_by_name (string) const; - boost::shared_ptr<Bundle> bundle_by_ports (vector<string> const &) const; sigc::signal<void,boost::shared_ptr<Bundle> > BundleAdded; sigc::signal<void,boost::shared_ptr<Bundle> > BundleRemoved; @@ -1564,7 +1564,8 @@ class Session : public PBD::StatefulDestructible typedef list<boost::shared_ptr<Bundle> > BundleList; mutable Glib::Mutex bundle_lock; BundleList _bundles; - int load_bundles (const XMLNode&); + XMLNode* _bundle_xml_node; + int load_bundles (XMLNode const &); void reverse_diskstream_buffers (); diff --git a/libs/ardour/ardour/user_bundle.h b/libs/ardour/ardour/user_bundle.h new file mode 100644 index 0000000000..954e93d5d1 --- /dev/null +++ b/libs/ardour/ardour/user_bundle.h @@ -0,0 +1,72 @@ +/* + Copyright (C) 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 __ardour_user_bundle_h__ +#define __ardour_user_bundle_h__ + +#include <vector> +#include <glibmm/thread.h> +#include "pbd/stateful.h" +#include "ardour/bundle.h" + +namespace ARDOUR { + +class Session; + +class UserBundle : public Bundle, public PBD::Stateful { + + public: + UserBundle (std::string const &); + UserBundle (XMLNode const &, bool); + + uint32_t nchannels () const; + const ARDOUR::PortList& channel_ports (uint32_t) const; + + void add_channel (); + void set_channels (uint32_t); + void remove_channel (uint32_t); + void add_port_to_channel (uint32_t, std::string const &); + void remove_port_from_channel (uint32_t, std::string const &); + bool port_attached_to_channel (uint32_t, std::string const &) const; + XMLNode& get_state (); + + /// The number of channels is about to change + sigc::signal<void> ConfigurationWillChange; + /// The number of channels has changed + sigc::signal<void> ConfigurationHasChanged; + /// The port set associated with one of our channels is about to change + /// Parameter is the channel number + sigc::signal<void, int> PortsWillChange; + /// The port set associated with one of our channels has changed + /// Parameter is the channel number + sigc::signal<void, int> PortsHaveChanged; + + private: + + int set_state (const XMLNode &); + + /// mutex for _ports; + /// XXX: is this necessary? + mutable Glib::Mutex _ports_mutex; + std::vector<PortList> _ports; +}; + +} + +#endif diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 4552de1186..2e1b790848 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -763,7 +763,7 @@ AudioEngine::frames_per_cycle () * Note this can return NULL, it will NOT create a port if it is not found (any more). */ Port * -AudioEngine::get_port_by_name (const string& portname, bool keep) +AudioEngine::get_port_by_name (const string& portname, bool keep) const { Glib::Mutex::Lock lm (_process_lock); diff --git a/libs/ardour/auto_bundle.cc b/libs/ardour/auto_bundle.cc new file mode 100644 index 0000000000..9da32bbb7a --- /dev/null +++ b/libs/ardour/auto_bundle.cc @@ -0,0 +1,47 @@ +#include <cassert> +#include "ardour/auto_bundle.h" + +ARDOUR::AutoBundle::AutoBundle (bool i) + : Bundle (i) +{ + +} + +ARDOUR::AutoBundle::AutoBundle (std::string const & n, bool i) + : Bundle (n, i) +{ + +} + +uint32_t +ARDOUR::AutoBundle::nchannels () const +{ + Glib::Mutex::Lock lm (_ports_mutex); + return _ports.size (); +} + +const ARDOUR::PortList& +ARDOUR::AutoBundle::channel_ports (uint32_t c) const +{ + assert (c < nchannels()); + + Glib::Mutex::Lock lm (_ports_mutex); + return _ports[c]; +} + +void +ARDOUR::AutoBundle::set_channels (uint32_t n) +{ + Glib::Mutex::Lock lm (_ports_mutex); + _ports.resize (n); +} + +void +ARDOUR::AutoBundle::set_port (uint32_t c, std::string const & p) +{ + assert (c < nchannels ()); + + Glib::Mutex::Lock lm (_ports_mutex); + _ports[c].resize (1); + _ports[c][0] = p; +} diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 95f40f1f80..d0a233a356 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -34,7 +34,7 @@ #include <ardour/port.h> #include <ardour/audio_port.h> #include <ardour/midi_port.h> -#include <ardour/bundle.h> +#include <ardour/auto_bundle.h> #include <ardour/session.h> #include <ardour/cycle_timer.h> #include <ardour/panner.h> @@ -153,7 +153,7 @@ IO::IO (Session& s, const string& name, _session.add_controllable (_gain_control); - create_bundles (); + create_bundles_for_inputs_and_outputs (); } IO::IO (Session& s, const XMLNode& node, DataType dt) @@ -193,7 +193,7 @@ IO::IO (Session& s, const XMLNode& node, DataType dt) _session.add_controllable (_gain_control); - create_bundles (); + create_bundles_for_inputs_and_outputs (); } IO::~IO () @@ -334,24 +334,62 @@ IO::just_meter_input (nframes_t start_frame, nframes_t end_frame, _meter->run(bufs, start_frame, end_frame, nframes, offset); } + void -IO::drop_input_bundle () +IO::check_bundles_connected_to_inputs () { - _input_bundle.reset (); - 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.reset (); - 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<UserBundleInfo>& list, const PortSet& ports) +{ + std::vector<UserBundleInfo> new_list; + + for (std::vector<UserBundleInfo>::iterator i = list.begin(); i != list.end(); ++i) { + + uint32_t const N = i->bundle->nchannels (); + + if (ports.num_ports() < N) { + continue; + } + + bool ok = true; + 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) { @@ -378,7 +416,7 @@ IO::disconnect_input (Port* our_port, string other_port, void* src) return -1; } - drop_input_bundle (); + check_bundles_connected_to_inputs (); } } @@ -412,8 +450,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 (); } } @@ -448,7 +484,7 @@ IO::disconnect_output (Port* our_port, string other_port, void* src) return -1; } - drop_output_bundle (); + check_bundles_connected_to_outputs (); } } @@ -482,8 +518,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 (); } } @@ -544,7 +578,7 @@ 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 (); @@ -555,7 +589,7 @@ IO::remove_output_port (Port* port, void* src) } if (change == ConnectionsChanged) { - setup_bundles (); + setup_bundles_for_inputs_and_outputs (); } if (change != NoChange) { @@ -608,7 +642,6 @@ IO::add_output_port (string destination, void* src, DataType type) } _outputs.add (our_port); - drop_output_bundle (); setup_peak_meters (); reset_panner (); } @@ -624,7 +657,7 @@ IO::add_output_port (string destination, void* src, DataType type) // pan_changed (src); /* EMIT SIGNAL */ output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ - setup_bundles (); + setup_bundles_for_inputs_and_outputs (); _session.set_dirty (); return 0; @@ -655,7 +688,7 @@ 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 (); @@ -666,7 +699,7 @@ IO::remove_input_port (Port* port, void* src) } if (change == ConfigurationChanged) { - setup_bundles (); + setup_bundles_for_inputs_and_outputs (); } if (change != NoChange) { @@ -719,7 +752,6 @@ IO::add_input_port (string source, void* src, DataType type) } _inputs.add (our_port); - drop_input_bundle (); setup_peak_meters (); reset_panner (); } @@ -736,7 +768,7 @@ IO::add_input_port (string source, void* src, DataType type) // pan_changed (src); /* EMIT SIGNAL */ input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ - setup_bundles (); + setup_bundles_for_inputs_and_outputs (); _session.set_dirty (); return 0; @@ -755,7 +787,7 @@ IO::disconnect_inputs (void* src) _session.engine().disconnect (*i); } - drop_input_bundle (); + check_bundles_connected_to_inputs (); } } @@ -777,7 +809,7 @@ IO::disconnect_outputs (void* src) _session.engine().disconnect (*i); } - drop_output_bundle (); + check_bundles_connected_to_outputs (); } } @@ -841,7 +873,7 @@ 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 (); PortCountChanged (n_inputs()); /* EMIT SIGNAL */ @@ -1008,18 +1040,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) { PortCountChanged (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */ - setup_bundles (); + setup_bundles_for_inputs_and_outputs (); _session.set_dirty (); } @@ -1047,7 +1079,7 @@ IO::ensure_inputs (ChanCount count, bool clear, bool lockit, void* src) if (changed) { input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ - setup_bundles (); + setup_bundles_for_inputs_and_outputs (); _session.set_dirty (); } return 0; @@ -1106,7 +1138,7 @@ IO::ensure_outputs_locked (ChanCount count, bool clear, void* src) } if (changed) { - drop_output_bundle (); + check_bundles_connected_to_outputs (); PortCountChanged (n_outputs()); /* EMIT SIGNAL */ _session.set_dirty (); } @@ -1145,7 +1177,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src) if (changed) { output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */ - setup_bundles (); + setup_bundles_for_inputs_and_outputs (); } return 0; @@ -1209,8 +1241,6 @@ 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; LocaleGuard lg (X_("POSIX")); Glib::Mutex::Lock lm (io_lock); @@ -1218,83 +1248,91 @@ IO::state (bool full_state) id().print (buf, sizeof (buf)); node->add_property("id", buf); - str = ""; - - if (_input_bundle && !_input_bundle->dynamic()) { - node->add_property ("input-connection", _input_bundle->name()); - need_ins = false; + for ( + std::vector<UserBundleInfo>::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 && !_output_bundle->dynamic()) { - node->add_property ("output-connection", _output_bundle->name()); - need_outs = false; + for ( + std::vector<UserBundleInfo>::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(); + const char **connections = i->get_connections(); + + if (connections && connections[0]) { + str += '{'; - if (connections && connections[0]) { - str += '{'; + for (int n = 0; connections && connections[n]; ++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 (connections[n]); + } + + str += '}'; + + free (connections); + } + 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) { + const char **connections = i->get_connections(); + + if (connections && connections[0]) { - 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 (int n = 0; connections[n]; ++n) { + if (n) { + str += ','; } - - str += '}'; - free (connections); - } - else { - str += "{}"; + str += _session.engine().make_port_name_relative (connections[n]); } + + str += '}'; + + free (connections); + } + 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 ()); @@ -1575,55 +1613,12 @@ IO::ports_became_legal () int IO::create_ports (const XMLNode& node) { - const XMLProperty* prop; + XMLProperty const * prop; int num_inputs = 0; int num_outputs = 0; - /* 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) { - - boost::shared_ptr<Bundle> c = _session.bundle_by_name (prop->value()); - - 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; - } - } - - num_inputs = c->nchannels(); - - } else if ((prop = node.property ("inputs")) != 0) { - + if ((prop = node.property ("inputs")) != 0) { num_inputs = count (prop->value().begin(), prop->value().end(), '{'); - } - - if ((prop = node.property ("output-connection")) != 0) { - boost::shared_ptr<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; - } - } - - num_outputs = c->nchannels (); - } else if ((prop = node.property ("outputs")) != 0) { num_outputs = count (prop->value().begin(), prop->value().end(), '{'); } @@ -1648,57 +1643,48 @@ IO::create_ports (const XMLNode& node) int IO::make_connections (const XMLNode& node) { - const XMLProperty* prop; - - if ((prop = node.property ("input-connection")) != 0) { - boost::shared_ptr<Bundle> c = _session.bundle_by_name (prop->value()); - - 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; - } - } - - connect_input_ports_to_bundle (c, this); - - } else if ((prop = node.property ("inputs")) != 0) { + XMLProperty const * prop; + + if ((prop = node.property ("inputs")) != 0) { if (set_inputs (prop->value())) { error << string_compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg; return -1; } } - - if ((prop = node.property ("output-bundle")) != 0) { - boost::shared_ptr<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; - } - } - - connect_output_ports_to_bundle (c, this); - - } else if ((prop = node.property ("outputs")) != 0) { + + if ((prop = node.property ("outputs")) != 0) { if (set_outputs (prop->value())) { error << string_compose(_("improper output channel list in XML node (%1)"), prop->value()) << endmsg; 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<Bundle> b = _session.bundle_by_name (prop->value()); + if (b) { + connect_input_ports_to_bundle (b, this); + } else { + error << string_compose(_("Unknown bundle \"%1\" listed for input of %2"), prop->value(), _name) << endmsg; + } + } + + } else if ((*i)->name() == "OutputBundle") { + XMLProperty const * prop = (*i)->property ("name"); + if (prop) { + boost::shared_ptr<Bundle> b = _session.bundle_by_name (prop->value()); + if (b) { + connect_output_ports_to_bundle (b, this); + } else { + error << string_compose(_("Unknown bundle \"%1\" listed for output of %2"), prop->value(), _name) << endmsg; + } + } + } + } return 0; } @@ -1880,7 +1866,7 @@ IO::set_name (const string& str) bool const r = SessionObject::set_name(name); - setup_bundles (); + setup_bundles_for_inputs_and_outputs (); return r; } @@ -1960,61 +1946,21 @@ IO::input_latency () const int IO::connect_input_ports_to_bundle (boost::shared_ptr<Bundle> 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 */ + /* Connect to the bundle, not worrying about any connections + that are already made. */ + + uint32_t const channels = c->nchannels (); - 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))) { + for (uint32_t n = 0; n < channels; ++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; @@ -2023,13 +1969,23 @@ IO::connect_input_ports_to_bundle (boost::shared_ptr<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<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c); + if (ub) { + + /* See if we already know about this one */ + std::vector<UserBundleInfo>::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 */ @@ -2039,62 +1995,22 @@ IO::connect_input_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src) int IO::connect_output_ports_to_bundle (boost::shared_ptr<Bundle> 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)); + /* Connect to the bundle, not worrying about any connections + that are already made. */ - } else if (_outputs.port(n)->connected() > 1) { + uint32_t const channels = c->nchannels (); - /* OK, it is connected to the port we want, - but its also connected to other ports. - Change that situation. - */ + for (uint32_t n = 0; n < channels; ++n) { - /* XXX could be optimized to not drop - the one we want. - */ - - _session.engine().disconnect (*_outputs.port(n)); - } - } - } + const PortList& pl = c->channel_ports (n); - /* second pass: connect all requested ports where necessary */ + for (PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) { - 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))) { + if (!_outputs.port(n)->connected_to (*i)) { if (_session.engine().connect (_outputs.port(n)->name(), *i)) { return -1; @@ -2103,12 +2019,22 @@ IO::connect_output_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src) } } - _output_bundle = c; + /* If this is a UserBundle, make a note of what we've done */ - 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)); + boost::shared_ptr<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c); + if (ub) { + + /* See if we already know about this one */ + std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_outputs.begin(); + while (i != _bundles_connected_to_outputs.end() && i->bundle != ub) { + ++i; + } + + 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 */ @@ -2159,27 +2085,31 @@ IO::reset_panners () } void -IO::input_bundle_connection_changed (int ignored) +IO::bundle_configuration_will_change () { - connect_input_ports_to_bundle (_input_bundle, this); + //XXX +// connect_input_ports_to_bundle (_input_bundle, this); } void -IO::input_bundle_configuration_changed () +IO::bundle_configuration_has_changed () { - connect_input_ports_to_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) { - connect_output_ports_to_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) { - connect_output_ports_to_bundle (_output_bundle, this); + //XXX +// connect_output_ports_to_bundle (_output_bundle, this); } void @@ -2483,27 +2413,25 @@ IO::update_port_total_latencies () */ void -IO::setup_bundles () +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, 0); - int const ins = n_inputs().n_total(); - _bundle_for_inputs->set_nchannels (ins); - - for (int i = 0; i < ins; ++i) { - _bundle_for_inputs->add_port_to_channel (i, _inputs.port(i)->name ()); - } + _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, 0); - int const outs = n_outputs().n_total(); - _bundle_for_outputs->set_nchannels (outs); - - for (int i = 0; i < outs; ++i) { - _bundle_for_outputs->add_port_to_channel (i, _outputs.port(i)->name ()); - } + _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()); + } } @@ -2512,64 +2440,131 @@ IO::setup_bundles () */ void -IO::create_bundles () +IO::create_bundles_for_inputs_and_outputs () { - _bundle_for_inputs = boost::shared_ptr<Bundle> ( - new InputBundle ("", true) - ); - - _bundle_for_outputs = boost::shared_ptr<Bundle> ( - new OutputBundle ("", true) - ); - - setup_bundles (); + _bundle_for_inputs = boost::shared_ptr<AutoBundle> (new AutoBundle (true)); + _bundle_for_outputs = boost::shared_ptr<AutoBundle> (new AutoBundle (false)); + setup_bundles_for_inputs_and_outputs (); } -boost::shared_ptr<Bundle> -IO::input_bundle() +/** 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<Bundle> b, std::vector<boost::shared_ptr<Bundle> >* bundles) { - if (_input_bundle) { - return _input_bundle; + boost::shared_ptr<AutoBundle> ab = boost::dynamic_pointer_cast<AutoBundle, Bundle> (b); + if (ab == 0 || ab->ports_are_outputs() == false) { + return; + } + + if (ab->nchannels () != n_inputs().n_total ()) { + return; } - /* XXX: will only report the first bundle found; should really return a list, I think */ - - /* check that _input_bundle is right wrt the connections that are currently made */ + for (uint32_t i = 0; i < n_inputs().n_total (); ++i) { - /* make a vector of the first output connected to each of our inputs */ - std::vector<std::string> connected; - for (uint32_t i = 0; i < _inputs.num_ports(); ++i) { - const char** c = _inputs.port(i)->get_connections (); - if (c) { - connected.push_back (c[0]); + PortList const & pl = b->channel_ports (i); + + if (pl.empty()) { + return; + } + + if (!input(i)->connected_to (pl[0])) { + return; } } - _input_bundle = _session.bundle_by_ports (connected); - return _input_bundle; + bundles->push_back (b); +} + +/** @return Bundles connected to our inputs */ +std::vector<boost::shared_ptr<Bundle> > +IO::bundles_connected_to_inputs () +{ + std::vector<boost::shared_ptr<Bundle> > bundles; + + /* User bundles */ + for (std::vector<UserBundleInfo>::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; } -boost::shared_ptr<Bundle> -IO::output_bundle() +/** 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<Bundle> b, std::vector<boost::shared_ptr<Bundle> >* bundles) { - if (_output_bundle) { - return _output_bundle; + boost::shared_ptr<AutoBundle> ab = boost::dynamic_pointer_cast<AutoBundle, Bundle> (b); + if (ab == 0 || ab->ports_are_inputs() == false) { + return; + } + + if (ab->nchannels () != n_outputs().n_total ()) { + return; } - - /* XXX: will only report the first bundle found; should really return a list, I think */ - - /* check that _output_bundle is right wrt the connections that are currently made */ - /* make a vector of the first input connected to each of our outputs */ - std::vector<std::string> connected; - for (uint32_t i = 0; i < _outputs.num_ports(); ++i) { - const char** c = _outputs.port(i)->get_connections (); - if (c) { - connected.push_back (c[0]); + 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; } } - _output_bundle = _session.bundle_by_ports (connected); - return _output_bundle; + bundles->push_back (b); +} + + +/* @return Bundles connected to our outputs */ +std::vector<boost::shared_ptr<Bundle> > +IO::bundles_connected_to_outputs () +{ + std::vector<boost::shared_ptr<Bundle> > bundles; + + /* User bundles */ + for (std::vector<UserBundleInfo>::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<UserBundle> 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) + ); } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 1382fa3f0a..e155800d23 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -62,7 +62,7 @@ #include <ardour/processor.h> #include <ardour/plugin_insert.h> #include <ardour/port_insert.h> -#include <ardour/bundle.h> +#include <ardour/auto_bundle.h> #include <ardour/slave.h> #include <ardour/tempo.h> #include <ardour/audio_track.h> @@ -123,6 +123,7 @@ Session::Session (AudioEngine &eng, diskstreams (new DiskstreamList), routes (new RouteList), auditioner ((Auditioner*) 0), + _bundle_xml_node (0), _click_io ((IO*) 0), main_outs (0) { @@ -223,6 +224,7 @@ Session::Session (AudioEngine &eng, _send_smpte_update (false), diskstreams (new DiskstreamList), routes (new RouteList), + _bundle_xml_node (0), main_outs (0) { @@ -599,22 +601,22 @@ Session::when_engine_running () char buf[32]; snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1); - shared_ptr<Bundle> c (new InputBundle (buf, true)); - c->set_nchannels (1); - c->add_port_to_channel (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); + shared_ptr<AutoBundle> c (new AutoBundle (buf, true)); + c->set_channels (1); + c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); - add_bundle (c); + add_bundle (c); } for (uint32_t np = 0; np < n_physical_inputs; ++np) { char buf[32]; snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1); - shared_ptr<Bundle> c (new OutputBundle (buf, true)); - c->set_nchannels (1); - c->add_port_to_channel (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); + shared_ptr<AutoBundle> c (new AutoBundle (buf, false)); + c->set_channels (1); + c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); - add_bundle (c); + add_bundle (c); } /* TWO: STEREO */ @@ -623,24 +625,24 @@ Session::when_engine_running () char buf[32]; snprintf (buf, sizeof (buf), _("out %" PRIu32 "+%" PRIu32), np+1, np+2); - shared_ptr<Bundle> c (new InputBundle (buf, true)); - c->set_nchannels (2); - c->add_port_to_channel (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); - c->add_port_to_channel (1, _engine.get_nth_physical_output (DataType::AUDIO, np+1)); + shared_ptr<AutoBundle> c (new AutoBundle (buf, true)); + c->set_channels (2); + c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np)); + c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1)); - add_bundle (c); + add_bundle (c); } for (uint32_t np = 0; np < n_physical_inputs; np +=2) { char buf[32]; snprintf (buf, sizeof (buf), _("in %" PRIu32 "+%" PRIu32), np+1, np+2); - shared_ptr<Bundle> c (new OutputBundle (buf, true)); - c->set_nchannels (2); - c->add_port_to_channel (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); - c->add_port_to_channel (1, _engine.get_nth_physical_input (DataType::AUDIO, np+1)); + shared_ptr<AutoBundle> c (new AutoBundle (buf, false)); + c->set_channels (2); + c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np)); + c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1)); - add_bundle (c); + add_bundle (c); } /* THREE MASTER */ @@ -685,13 +687,13 @@ Session::when_engine_running () } - shared_ptr<Bundle> c (new OutputBundle (_("Master Out"), true)); + shared_ptr<AutoBundle> c (new AutoBundle (_("Master Out"), true)); - c->set_nchannels (_master_out->n_inputs().n_total()); - for (uint32_t n = 0; n < _master_out->n_inputs ().n_total(); ++n) { - c->add_port_to_channel ((int) n, _master_out->input(n)->name()); - } - add_bundle (c); + c->set_channels (_master_out->n_inputs().n_total()); + for (uint32_t n = 0; n < _master_out->n_inputs ().n_total(); ++n) { + c->set_port (n, _master_out->input(n)->name()); + } + add_bundle (c); } hookup_io (); @@ -802,7 +804,13 @@ Session::hookup_io () for (RouteList::iterator x = r->begin(); x != r->end(); ++x) { (*x)->set_control_outs (cports); } - } + } + + /* load bundles, which we may have postponed earlier on */ + if (_bundle_xml_node) { + load_bundles (*_bundle_xml_node); + delete _bundle_xml_node; + } /* Tell all IO objects to connect themselves together */ @@ -3739,35 +3747,6 @@ Session::bundle_by_name (string name) const return boost::shared_ptr<Bundle> (); } -boost::shared_ptr<Bundle> -Session::bundle_by_ports (std::vector<std::string> const & wanted_ports) const -{ - Glib::Mutex::Lock lm (bundle_lock); - - for (BundleList::const_iterator i = _bundles.begin(); i != _bundles.end(); ++i) { - if ((*i)->nchannels() != wanted_ports.size()) { - continue; - } - - bool match = true; - for (uint32_t j = 0; j < (*i)->nchannels(); ++j) { - Bundle::PortList const p = (*i)->channel_ports (j); - if (p.empty() || p[0] != wanted_ports[j]) { - /* not this bundle */ - match = false; - break; - } - } - - if (match) { - /* matched bundle */ - return *i; - } - } - - return boost::shared_ptr<Bundle> (); -} - void Session::tempo_map_changed (Change ignored) { diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 0534da6c89..c711b227e7 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -953,12 +953,13 @@ Session::state(bool full_state) node->add_child_nocopy (loc.get_state()); } - child = node->add_child ("Connections"); + child = node->add_child ("Bundles"); { Glib::Mutex::Lock lm (bundle_lock); for (BundleList::iterator i = _bundles.begin(); i != _bundles.end(); ++i) { - if (!(*i)->dynamic()) { - child->add_child_nocopy ((*i)->get_state()); + boost::shared_ptr<UserBundle> b = boost::dynamic_pointer_cast<UserBundle> (*i); + if (b) { + child->add_child_nocopy (b->get_state()); } } } @@ -1197,13 +1198,16 @@ Session::set_state (const XMLNode& node) goto out; } - if ((child = find_named_node (node, "Connections")) == 0) { - error << _("Session: XML state has no connections section") << endmsg; - goto out; - } else if (load_bundles (*child)) { + if ((child = find_named_node (node, "Bundles")) == 0) { + error << _("Session: XML state has no bundles section") << endmsg; goto out; + } else { + /* We can't load Bundles yet as they need to be able + to convert from port names to Port objects, which can't happen until + later */ + _bundle_xml_node = new XMLNode (*child); } - + if ((child = find_named_node (node, "EditGroups")) == 0) { error << _("Session: XML state has no edit groups section") << endmsg; goto out; @@ -1237,7 +1241,7 @@ Session::set_state (const XMLNode& node) } else if (_click_io) { _click_io->set_state (*child); } - + if ((child = find_named_node (node, "ControlProtocols")) != 0) { ControlProtocolManager::instance().set_protocol_states (*child); } @@ -1876,23 +1880,23 @@ Session::automation_dir () const } int -Session::load_bundles (const XMLNode& node) -{ - XMLNodeList nlist = node.children(); - XMLNodeConstIterator niter; - - set_dirty(); - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((*niter)->name() == "InputConnection") { - add_bundle (boost::shared_ptr<Bundle> (new InputBundle (**niter))); - } else if ((*niter)->name() == "OutputConnection") { - add_bundle (boost::shared_ptr<Bundle> (new OutputBundle (**niter))); - } else { - error << string_compose(_("Unknown node \"%1\" found in Connections list from state file"), (*niter)->name()) << endmsg; - return -1; - } - } +Session::load_bundles (XMLNode const & node) +{ + XMLNodeList nlist = node.children(); + XMLNodeConstIterator niter; + + set_dirty(); + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + if ((*niter)->name() == "InputBundle") { + add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, true))); + } else if ((*niter)->name() == "OutputBundle") { + add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, false))); + } else { + error << string_compose(_("Unknown node \"%1\" found in Bundles list from state file"), (*niter)->name()) << endmsg; + return -1; + } + } return 0; } diff --git a/libs/ardour/user_bundle.cc b/libs/ardour/user_bundle.cc new file mode 100644 index 0000000000..471d823496 --- /dev/null +++ b/libs/ardour/user_bundle.cc @@ -0,0 +1,198 @@ +#include <cassert> +#include <pbd/failed_constructor.h> +#include <pbd/compose.h> +#include <pbd/xml++.h> +#include "ardour/user_bundle.h" +#include "ardour/port_set.h" +#include "ardour/io.h" +#include "ardour/session.h" +#include "ardour/audioengine.h" +#include "i18n.h" + +ARDOUR::UserBundle::UserBundle (std::string const & n) + : Bundle (n) +{ + +} + +ARDOUR::UserBundle::UserBundle (XMLNode const & x, bool i) + : Bundle (i) +{ + if (set_state (x)) { + throw failed_constructor (); + } +} + +uint32_t +ARDOUR::UserBundle::nchannels () const +{ + Glib::Mutex::Lock lm (_ports_mutex); + return _ports.size (); +} + +const ARDOUR::PortList& +ARDOUR::UserBundle::channel_ports (uint32_t n) const +{ + assert (n < nchannels ()); + + Glib::Mutex::Lock lm (_ports_mutex); + return _ports[n]; +} + +void +ARDOUR::UserBundle::add_port_to_channel (uint32_t c, std::string const & p) +{ + assert (c < nchannels ()); + + PortsWillChange (c); + + { + Glib::Mutex::Lock lm (_ports_mutex); + _ports[c].push_back (p); + } + + PortsHaveChanged (c); +} + +void +ARDOUR::UserBundle::remove_port_from_channel (uint32_t c, std::string const & p) +{ + assert (c < nchannels ()); + + PortsWillChange (c); + + { + Glib::Mutex::Lock lm (_ports_mutex); + PortList::iterator i = std::find (_ports[c].begin(), _ports[c].end(), p); + if (i != _ports[c].end()) { + _ports[c].erase (i); + } + } + + PortsHaveChanged (c); +} + +bool +ARDOUR::UserBundle::port_attached_to_channel (uint32_t c, std::string const & p) const +{ + assert (c < nchannels ()); + + Glib::Mutex::Lock lm (_ports_mutex); + return std::find (_ports[c].begin(), _ports[c].end(), p) != _ports[c].end(); +} + +void +ARDOUR::UserBundle::add_channel () +{ + ConfigurationWillChange (); + + { + Glib::Mutex::Lock lm (_ports_mutex); + _ports.resize (_ports.size() + 1); + } + + ConfigurationHasChanged (); +} + +void +ARDOUR::UserBundle::set_channels (uint32_t n) +{ + ConfigurationWillChange (); + + { + Glib::Mutex::Lock lm (_ports_mutex); + _ports.resize (n); + } + + ConfigurationHasChanged (); +} + +void +ARDOUR::UserBundle::remove_channel (uint32_t r) +{ + assert (r < nchannels ()); + + ConfigurationWillChange (); + + { + Glib::Mutex::Lock lm (_ports_mutex); + _ports.erase (_ports.begin() + r, _ports.begin() + r + 1); + } + + ConfigurationHasChanged (); +} + +int +ARDOUR::UserBundle::set_state (XMLNode const & node) +{ + XMLProperty const * name; + + if ((name = node.property ("name")) == 0) { + PBD::error << _("Node for Bundle has no \"name\" property") << endmsg; + return -1; + } + + set_name (name->value ()); + + XMLNodeList const channels = node.children (); + + int n = 0; + for (XMLNodeConstIterator i = channels.begin(); i != channels.end(); ++i) { + + if ((*i)->name() != "Channel") { + PBD::error << string_compose (_("Unknown node \"%s\" in Bundle"), (*i)->name()) << endmsg; + return -1; + } + + add_channel (); + + XMLNodeList const ports = (*i)->children (); + + for (XMLNodeConstIterator j = ports.begin(); j != ports.end(); ++j) { + if ((*j)->name() != "Port") { + PBD::error << string_compose (_("Unknown node \"%s\" in Bundle"), (*j)->name()) << endmsg; + return -1; + } + + if ((name = (*j)->property ("name")) == 0) { + PBD::error << _("Node for Port has no \"name\" property") << endmsg; + return -1; + } + + add_port_to_channel (n, name->value ()); + } + + ++n; + } + + return 0; +} + +XMLNode& +ARDOUR::UserBundle::get_state () +{ + XMLNode *node; + + if (ports_are_inputs ()) { + node = new XMLNode ("InputBundle"); + } else { + node = new XMLNode ("OutputBundle"); + } + + node->add_property ("name", name ()); + + for (std::vector<PortList>::iterator i = _ports.begin(); i != _ports.end(); ++i) { + + XMLNode* c = new XMLNode ("Channel"); + + for (PortList::iterator j = i->begin(); j != i->end(); ++j) { + XMLNode* p = new XMLNode ("Port"); + p->add_property ("name", *j); + c->add_child_nocopy (*p); + } + + node->add_child_nocopy (*c); + } + + return *node; +} |