2 Copyright (C) 2013 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "pbd/error.h"
22 #include "midi++/manager.h"
24 #include "ardour/port_manager.h"
25 #include "ardour/audio_port.h"
26 #include "ardour/midi_port.h"
30 using namespace ARDOUR;
35 PortManager::PortManager ()
37 , _port_remove_in_progress (false)
42 PortManager::remove_all_ports ()
44 /* make sure that JACK callbacks that will be invoked as we cleanup
45 * ports know that they have nothing to do.
48 _port_remove_in_progress = true;
50 /* process lock MUST be held by caller
54 RCUWriter<Ports> writer (ports);
55 boost::shared_ptr<Ports> ps = writer.get_copy ();
59 /* clear dead wood list in RCU */
63 _port_remove_in_progress = false;
68 PortManager::make_port_name_relative (const string& portname) const
70 string::size_type len;
72 string self = _impl->my_name();
74 len = portname.length();
76 for (n = 0; n < len; ++n) {
77 if (portname[n] == ':') {
82 if ((n != len) && (portname.substr (0, n) == self)) {
83 return portname.substr (n+1);
90 PortManager::make_port_name_non_relative (const string& portname) const
94 if (portname.find_first_of (':') != string::npos) {
98 str = _impl->my_name();
106 PortManager::port_is_mine (const string& portname) const
108 string self = _impl->my_name();
110 if (portname.find_first_of (':') != string::npos) {
111 if (portname.substr (0, self.length ()) != self) {
120 PortManager::port_is_physical (const std::string& portname) const
122 PortEngine::PortHandle ph = _impl->get_port_by_name (portname);
127 return _impl->port_is_physical (ph);
130 /** @param name Full or short name of port
131 * @return Corresponding Port or 0.
134 boost::shared_ptr<Port>
135 PortManager::get_port_by_name (const string& portname)
137 if (!_impl->connected()) {
138 fatal << _("get_port_by_name() called before engine was started") << endmsg;
142 if (!port_is_mine (portname)) {
143 /* not an ardour port */
144 return boost::shared_ptr<Port> ();
147 boost::shared_ptr<Ports> pr = ports.reader();
148 std::string rel = make_port_name_relative (portname);
149 Ports::iterator x = pr->find (rel);
151 if (x != pr->end()) {
152 /* its possible that the port was renamed by some 3rd party and
153 we don't know about it. check for this (the check is quick
154 and cheap), and if so, rename the port (which will alter
155 the port map as a side effect).
157 const std::string check = make_port_name_relative (_impl->get_port_name (x->second->port_handle()));
159 x->second->set_name (check);
164 return boost::shared_ptr<Port> ();
168 PortManager::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
170 RCUWriter<Ports> writer (ports);
171 boost::shared_ptr<Ports> p = writer.get_copy();
172 Ports::iterator x = p->find (old_relative_name);
175 boost::shared_ptr<Port> port = x->second;
177 p->insert (make_pair (new_relative_name, port));
182 PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s)
184 return _impl->get_ports (port_name_pattern, type, flags, s);
188 PortManager::port_registration_failure (const std::string& portname)
190 string full_portname = _impl->my_name();
191 full_portname += ':';
192 full_portname += portname;
195 PortEngine::PortHandle p = _impl->get_port_by_name (full_portname);
199 reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
201 reason = string_compose (_("No more ports are available. You will need to stop %1 and restart with more ports if you need this many tracks."), PROGRAM_NAME);
204 throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
207 boost::shared_ptr<Port>
208 PortManager::register_port (DataType dtype, const string& portname, bool input)
210 boost::shared_ptr<Port> newport;
213 if (dtype == DataType::AUDIO) {
214 newport.reset (new AudioPort (portname, (input ? IsInput : IsOutput)));
215 } else if (dtype == DataType::MIDI) {
216 newport.reset (new MidiPort (portname, (input ? IsInput : IsOutput)));
218 throw PortRegistrationFailure("unable to create port (unknown type)");
221 RCUWriter<Ports> writer (ports);
222 boost::shared_ptr<Ports> ps = writer.get_copy ();
223 ps->insert (make_pair (make_port_name_relative (portname), newport));
225 /* writer goes out of scope, forces update */
230 catch (PortRegistrationFailure& err) {
232 } catch (std::exception& e) {
233 throw PortRegistrationFailure(string_compose(
234 _("unable to create port: %1"), e.what()).c_str());
236 throw PortRegistrationFailure("unable to create port (unknown error)");
240 boost::shared_ptr<Port>
241 PortManager::register_input_port (DataType type, const string& portname)
243 return register_port (type, portname, true);
246 boost::shared_ptr<Port>
247 PortManager::register_output_port (DataType type, const string& portname)
249 return register_port (type, portname, false);
253 PortManager::unregister_port (boost::shared_ptr<Port> port)
255 /* caller must hold process lock */
257 if (!_impl->connected()) {
258 /* probably happening when the engine has been halted by JACK,
259 in which case, there is nothing we can do here.
265 RCUWriter<Ports> writer (ports);
266 boost::shared_ptr<Ports> ps = writer.get_copy ();
267 Ports::iterator x = ps->find (make_port_name_relative (port->name()));
269 if (x != ps->end()) {
273 /* writer goes out of scope, forces update */
282 PortManager::connected (const string& port_name)
284 PortEngine::PortHandle handle = _impl->get_port_by_name (port_name);
290 return _impl->connected (handle);
294 PortManager::connect (const string& source, const string& destination)
298 if (!_impl->connected()) {
302 string s = make_port_name_non_relative (source);
303 string d = make_port_name_non_relative (destination);
305 boost::shared_ptr<Port> src = get_port_by_name (s);
306 boost::shared_ptr<Port> dst = get_port_by_name (d);
309 ret = src->connect (d);
311 ret = dst->connect (s);
313 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
318 /* already exists - no error, no warning */
319 } else if (ret < 0) {
320 error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
321 source, s, destination, d)
329 PortManager::disconnect (const string& source, const string& destination)
333 if (!_impl->connected()) {
337 string s = make_port_name_non_relative (source);
338 string d = make_port_name_non_relative (destination);
340 boost::shared_ptr<Port> src = get_port_by_name (s);
341 boost::shared_ptr<Port> dst = get_port_by_name (d);
344 ret = src->disconnect (d);
346 ret = dst->disconnect (s);
348 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
355 PortManager::disconnect (boost::shared_ptr<Port> port)
357 return port->disconnect_all ();
361 PortManager::reestablish_ports ()
365 boost::shared_ptr<Ports> p = ports.reader ();
367 for (i = p->begin(); i != p->end(); ++i) {
368 if (i->second->reestablish ()) {
379 MIDI::Manager::instance()->reestablish ();
385 PortManager::reconnect_ports ()
387 boost::shared_ptr<Ports> p = ports.reader ();
389 /* re-establish connections */
391 for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
392 i->second->reconnect ();
395 MIDI::Manager::instance()->reconnect ();
401 PortManager::connect_callback (const string& a, const string& b, bool conn)
403 boost::shared_ptr<Port> port_a;
404 boost::shared_ptr<Port> port_b;
406 boost::shared_ptr<Ports> pr = ports.reader ();
408 x = pr->find (make_port_name_relative (a));
409 if (x != pr->end()) {
413 x = pr->find (make_port_name_relative (b));
414 if (x != pr->end()) {
418 PortConnectedOrDisconnected (
426 PortManager::registration_callback ()
428 if (!_port_remove_in_progress) {
429 PortRegisteredOrUnregistered (); /* EMIT SIGNAL */