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
74 string::size_type len;
76 string self = _impl->my_name();
78 len = portname.length();
80 for (n = 0; n < len; ++n) {
81 if (portname[n] == ':') {
86 if ((n != len) && (portname.substr (0, n) == self)) {
87 return portname.substr (n+1);
94 PortManager::make_port_name_non_relative (const string& portname) const
98 if (portname.find_first_of (':') != string::npos) {
102 str = _impl->my_name();
110 PortManager::port_is_mine (const string& portname) const
116 string self = _impl->my_name();
118 if (portname.find_first_of (':') != string::npos) {
119 if (portname.substr (0, self.length ()) != self) {
128 PortManager::port_is_physical (const std::string& portname) const
134 PortEngine::PortHandle ph = _impl->get_port_by_name (portname);
139 return _impl->port_is_physical (ph);
143 PortManager::get_physical_outputs (DataType type, std::vector<std::string>& s)
148 _impl->get_physical_outputs (type, s);
152 PortManager::get_physical_inputs (DataType type, std::vector<std::string>& s)
158 _impl->get_physical_inputs (type, s);
162 PortManager::n_physical_outputs () const
165 return ChanCount::ZERO;
168 return _impl->n_physical_outputs ();
172 PortManager::n_physical_inputs () const
175 return ChanCount::ZERO;
177 return _impl->n_physical_inputs ();
180 /** @param name Full or short name of port
181 * @return Corresponding Port or 0.
184 boost::shared_ptr<Port>
185 PortManager::get_port_by_name (const string& portname)
188 return boost::shared_ptr<Port>();
191 if (!port_is_mine (portname)) {
192 /* not an ardour port */
193 return boost::shared_ptr<Port> ();
196 boost::shared_ptr<Ports> pr = ports.reader();
197 std::string rel = make_port_name_relative (portname);
198 Ports::iterator x = pr->find (rel);
200 if (x != pr->end()) {
201 /* its possible that the port was renamed by some 3rd party and
202 we don't know about it. check for this (the check is quick
203 and cheap), and if so, rename the port (which will alter
204 the port map as a side effect).
206 const std::string check = make_port_name_relative (_impl->get_port_name (x->second->port_handle()));
208 x->second->set_name (check);
213 return boost::shared_ptr<Port> ();
217 PortManager::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
219 RCUWriter<Ports> writer (ports);
220 boost::shared_ptr<Ports> p = writer.get_copy();
221 Ports::iterator x = p->find (old_relative_name);
224 boost::shared_ptr<Port> port = x->second;
226 p->insert (make_pair (new_relative_name, port));
231 PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s)
237 return _impl->get_ports (port_name_pattern, type, flags, s);
241 PortManager::port_registration_failure (const std::string& portname)
247 string full_portname = _impl->my_name();
248 full_portname += ':';
249 full_portname += portname;
252 PortEngine::PortHandle p = _impl->get_port_by_name (full_portname);
256 reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
258 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);
261 throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
264 boost::shared_ptr<Port>
265 PortManager::register_port (DataType dtype, const string& portname, bool input)
267 boost::shared_ptr<Port> newport;
270 if (dtype == DataType::AUDIO) {
271 newport.reset (new AudioPort (portname, (input ? IsInput : IsOutput)));
272 } else if (dtype == DataType::MIDI) {
273 newport.reset (new MidiPort (portname, (input ? IsInput : IsOutput)));
275 throw PortRegistrationFailure("unable to create port (unknown type)");
278 RCUWriter<Ports> writer (ports);
279 boost::shared_ptr<Ports> ps = writer.get_copy ();
280 ps->insert (make_pair (make_port_name_relative (portname), newport));
282 /* writer goes out of scope, forces update */
287 catch (PortRegistrationFailure& err) {
289 } catch (std::exception& e) {
290 throw PortRegistrationFailure(string_compose(
291 _("unable to create port: %1"), e.what()).c_str());
293 throw PortRegistrationFailure("unable to create port (unknown error)");
297 boost::shared_ptr<Port>
298 PortManager::register_input_port (DataType type, const string& portname)
300 return register_port (type, portname, true);
303 boost::shared_ptr<Port>
304 PortManager::register_output_port (DataType type, const string& portname)
306 return register_port (type, portname, false);
310 PortManager::unregister_port (boost::shared_ptr<Port> port)
312 /* caller must hold process lock */
315 RCUWriter<Ports> writer (ports);
316 boost::shared_ptr<Ports> ps = writer.get_copy ();
317 Ports::iterator x = ps->find (make_port_name_relative (port->name()));
319 if (x != ps->end()) {
323 /* writer goes out of scope, forces update */
332 PortManager::connected (const string& port_name)
338 PortEngine::PortHandle handle = _impl->get_port_by_name (port_name);
344 return _impl->connected (handle);
348 PortManager::connect (const string& source, const string& destination)
352 string s = make_port_name_non_relative (source);
353 string d = make_port_name_non_relative (destination);
355 boost::shared_ptr<Port> src = get_port_by_name (s);
356 boost::shared_ptr<Port> dst = get_port_by_name (d);
359 ret = src->connect (d);
361 ret = dst->connect (s);
363 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
368 /* already exists - no error, no warning */
369 } else if (ret < 0) {
370 error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
371 source, s, destination, d)
379 PortManager::disconnect (const string& source, const string& destination)
383 string s = make_port_name_non_relative (source);
384 string d = make_port_name_non_relative (destination);
386 boost::shared_ptr<Port> src = get_port_by_name (s);
387 boost::shared_ptr<Port> dst = get_port_by_name (d);
390 ret = src->disconnect (d);
392 ret = dst->disconnect (s);
394 /* neither port is known to us, and this API isn't intended for use as a general patch bay */
401 PortManager::disconnect (boost::shared_ptr<Port> port)
403 return port->disconnect_all ();
407 PortManager::reestablish_ports ()
411 boost::shared_ptr<Ports> p = ports.reader ();
413 for (i = p->begin(); i != p->end(); ++i) {
414 if (i->second->reestablish ()) {
425 MIDI::Manager::instance()->reestablish ();
431 PortManager::reconnect_ports ()
433 boost::shared_ptr<Ports> p = ports.reader ();
435 /* re-establish connections */
437 for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
438 i->second->reconnect ();
441 MIDI::Manager::instance()->reconnect ();
447 PortManager::connect_callback (const string& a, const string& b, bool conn)
449 boost::shared_ptr<Port> port_a;
450 boost::shared_ptr<Port> port_b;
452 boost::shared_ptr<Ports> pr = ports.reader ();
454 x = pr->find (make_port_name_relative (a));
455 if (x != pr->end()) {
459 x = pr->find (make_port_name_relative (b));
460 if (x != pr->end()) {
464 PortConnectedOrDisconnected (
472 PortManager::registration_callback ()
474 if (!_port_remove_in_progress) {
475 PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
480 PortManager::can_request_input_monitoring () const
486 return _impl->can_monitor_input ();
490 PortManager::request_input_monitoring (const string& name, bool yn) const
496 PortEngine::PortHandle ph = _impl->get_port_by_name (name);
499 _impl->request_input_monitoring (ph, yn);
504 PortManager::ensure_input_monitoring (const string& name, bool yn) const
510 PortEngine::PortHandle ph = _impl->get_port_by_name (name);
513 _impl->ensure_input_monitoring (ph, yn);
518 PortManager::port_name_size() const
524 return _impl->port_name_size ();
528 PortManager::my_name() const
534 return _impl->my_name();
538 PortManager::graph_order_callback ()
540 if (!_port_remove_in_progress) {
541 GraphReordered(); /* EMIT SIGNAL */