infrastructure for MIDI-input-follows-selection
[ardour.git] / libs / ardour / port_manager.cc
1 /*
2     Copyright (C) 2013 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #ifdef COMPILER_MSVC
21 #include <io.h> // Microsoft's nearest equivalent to <unistd.h>
22 #include <ardourext/misc.h>
23 #else
24 #include <regex.h>
25 #endif
26
27 #include "pbd/convert.h"
28 #include "pbd/error.h"
29
30 #include "ardour/async_midi_port.h"
31 #include "ardour/audio_backend.h"
32 #include "ardour/audio_port.h"
33 #include "ardour/debug.h"
34 #include "ardour/midi_port.h"
35 #include "ardour/midiport_manager.h"
36 #include "ardour/port_manager.h"
37 #include "ardour/profile.h"
38 #include "ardour/session.h"
39
40 #include "pbd/i18n.h"
41
42 using namespace ARDOUR;
43 using namespace PBD;
44 using std::string;
45 using std::vector;
46
47 PortManager::PortManager ()
48         : ports (new Ports)
49         , _port_remove_in_progress (false)
50         , _port_deletions_pending (8192) /* ick, arbitrary sizing */
51 {
52 }
53
54 void
55 PortManager::clear_pending_port_deletions ()
56 {
57         Port* p;
58
59         DEBUG_TRACE (DEBUG::Ports, string_compose ("pending port deletions: %1\n", _port_deletions_pending.read_space()));
60
61         while (_port_deletions_pending.read (&p, 1) == 1) {
62                 delete p;
63         }
64 }
65
66 void
67 PortManager::remove_all_ports ()
68 {
69         /* make sure that JACK callbacks that will be invoked as we cleanup
70          * ports know that they have nothing to do.
71          */
72
73         _port_remove_in_progress = true;
74
75         /* process lock MUST be held by caller
76         */
77
78         {
79                 RCUWriter<Ports> writer (ports);
80                 boost::shared_ptr<Ports> ps = writer.get_copy ();
81                 ps->clear ();
82         }
83
84         /* clear dead wood list in RCU */
85
86         ports.flush ();
87
88         /* clear out pending port deletion list. we know this is safe because
89          * the auto connect thread in Session is already dead when this is
90          * done. It doesn't use shared_ptr<Port> anyway.
91          */
92
93         _port_deletions_pending.reset ();
94
95         _port_remove_in_progress = false;
96 }
97
98
99 string
100 PortManager::make_port_name_relative (const string& portname) const
101 {
102         if (!_backend) {
103                 return portname;
104         }
105
106         string::size_type colon = portname.find (':');
107
108         if (colon == string::npos) {
109                 return portname;
110         }
111
112         if (portname.substr (0, colon) == _backend->my_name()) {
113                 return portname.substr (colon+1);
114         }
115
116         return portname;
117 }
118
119 string
120 PortManager::make_port_name_non_relative (const string& portname) const
121 {
122         string str;
123
124         if (portname.find_first_of (':') != string::npos) {
125                 return portname;
126         }
127
128         str  = _backend->my_name();
129         str += ':';
130         str += portname;
131
132         return str;
133 }
134
135 std::string
136 PortManager::get_pretty_name_by_name(const std::string& portname) const
137 {
138         PortEngine::PortHandle ph = _backend->get_port_by_name (portname);
139         if (ph) {
140                 std::string value;
141                 std::string type;
142                 if (0 == _backend->get_port_property (ph,
143                                         "http://jackaudio.org/metadata/pretty-name",
144                                         value, type))
145                 {
146                         return value;
147                 }
148         }
149         return "";
150 }
151
152 bool
153 PortManager::port_is_mine (const string& portname) const
154 {
155         if (!_backend) {
156                 return true;
157         }
158
159         string self = _backend->my_name();
160
161         if (portname.find_first_of (':') != string::npos) {
162                 if (portname.substr (0, self.length ()) != self) {
163                         return false;
164                 }
165         }
166
167         return true;
168 }
169
170 bool
171 PortManager::port_is_physical (const std::string& portname) const
172 {
173         if (!_backend) {
174                 return false;
175         }
176
177         PortEngine::PortHandle ph = _backend->get_port_by_name (portname);
178         if (!ph) {
179                 return false;
180         }
181
182         return _backend->port_is_physical (ph);
183 }
184
185 void
186 PortManager::get_physical_outputs (DataType type, std::vector<std::string>& s)
187 {
188         if (!_backend) {
189                 s.clear ();
190                 return;
191         }
192         _backend->get_physical_outputs (type, s);
193 }
194
195 void
196 PortManager::get_physical_inputs (DataType type, std::vector<std::string>& s)
197 {
198         if (!_backend) {
199                 s.clear ();
200                 return;
201         }
202
203         _backend->get_physical_inputs (type, s);
204 }
205
206 ChanCount
207 PortManager::n_physical_outputs () const
208 {
209         if (!_backend) {
210                 return ChanCount::ZERO;
211         }
212
213         return _backend->n_physical_outputs ();
214 }
215
216 ChanCount
217 PortManager::n_physical_inputs () const
218 {
219         if (!_backend) {
220                 return ChanCount::ZERO;
221         }
222         return _backend->n_physical_inputs ();
223 }
224
225 /** @param name Full or short name of port
226  *  @return Corresponding Port or 0.
227  */
228
229 boost::shared_ptr<Port>
230 PortManager::get_port_by_name (const string& portname)
231 {
232         if (!_backend) {
233                 return boost::shared_ptr<Port>();
234         }
235
236         if (!port_is_mine (portname)) {
237                 /* not an ardour port */
238                 return boost::shared_ptr<Port> ();
239         }
240
241         boost::shared_ptr<Ports> pr = ports.reader();
242         std::string rel = make_port_name_relative (portname);
243         Ports::iterator x = pr->find (rel);
244
245         if (x != pr->end()) {
246                 /* its possible that the port was renamed by some 3rd party and
247                    we don't know about it. check for this (the check is quick
248                    and cheap), and if so, rename the port (which will alter
249                    the port map as a side effect).
250                 */
251                 const std::string check = make_port_name_relative (_backend->get_port_name (x->second->port_handle()));
252                 if (check != rel) {
253                         x->second->set_name (check);
254                 }
255                 return x->second;
256         }
257
258         return boost::shared_ptr<Port> ();
259 }
260
261 void
262 PortManager::port_renamed (const std::string& old_relative_name, const std::string& new_relative_name)
263 {
264         RCUWriter<Ports> writer (ports);
265         boost::shared_ptr<Ports> p = writer.get_copy();
266         Ports::iterator x = p->find (old_relative_name);
267
268         if (x != p->end()) {
269                 boost::shared_ptr<Port> port = x->second;
270                 p->erase (x);
271                 p->insert (make_pair (new_relative_name, port));
272         }
273 }
274
275 int
276 PortManager::get_ports (DataType type, PortList& pl)
277 {
278         boost::shared_ptr<Ports> plist = ports.reader();
279         for (Ports::iterator p = plist->begin(); p != plist->end(); ++p) {
280                 if (p->second->type() == type) {
281                         pl.push_back (p->second);
282                 }
283         }
284         return pl.size();
285 }
286
287 int
288 PortManager::get_ports (const string& port_name_pattern, DataType type, PortFlags flags, vector<string>& s)
289 {
290         s.clear();
291
292         if (!_backend) {
293                 return 0;
294         }
295
296         return _backend->get_ports (port_name_pattern, type, flags, s);
297 }
298
299 void
300 PortManager::port_registration_failure (const std::string& portname)
301 {
302         if (!_backend) {
303                 return;
304         }
305
306         string full_portname = _backend->my_name();
307         full_portname += ':';
308         full_portname += portname;
309
310
311         PortEngine::PortHandle p = _backend->get_port_by_name (full_portname);
312         string reason;
313
314         if (p) {
315                 reason = string_compose (_("a port with the name \"%1\" already exists: check for duplicated track/bus names"), portname);
316         } else {
317                 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);
318         }
319
320         throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
321 }
322
323 struct PortDeleter
324 {
325         void operator() (Port* p) {
326                 AudioEngine::instance()->add_pending_port_deletion (p);
327         }
328 };
329
330 boost::shared_ptr<Port>
331 PortManager::register_port (DataType dtype, const string& portname, bool input, bool async, PortFlags flags)
332 {
333         boost::shared_ptr<Port> newport;
334
335         /* limit the possible flags that can be set */
336
337         flags = PortFlags (flags & (Hidden|Shadow|IsTerminal));
338
339         try {
340                 if (dtype == DataType::AUDIO) {
341                         DEBUG_TRACE (DEBUG::Ports, string_compose ("registering AUDIO port %1, input %2\n",
342                                                                    portname, input));
343                         newport.reset (new AudioPort (portname, PortFlags ((input ? IsInput : IsOutput) | flags)),
344                                        PortDeleter());
345                 } else if (dtype == DataType::MIDI) {
346                         if (async) {
347                                 DEBUG_TRACE (DEBUG::Ports, string_compose ("registering ASYNC MIDI port %1, input %2\n",
348                                                                            portname, input));
349                                 newport.reset (new AsyncMIDIPort (portname, PortFlags ((input ? IsInput : IsOutput) | flags)),
350                                                PortDeleter());
351                         } else {
352                                 DEBUG_TRACE (DEBUG::Ports, string_compose ("registering MIDI port %1, input %2\n",
353                                                                            portname, input));
354                                 newport.reset (new MidiPort (portname, PortFlags ((input ? IsInput : IsOutput) | flags)),
355                                                PortDeleter());
356                         }
357                 } else {
358                         throw PortRegistrationFailure("unable to create port (unknown type)");
359                 }
360
361                 RCUWriter<Ports> writer (ports);
362                 boost::shared_ptr<Ports> ps = writer.get_copy ();
363                 ps->insert (make_pair (make_port_name_relative (portname), newport));
364
365                 /* writer goes out of scope, forces update */
366
367         }
368
369         catch (PortRegistrationFailure& err) {
370                 throw err;
371         } catch (std::exception& e) {
372                 throw PortRegistrationFailure(string_compose(
373                                 _("unable to create port: %1"), e.what()).c_str());
374         } catch (...) {
375                 throw PortRegistrationFailure("unable to create port (unknown error)");
376         }
377
378         DEBUG_TRACE (DEBUG::Ports, string_compose ("\t%2 port registration success, ports now = %1\n", ports.reader()->size(), this));
379         return newport;
380 }
381
382 boost::shared_ptr<Port>
383 PortManager::register_input_port (DataType type, const string& portname, bool async, PortFlags extra_flags)
384 {
385         return register_port (type, portname, true, async, extra_flags);
386 }
387
388 boost::shared_ptr<Port>
389 PortManager::register_output_port (DataType type, const string& portname, bool async, PortFlags extra_flags)
390 {
391         return register_port (type, portname, false, async, extra_flags);
392 }
393
394 int
395 PortManager::unregister_port (boost::shared_ptr<Port> port)
396 {
397         /* This is a little subtle. We do not call the backend's port
398          * unregistration code from here. That is left for the Port
399          * destructor. We are trying to drop references to the Port object
400          * here, so that its destructor will run and it will unregister itself.
401          */
402
403         /* caller must hold process lock */
404
405         {
406                 RCUWriter<Ports> writer (ports);
407                 boost::shared_ptr<Ports> ps = writer.get_copy ();
408                 Ports::iterator x = ps->find (make_port_name_relative (port->name()));
409
410                 if (x != ps->end()) {
411                         DEBUG_TRACE (DEBUG::Ports, string_compose ("removing %1 from port map (uc=%2)\n", port->name(), port.use_count()));
412                         ps->erase (x);
413                 }
414
415                 /* writer goes out of scope, forces update */
416         }
417
418         ports.flush ();
419
420         return 0;
421 }
422
423 bool
424 PortManager::connected (const string& port_name)
425 {
426         if (!_backend) {
427                 return false;
428         }
429
430         PortEngine::PortHandle handle = _backend->get_port_by_name (port_name);
431
432         if (!handle) {
433                 return false;
434         }
435
436         return _backend->connected (handle);
437 }
438
439 bool
440 PortManager::physically_connected (const string& port_name)
441 {
442         if (!_backend) {
443                 return false;
444         }
445
446         PortEngine::PortHandle handle = _backend->get_port_by_name (port_name);
447
448         if (!handle) {
449                 return false;
450         }
451
452         return _backend->physically_connected (handle);
453 }
454
455 int
456 PortManager::get_connections (const string& port_name, std::vector<std::string>& s)
457 {
458         if (!_backend) {
459                 s.clear ();
460                 return 0;
461         }
462
463         PortEngine::PortHandle handle = _backend->get_port_by_name (port_name);
464
465         if (!handle) {
466                 s.clear ();
467                 return 0;
468         }
469
470         return _backend->get_connections (handle, s);
471 }
472
473 int
474 PortManager::connect (const string& source, const string& destination)
475 {
476         int ret;
477
478         string s = make_port_name_non_relative (source);
479         string d = make_port_name_non_relative (destination);
480
481         boost::shared_ptr<Port> src = get_port_by_name (s);
482         boost::shared_ptr<Port> dst = get_port_by_name (d);
483
484         if (src) {
485                 ret = src->connect (d);
486         } else if (dst) {
487                 ret = dst->connect (s);
488         } else {
489                 /* neither port is known to us ...hand-off to the PortEngine
490                  */
491                 if (_backend) {
492                         ret = _backend->connect (s, d);
493                 } else {
494                         ret = -1;
495                 }
496         }
497
498         if (ret > 0) {
499                 /* already exists - no error, no warning */
500         } else if (ret < 0) {
501                 error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
502                                         source, s, destination, d)
503                       << endmsg;
504         }
505
506         return ret;
507 }
508
509 int
510 PortManager::disconnect (const string& source, const string& destination)
511 {
512         int ret;
513
514         string s = make_port_name_non_relative (source);
515         string d = make_port_name_non_relative (destination);
516
517         boost::shared_ptr<Port> src = get_port_by_name (s);
518         boost::shared_ptr<Port> dst = get_port_by_name (d);
519
520         if (src) {
521                 ret = src->disconnect (d);
522         } else if (dst) {
523                 ret = dst->disconnect (s);
524         } else {
525                 /* neither port is known to us ...hand-off to the PortEngine
526                  */
527                 if (_backend) {
528                         ret = _backend->disconnect (s, d);
529                 } else {
530                         ret = -1;
531                 }
532         }
533         return ret;
534 }
535
536 int
537 PortManager::disconnect (boost::shared_ptr<Port> port)
538 {
539         return port->disconnect_all ();
540 }
541
542 int
543 PortManager::reestablish_ports ()
544 {
545         Ports::iterator i;
546
547         boost::shared_ptr<Ports> p = ports.reader ();
548
549         DEBUG_TRACE (DEBUG::Ports, string_compose ("reestablish %1 ports\n", p->size()));
550
551         for (i = p->begin(); i != p->end(); ++i) {
552                 if (i->second->reestablish ()) {
553                         error << string_compose (_("Re-establising port %1 failed"), i->second->name()) << endmsg;
554                         std::cerr << string_compose (_("Re-establising port %1 failed"), i->second->name()) << std::endl;
555                         break;
556                 }
557         }
558
559         if (i != p->end()) {
560                 /* failed */
561                 remove_all_ports ();
562                 return -1;
563         }
564
565         return 0;
566 }
567
568 int
569 PortManager::reconnect_ports ()
570 {
571         boost::shared_ptr<Ports> p = ports.reader ();
572
573         if (!Profile->get_trx()) {
574                 /* re-establish connections */
575
576                 DEBUG_TRACE (DEBUG::Ports, string_compose ("reconnect %1 ports\n", p->size()));
577
578                 for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
579                         i->second->reconnect ();
580                 }
581         }
582
583         return 0;
584 }
585
586 void
587 PortManager::connect_callback (const string& a, const string& b, bool conn)
588 {
589         boost::shared_ptr<Port> port_a;
590         boost::shared_ptr<Port> port_b;
591         Ports::iterator x;
592         boost::shared_ptr<Ports> pr = ports.reader ();
593
594         x = pr->find (make_port_name_relative (a));
595         if (x != pr->end()) {
596                 port_a = x->second;
597         }
598
599         x = pr->find (make_port_name_relative (b));
600         if (x != pr->end()) {
601                 port_b = x->second;
602         }
603
604         PortConnectedOrDisconnected (
605                 port_a, a,
606                 port_b, b,
607                 conn
608                 ); /* EMIT SIGNAL */
609 }
610
611 void
612 PortManager::registration_callback ()
613 {
614         if (!_port_remove_in_progress) {
615                 PortRegisteredOrUnregistered (); /* EMIT SIGNAL */
616         }
617 }
618
619 bool
620 PortManager::can_request_input_monitoring () const
621 {
622         if (!_backend) {
623                 return false;
624         }
625
626         return _backend->can_monitor_input ();
627 }
628
629 void
630 PortManager::request_input_monitoring (const string& name, bool yn) const
631 {
632         if (!_backend) {
633                 return;
634         }
635
636         PortEngine::PortHandle ph = _backend->get_port_by_name (name);
637
638         if (ph) {
639                 _backend->request_input_monitoring (ph, yn);
640         }
641 }
642
643 void
644 PortManager::ensure_input_monitoring (const string& name, bool yn) const
645 {
646         if (!_backend) {
647                 return;
648         }
649
650         PortEngine::PortHandle ph = _backend->get_port_by_name (name);
651
652         if (ph) {
653                 _backend->ensure_input_monitoring (ph, yn);
654         }
655 }
656
657 uint32_t
658 PortManager::port_name_size() const
659 {
660         if (!_backend) {
661                 return 0;
662         }
663
664         return _backend->port_name_size ();
665 }
666
667 string
668 PortManager::my_name() const
669 {
670         if (!_backend) {
671                 return string();
672         }
673
674         return _backend->my_name();
675 }
676
677 int
678 PortManager::graph_order_callback ()
679 {
680         if (!_port_remove_in_progress) {
681                 GraphReordered(); /* EMIT SIGNAL */
682         }
683
684         return 0;
685 }
686
687 void
688 PortManager::cycle_start (pframes_t nframes)
689 {
690         Port::set_global_port_buffer_offset (0);
691         Port::set_cycle_framecnt (nframes);
692
693         _cycle_ports = ports.reader ();
694
695         for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
696                 p->second->cycle_start (nframes);
697         }
698 }
699
700 void
701 PortManager::cycle_end (pframes_t nframes)
702 {
703         for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
704                 p->second->cycle_end (nframes);
705         }
706
707         for (Ports::iterator p = _cycle_ports->begin(); p != _cycle_ports->end(); ++p) {
708                 p->second->flush_buffers (nframes);
709         }
710
711         _cycle_ports.reset ();
712
713         /* we are done */
714 }
715
716 void
717 PortManager::silence (pframes_t nframes, Session *s)
718 {
719         for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
720                 if (s && i->second == s->mtc_output_port ()) {
721                         continue;
722                 }
723                 if (s && i->second == s->midi_clock_output_port ()) {
724                         continue;
725                 }
726                 if (s && i->second == s->ltc_output_port ()) {
727                         continue;
728                 }
729                 if (boost::dynamic_pointer_cast<AsyncMIDIPort>(i->second)) {
730                         continue;
731                 }
732                 if (i->second->sends_output()) {
733                         i->second->get_buffer(nframes).silence(nframes);
734                 }
735         }
736 }
737
738 void
739 PortManager::silence_outputs (pframes_t nframes)
740 {
741         std::vector<std::string> port_names;
742         if (get_ports("", DataType::AUDIO, IsOutput, port_names)) {
743                 for (std::vector<std::string>::iterator p = port_names.begin(); p != port_names.end(); ++p) {
744                         if (!port_is_mine(*p)) {
745                                 continue;
746                         }
747                         PortEngine::PortHandle ph = _backend->get_port_by_name (*p);
748                         if (!ph) {
749                                 continue;
750                         }
751                         void *buf = _backend->get_buffer(ph, nframes);
752                         if (!buf) {
753                                 continue;
754                         }
755                         memset (buf, 0, sizeof(float) * nframes);
756                 }
757         }
758
759         if (get_ports("", DataType::MIDI, IsOutput, port_names)) {
760                 for (std::vector<std::string>::iterator p = port_names.begin(); p != port_names.end(); ++p) {
761                         if (!port_is_mine(*p)) {
762                                 continue;
763                         }
764                         PortEngine::PortHandle ph = _backend->get_port_by_name (*p);
765                         if (!ph) {
766                                 continue;
767                         }
768                         void *buf = _backend->get_buffer(ph, nframes);
769                         if (!buf) {
770                                 continue;
771                         }
772                         _backend->midi_clear (buf);
773                 }
774         }
775 }
776
777 void
778 PortManager::check_monitoring ()
779 {
780         for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
781
782                 bool x;
783
784                 if (i->second->last_monitor() != (x = i->second->monitoring_input ())) {
785                         i->second->set_last_monitor (x);
786                         /* XXX I think this is dangerous, due to
787                            a likely mutex in the signal handlers ...
788                         */
789                         i->second->MonitorInputChanged (x); /* EMIT SIGNAL */
790                 }
791         }
792 }
793
794 void
795 PortManager::fade_out (gain_t base_gain, gain_t gain_step, pframes_t nframes)
796 {
797         for (Ports::iterator i = _cycle_ports->begin(); i != _cycle_ports->end(); ++i) {
798
799                 if (i->second->sends_output()) {
800
801                         boost::shared_ptr<AudioPort> ap = boost::dynamic_pointer_cast<AudioPort> (i->second);
802                         if (ap) {
803                                 Sample* s = ap->engine_get_whole_audio_buffer ();
804                                 gain_t g = base_gain;
805
806                                 for (pframes_t n = 0; n < nframes; ++n) {
807                                         *s++ *= g;
808                                         g -= gain_step;
809                                 }
810                         }
811                 }
812         }
813 }
814
815 PortEngine&
816 PortManager::port_engine()
817 {
818         assert (_backend);
819         return *_backend;
820 }
821
822 bool
823 PortManager::port_is_control_only (std::string const& name)
824 {
825         static regex_t compiled_pattern;
826         static string pattern;
827
828         if (pattern.empty()) {
829
830                 /* This is a list of regular expressions that match ports
831                  * related to physical MIDI devices that we do not want to
832                  * expose as normal physical ports.
833                  */
834
835                 const char * const control_only_ports[] = {
836                         X_(".*Ableton Push.*"),
837                         X_(".*FaderPort .*"),
838                 };
839
840                 pattern = "(";
841                 for (size_t n = 0; n < sizeof (control_only_ports)/sizeof (control_only_ports[0]); ++n) {
842                         if (n > 0) {
843                                 pattern += '|';
844                         }
845                         pattern += control_only_ports[n];
846                 }
847                 pattern += ')';
848
849                 regcomp (&compiled_pattern, pattern.c_str(), REG_EXTENDED|REG_NOSUB);
850         }
851
852         return regexec (&compiled_pattern, name.c_str(), 0, 0, 0) == 0;
853 }
854
855 bool
856 PortManager::port_is_for_midi_selection (std::string const & name)
857 {
858         Glib::Threads::Mutex::Lock lm (midi_selection_ports_mutex);
859         return find (_midi_selection_ports.begin(), _midi_selection_ports.end(), name) != _midi_selection_ports.end();
860 }
861
862 void
863 PortManager::get_midi_selection_ports (MidiSelectionPorts& copy) const
864 {
865         Glib::Threads::Mutex::Lock lm (midi_selection_ports_mutex);
866         copy = _midi_selection_ports;
867 }
868
869 void
870 PortManager::add_to_midi_selection_ports (string const & port)
871 {
872         bool emit = false;
873
874         {
875                 Glib::Threads::Mutex::Lock lm (midi_selection_ports_mutex);
876                 if (find (_midi_selection_ports.begin(), _midi_selection_ports.end(), port) == _midi_selection_ports.end()) {
877                         _midi_selection_ports.push_back (port);
878                         emit = true;
879                 }
880         }
881
882         if (emit) {
883                 MidiSelectionPortsChanged (); /* EMIT SIGNAL */
884         }
885 }
886
887 void
888 PortManager::remove_from_midi_selection_ports (string const & port)
889 {
890         bool emit = false;
891
892         {
893                 Glib::Threads::Mutex::Lock lm (midi_selection_ports_mutex);
894                 MidiSelectionPorts::iterator x = find (_midi_selection_ports.begin(), _midi_selection_ports.end(), port);
895                 if (x != _midi_selection_ports.end()) {
896                         _midi_selection_ports.erase (x);
897                         emit = true;
898                 }
899         }
900
901         if (emit) {
902                 MidiSelectionPortsChanged (); /* EMIT SIGNAL */
903         }
904 }
905
906 void
907 PortManager::clear_midi_selection_ports ()
908 {
909         {
910                 Glib::Threads::Mutex::Lock lm (midi_selection_ports_mutex);
911                 _midi_selection_ports.clear ();
912         }
913
914         MidiSelectionPortsChanged (); /* EMIT SIGNAL */
915 }