/*
- Copyright (C) 2002 Paul Davis
+ Copyright (C) 2002 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
#include <algorithm>
-#include <pbd/failed_constructor.h>
-#include <ardour/ardour.h>
-#include <ardour/bundle.h>
-#include <pbd/xml++.h>
+#include "pbd/failed_constructor.h"
+#include "ardour/ardour.h"
+#include "ardour/bundle.h"
+#include "ardour/audioengine.h"
+#include "ardour/port.h"
+#include "pbd/xml++.h"
#include "i18n.h"
+using namespace std;
using namespace ARDOUR;
using namespace PBD;
-/** Construct a Bundle from an XML node.
- * @param node XML node.
+/** Construct an audio bundle.
+ * @param i true if ports are inputs, otherwise false.
*/
-Bundle::Bundle (const XMLNode& node)
+Bundle::Bundle (bool i)
+ : _ports_are_inputs (i),
+ _signals_suspended (false),
+ _pending_change (Change (0))
{
- if (set_state (node)) {
- throw failed_constructor();
- }
+
}
-/** Construct an InputBundle from an XML node.
- * @param node XML node.
+
+/** Construct an audio bundle.
+ * @param n Name.
+ * @param i true if ports are inputs, otherwise false.
*/
-InputBundle::InputBundle (const XMLNode& node)
- : Bundle (node)
+Bundle::Bundle (std::string const & n, bool i)
+ : _name (n),
+ _ports_are_inputs (i),
+ _signals_suspended (false),
+ _pending_change (Change (0))
{
-
+
}
-/** Construct an OutputBundle from an XML node.
- * @param node XML node.
- */
-OutputBundle::OutputBundle (const XMLNode& node)
- : Bundle (node)
+Bundle::Bundle (boost::shared_ptr<Bundle> other)
+ : _channel (other->_channel),
+ _name (other->_name),
+ _ports_are_inputs (other->_ports_are_inputs),
+ _signals_suspended (other->_signals_suspended),
+ _pending_change (other->_pending_change)
{
-
+
}
-/** Set the name.
- * @param name New name.
- */
-void
-Bundle::set_name (string name, void *src)
+ChanCount
+Bundle::nchannels () const
{
- _name = name;
- NameChanged (src);
+ Glib::Mutex::Lock lm (_channel_mutex);
+
+ ChanCount c;
+ for (vector<Channel>::const_iterator i = _channel.begin(); i != _channel.end(); ++i) {
+ c.set (i->type, c.get (i->type) + 1);
+ }
+
+ return c;
}
-/** Add an association between one of our channels and a JACK port.
- * @param ch Channel index.
- * @param portname JACK port name to associate with.
+Bundle::PortList const &
+Bundle::channel_ports (uint32_t c) const
+{
+ assert (c < nchannels().n_total());
+
+ Glib::Mutex::Lock lm (_channel_mutex);
+ return _channel[c].ports;
+}
+
+/** Add an association between one of our channels and a port.
+ * @param ch Channel index.
+ * @param portname full port name to associate with (including prefix).
*/
void
-Bundle::add_port_to_channel (int ch, string portname)
+Bundle::add_port_to_channel (uint32_t ch, string portname)
{
+ assert (ch < nchannels().n_total());
+ assert (portname.find_first_of (':') != string::npos);
+
{
- Glib::Mutex::Lock lm (channels_lock);
- _channels[ch].push_back (portname);
+ Glib::Mutex::Lock lm (_channel_mutex);
+ _channel[ch].ports.push_back (portname);
}
-
- PortsChanged (ch); /* EMIT SIGNAL */
+
+ emit_changed (PortsChanged);
}
-/** Disassociate a JACK port from one of our channels.
- * @param ch Channel index.
- * @param portname JACK port name to disassociate from.
+/** Disassociate a port from one of our channels.
+ * @param ch Channel index.
+ * @param portname port name to disassociate from.
*/
-
void
-Bundle::remove_port_from_channel (int ch, string portname)
+Bundle::remove_port_from_channel (uint32_t ch, string portname)
{
+ assert (ch < nchannels().n_total());
+
bool changed = false;
{
- Glib::Mutex::Lock lm (channels_lock);
- PortList& pl = _channels[ch];
+ Glib::Mutex::Lock lm (_channel_mutex);
+ PortList& pl = _channel[ch].ports;
PortList::iterator i = find (pl.begin(), pl.end(), portname);
-
+
if (i != pl.end()) {
pl.erase (i);
changed = true;
}
if (changed) {
- PortsChanged (ch); /* EMIT SIGNAL */
+ emit_changed (PortsChanged);
}
}
-/**
- * @param ch Channel index.
- * @return List of JACK ports that this channel is connected to.
+/** Set a single port to be associated with a channel, removing any others.
+ * @param ch Channel.
+ * @param portname Full port name, including prefix.
*/
-const Bundle::PortList&
-Bundle::channel_ports (int ch) const
+void
+Bundle::set_port (uint32_t ch, string portname)
{
- Glib::Mutex::Lock lm (channels_lock);
- return _channels[ch];
+ assert (ch < nchannels().n_total());
+ assert (portname.find_first_of (':') != string::npos);
+
+ {
+ Glib::Mutex::Lock lm (_channel_mutex);
+ _channel[ch].ports.clear ();
+ _channel[ch].ports.push_back (portname);
+ }
+
+ emit_changed (PortsChanged);
}
-/** operator== for Bundles; they are equal if their channels are the same.
- * @param other Bundle to compare with this one.
+/** @param n Channel name */
+void
+Bundle::add_channel (std::string const & n, DataType t)
+{
+ {
+ Glib::Mutex::Lock lm (_channel_mutex);
+ _channel.push_back (Channel (n, t));
+ }
+
+ emit_changed (ConfigurationChanged);
+}
+
+/** @param n Channel name */
+void
+Bundle::add_channel (std::string const & n, DataType t, PortList p)
+{
+ {
+ Glib::Mutex::Lock lm (_channel_mutex);
+ _channel.push_back (Channel (n, t, p));
+ }
+
+ emit_changed (ConfigurationChanged);
+}
+
+/** @param n Channel name */
+void
+Bundle::add_channel (std::string const & n, DataType t, std::string const & p)
+{
+ {
+ Glib::Mutex::Lock lm (_channel_mutex);
+ _channel.push_back (Channel (n, t, p));
+ }
+
+ emit_changed (ConfigurationChanged);
+}
+
+bool
+Bundle::port_attached_to_channel (uint32_t ch, std::string portname)
+{
+ assert (ch < nchannels().n_total());
+
+ Glib::Mutex::Lock lm (_channel_mutex);
+ return (std::find (_channel[ch].ports.begin (), _channel[ch].ports.end (), portname) != _channel[ch].ports.end ());
+}
+
+/** Remove a channel.
+ * @param ch Channel.
+ */
+void
+Bundle::remove_channel (uint32_t ch)
+{
+ assert (ch < nchannels().n_total());
+
+ Glib::Mutex::Lock lm (_channel_mutex);
+ _channel.erase (_channel.begin () + ch);
+}
+
+/** Remove all channels */
+void
+Bundle::remove_channels ()
+{
+ Glib::Mutex::Lock lm (_channel_mutex);
+
+ _channel.clear ();
+}
+
+/** @param p Port name.
+ * @return true if any channel is associated with p.
+ */
+bool
+Bundle::offers_port (std::string p) const
+{
+ Glib::Mutex::Lock lm (_channel_mutex);
+
+ for (std::vector<Channel>::const_iterator i = _channel.begin(); i != _channel.end(); ++i) {
+ for (PortList::const_iterator j = i->ports.begin(); j != i->ports.end(); ++j) {
+ if (*j == p) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/** @param p Port name.
+ * @return true if this bundle offers this port on its own on a channel.
*/
bool
-Bundle::operator== (const Bundle& other) const
+Bundle::offers_port_alone (std::string p) const
{
- return other._channels == _channels;
+ Glib::Mutex::Lock lm (_channel_mutex);
+
+ for (std::vector<Channel>::const_iterator i = _channel.begin(); i != _channel.end(); ++i) {
+ if (i->ports.size() == 1 && i->ports[0] == p) {
+ return true;
+ }
+ }
+
+ return false;
}
-/** Set the number of channels.
- * @param n New number of channels.
+/** @param ch Channel.
+ * @return Channel name.
*/
+std::string
+Bundle::channel_name (uint32_t ch) const
+{
+ assert (ch < nchannels().n_total());
+
+ Glib::Mutex::Lock lm (_channel_mutex);
+ return _channel[ch].name;
+}
+/** Set the name of a channel.
+ * @param ch Channel.
+ * @param n New name.
+ */
void
-Bundle::set_nchannels (int n)
+Bundle::set_channel_name (uint32_t ch, std::string const & n)
{
+ assert (ch < nchannels().n_total());
+
{
- Glib::Mutex::Lock lm (channels_lock);
- _channels.clear ();
- for (int i = 0; i < n; ++i) {
- _channels.push_back (PortList());
- }
+ Glib::Mutex::Lock lm (_channel_mutex);
+ _channel[ch].name = n;
}
- ConfigurationChanged (); /* EMIT SIGNAL */
+ emit_changed (NameChanged);
}
-XMLNode&
-Bundle::get_state ()
+/** Take the channels from another bundle and add them to this bundle,
+ * so that channels from other are added to this (with their ports)
+ * and are named "<other_bundle_name> <other_channel_name>".
+ */
+void
+Bundle::add_channels_from_bundle (boost::shared_ptr<Bundle> other)
{
- XMLNode *node;
- string str;
+ uint32_t const ch = nchannels().n_total();
- if (dynamic_cast<InputBundle *> (this)) {
- node = new XMLNode ("InputConnection");
- } else {
- node = new XMLNode ("OutputConnection");
- }
+ for (uint32_t i = 0; i < other->nchannels().n_total(); ++i) {
+
+ std::stringstream s;
+ s << other->name() << " " << other->channel_name(i);
- node->add_property ("name", _name);
+ add_channel (s.str(), other->channel_type(i));
- for (vector<PortList>::iterator i = _channels.begin(); i != _channels.end(); ++i) {
+ PortList const& pl = other->channel_ports (i);
+ for (uint32_t j = 0; j < pl.size(); ++j) {
+ add_port_to_channel (ch + i, pl[j]);
+ }
+ }
+}
+
+/** Connect the ports associated with our channels to the ports associated
+ * with another bundle's channels.
+ * @param other Other bundle.
+ * @param engine AudioEngine to use to make the connections.
+ */
+void
+Bundle::connect (boost::shared_ptr<Bundle> other, AudioEngine & engine)
+{
+ uint32_t const N = nchannels().n_total();
+ assert (N == other->nchannels().n_total());
- str += '{';
+ for (uint32_t i = 0; i < N; ++i) {
+ Bundle::PortList const & our_ports = channel_ports (i);
+ Bundle::PortList const & other_ports = other->channel_ports (i);
- for (vector<string>::iterator ii = (*i).begin(); ii != (*i).end(); ++ii) {
- if (ii != (*i).begin()) {
- str += ',';
+ for (Bundle::PortList::const_iterator j = our_ports.begin(); j != our_ports.end(); ++j) {
+ for (Bundle::PortList::const_iterator k = other_ports.begin(); k != other_ports.end(); ++k) {
+ engine.connect (*j, *k);
}
- str += *ii;
}
- str += '}';
}
+}
+
+void
+Bundle::disconnect (boost::shared_ptr<Bundle> other, AudioEngine & engine)
+{
+ uint32_t const N = nchannels().n_total();
+ assert (N == other->nchannels().n_total());
- node->add_property ("connections", str);
+ for (uint32_t i = 0; i < N; ++i) {
+ Bundle::PortList const & our_ports = channel_ports (i);
+ Bundle::PortList const & other_ports = other->channel_ports (i);
- return *node;
+ for (Bundle::PortList::const_iterator j = our_ports.begin(); j != our_ports.end(); ++j) {
+ for (Bundle::PortList::const_iterator k = other_ports.begin(); k != other_ports.end(); ++k) {
+ engine.disconnect (*j, *k);
+ }
+ }
+ }
}
-int
-Bundle::set_state (const XMLNode& node)
+/** Remove all ports from all channels */
+void
+Bundle::remove_ports_from_channels ()
{
- const XMLProperty *prop;
+ {
+ Glib::Mutex::Lock lm (_channel_mutex);
+ for (uint32_t c = 0; c < _channel.size(); ++c) {
+ _channel[c].ports.clear ();
+ }
- if ((prop = node.property ("name")) == 0) {
- error << _("Node for Connection has no \"name\" property") << endmsg;
- return -1;
}
- _name = prop->value();
- _dynamic = false;
-
- if ((prop = node.property ("connections")) == 0) {
- error << _("Node for Connection has no \"connections\" property") << endmsg;
- return -1;
+ emit_changed (PortsChanged);
+}
+
+/** Remove all ports from a given channel.
+ * @param ch Channel.
+ */
+void
+Bundle::remove_ports_from_channel (uint32_t ch)
+{
+ assert (ch < nchannels().n_total());
+
+ {
+ Glib::Mutex::Lock lm (_channel_mutex);
+ _channel[ch].ports.clear ();
}
-
- set_channels (prop->value());
- return 0;
+ emit_changed (PortsChanged);
}
-/** Set up channels from an XML property string.
- * @param str String.
- * @return 0 on success, -1 on error.
- */
-int
-Bundle::set_channels (const string& str)
-{
- vector<string> ports;
- int i;
- int n;
- int nchannels;
-
- if ((nchannels = count (str.begin(), str.end(), '{')) == 0) {
- return 0;
+void
+Bundle::suspend_signals ()
+{
+ _signals_suspended = true;
+}
+
+void
+Bundle::resume_signals ()
+{
+ if (_pending_change) {
+ Changed (_pending_change);
+ _pending_change = Change (0);
}
- set_nchannels (nchannels);
+ _signals_suspended = false;
+}
- string::size_type start, end, ostart;
+void
+Bundle::emit_changed (Change c)
+{
+ if (_signals_suspended) {
+ _pending_change = Change (int (_pending_change) | int (c));
+ } else {
+ Changed (c);
+ }
+}
- ostart = 0;
- start = 0;
- end = 0;
- i = 0;
+bool
+Bundle::connected_to (boost::shared_ptr<Bundle> other, AudioEngine & engine)
+{
+ if (_ports_are_inputs == other->_ports_are_inputs || nchannels() != other->nchannels()) {
+ return false;
+ }
- while ((start = str.find_first_of ('{', ostart)) != string::npos) {
- start += 1;
+ for (uint32_t i = 0; i < nchannels().n_total(); ++i) {
+ Bundle::PortList const & A = channel_ports (i);
+ Bundle::PortList const & B = other->channel_ports (i);
- if ((end = str.find_first_of ('}', start)) == string::npos) {
- error << string_compose(_("IO: badly formed string in XML node for inputs \"%1\""), str) << endmsg;
- return -1;
- }
+ for (uint32_t j = 0; j < A.size(); ++j) {
+ for (uint32_t k = 0; k < B.size(); ++k) {
- if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
- error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
+ Port* p = engine.get_port_by_name (A[j]);
+ Port* q = engine.get_port_by_name (B[k]);
- return -1;
-
- } else if (n > 0) {
+ if (!p && !q) {
+ return false;
+ }
- for (int x = 0; x < n; ++x) {
- add_port_to_channel (i, ports[x]);
+ if (p && !p->connected_to (B[k])) {
+ return false;
+ } else if (q && !q->connected_to (A[j])) {
+ return false;
+ }
}
}
-
- ostart = end+1;
- i++;
}
- return 0;
+ return true;
}
-int
-Bundle::parse_io_string (const string& str, vector<string>& ports)
+void
+Bundle::set_ports_are_inputs ()
{
- string::size_type pos, opos;
+ _ports_are_inputs = true;
+ emit_changed (DirectionChanged);
+}
- if (str.length() == 0) {
- return 0;
- }
+void
+Bundle::set_ports_are_outputs ()
+{
+ _ports_are_inputs = false;
+ emit_changed (DirectionChanged);
+}
- pos = 0;
- opos = 0;
+/** Set the name.
+ * @param n New name.
+ */
+void
+Bundle::set_name (string const & n)
+{
+ _name = n;
+ emit_changed (NameChanged);
+}
- ports.clear ();
+/** @param b Other bundle.
+ * @return true if b has the same number of channels as this bundle, and those channels have corresponding ports.
+ */
+bool
+Bundle::has_same_ports (boost::shared_ptr<Bundle> b) const
+{
+ uint32_t const N = nchannels().n_total();
- while ((pos = str.find_first_of (',', opos)) != string::npos) {
- ports.push_back (str.substr (opos, pos - opos));
- opos = pos + 1;
+ if (b->nchannels().n_total() != N) {
+ return false;
}
-
- if (opos < str.length()) {
- ports.push_back (str.substr(opos));
+
+ /* XXX: probably should sort channel port lists before comparing them */
+
+ for (uint32_t i = 0; i < N; ++i) {
+ if (channel_ports (i) != b->channel_ports (i)) {
+ return false;
+ }
}
- return ports.size();
+ return true;
}
+DataType
+Bundle::channel_type (uint32_t c) const
+{
+ assert (c < nchannels().n_total());
+
+ Glib::Mutex::Lock lm (_channel_mutex);
+ return _channel[c].type;
+}
+