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.
21 #include <io.h> // Microsoft's nearest equivalent to <unistd.h>
22 #include <ardourext/misc.h>
27 #include <glibmm/fileutils.h>
28 #include <glibmm/miscutils.h>
30 #include "pbd/error.h"
32 #include "ardour/async_midi_port.h"
33 #include "ardour/audio_backend.h"
34 #include "ardour/audio_port.h"
35 #include "ardour/debug.h"
36 #include "ardour/filesystem_paths.h"
37 #include "ardour/midi_port.h"
38 #include "ardour/midiport_manager.h"
39 #include "ardour/port_manager.h"
40 #include "ardour/profile.h"
41 #include "ardour/session.h"
42 #include "ardour/types_convert.h"
46 using namespace ARDOUR;
51 PortManager::PortManager ()
53 , _port_remove_in_progress (false)
54 , _port_deletions_pending (8192) /* ick, arbitrary sizing */
55 , midi_info_dirty (true)
57 load_midi_port_info ();
61 PortManager::clear_pending_port_deletions ()
65 DEBUG_TRACE (DEBUG::Ports, string_compose ("pending port deletions: %1\n", _port_deletions_pending.read_space()));
67 while (_port_deletions_pending.read (&p, 1) == 1) {
73 PortManager::remove_all_ports ()
75 /* make sure that JACK callbacks that will be invoked as we cleanup
76 * ports know that they have nothing to do.
79 _port_remove_in_progress = true;
81 /* process lock MUST be held by caller
85 RCUWriter<Ports> writer (ports);
86 boost::shared_ptr<Ports> ps = writer.get_copy ();
90 /* clear dead wood list in RCU */
94 /* clear out pending port deletion list. we know this is safe because
95 * the auto connect thread in Session is already dead when this is
96 * done. It doesn't use shared_ptr<Port> anyway.
99 _port_deletions_pending.reset ();
101 _port_remove_in_progress = false;
106 PortManager::make_port_name_relative (const string& portname) const
112 string::size_type colon = portname.find (':');
114 if (colon == string::npos) {
118 if (portname.substr (0, colon) == _backend->my_name()) {
119 return portname.substr (colon+1);
126 PortManager::make_port_name_non_relative (const string& portname) const
130 if (portname.find_first_of (':') != string::npos) {
134 str = _backend->my_name();
142 PortManager::get_pretty_name_by_name(const std::string& portname) const
144 PortEngine::PortHandle ph = _backend->get_port_by_name (portname);
148 if (0 == _backend->get_port_property (ph,
149 "http://jackaudio.org/metadata/pretty-name",
159 PortManager::port_is_mine (const string& portname) const
165 string self = _backend->my_name();
167 if (portname.find_first_of (':') != string::npos) {
168 if (portname.substr (0, self.length ()) != self) {
177 PortManager::port_is_physical (const std::string& portname) const
183 PortEngine::PortHandle ph = _backend->get_port_by_name (portname);
188 return _backend->port_is_physical (ph);
192 PortManager::filter_midi_ports (vector<string>& ports, MidiPortFlags include, MidiPortFlags exclude)
194 if (!include && !exclude) {
198 for (vector<string>::iterator si = ports.begin(); si != ports.end(); ) {
200 PortManager::MidiPortInformation mpi = midi_port_information (*si);
202 if (mpi.pretty_name.empty()) {
203 /* no information !!! */
209 if ((mpi.properties & include) != include) {
210 /* properties do not include requested ones */
211 si = ports.erase (si);
217 if ((mpi.properties & exclude)) {
218 /* properties include ones to avoid */
219 si = ports.erase (si);
229 PortManager::get_physical_outputs (DataType type, std::vector<std::string>& s, MidiPortFlags include, MidiPortFlags exclude)
235 _backend->get_physical_outputs (type, s);
236 filter_midi_ports (s, include, exclude);
240 PortManager::get_physical_inputs (DataType type, std::vector<std::string>& s, MidiPortFlags include, MidiPortFlags exclude)
247 _backend->get_physical_inputs (type, s);
248 filter_midi_ports (s, include, exclude);
252 PortManager::n_physical_outputs () const
255 return ChanCount::ZERO;
258 return _backend->n_physical_outputs ();
262 PortManager::n_physical_inputs () const
265 return ChanCount::ZERO;
267 return _backend->n_physical_inputs ();
270 /** @param name Full or short name of port
271 * @return Corresponding Port or 0.
274 boost::shared_ptr<Port>
275 PortManager::get_port_by_name (const string& portname)
278 return boost::shared_ptr<Port>();
281 if (!port_is_mine (portname)) {
282 /* not an ardour port */
283 return boost::shared_ptr<Port> ();
286 boost::shared_ptr<Ports> pr = ports.reader();
287 std::string rel = make_port_name_relative (portname);
288 Ports::iterator x = pr->find (rel);
290 if (x != pr->end()) {
291 /* its possible that the port was renamed by some 3rd party and
292 we don't know about it. check for this (the check is quick
293 and cheap), and if so, rename the port (which will alter
294 the port map as a side effect).
296 const std::string check = make_port_name_relative (_backend->get_port_name (x->second->port_handle()));
298 x->second->set_name (check);
303 return boost::shared_ptr<Port> ();
307 PortManager::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
309 RCUWriter<Ports> writer (ports);
310 boost::shared_ptr<Ports> p = writer.get_copy();
311 Ports::iterator x = p->find (old_relative_name);
314 boost::shared_ptr<Port> port = x->second;
316 p->insert (make_pair (new_relative_name, port));
321 PortManager::get_ports (DataType type, PortList& pl)
323 boost::shared_ptr<Ports> plist = ports.reader();
324 for (Ports::iterator p = plist->begin(); p != plist->end(); ++p) {
325 if (p->second->type() == type) {
326 pl.push_back (p->second);
333 PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s)
341 return _backend->get_ports (port_name_pattern, type, flags, s);
345 PortManager::port_registration_failure (const std::string& portname)
351 string full_portname = _backend->my_name();
352 full_portname += ':';
353 full_portname += portname;
356 PortEngine::PortHandle p = _backend->get_port_by_name (full_portname);
360 reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
362 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);
365 throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
370 void operator() (Port* p) {
371 AudioEngine::instance()->add_pending_port_deletion (p);
375 boost::shared_ptr<Port>
376 PortManager::register_port (DataType dtype, const string& portname, bool input, bool async, PortFlags flags)
378 boost::shared_ptr<Port> newport;
380 /* limit the possible flags that can be set */
382 flags = PortFlags (flags & (Hidden|Shadow|IsTerminal));
385 if (dtype == DataType::AUDIO) {
386 DEBUG_TRACE (DEBUG::Ports, string_compose ("registering AUDIO port %1, input %2\n",
388 newport.reset (new AudioPort (portname, PortFlags ((input ? IsInput : IsOutput) | flags)),
390 } else if (dtype == DataType::MIDI) {
392 DEBUG_TRACE (DEBUG::Ports, string_compose ("registering ASYNC MIDI port %1, input %2\n",
394 newport.reset (new AsyncMIDIPort (portname, PortFlags ((input ? IsInput : IsOutput) | flags)),
397 DEBUG_TRACE (DEBUG::Ports, string_compose ("registering MIDI port %1, input %2\n",
399 newport.reset (new MidiPort (portname, PortFlags ((input ? IsInput : IsOutput) | flags)),
403 throw PortRegistrationFailure("unable to create port (unknown type)");
406 RCUWriter<Ports> writer (ports);
407 boost::shared_ptr<Ports> ps = writer.get_copy ();
408 ps->insert (make_pair (make_port_name_relative (portname), newport));
410 /* writer goes out of scope, forces update */
414 catch (PortRegistrationFailure& err) {
416 } catch (std::exception& e) {
417 throw PortRegistrationFailure(string_compose(
418 _("unable to create port: %1"), e.what()).c_str());
420 throw PortRegistrationFailure("unable to create port (unknown error)");
423 DEBUG_TRACE (DEBUG::Ports, string_compose ("\t%2 port registration success, ports now = %1\n", ports.reader()->size(), this));
427 boost::shared_ptr<Port>
428 PortManager::register_input_port (DataType type, const string& portname, bool async, PortFlags extra_flags)
430 return register_port (type, portname, true, async, extra_flags);
433 boost::shared_ptr<Port>
434 PortManager::register_output_port (DataType type, const string& portname, bool async, PortFlags extra_flags)
436 return register_port (type, portname, false, async, extra_flags);
440 PortManager::unregister_port (boost::shared_ptr<Port> port)
442 /* This is a little subtle. We do not call the backend's port
443 * unregistration code from here. That is left for the Port
444 * destructor. We are trying to drop references to the Port object
445 * here, so that its destructor will run and it will unregister itself.
448 /* caller must hold process lock */
451 RCUWriter<Ports> writer (ports);
452 boost::shared_ptr<Ports> ps = writer.get_copy ();
453 Ports::iterator x = ps->find (make_port_name_relative (port->name()));
455 if (x != ps->end()) {
456 DEBUG_TRACE (DEBUG::Ports, string_compose ("removing %1 from port map (uc=%2)\n", port->name(), port.use_count()));
460 /* writer goes out of scope, forces update */
469 PortManager::connected (const string& port_name)
475 PortEngine::PortHandle handle = _backend->get_port_by_name (port_name);
481 return _backend->connected (handle);
485 PortManager::physically_connected (const string& port_name)
491 PortEngine::PortHandle handle = _backend->get_port_by_name (port_name);
497 return _backend->physically_connected (handle);
501 PortManager::get_connections (const string& port_name, std::vector<std::string>& s)
508 PortEngine::PortHandle handle = _backend->get_port_by_name (port_name);
515 return _backend->get_connections (handle, s);
519 PortManager::connect (const string& source, const string& destination)
523 string s = make_port_name_non_relative (source);
524 string d = make_port_name_non_relative (destination);
526 boost::shared_ptr<Port> src = get_port_by_name (s);
527 boost::shared_ptr<Port> dst = get_port_by_name (d);
530 ret = src->connect (d);
532 ret = dst->connect (s);
534 /* neither port is known to us ...hand-off to the PortEngine
537 ret = _backend->connect (s, d);
544 /* already exists - no error, no warning */
545 } else if (ret < 0) {
546 error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
547 source, s, destination, d)
555 PortManager::disconnect (const string& source, const string& destination)
559 string s = make_port_name_non_relative (source);
560 string d = make_port_name_non_relative (destination);
562 boost::shared_ptr<Port> src = get_port_by_name (s);
563 boost::shared_ptr<Port> dst = get_port_by_name (d);
566 ret = src->disconnect (d);
568 ret = dst->disconnect (s);
570 /* neither port is known to us ...hand-off to the PortEngine
573 ret = _backend->disconnect (s, d);
582 PortManager::disconnect (boost::shared_ptr<Port> port)
584 return port->disconnect_all ();
588 PortManager::disconnect (std::string const & name)
590 PortEngine::PortHandle ph = _backend->get_port_by_name (name);
592 return _backend->disconnect_all (ph);
598 PortManager::reestablish_ports ()
602 boost::shared_ptr<Ports> p = ports.reader ();
604 DEBUG_TRACE (DEBUG::Ports, string_compose ("reestablish %1 ports\n", p->size()));
606 for (i = p->begin(); i != p->end(); ++i) {
607 if (i->second->reestablish ()) {
608 error << string_compose (_("Re-establising port %1 failed"), i->second->name()) << endmsg;
609 std::cerr << string_compose (_("Re-establising port %1 failed"), i->second->name()) << std::endl;
624 PortManager::reconnect_ports ()
626 boost::shared_ptr<Ports> p = ports.reader ();
628 if (!Profile->get_trx()) {
629 /* re-establish connections */
631 DEBUG_TRACE (DEBUG::Ports, string_compose ("reconnect %1 ports\n", p->size()));
633 for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
634 i->second->reconnect ();
642 PortManager::connect_callback (const string& a, const string& b, bool conn)
644 boost::shared_ptr<Port> port_a;
645 boost::shared_ptr<Port> port_b;
647 boost::shared_ptr<Ports> pr = ports.reader ();
649 x = pr->find (make_port_name_relative (a));
650 if (x != pr->end()) {
654 x = pr->find (make_port_name_relative (b));
655 if (x != pr->end()) {
659 PortConnectedOrDisconnected (
667 PortManager::registration_callback ()
669 if (!_port_remove_in_progress) {
672 Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
673 midi_info_dirty = true;
676 PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
681 PortManager::can_request_input_monitoring () const
687 return _backend->can_monitor_input ();
691 PortManager::request_input_monitoring (const string& name, bool yn) const
697 PortEngine::PortHandle ph = _backend->get_port_by_name (name);
700 _backend->request_input_monitoring (ph, yn);
705 PortManager::ensure_input_monitoring (const string& name, bool yn) const
711 PortEngine::PortHandle ph = _backend->get_port_by_name (name);
714 _backend->ensure_input_monitoring (ph, yn);
719 PortManager::port_name_size() const
725 return _backend->port_name_size ();
729 PortManager::my_name() const
735 return _backend->my_name();
739 PortManager::graph_order_callback ()
741 if (!_port_remove_in_progress) {
742 GraphReordered(); /* EMIT SIGNAL */
749 PortManager::cycle_start (pframes_t nframes)
751 Port::set_global_port_buffer_offset (0);
752 Port::set_cycle_framecnt (nframes);
754 _cycle_ports = ports.reader ();
756 for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
757 p->second->cycle_start (nframes);
762 PortManager::cycle_end (pframes_t nframes)
764 for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
765 p->second->cycle_end (nframes);
768 for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
769 p->second->flush_buffers (nframes);
772 _cycle_ports.reset ();
778 PortManager::silence (pframes_t nframes, Session *s)
780 for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
781 if (s && i->second == s->mtc_output_port ()) {
784 if (s && i->second == s->midi_clock_output_port ()) {
787 if (s && i->second == s->ltc_output_port ()) {
790 if (boost::dynamic_pointer_cast<AsyncMIDIPort>(i->second)) {
793 if (i->second->sends_output()) {
794 i->second->get_buffer(nframes).silence(nframes);
800 PortManager::silence_outputs (pframes_t nframes)
802 std::vector<std::string> port_names;
803 if (get_ports("", DataType::AUDIO, IsOutput, port_names)) {
804 for (std::vector<std::string>::iterator p = port_names.begin(); p != port_names.end(); ++p) {
805 if (!port_is_mine(*p)) {
808 PortEngine::PortHandle ph = _backend->get_port_by_name (*p);
812 void *buf = _backend->get_buffer(ph, nframes);
816 memset (buf, 0, sizeof(float) * nframes);
820 if (get_ports("", DataType::MIDI, IsOutput, port_names)) {
821 for (std::vector<std::string>::iterator p = port_names.begin(); p != port_names.end(); ++p) {
822 if (!port_is_mine(*p)) {
825 PortEngine::PortHandle ph = _backend->get_port_by_name (*p);
829 void *buf = _backend->get_buffer(ph, nframes);
833 _backend->midi_clear (buf);
839 PortManager::check_monitoring ()
841 for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
845 if (i->second->last_monitor() != (x = i->second->monitoring_input ())) {
846 i->second->set_last_monitor (x);
847 /* XXX I think this is dangerous, due to
848 a likely mutex in the signal handlers ...
850 i->second->MonitorInputChanged (x); /* EMIT SIGNAL */
856 PortManager::fade_out (gain_t base_gain, gain_t gain_step, pframes_t nframes)
858 for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
860 if (i->second->sends_output()) {
862 boost::shared_ptr<AudioPort> ap = boost::dynamic_pointer_cast<AudioPort> (i->second);
864 Sample* s = ap->engine_get_whole_audio_buffer ();
865 gain_t g = base_gain;
867 for (pframes_t n = 0; n < nframes; ++n) {
877 PortManager::port_engine()
884 PortManager::port_is_control_only (std::string const& name)
886 static regex_t compiled_pattern;
887 static string pattern;
889 if (pattern.empty()) {
891 /* This is a list of regular expressions that match ports
892 * related to physical MIDI devices that we do not want to
893 * expose as normal physical ports.
896 const char * const control_only_ports[] = {
897 X_(".*Ableton Push.*"),
898 X_(".*FaderPort .*"),
899 X_(".*FaderPort8 .*"),
903 for (size_t n = 0; n < sizeof (control_only_ports)/sizeof (control_only_ports[0]); ++n) {
907 pattern += control_only_ports[n];
911 regcomp (&compiled_pattern, pattern.c_str(), REG_EXTENDED|REG_NOSUB);
914 return regexec (&compiled_pattern, name.c_str(), 0, 0, 0) == 0;
917 PortManager::MidiPortInformation
918 PortManager::midi_port_information (std::string const & name)
920 Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
922 fill_midi_port_info_locked ();
924 MidiPortInfo::iterator x = midi_port_info.find (name);
926 if (x != midi_port_info.end()) {
930 return MidiPortInformation ();
934 PortManager::get_known_midi_ports (vector<string>& copy)
936 Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
938 fill_midi_port_info_locked ();
940 for (MidiPortInfo::const_iterator x = midi_port_info.begin(); x != midi_port_info.end(); ++x) {
941 copy.push_back (x->first);
946 PortManager::get_midi_selection_ports (vector<string>& copy)
948 Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
950 fill_midi_port_info_locked ();
952 for (MidiPortInfo::const_iterator x = midi_port_info.begin(); x != midi_port_info.end(); ++x) {
953 if (x->second.properties & MidiPortSelection) {
954 copy.push_back (x->first);
960 PortManager::set_midi_port_pretty_name (string const & port, string const & pretty)
963 Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
965 fill_midi_port_info_locked ();
967 MidiPortInfo::iterator x = midi_port_info.find (port);
968 if (x == midi_port_info.end()) {
971 x->second.pretty_name = pretty;
974 /* push into back end */
976 PortEngine::PortHandle ph = _backend->get_port_by_name (port);
979 _backend->set_port_property (ph, "http://jackaudio.org/metadata/pretty-name", pretty, string());
982 MidiPortInfoChanged (); /* EMIT SIGNAL*/
986 PortManager::add_midi_port_flags (string const & port, MidiPortFlags flags)
991 Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
993 fill_midi_port_info_locked ();
995 MidiPortInfo::iterator x = midi_port_info.find (port);
996 if (x != midi_port_info.end()) {
997 if ((x->second.properties & flags) != flags) { // at least one missing
998 x->second.properties = MidiPortFlags (x->second.properties | flags);
1005 if (flags & MidiPortSelection) {
1006 MidiSelectionPortsChanged (); /* EMIT SIGNAL */
1009 if (flags != MidiPortSelection) {
1010 MidiPortInfoChanged (); /* EMIT SIGNAL */
1013 save_midi_port_info ();
1018 PortManager::remove_midi_port_flags (string const & port, MidiPortFlags flags)
1023 Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
1025 fill_midi_port_info_locked ();
1027 MidiPortInfo::iterator x = midi_port_info.find (port);
1028 if (x != midi_port_info.end()) {
1029 if (x->second.properties & flags) { // at least one is set
1030 x->second.properties = MidiPortFlags (x->second.properties & ~flags);
1037 if (flags & MidiPortSelection) {
1038 MidiSelectionPortsChanged (); /* EMIT SIGNAL */
1041 if (flags != MidiPortSelection) {
1042 MidiPortInfoChanged (); /* EMIT SIGNAL */
1045 save_midi_port_info ();
1050 PortManager::midi_port_info_file ()
1052 return Glib::build_filename (user_config_directory(), X_("midi_port_info"));
1056 PortManager::save_midi_port_info ()
1058 string path = midi_port_info_file ();
1060 XMLNode* root = new XMLNode (X_("MidiPortInfo"));
1063 Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
1065 if (midi_port_info.empty()) {
1070 for (MidiPortInfo::iterator i = midi_port_info.begin(); i != midi_port_info.end(); ++i) {
1071 XMLNode* node = new XMLNode (X_("port"));
1072 node->set_property (X_("name"), i->first);
1073 node->set_property (X_("input"), i->second.input);
1074 node->set_property (X_("properties"), i->second.properties);
1075 root->add_child_nocopy (*node);
1081 tree.set_root (root);
1083 if (!tree.write (path)) {
1084 error << string_compose (_("Could not save MIDI port info to %1"), path) << endmsg;
1089 PortManager::load_midi_port_info ()
1091 string path = midi_port_info_file ();
1094 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
1098 if (!tree.read (path)) {
1099 error << string_compose (_("Cannot load MIDI port info from %1"), path) << endmsg;
1103 midi_port_info.clear ();
1105 for (XMLNodeConstIterator i = tree.root()->children().begin(); i != tree.root()->children().end(); ++i) {
1106 MidiPortInformation mpi;
1109 if (!(*i)->get_property (X_("name"), name) ||
1110 !(*i)->get_property (X_("input"), mpi.input) ||
1111 !(*i)->get_property (X_("properties"), mpi.properties)) {
1115 midi_port_info.insert (make_pair (name, mpi));
1120 PortManager::fill_midi_port_info ()
1122 Glib::Threads::Mutex::Lock lm (midi_port_info_mutex);
1123 fill_midi_port_info_locked ();
1127 PortManager::fill_midi_port_info_locked ()
1129 /* MIDI info mutex MUST be held */
1131 if (!midi_info_dirty) {
1135 std::vector<string> ports;
1137 AudioEngine::instance()->get_ports (string(), DataType::MIDI, IsOutput, ports);
1139 for (vector<string>::iterator p = ports.begin(); p != ports.end(); ++p) {
1141 if (port_is_mine (*p)) {
1145 if (midi_port_info.find (*p) == midi_port_info.end()) {
1146 MidiPortInformation mpi;
1147 mpi.pretty_name = *p;
1150 if (port_is_control_only (*p)) {
1151 mpi.properties = MidiPortFlags (mpi.properties | MidiPortControl);
1154 if ((*p.find (X_("Midi Through")) != string::npos ||
1155 (*p).find (X_("Midi-Through")) != string::npos))
1157 mpi.properties = MidiPortFlags (mpi.properties | MidiPortVirtual);
1160 midi_port_info.insert (make_pair (*p, mpi));
1164 AudioEngine::instance()->get_ports (string(), DataType::MIDI, IsInput, ports);
1166 for (vector<string>::iterator p = ports.begin(); p != ports.end(); ++p) {
1168 if (port_is_mine (*p)) {
1172 if (midi_port_info.find (*p) == midi_port_info.end()) {
1173 MidiPortInformation mpi;
1174 mpi.pretty_name = *p;
1177 if (port_is_control_only (*p)) {
1178 mpi.properties = MidiPortFlags (mpi.properties | MidiPortControl);
1181 if ((*p.find (X_("Midi Through")) != string::npos ||
1182 (*p).find (X_("Midi-Through")) != string::npos))
1184 mpi.properties = MidiPortFlags (mpi.properties | MidiPortVirtual);
1187 midi_port_info.insert (make_pair (*p, mpi));
1191 /* now push/pull pretty name information between backend and the
1195 // rg: I don't understand what this attempts to solve
1197 // Naming ports should be left to the backend:
1198 // Ardour cannot associate numeric IDs with corresponding hardware.
1199 // (see also 7dde6c3b)
1201 for (MidiPortInfo::iterator x = midi_port_info.begin(); x != midi_port_info.end(); ++x) {
1202 PortEngine::PortHandle ph = _backend->get_port_by_name (x->first);
1205 /* port info saved from some condition where this port
1206 * existed, but no longer does (i.e. device unplugged
1212 if (!x->second.pretty_name.empty () && x->second.pretty_name != x->first) {
1213 /* name set in port info ... propagate */
1214 _backend->set_port_property (ph, "http://jackaudio.org/metadata/pretty-name", x->second.pretty_name, string());
1216 /* check with backend for pre-existing pretty name */
1219 if (0 == _backend->get_port_property (ph,
1220 "http://jackaudio.org/metadata/pretty-name",
1222 x->second.pretty_name = value;
1227 midi_info_dirty = false;