#include "ardour/audioengine.h"
#include "ardour/buffer.h"
+#include "ardour/buffer_set.h"
#include "ardour/debug.h"
#include "ardour/io.h"
-#include "ardour/route.h"
#include "ardour/port.h"
-#include "ardour/audio_port.h"
-#include "ardour/midi_port.h"
+#include "ardour/route.h"
#include "ardour/session.h"
-#include "ardour/cycle_timer.h"
-#include "ardour/buffer_set.h"
-#include "ardour/meter.h"
-#include "ardour/amp.h"
#include "ardour/user_bundle.h"
#include "i18n.h"
, _default_type (default_type)
{
_active = true;
+ Port::PostDisconnect.connect_same_thread (*this, boost::bind (&IO::disconnect_check, this, _1, _2));
pending_state_node = 0;
setup_bundle ();
}
{
_active = true;
pending_state_node = 0;
+ Port::PostDisconnect.connect_same_thread (*this, boost::bind (&IO::disconnect_check, this, _1, _2));
set_state (node, Stateful::loading_state_version);
setup_bundle ();
}
}
+void
+IO::disconnect_check (boost::shared_ptr<Port> a, boost::shared_ptr<Port> b)
+{
+ /* this could be called from within our own ::disconnect() method(s)
+ or from somewhere that operates directly on a port. so, we don't
+ know for sure if we can take this lock or not. if we fail,
+ we assume that its safely locked by our own ::disconnect().
+ */
+
+ Glib::Mutex::Lock tm (io_lock, Glib::TRY_LOCK);
+
+ if (tm.locked()) {
+ /* we took the lock, so we cannot be here from inside
+ * ::disconnect()
+ */
+ if (_ports.contains (a) || _ports.contains (b)) {
+ changed (IOChange (IOChange::ConnectionsChanged), this); /* EMIT SIGNAL */
+ }
+ } else {
+ /* we didn't get the lock, so assume that we're inside
+ * ::disconnect(), and it will call changed() appropriately.
+ */
+ }
+}
+
void
IO::increment_port_buffer_offset (pframes_t offset)
{
}
}
-void
-IO::check_bundles_connected ()
-{
- check_bundles (_bundles_connected, ports());
-}
-
-/** Check the bundles in list to see which are connected to a given PortSet,
- * and update list with those that are connected such that every port on every
- * bundle channel x is connected to port x in ports.
+/** Set _bundles_connected to those bundles that are connected such that every
+ * port on every bundle channel x is connected to port x in _ports.
*/
void
-IO::check_bundles (std::vector<UserBundleInfo*>& list, const PortSet& ports)
+IO::check_bundles_connected ()
{
std::vector<UserBundleInfo*> new_list;
- for (std::vector<UserBundleInfo*>::iterator i = list.begin(); i != list.end(); ++i) {
+ for (std::vector<UserBundleInfo*>::iterator i = _bundles_connected.begin(); i != _bundles_connected.end(); ++i) {
uint32_t const N = (*i)->bundle->nchannels().n_total();
/* Every port on bundle channel j must be connected to our input j */
Bundle::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) {
+ if (_ports.port(j)->connected_to (pl[k]) == false) {
ok = false;
break;
}
}
}
- list = new_list;
+ _bundles_connected = new_list;
}
int
-IO::disconnect (Port* our_port, string other_port, void* src)
+IO::disconnect (boost::shared_ptr<Port> our_port, string other_port, void* src)
{
if (other_port.length() == 0 || our_port == 0) {
return 0;
}
int
-IO::connect (Port* our_port, string other_port, void* src)
+IO::connect (boost::shared_ptr<Port> our_port, string other_port, void* src)
{
if (other_port.length() == 0 || our_port == 0) {
return 0;
}
int
-IO::remove_port (Port* port, void* src)
+IO::remove_port (boost::shared_ptr<Port> port, void* src)
{
ChanCount before = _ports.count ();
ChanCount after = before;
after.set (port->type(), after.get (port->type()) - 1);
- bool const r = PortCountChanging (after); /* EMIT SIGNAL */
- if (r) {
+ boost::optional<bool> const r = PortCountChanging (after); /* EMIT SIGNAL */
+ if (r.get_value_or (false)) {
return -1;
}
change.type = IOChange::Type (change.type | IOChange::ConnectionsChanged);
}
- _session.engine().unregister_port (*port);
+ _session.engine().unregister_port (port);
check_bundles_connected ();
}
}
int
IO::add_port (string destination, void* src, DataType type)
{
- Port* our_port;
+ boost::shared_ptr<Port> our_port;
if (type == DataType::NIL) {
type = _default_type;
}
+ ChanCount before = _ports.count ();
+ ChanCount after = before;
+ after.set (type, after.get (type) + 1);
+
+ bool const r = PortCountChanging (after); /* EMIT SIGNAL */
+ if (r) {
+ return -1;
+ }
+
IOChange change;
{
/* Create a new port */
string portname = build_legal_port_name (type);
-
+
if (_direction == Input) {
if ((our_port = _session.engine().register_input_port (type, portname)) == 0) {
error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg;
change.before = _ports.count ();
_ports.add (our_port);
}
-
+
PortCountChanged (n_ports()); /* EMIT SIGNAL */
-
- // pan_changed (src); /* EMIT SIGNAL */
change.type = IOChange::ConfigurationChanged;
change.after = _ports.count ();
changed (change, src); /* EMIT SIGNAL */
_buffers.attach_buffers (_ports);
}
- if (destination.length()) {
+ if (!destination.empty()) {
if (our_port->connect (destination)) {
return -1;
}
}
/** Caller must hold process lock */
-bool
-IO::ensure_ports_locked (ChanCount count, bool clear, void* /*src*/)
+int
+IO::ensure_ports_locked (ChanCount count, bool clear, bool& changed)
{
assert (!AudioEngine::instance()->process_lock().trylock());
- Port* port = 0;
- bool changed = false;
+ boost::shared_ptr<Port> port;
+
+ changed = false;
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
assert(port);
_ports.remove(port);
- _session.engine().unregister_port (*port);
+ _session.engine().unregister_port (port);
changed = true;
}
}
}
- return changed;
+ return 0;
}
/** Caller must hold process lock */
{
Glib::Mutex::Lock im (io_lock);
- changed = ensure_ports_locked (count, clear, src);
+ if (ensure_ports_locked (count, clear, changed)) {
+ return -1;
+ }
}
if (changed) {
}
XMLNode&
-IO::get_state (void)
+IO::get_state ()
{
return state (true);
}
node->add_child_nocopy (*pnode);
}
+ snprintf (buf, sizeof (buf), "%" PRId64, _user_latency);
+ node->add_property (X_("user-latency"), buf);
+
return *node;
}
assert(_default_type != DataType::NIL);
}
- if ((prop = node.property ("id")) != 0) {
- _id = prop->value ();
- }
+ set_id (node);
if ((prop = node.property ("direction")) != 0) {
_direction = (Direction) string_2_enum (prop->value(), _direction);
ConnectingLegal.connect_same_thread (connection_legal_c, boost::bind (&IO::connecting_became_legal, this));
}
+ if ((prop = node.property ("user-latency")) != 0) {
+ _user_latency = atoi (prop->value ());
+ }
return 0;
}
assert(_default_type != DataType::NIL);
}
- if ((prop = node.property ("id")) != 0) {
- _id = prop->value ();
- }
+ set_id (node);
_direction = in ? Input : Output;
continue;
}
- Port* p = port_by_name (prop->value());
+ boost::shared_ptr<Port> p = port_by_name (prop->value());
if (p) {
for (XMLNodeConstIterator c = (*i)->children().begin(); c != (*i)->children().end(); ++c) {
return 0;
}
+void
+IO::prepare_for_reset (XMLNode& node, const std::string& name)
+{
+ /* reset name */
+ node.add_property ("name", name);
+
+ /* now find connections and reset the name of the port
+ in one so that when we re-use it it will match
+ the name of the thing we're applying it to.
+ */
+
+ XMLProperty* prop;
+ XMLNodeList children = node.children();
+
+ for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
+
+ if ((*i)->name() == "Port") {
+
+ prop = (*i)->property (X_("name"));
+
+ if (prop) {
+ string new_name;
+ string old = prop->value();
+ string::size_type slash = old.find ('/');
+
+ if (slash != string::npos) {
+ /* port name is of form: <IO-name>/<port-name> */
+
+ new_name = name;
+ new_name += old.substr (old.find ('/'));
+
+ prop->set_value (new_name);
+ }
+ }
+ }
+ }
+}
+
int
IO::make_connections_2X (const XMLNode& node, int /*version*/, bool in)
int
IO::enable_connecting ()
{
+ Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock());
connecting_legal = true;
boost::optional<int> r = ConnectingLegal ();
return r.get_value_or (0);
void
IO::bundle_changed (Bundle::Change /*c*/)
{
- //XXX
+ /* XXX */
// connect_input_ports_to_bundle (_input_bundle, this);
}
char buf1[name_size+1];
char buf2[name_size+1];
- snprintf (buf1, name_size+1, ("%.*s/%s"), limit, _name.val().c_str(), suffix.c_str());
+ /* colons are illegal in port names, so fix that */
+
+ string nom = _name.val();
+ replace_all (nom, ":", ";");
+
+ snprintf (buf1, name_size+1, ("%.*s/%s"), limit, nom.c_str(), suffix.c_str());
int port_number = find_port_hole (buf1);
snprintf (buf2, name_size+1, "%s %d", buf1, port_number);
}
-AudioPort*
+boost::shared_ptr<AudioPort>
IO::audio(uint32_t n) const
{
return _ports.nth_audio_port (n);
}
-MidiPort*
+boost::shared_ptr<MidiPort>
IO::midi(uint32_t n) const
{
return _ports.nth_midi_port (n);
void
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);
+ node.add_property (X_("name"), new_name);
+ XMLNodeList children = node.children ();
+ for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
+ if ((*i)->name() == X_("Port")) {
+ string const old_name = (*i)->property(X_("name"))->value();
+ string const old_name_second_part = old_name.substr (old_name.find_first_of ("/") + 1);
+ (*i)->add_property (X_("name"), string_compose ("%1/%2", new_name, old_name_second_part));
+ }
}
}
return false;
}
-/** Caller must hold process lock */
+/** Call a processor's ::run() method, giving it our buffers
+ * Caller must hold process lock.
+ */
void
IO::process_input (boost::shared_ptr<Processor> proc, framepos_t start_frame, framepos_t end_frame, pframes_t nframes)
{
/* don't read the data into new buffers - just use the port buffers directly */
+ if (n_ports().n_total() == 0) {
+ /* We have no ports, so nothing to process */
+ return;
+ }
+
_buffers.get_jack_port_addresses (_ports, nframes);
proc->run (_buffers, start_frame, end_frame, nframes, true);
}
}
}
-Port*
+boost::shared_ptr<Port>
IO::port_by_name (const std::string& str) const
{
/* to be called only from ::set_state() - no locking */
for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) {
- const Port& p(*i);
-
- if (p.name() == str) {
- return const_cast<Port*>(&p);
+ if (i->name() == str) {
+ return boost::const_pointer_cast<Port> (*i);
}
}
- return 0;
+ return boost::shared_ptr<Port> ();
}
bool
}
bool
-IO::has_port (Port* p) const
+IO::has_port (boost::shared_ptr<Port> p) const
{
Glib::Mutex::Lock lm (io_lock);
return _ports.contains (p);