/*
- Copyright (C) 2000-2006 Paul Davis
+ Copyright (C) 2000-2006 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 "ardour/midi_port.h"
#include "ardour/session.h"
#include "ardour/cycle_timer.h"
-#include "ardour/panner.h"
#include "ardour/buffer_set.h"
#include "ardour/meter.h"
#include "ardour/amp.h"
#include <cmath>
/*
- A bug in OS X's cmath that causes isnan() and isinf() to be
+ A bug in OS X's cmath that causes isnan() and isinf() to be
"undeclared". the following works around that
*/
_active = true;
pending_state_node = 0;
- set_state (node);
+ set_state (node, Stateful::loading_state_version);
setup_bundles ();
}
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 ();
return 0;
}
- {
+ {
BLOCK_PROCESS_CALLBACK ();
-
+
{
Glib::Mutex::Lock lm (io_lock);
-
+
/* check that our_port is really one of ours */
-
+
if ( ! _ports.contains(our_port)) {
return -1;
}
-
+
/* disconnect it from the source */
-
+
if (our_port->disconnect (other_port)) {
error << string_compose(_("IO: cannot disconnect port %1 from %2"), our_port->name(), other_port) << endmsg;
return -1;
{
BLOCK_PROCESS_CALLBACK ();
-
+
{
Glib::Mutex::Lock lm (io_lock);
-
+
/* check that our_port is really one of ours */
-
+
if ( ! _ports.contains(our_port) ) {
return -1;
}
-
+
/* connect it to the source */
if (our_port->connect (other_port)) {
{
BLOCK_PROCESS_CALLBACK ();
-
+
{
Glib::Mutex::Lock lm (io_lock);
if (port->connected()) {
change = IOChange (change|ConnectionsChanged);
- }
+ }
_session.engine().unregister_port (*port);
check_bundles_connected ();
{
BLOCK_PROCESS_CALLBACK ();
-
- {
+
+ {
Glib::Mutex::Lock lm (io_lock);
-
+
/* Create a new output port */
-
+
string portname = build_legal_port_name (type);
if (_direction == Input) {
return -1;
}
}
-
+
// pan_changed (src); /* EMIT SIGNAL */
changed (ConfigurationChanged, src); /* EMIT SIGNAL */
setup_bundles ();
int
IO::disconnect (void* src)
{
- {
+ {
BLOCK_PROCESS_CALLBACK ();
-
+
{
Glib::Mutex::Lock lm (io_lock);
-
+
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
i->disconnect_all ();
}
check_bundles_connected ();
}
}
-
+
changed (ConnectionsChanged, src); /* EMIT SIGNAL */
-
+
return 0;
}
{
Port* port = 0;
bool changed = false;
-
+
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-
+
const size_t n = count.get(*t);
-
+
/* remove unused ports */
for (size_t i = n_ports().get(*t); i > n; --i) {
port = _ports.port(*t, i-1);
-
+
assert(port);
_ports.remove(port);
_session.engine().unregister_port (*port);
changed = true;
}
}
-
+
if (changed) {
check_bundles_connected ();
PortCountChanged (n_ports()); /* EMIT SIGNAL */
_session.set_dirty ();
}
-
+
if (clear) {
/* disconnect all existing ports so that we get a fresh start */
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
}
for (PortSet::iterator i = _ports.begin(); i != _ports.end(); ++i) {
-
+
vector<string> connections;
XMLNode* pnode = new XMLNode (X_("Port"));
to be re-established even when our
client name is different.
*/
-
+
XMLNode* cnode = new XMLNode (X_("Connection"));
cnode->add_property (X_("other"), _session.engine().make_port_name_relative (*ci));
pnode->add_child_nocopy (*cnode);
- }
+ }
}
-
+
node->add_child_nocopy (*pnode);
}
}
int
-IO::set_state (const XMLNode& node)
+IO::set_state (const XMLNode& node, int version)
{
+ /* callers for version < 3000 need to call set_state_2X directly, as A3 IOs
+ * are input OR output, not both, so the direction needs to be specified
+ * by the caller.
+ */
+ assert (version >= 3000);
+
const XMLProperty* prop;
XMLNodeConstIterator iter;
LocaleGuard lg (X_("POSIX"));
error << string_compose(_("incorrect XML node \"%1\" passed to IO object"), node.name()) << endmsg;
return -1;
}
-
+
if ((prop = node.property ("name")) != 0) {
set_name (prop->value());
}
_direction = (Direction) string_2_enum (prop->value(), _direction);
}
- if (create_ports (node)) {
+ if (create_ports (node, version)) {
return -1;
}
if (connecting_legal) {
-
- if (make_connections (node)) {
+
+ if (make_connections (node, version, false)) {
return -1;
}
} else {
pending_state_node = new XMLNode (node);
+ pending_state_node_version = version;
+ pending_state_node_in = false;
connection_legal_c = ConnectingLegal.connect (mem_fun (*this, &IO::connecting_became_legal));
}
return 0;
}
+int
+IO::set_state_2X (const XMLNode& node, int version, bool in)
+{
+ const XMLProperty* prop;
+ XMLNodeConstIterator iter;
+ LocaleGuard lg (X_("POSIX"));
+
+ /* force use of non-localized representation of decimal point,
+ since we use it a lot in XML files and so forth.
+ */
+
+ if (node.name() != state_node_name) {
+ error << string_compose(_("incorrect XML node \"%1\" passed to IO object"), node.name()) << endmsg;
+ return -1;
+ }
+
+ if ((prop = node.property ("name")) != 0) {
+ set_name (prop->value());
+ }
+
+ if ((prop = node.property (X_("default-type"))) != 0) {
+ _default_type = DataType(prop->value());
+ assert(_default_type != DataType::NIL);
+ }
+
+ if ((prop = node.property ("id")) != 0) {
+ _id = prop->value ();
+ }
+
+ _direction = in ? Input : Output;
+
+ if (create_ports (node, version)) {
+ return -1;
+ }
+
+ if (connecting_legal) {
+
+ if (make_connections_2X (node, version, in)) {
+ return -1;
+ }
+
+ } else {
+
+ pending_state_node = new XMLNode (node);
+ pending_state_node_version = version;
+ pending_state_node_in = in;
+ connection_legal_c = ConnectingLegal.connect (mem_fun (*this, &IO::connecting_became_legal));
+ }
+
+ return 0;
+}
+
int
IO::connecting_became_legal ()
{
connection_legal_c.disconnect ();
- ret = make_connections (*pending_state_node);
-
+ ret = make_connections (*pending_state_node, pending_state_node_version, pending_state_node_in);
+
delete pending_state_node;
pending_state_node = 0;
static const string digits = "0123456789";
const string &default_name = (_direction == Input ? _("in") : _("out"));
const string &bundle_type_name = (_direction == Input ? _("input") : _("output"));
-
+
boost::shared_ptr<Bundle> c = _session.bundle_by_name (desired_name);
if (!c) {
// 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) {
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] == '+') {
// find highest set bit
mask = 1;
while ((mask <= bundle_number) && (mask <<= 1)) {}
-
- // "wrap" bundle number into largest possible power of 2
+
+ // "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) {
}
int
-IO::get_port_counts (const XMLNode& node, ChanCount& n, boost::shared_ptr<Bundle>& c)
+IO::get_port_counts_2X (XMLNode const & node, int /*version*/, ChanCount& n, boost::shared_ptr<Bundle>& /*c*/)
{
+ XMLProperty const * prop;
+ XMLNodeList children = node.children ();
+
+ uint32_t n_audio = 0;
+
+ for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
+
+ if ((prop = node.property ("inputs")) != 0 && _direction == Input) {
+ n_audio = count (prop->value().begin(), prop->value().end(), '{');
+ } else if ((prop = node.property ("outputs")) != 0 && _direction == Output) {
+ n_audio = count (prop->value().begin(), prop->value().end(), '{');
+ }
+ }
+
+ ChanCount cnt;
+ cnt.set_audio (n_audio);
+ n = ChanCount::max (n, cnt);
+
+ return 0;
+}
+
+int
+IO::get_port_counts (const XMLNode& node, int version, ChanCount& n, boost::shared_ptr<Bundle>& c)
+{
+ if (version < 3000) {
+ return get_port_counts_2X (node, version, n, c);
+ }
+
XMLProperty const * prop;
XMLNodeConstIterator iter;
uint32_t n_audio = 0;
}
return 0;
}
-
+
for (iter = node.children().begin(); iter != node.children().end(); ++iter) {
if ((*iter)->name() == X_("Bundle")) {
}
}
}
-
+
n = ChanCount::max (n, cnt);
return 0;
}
int
-IO::create_ports (const XMLNode& node)
+IO::create_ports (const XMLNode& node, int version)
{
ChanCount n;
boost::shared_ptr<Bundle> c;
-
- get_port_counts (node, n, c);
-
+
+ get_port_counts (node, version, n, c);
+
if (ensure_ports (n, true, true, this)) {
error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg;
return -1;
}
int
-IO::make_connections (const XMLNode& node)
+IO::make_connections (const XMLNode& node, int version, bool in)
{
+ if (version < 3000) {
+ return make_connections_2X (node, version, in);
+ }
+
const XMLProperty* prop;
for (XMLNodeConstIterator i = node.children().begin(); i != node.children().end(); ++i) {
if (!prop) {
continue;
}
-
+
Port* p = port_by_name (prop->value());
if (p) {
- for (XMLNodeConstIterator c = (*i)->children().begin(); c != (*i)->children().end(); ++c) {
+ for (XMLNodeConstIterator c = (*i)->children().begin(); c != (*i)->children().end(); ++c) {
XMLNode* cnode = (*c);
-
+
if (cnode->name() != X_("Connection")) {
continue;
}
-
+
if ((prop = cnode->property (X_("other"))) == 0) {
continue;
}
-
+
if (prop) {
p->connect (prop->value());
}
}
}
}
-
+
+ return 0;
+}
+
+
+int
+IO::make_connections_2X (const XMLNode& node, int /*version*/, bool in)
+{
+ const XMLProperty* prop;
+
+ /* XXX: bundles ("connections" as was) */
+
+ if ((prop = node.property ("inputs")) != 0 && in) {
+
+ string::size_type ostart = 0;
+ string::size_type start = 0;
+ string::size_type end = 0;
+ int i = 0;
+ int n;
+ vector<string> ports;
+
+ string const str = prop->value ();
+
+ while ((start = str.find_first_of ('{', ostart)) != string::npos) {
+ start += 1;
+
+ 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;
+ }
+
+ if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
+ error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
+
+ return -1;
+
+ } else if (n > 0) {
+
+
+ for (int x = 0; x < n; ++x) {
+ /* XXX: this is a bit of a hack; need to check if it's always valid */
+ string::size_type const p = ports[x].find ("/out");
+ if (p != string::npos) {
+ ports[x].replace (p, 4, "/audio_out");
+ }
+ nth(i)->connect (ports[x]);
+ }
+ }
+
+ ostart = end+1;
+ i++;
+ }
+
+ }
+
+ if ((prop = node.property ("outputs")) != 0 && !in) {
+
+ string::size_type ostart = 0;
+ string::size_type start = 0;
+ string::size_type end = 0;
+ int i = 0;
+ int n;
+ vector<string> ports;
+
+ string const str = prop->value ();
+
+ while ((start = str.find_first_of ('{', ostart)) != string::npos) {
+ start += 1;
+
+ if ((end = str.find_first_of ('}', start)) == string::npos) {
+ error << string_compose(_("IO: badly formed string in XML node for outputs \"%1\""), str) << endmsg;
+ return -1;
+ }
+
+ if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
+ error << string_compose(_("IO: bad output string in XML node \"%1\""), str) << endmsg;
+
+ return -1;
+
+ } else if (n > 0) {
+
+ for (int x = 0; x < n; ++x) {
+ /* XXX: this is a bit of a hack; need to check if it's always valid */
+ string::size_type const p = ports[x].find ("/in");
+ if (p != string::npos) {
+ ports[x].replace (p, 3, "/audio_in");
+ }
+ nth(i)->connect (ports[x]);
+ }
+ }
+
+ ostart = end+1;
+ i++;
+ }
+ }
+
return 0;
}
int i;
int n;
uint32_t nports;
-
+
if ((nports = count (str.begin(), str.end(), '{')) == 0) {
return 0;
}
error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
return -1;
-
+
} else if (n > 0) {
for (int x = 0; x < n; ++x) {
ports.push_back (str.substr (opos, pos - opos));
opos = pos + 1;
}
-
+
if (opos < str.length()) {
ports.push_back (str.substr(opos));
}
ports.push_back (str.substr (opos, pos - opos));
opos = pos + 1;
}
-
+
if (opos < str.length()) {
ports.push_back (str.substr(opos));
}
if (name == _name) {
return true;
}
-
+
/* replace all colons in the name. i wish we didn't have to do this */
if (replace_all (name, ":", "-")) {
for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) {
if ((latency = i->total_latency ()) > max_latency) {
max_latency = latency;
- }
+ }
}
return max_latency;
Glib::Mutex::Lock lm2 (io_lock);
c->disconnect (_bundle, _session.engine());
-
+
/* If this is a UserBundle, make a note of what we've done */
boost::shared_ptr<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c);
} else {
throw unknown_type();
}
-
+
+ /* note that if "in" or "out" are translated it will break a session
+ across locale switches because a port's connection list will
+ show (old) translated names, but the current port name will
+ use the (new) translated name.
+ */
+
if (_direction == Input) {
- suffix += _("_in");
+ suffix += X_("_in");
} else {
- suffix += _("_out");
+ suffix += X_("_out");
}
// allow up to 4 digits for the output port number, plus the slash, suffix and extra space
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 = find_port_hole (buf1);
snprintf (buf2, name_size+1, "%s %d", buf1, port_number);
}
}
}
-
+
return bundles;
}
IO::bundle_channel_name (uint32_t c, uint32_t n) const
{
char buf[32];
-
+
switch (n) {
case 1:
return _("mono");
IO::name_from_state (const XMLNode& node)
{
const XMLProperty* prop;
-
+
if ((prop = node.property ("name")) != 0) {
return prop->value();
- }
-
+ }
+
return string();
}
IO::set_name_in_state (XMLNode& node, const string& new_name)
{
const XMLProperty* prop;
-
+
if ((prop = node.property ("name")) != 0) {
node.add_property ("name", new_name);
- }
+ }
}
bool
uint32_t i, j;
uint32_t no = n_ports().n_total();
uint32_t ni = other->n_ports ().n_total();
-
+
for (i = 0; i < no; ++i) {
for (j = 0; j < ni; ++j) {
if (nth(i)->connected_to (other->nth(j)->name())) {
IO::collect_input (BufferSet& bufs, nframes_t nframes, ChanCount offset)
{
assert(bufs.available() >= _ports.count());
-
+
if (_ports.count() == ChanCount::ZERO) {
return;
}
IO::copy_to_outputs (BufferSet& bufs, DataType type, nframes_t nframes, nframes_t offset)
{
// Copy any buffers 1:1 to outputs
-
+
PortSet::iterator o = _ports.begin(type);
BufferSet::iterator i = bufs.begin(type);
BufferSet::iterator prev = i;
++i;
++o;
}
-
+
// Copy last buffer to any extra outputs
while (o != _ports.end(type)) {