Make ContourdesignControlProtocol::_button_actions private again ...
[ardour.git] / libs / surfaces / generic_midi / generic_midi_control_protocol.cc
1 /*
2     Copyright (C) 2006 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 #include <stdint.h>
21
22 #include <sstream>
23 #include <algorithm>
24
25 #ifdef COMPILER_MSVC
26 #include <io.h> // Microsoft's nearest equivalent to <unistd.h>
27 #include <ardourext/misc.h>
28 #else
29 #include <regex.h>
30 #endif
31
32 #include <glibmm/fileutils.h>
33 #include <glibmm/miscutils.h>
34
35 #include "pbd/compose.h"
36 #include "pbd/convert.h"
37 #include "pbd/error.h"
38 #include "pbd/failed_constructor.h"
39 #include "pbd/file_utils.h"
40 #include "pbd/strsplit.h"
41 #include "pbd/types_convert.h"
42 #include "pbd/xml++.h"
43
44 #include "midi++/port.h"
45
46 #include "ardour/async_midi_port.h"
47 #include "ardour/audioengine.h"
48 #include "ardour/auditioner.h"
49 #include "ardour/filesystem_paths.h"
50 #include "ardour/session.h"
51 #include "ardour/midi_ui.h"
52 #include "ardour/plugin_insert.h"
53 #include "ardour/rc_configuration.h"
54 #include "ardour/midiport_manager.h"
55 #include "ardour/debug.h"
56
57 #include "generic_midi_control_protocol.h"
58 #include "midicontrollable.h"
59 #include "midifunction.h"
60 #include "midiaction.h"
61
62 using namespace ARDOUR;
63 using namespace PBD;
64 using namespace std;
65
66 #include "pbd/i18n.h"
67
68 #define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
69
70 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
71         : ControlProtocol (s, _("Generic MIDI"))
72         , connection_state (ConnectionState (0))
73         , _motorised (false)
74         , _threshold (10)
75         , gui (0)
76 {
77         _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort> (s.midi_input_port ());
78         _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort> (s.midi_output_port ());
79
80         _input_bundle.reset (new ARDOUR::Bundle (_("Generic MIDI Control In"), true));
81         _output_bundle.reset (new ARDOUR::Bundle (_("Generic MIDI Control Out"), false));
82
83         _input_bundle->add_channel (
84                 boost::static_pointer_cast<MidiPort>(_input_port)->name(),
85                 ARDOUR::DataType::MIDI,
86                 session->engine().make_port_name_non_relative (boost::static_pointer_cast<MidiPort>(_input_port)->name())
87                 );
88
89         _output_bundle->add_channel (
90                 boost::static_pointer_cast<MidiPort>(_output_port)->name(),
91                 ARDOUR::DataType::MIDI,
92                 session->engine().make_port_name_non_relative (boost::static_pointer_cast<MidiPort>(_output_port)->name())
93                 );
94
95         session->BundleAddedOrRemoved ();
96
97         do_feedback = false;
98         _feedback_interval = 10000; // microseconds
99         last_feedback_time = 0;
100
101         _current_bank = 0;
102         _bank_size = 0;
103
104         /* these signals are emitted by the MidiControlUI's event loop thread
105          * and we may as well handle them right there in the same the same
106          * thread
107          */
108
109         Controllable::StartLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::start_learning, this, _1));
110                                          Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1));
111
112         /* this signal is emitted by the process() callback, and if
113          * send_feedback() is going to do anything, it should do it in the
114          * context of the process() callback itself.
115          */
116
117         Session::SendFeedback.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::send_feedback, this));
118         //Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
119
120         /* this one is cross-thread */
121
122         PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), midi_ui_context());
123
124         /* Catch port connections and disconnections (cross-thread) */
125         ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR,
126                                                                               boost::bind (&GenericMidiControlProtocol::connection_handler, this, _1, _2, _3, _4, _5),
127                                                                               midi_ui_context());
128
129         reload_maps ();
130 }
131
132 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
133 {
134         drop_all ();
135         tear_down_gui ();
136 }
137
138 list<boost::shared_ptr<ARDOUR::Bundle> >
139 GenericMidiControlProtocol::bundles ()
140 {
141         list<boost::shared_ptr<ARDOUR::Bundle> > b;
142
143         if (_input_bundle) {
144                 b.push_back (_input_bundle);
145                 b.push_back (_output_bundle);
146         }
147
148         return b;
149 }
150
151
152 static const char * const midimap_env_variable_name = "ARDOUR_MIDIMAPS_PATH";
153 static const char* const midi_map_dir_name = "midi_maps";
154 static const char* const midi_map_suffix = ".map";
155
156 Searchpath
157 system_midi_map_search_path ()
158 {
159         bool midimap_path_defined = false;
160         std::string spath_env (Glib::getenv (midimap_env_variable_name, midimap_path_defined));
161
162         if (midimap_path_defined) {
163                 return spath_env;
164         }
165
166         Searchpath spath (ardour_data_search_path());
167         spath.add_subdirectory_to_paths(midi_map_dir_name);
168         return spath;
169 }
170
171 static std::string
172 user_midi_map_directory ()
173 {
174         return Glib::build_filename (user_config_directory(), midi_map_dir_name);
175 }
176
177 static bool
178 midi_map_filter (const string &str, void* /*arg*/)
179 {
180         return (str.length() > strlen(midi_map_suffix) &&
181                 str.find (midi_map_suffix) == (str.length() - strlen (midi_map_suffix)));
182 }
183
184 void
185 GenericMidiControlProtocol::reload_maps ()
186 {
187         vector<string> midi_maps;
188         Searchpath spath (system_midi_map_search_path());
189         spath += user_midi_map_directory ();
190
191         find_files_matching_filter (midi_maps, spath, midi_map_filter, 0, false, true);
192
193         if (midi_maps.empty()) {
194                 cerr << "No MIDI maps found using " << spath.to_string() << endl;
195                 return;
196         }
197
198         for (vector<string>::iterator i = midi_maps.begin(); i != midi_maps.end(); ++i) {
199                 string fullpath = *i;
200
201                 XMLTree tree;
202
203                 if (!tree.read (fullpath.c_str())) {
204                         continue;
205                 }
206
207                 MapInfo mi;
208
209                 std::string str;
210                 if (!tree.root()->get_property ("name", str)) {
211                         continue;
212                 }
213
214                 mi.name = str;
215                 mi.path = fullpath;
216
217                 map_info.push_back (mi);
218         }
219 }
220
221 void
222 GenericMidiControlProtocol::drop_all ()
223 {
224         DEBUG_TRACE (DEBUG::GenericMidi, "Drop all bindings\n");
225         Glib::Threads::Mutex::Lock lm (pending_lock);
226         Glib::Threads::Mutex::Lock lm2 (controllables_lock);
227
228         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
229                 delete *i;
230         }
231         controllables.clear ();
232
233         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
234                 (*i)->connection.disconnect();
235                 if ((*i)->own_mc) {
236                         delete (*i)->mc;
237                 }
238                 delete *i;
239         }
240         pending_controllables.clear ();
241
242         for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
243                 delete *i;
244         }
245         functions.clear ();
246
247         for (MIDIActions::iterator i = actions.begin(); i != actions.end(); ++i) {
248                 delete *i;
249         }
250         actions.clear ();
251 }
252
253 void
254 GenericMidiControlProtocol::drop_bindings ()
255 {
256         DEBUG_TRACE (DEBUG::GenericMidi, "Drop bindings, leave learned\n");
257         Glib::Threads::Mutex::Lock lm2 (controllables_lock);
258
259         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
260                 if (!(*i)->learned()) {
261                         delete *i;
262                         i = controllables.erase (i);
263                 } else {
264                         ++i;
265                 }
266         }
267
268         for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
269                 delete *i;
270         }
271         functions.clear ();
272
273         _current_binding = "";
274         _bank_size = 0;
275         _current_bank = 0;
276 }
277
278 int
279 GenericMidiControlProtocol::set_active (bool /*yn*/)
280 {
281         /* nothing to do here: the MIDI UI thread in libardour handles all our
282            I/O needs.
283         */
284         return 0;
285 }
286
287 void
288 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
289 {
290         _feedback_interval = ms;
291 }
292
293 void
294 GenericMidiControlProtocol::send_feedback ()
295 {
296         /* This is executed in RT "process" context", so no blocking calls
297          */
298
299         if (!do_feedback) {
300                 return;
301         }
302
303         microseconds_t now = get_microseconds ();
304
305         if (last_feedback_time != 0) {
306                 if ((now - last_feedback_time) < _feedback_interval) {
307                         return;
308                 }
309         }
310
311         _send_feedback ();
312
313         last_feedback_time = now;
314 }
315
316 void
317 GenericMidiControlProtocol::_send_feedback ()
318 {
319         /* This is executed in RT "process" context", so no blocking calls
320          */
321
322         const int32_t bufsize = 16 * 1024; /* XXX too big */
323         MIDI::byte buf[bufsize];
324         int32_t bsize = bufsize;
325
326         /* XXX: due to bugs in some ALSA / JACK MIDI bridges, we have to do separate
327            writes for each controllable here; if we send more than one MIDI message
328            in a single jack_midi_event_write then some bridges will only pass the
329            first on to ALSA.
330         */
331
332         Glib::Threads::Mutex::Lock lm (controllables_lock, Glib::Threads::TRY_LOCK);
333         if (!lm.locked ()) {
334                 return;
335         }
336
337         for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
338                 MIDI::byte* end = (*r)->write_feedback (buf, bsize);
339                 if (end != buf) {
340                         _output_port->write (buf, (int32_t) (end - buf), 0);
341                 }
342         }
343 }
344
345 bool
346 GenericMidiControlProtocol::start_learning (boost::weak_ptr <Controllable> wc)
347 {
348         boost::shared_ptr<Controllable> c = wc.lock ();
349         if (!c) {
350                 return false;
351         }
352
353         Glib::Threads::Mutex::Lock lm2 (controllables_lock);
354         DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Learn binding: Controlable number: %1\n", c));
355
356         /* drop any existing mappings for the same controllable for which
357          * learning has just started.
358          */
359
360         MIDIControllables::iterator tmp;
361         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
362                 tmp = i;
363                 ++tmp;
364                 if ((*i)->get_controllable() == c) {
365                         delete (*i);
366                         controllables.erase (i);
367                 }
368                 i = tmp;
369         }
370
371         /* check pending controllables (those for which a learn is underway) to
372          * see if it is for the same one for which learning has just started.
373          */
374
375         {
376                 Glib::Threads::Mutex::Lock lm (pending_lock);
377
378                 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
379                         if (((*i)->mc)->get_controllable() == c) {
380                                 (*i)->connection.disconnect();
381                                 if ((*i)->own_mc) {
382                                         delete (*i)->mc;
383                                 }
384                                 delete *i;
385                                 i = pending_controllables.erase (i);
386                         } else {
387                                 ++i;
388                         }
389                 }
390         }
391
392         MIDIControllable* mc = 0;
393         bool own_mc = false;
394
395         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
396                 if ((*i)->get_controllable() && ((*i)->get_controllable()->id() == c->id())) {
397                         mc = *i;
398                         break;
399                 }
400         }
401
402         if (!mc) {
403                 mc = new MIDIControllable (this, *_input_port->parser(), c, false);
404                 own_mc = true;
405         }
406
407         /* stuff the new controllable into pending */
408
409         {
410                 Glib::Threads::Mutex::Lock lm (pending_lock);
411
412                 MIDIPendingControllable* element = new MIDIPendingControllable (mc, own_mc);
413                 c->LearningFinished.connect_same_thread (element->connection, boost::bind (&GenericMidiControlProtocol::learning_stopped, this, mc));
414
415                 pending_controllables.push_back (element);
416         }
417         mc->learn_about_external_control ();
418         return true;
419 }
420
421 void
422 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
423 {
424         Glib::Threads::Mutex::Lock lm (pending_lock);
425         Glib::Threads::Mutex::Lock lm2 (controllables_lock);
426
427         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
428                 if ( (*i)->mc == mc) {
429                         (*i)->connection.disconnect();
430                         delete *i;
431                         i = pending_controllables.erase(i);
432                 } else {
433                         ++i;
434                 }
435         }
436
437         /* add the controllable for which learning stopped to our list of
438          * controllables
439          */
440
441         controllables.push_back (mc);
442 }
443
444 void
445 GenericMidiControlProtocol::stop_learning (boost::weak_ptr<PBD::Controllable> wc)
446 {
447         boost::shared_ptr<Controllable> c = wc.lock ();
448         if (!c) {
449                 return;
450         }
451
452         Glib::Threads::Mutex::Lock lm (pending_lock);
453         Glib::Threads::Mutex::Lock lm2 (controllables_lock);
454         MIDIControllable* dptr = 0;
455
456         /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
457            relevant MIDIControllable and remove it from the pending list.
458         */
459
460         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
461                 if (((*i)->mc)->get_controllable() == c) {
462                         (*i)->mc->stop_learning ();
463                         dptr = (*i)->mc;
464                         (*i)->connection.disconnect();
465
466                         delete *i;
467                         pending_controllables.erase (i);
468                         break;
469                 }
470         }
471
472         delete dptr;
473 }
474
475 void
476 GenericMidiControlProtocol::check_used_event (int pos, int control_number)
477 {
478         Glib::Threads::Mutex::Lock lm2 (controllables_lock);
479
480         MIDI::channel_t channel = (pos & 0xf);
481         MIDI::byte value = control_number;
482
483         DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("checking for used event: Channel: %1 Controller: %2 value: %3\n", (int) channel, (pos & 0xf0), (int) value));
484
485         // Remove any old binding for this midi channel/type/value pair
486         for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
487                 MIDIControllable* existingBinding = (*iter);
488                 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
489                         if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
490                                 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
491                                 delete existingBinding;
492                                 iter = controllables.erase (iter);
493                         } else {
494                                 ++iter;
495                         }
496                 } else {
497                         ++iter;
498                 }
499         }
500
501         for (MIDIFunctions::iterator iter = functions.begin(); iter != functions.end();) {
502                 MIDIFunction* existingBinding = (*iter);
503                 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
504                         if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
505                                 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
506                                 delete existingBinding;
507                                 iter = functions.erase (iter);
508                         } else {
509                                 ++iter;
510                         }
511                 } else {
512                         ++iter;
513                 }
514         }
515
516         for (MIDIActions::iterator iter = actions.begin(); iter != actions.end();) {
517                 MIDIAction* existingBinding = (*iter);
518                 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
519                         if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
520                                 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
521                                 delete existingBinding;
522                                 iter = actions.erase (iter);
523                         } else {
524                                 ++iter;
525                         }
526                 } else {
527                         ++iter;
528                 }
529         }
530
531 }
532
533 XMLNode&
534 GenericMidiControlProtocol::get_state ()
535 {
536         XMLNode& node (ControlProtocol::get_state());
537
538         node.set_property (X_("feedback-interval"), _feedback_interval);
539         node.set_property (X_("threshold"), _threshold);
540         node.set_property (X_("motorized"), _motorised);
541
542         if (!_current_binding.empty()) {
543                 node.set_property ("binding", _current_binding);
544         }
545
546         XMLNode* children = new XMLNode (X_("Controls"));
547
548         node.add_child_nocopy (*children);
549
550         Glib::Threads::Mutex::Lock lm2 (controllables_lock);
551         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
552
553                 /* we don't care about bindings that come from a bindings map, because
554                    they will all be reset/recreated when we load the relevant bindings
555                    file.
556                 */
557
558                 if ((*i)->get_controllable() && (*i)->learned()) {
559                         children->add_child_nocopy ((*i)->get_state());
560                 }
561         }
562
563         return node;
564 }
565
566 int
567 GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
568 {
569         XMLNodeList nlist;
570         XMLNodeConstIterator niter;
571
572         if (ControlProtocol::set_state (node, version)) {
573                 return -1;
574         }
575
576         if (!node.get_property ("feedback-interval", _feedback_interval)) {
577                 _feedback_interval = 10000;
578         }
579
580         if (!node.get_property ("threshold", _threshold)) {
581                 _threshold = 10;
582         }
583
584         if (!node.get_property ("motorized", _motorised)) {
585                 _motorised = false;
586         }
587
588         boost::shared_ptr<Controllable> c;
589
590         {
591                 Glib::Threads::Mutex::Lock lm (pending_lock);
592                 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
593                         (*i)->connection.disconnect();
594                         if ((*i)->own_mc) {
595                                 delete (*i)->mc;
596                         }
597                         delete *i;
598                 }
599                 pending_controllables.clear ();
600         }
601
602         std::string str;
603         // midi map has to be loaded first so learned binding can go on top
604         if (node.get_property ("binding", str)) {
605                 for (list<MapInfo>::iterator x = map_info.begin(); x != map_info.end(); ++x) {
606                         if (str == (*x).name) {
607                                 load_bindings ((*x).path);
608                                 break;
609                         }
610                 }
611         }
612
613         /* Load up specific bindings from the
614          * <Controls><MidiControllable>...</MidiControllable><Controls> section
615          */
616
617         bool load_dynamic_bindings = false;
618         node.get_property ("session-state", load_dynamic_bindings);
619
620         if (load_dynamic_bindings) {
621                 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
622                 nlist = node.children(); // "Controls"
623
624                 if (!nlist.empty()) {
625                         nlist = nlist.front()->children(); // "MIDIControllable" ...
626
627                         if (!nlist.empty()) {
628                                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
629
630                                         PBD::ID id;
631                                         if ((*niter)->get_property ("id", id)) {
632
633                                                 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Relearned binding for session: Control ID: %1\n", id.to_s()));
634                                                 boost::shared_ptr<PBD::Controllable> c = Controllable::by_id (id);
635
636                                                 if (c) {
637                                                         MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), c, false);
638
639                                                         if (mc->set_state (**niter, version) == 0) {
640                                                                 controllables.push_back (mc);
641                                                         } else {
642                                                                 warning << string_compose ("Generic MIDI control: Failed to set state for Control ID: %1\n", id.to_s());
643                                                                 delete mc;
644                                                         }
645
646                                                 } else {
647                                                         warning << string_compose (
648                                                                 _("Generic MIDI control: controllable %1 not found in session (ignored)"),
649                                                                 id.to_s()) << endmsg;
650                                                 }
651                                         }
652                                 }
653                         }
654                 }
655         }
656
657         return 0;
658 }
659
660 int
661 GenericMidiControlProtocol::set_feedback (bool yn)
662 {
663         do_feedback = yn;
664         last_feedback_time = 0;
665         return 0;
666 }
667
668 bool
669 GenericMidiControlProtocol::get_feedback () const
670 {
671         return do_feedback;
672 }
673
674 int
675 GenericMidiControlProtocol::load_bindings (const string& xmlpath)
676 {
677         DEBUG_TRACE (DEBUG::GenericMidi, "Load bindings: Reading midi map\n");
678         XMLTree state_tree;
679
680         if (!state_tree.read (xmlpath.c_str())) {
681                 error << string_compose(_("Could not understand MIDI bindings file %1"), xmlpath) << endmsg;
682                 return -1;
683         }
684
685         XMLNode* root = state_tree.root();
686
687         if (root->name() != X_("ArdourMIDIBindings")) {
688                 error << string_compose (_("MIDI Bindings file %1 is not really a MIDI bindings file"), xmlpath) << endmsg;
689                 return -1;
690         }
691
692         const XMLProperty* prop;
693
694         if ((prop = root->property ("version")) == 0) {
695                 return -1;
696         }
697
698         const XMLNodeList& children (root->children());
699         XMLNodeConstIterator citer;
700         XMLNodeConstIterator gciter;
701
702         MIDIControllable* mc;
703
704         drop_all ();
705
706         DEBUG_TRACE (DEBUG::GenericMidi, "Loading bindings\n");
707         for (citer = children.begin(); citer != children.end(); ++citer) {
708
709                 if ((*citer)->name() == "DeviceInfo") {
710
711                         if ((*citer)->get_property ("bank-size", _bank_size)) {
712                                 _current_bank = 0;
713                         }
714
715                         if (!(*citer)->get_property ("motorized", _motorised)) {
716                                 _motorised = false;
717                         }
718
719                         if (!(*citer)->get_property ("threshold", _threshold)) {
720                                 _threshold = 10;
721                         }
722                 }
723
724                 if ((*citer)->name() == "Binding") {
725                         const XMLNode* child = *citer;
726
727                         if (child->property ("uri")) {
728                                 /* controllable */
729
730                                 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
731                                 if ((mc = create_binding (*child)) != 0) {
732                                         controllables.push_back (mc);
733                                 }
734
735                         } else if (child->property ("function")) {
736
737                                 /* function */
738                                 MIDIFunction* mf;
739
740                                 if ((mf = create_function (*child)) != 0) {
741                                         functions.push_back (mf);
742                                 }
743
744                         } else if (child->property ("action")) {
745                                 MIDIAction* ma;
746
747                                 if ((ma = create_action (*child)) != 0) {
748                                         actions.push_back (ma);
749                                 }
750                         }
751                 }
752         }
753
754         if ((prop = root->property ("name")) != 0) {
755                 _current_binding = prop->value ();
756         }
757
758         reset_controllables ();
759
760         return 0;
761 }
762
763 MIDIControllable*
764 GenericMidiControlProtocol::create_binding (const XMLNode& node)
765 {
766         const XMLProperty* prop;
767         MIDI::byte detail;
768         MIDI::channel_t channel;
769         string uri;
770         MIDI::eventType ev;
771         int intval;
772         bool momentary;
773         MIDIControllable::CtlType ctltype;
774         MIDIControllable::Encoder encoder = MIDIControllable::No_enc;
775         bool rpn_value = false;
776         bool nrpn_value = false;
777         bool rpn_change = false;
778         bool nrpn_change = false;
779
780         if ((prop = node.property (X_("ctl"))) != 0) {
781                 ctltype = MIDIControllable::Ctl_Momentary;
782                 ev = MIDI::controller;
783         } else if ((prop = node.property (X_("ctl-toggle"))) !=0) {
784                 ctltype = MIDIControllable::Ctl_Toggle;
785                 ev = MIDI::controller;
786         } else if ((prop = node.property (X_("ctl-dial"))) !=0) {
787                 ctltype = MIDIControllable::Ctl_Dial;
788                 ev = MIDI::controller;
789         } else if ((prop = node.property (X_("note"))) != 0) {
790                 ev = MIDI::on;
791         } else if ((prop = node.property (X_("pgm"))) != 0) {
792                 ev = MIDI::program;
793         } else if ((prop = node.property (X_("pb"))) != 0) {
794                 ev = MIDI::pitchbend;
795         } else if ((prop = node.property (X_("enc-l"))) != 0) {
796                 encoder = MIDIControllable::Enc_L;
797                 ev = MIDI::controller;
798         } else if ((prop = node.property (X_("enc-r"))) != 0) {
799                 encoder = MIDIControllable::Enc_R;
800                 ev = MIDI::controller;
801         } else if ((prop = node.property (X_("enc-2"))) != 0) {
802                 encoder = MIDIControllable::Enc_2;
803                 ev = MIDI::controller;
804         } else if ((prop = node.property (X_("enc-b"))) != 0) {
805                 encoder = MIDIControllable::Enc_B;
806                 ev = MIDI::controller;
807         } else if ((prop = node.property (X_("rpn"))) != 0) {
808                 rpn_value = true;
809         } else if ((prop = node.property (X_("nrpn"))) != 0) {
810                 nrpn_value = true;
811         } else if ((prop = node.property (X_("rpn-delta"))) != 0) {
812                 rpn_change = true;
813         } else if ((prop = node.property (X_("nrpn-delta"))) != 0) {
814                 nrpn_change = true;
815         } else {
816                 return 0;
817         }
818
819         if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
820                 return 0;
821         }
822
823         detail = (MIDI::byte) intval;
824
825         if ((prop = node.property (X_("channel"))) == 0) {
826                 return 0;
827         }
828
829         if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
830                 return 0;
831         }
832         channel = (MIDI::channel_t) intval;
833         /* adjust channel to zero-based counting */
834         if (channel > 0) {
835                 channel -= 1;
836         }
837
838         if ((prop = node.property (X_("momentary"))) != 0) {
839                 momentary = string_to<bool> (prop->value());
840         } else {
841                 momentary = false;
842         }
843
844         prop = node.property (X_("uri"));
845         uri = prop->value();
846
847         MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), momentary);
848
849         if (mc->init (uri)) {
850                 delete mc;
851                 return 0;
852         }
853
854         if (rpn_value) {
855                 mc->bind_rpn_value (channel, detail);
856         } else if (nrpn_value) {
857                 mc->bind_nrpn_value (channel, detail);
858         } else if (rpn_change) {
859                 mc->bind_rpn_change (channel, detail);
860         } else if (nrpn_change) {
861                 mc->bind_nrpn_change (channel, detail);
862         } else {
863                 mc->set_ctltype (ctltype);
864                 mc->set_encoder (encoder);
865                 mc->bind_midi (channel, ev, detail);
866         }
867
868         return mc;
869 }
870
871 void
872 GenericMidiControlProtocol::reset_controllables ()
873 {
874         Glib::Threads::Mutex::Lock lm2 (controllables_lock);
875
876         for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ) {
877                 MIDIControllable* existingBinding = (*iter);
878                 MIDIControllables::iterator next = iter;
879                 ++next;
880
881                 if (!existingBinding->learned()) {
882
883                         /* its entirely possible that the session doesn't have
884                          * the specified controllable (e.g. it has too few
885                          * tracks). if we find this to be the case, we just leave
886                          * the binding around, unbound, and it will do "late
887                          * binding" (or "lazy binding") if/when any data arrives.
888                          */
889
890                         existingBinding->lookup_controllable ();
891                 }
892
893                 iter = next;
894         }
895 }
896
897 boost::shared_ptr<Controllable>
898 GenericMidiControlProtocol::lookup_controllable (const string & str) const
899 {
900         boost::shared_ptr<Controllable> c;
901
902         DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("lookup controllable from \"%1\"\n", str));
903
904         if (!session) {
905                 DEBUG_TRACE (DEBUG::GenericMidi, "no session\n");
906                 return c;
907         }
908
909         /* step 1: split string apart */
910
911         string::size_type first_space = str.find_first_of (" ");
912
913         if (first_space == string::npos) {
914                 return c;
915         }
916
917         string front = str.substr (0, first_space);
918         vector<string> path;
919         split (front, path, '/');
920
921         if (path.size() < 2) {
922                 return c;
923         }
924
925         string back = str.substr (first_space);
926         vector<string> rest;
927         split (back, rest, ' ');
928
929         if (rest.empty()) {
930                 return c;
931         }
932
933         DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("parsed into path of %1, rest of %1\n", path.size(), rest.size()));
934
935         /* Step 2: analyse parts of the string to figure out what type of
936          * Stripable we're looking for
937          */
938
939         enum Type {
940                 Selection,
941                 PresentationOrder,
942                 Named,
943         };
944         Type type = Named;
945         int id = 1;
946         string name;
947
948         static regex_t compiled_pattern;
949         static bool compiled = false;
950
951         if (!compiled) {
952                 const char * const pattern = "^[BS]?[0-9]+";
953                 /* this pattern compilation is not going to fail */
954                 regcomp (&compiled_pattern, pattern, REG_EXTENDED|REG_NOSUB);
955                 /* leak compiled pattern */
956                 compiled = true;
957         }
958
959         /* Step 3: identify what "rest" looks like - name, or simple nueric, or
960          * banked/selection specifier
961          */
962
963         bool matched = (regexec (&compiled_pattern, rest[0].c_str(), 0, 0, 0) == 0);
964
965         if (matched) {
966                 bool banked = false;
967
968                 if (rest[0][0] == 'B') {
969                         banked = true;
970                         /* already matched digits, so we know atoi() will succeed */
971                         id = atoi (rest[0].substr (1));
972                         type = PresentationOrder;
973                 } else if (rest[0][0] == 'S') {
974                         /* already matched digits, so we know atoi() will succeed */
975                         id = atoi (rest[0].substr (1));
976                         type = Selection;
977                 } else if (isdigit (rest[0][0])) {
978                         /* already matched digits, so we know atoi() will succeed */
979                         id = atoi (rest[0]);
980                         type = PresentationOrder;
981                 } else {
982                         return c;
983                 }
984
985                 id -= 1; /* order is zero-based, but maps use 1-based */
986
987                 if (banked) {
988                         id += _current_bank * _bank_size;
989                 }
990
991         } else {
992
993                 type = Named;
994                 name = rest[0];
995         }
996
997         /* step 4: find the reference Stripable */
998
999         boost::shared_ptr<Stripable> s;
1000
1001         if (path[0] == X_("route") || path[0] == X_("rid")) {
1002
1003                 std::string name;
1004
1005                 switch (type) {
1006                 case PresentationOrder:
1007                         s = session->get_remote_nth_stripable (id, PresentationInfo::Route);
1008                         break;
1009                 case Named:
1010                         /* name */
1011                         name = rest[0];
1012
1013                         if (name == "Master" || name == X_("master")) {
1014                                 s = session->master_out();
1015                         } else if (name == X_("control") || name == X_("listen") || name == X_("monitor") || name == "Monitor") {
1016                                 s = session->monitor_out();
1017                         } else if (name == X_("auditioner")) {
1018                                 s = session->the_auditioner();
1019                         } else {
1020                                 s = session->route_by_name (name);
1021                         }
1022                         break;
1023
1024                 case Selection:
1025                         s = session->route_by_selected_count (id);
1026                         break;
1027                 }
1028
1029         } else if (path[0] == X_("vca")) {
1030
1031                 s = session->get_remote_nth_stripable (id, PresentationInfo::VCA);
1032
1033         } else if (path[0] == X_("bus")) {
1034
1035                 switch (type) {
1036                 case Named:
1037                         s = session->route_by_name (name);
1038                         break;
1039                 default:
1040                         s = session->get_remote_nth_stripable (id, PresentationInfo::Bus);
1041                 }
1042
1043         } else if (path[0] == X_("track")) {
1044
1045                 switch (type) {
1046                 case Named:
1047                         s = session->route_by_name (name);
1048                         break;
1049                 default:
1050                         s = session->get_remote_nth_stripable (id, PresentationInfo::Track);
1051                 }
1052         }
1053
1054         if (!s) {
1055                 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("no stripable found for \"%1\"\n", str));
1056                 return c;
1057         }
1058
1059         DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("found stripable %1\n", s->name()));
1060
1061         /* step 5: find the referenced controllable for that stripable.
1062          *
1063          * Some controls exist only for Route, so we need that too
1064          */
1065
1066         boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (s);
1067
1068         if (path[1] == X_("gain")) {
1069                 c = s->gain_control();
1070         } else if (path[1] == X_("trim")) {
1071                 c = s->trim_control ();
1072         } else if (path[1] == X_("solo")) {
1073                 c = s->solo_control();
1074         } else if (path[1] == X_("mute")) {
1075                 c = s->mute_control();
1076         } else if (path[1] == X_("recenable")) {
1077                 c = s->rec_enable_control ();
1078         } else if (path[1] == X_("panwidth")) {
1079                 c = s->pan_width_control ();
1080         } else if (path[1] == X_("pandirection") || path[1] == X_("balance")) {
1081                 c = s->pan_azimuth_control ();
1082         } else if (path[1] == X_("plugin")) {
1083
1084                 /* /route/plugin/parameter */
1085
1086                 if (path.size() == 3 && rest.size() == 3) {
1087                         if (path[2] == X_("parameter")) {
1088
1089                                 int plugin = atoi (rest[1]);
1090                                 int parameter_index = atoi (rest[2]);
1091
1092                                 /* revert to zero based counting */
1093                                 if (plugin > 0) {
1094                                         --plugin;
1095                                 }
1096                                 if (parameter_index > 0) {
1097                                         --parameter_index;
1098                                 }
1099
1100                                 if (r) {
1101                                         boost::shared_ptr<Processor> proc = r->nth_plugin (plugin);
1102
1103                                         if (proc) {
1104                                                 boost::shared_ptr<PluginInsert> p = boost::dynamic_pointer_cast<PluginInsert> (proc);
1105                                                 if (p) {
1106                                                         uint32_t param;
1107                                                         bool ok;
1108                                                         param = p->plugin()->nth_parameter (parameter_index, ok);
1109                                                         if (ok) {
1110                                                                 c = boost::dynamic_pointer_cast<Controllable> (proc->control (Evoral::Parameter (PluginAutomation, 0, param)));
1111                                                         }
1112                                                 }
1113                                         }
1114                                 }
1115                         }
1116                 }
1117
1118         } else if (path[1] == X_("send")) {
1119
1120                 if (path.size() == 3 && rest.size() == 2) {
1121                         if (path[2] == X_("gain")) {
1122                                 uint32_t send = atoi (rest[1]);
1123                                 if (send > 0) {
1124                                         --send;
1125                                 }
1126                                 c = s->send_level_controllable (send);
1127                         } else if (path[2] == X_("direction")) {
1128                                 /* XXX not implemented yet */
1129
1130                         } else if (path[2] == X_("enable")) {
1131                                 /* XXX not implemented yet */
1132                         }
1133                 }
1134
1135         } else if (path[1] == X_("eq")) {
1136
1137                 /* /route/eq/enable */
1138                 /* /route/eq/gain/<band> */
1139                 /* /route/eq/freq/<band> */
1140                 /* /route/eq/q/<band> */
1141                 /* /route/eq/shape/<band> */
1142
1143                 if (path.size() == 3) {
1144
1145                         if (path[2] == X_("enable")) {
1146                                 c = s->eq_enable_controllable ();
1147                         }
1148
1149                 } else if (path.size() == 4) {
1150
1151                         int band = atoi (path[3]); /* band number */
1152
1153                         if (path[2] == X_("gain")) {
1154                                 c = s->eq_gain_controllable (band);
1155                         } else if (path[2] == X_("freq")) {
1156                                 c = s->eq_freq_controllable (band);
1157                         } else if (path[2] == X_("q")) {
1158                                 c = s->eq_q_controllable (band);
1159                         } else if (path[2] == X_("shape")) {
1160                                 c = s->eq_shape_controllable (band);
1161                         }
1162                 }
1163
1164         } else if (path[1] == X_("filter")) {
1165
1166                 /* /route/filter/hi/freq */
1167
1168                 if (path.size() == 4) {
1169
1170                         int filter;
1171
1172                         if (path[2] == X_("hi")) {
1173                                 filter = 1; /* high pass filter */
1174                         } else {
1175                                 filter = 0; /* low pass filter */
1176                         }
1177
1178                         if (path[3] == X_("enable")) {
1179                                 c = s->filter_enable_controllable (filter);
1180                         } else if (path[3] == X_("freq")) {
1181                                 c = s->filter_freq_controllable (filter);
1182                         } else if (path[3] == X_("slope")) {
1183                                 c = s->filter_slope_controllable (filter);
1184                         }
1185
1186                 }
1187
1188         } else if (path[1] == X_("compressor")) {
1189
1190                 if (path.size() == 3) {
1191                         if (path[2] == X_("enable")) {
1192                                 c = s->comp_enable_controllable ();
1193                         } else if (path[2] == X_("threshold")) {
1194                                 c = s->comp_threshold_controllable ();
1195                         } else if (path[2] == X_("mode")) {
1196                                 c = s->comp_mode_controllable ();
1197                         } else if (path[2] == X_("speed")) {
1198                                 c = s->comp_speed_controllable ();
1199                         } else if (path[2] == X_("makeup")) {
1200                                 c = s->comp_makeup_controllable ();
1201                         }
1202                 }
1203         }
1204
1205         if (c) {
1206                 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("found controllable \"%1\"\n", c->name()));
1207         } else {
1208                 DEBUG_TRACE (DEBUG::GenericMidi, "no controllable found\n");
1209         }
1210
1211         return c;
1212 }
1213
1214 MIDIFunction*
1215 GenericMidiControlProtocol::create_function (const XMLNode& node)
1216 {
1217         const XMLProperty* prop;
1218         int intval;
1219         MIDI::byte detail = 0;
1220         MIDI::channel_t channel = 0;
1221         string uri;
1222         MIDI::eventType ev;
1223         MIDI::byte* data = 0;
1224         uint32_t data_size = 0;
1225         string argument;
1226
1227         if ((prop = node.property (X_("ctl"))) != 0) {
1228                 ev = MIDI::controller;
1229         } else if ((prop = node.property (X_("note"))) != 0) {
1230                 ev = MIDI::on;
1231         } else if ((prop = node.property (X_("pgm"))) != 0) {
1232                 ev = MIDI::program;
1233         } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
1234
1235                 if (prop->name() == X_("sysex")) {
1236                         ev = MIDI::sysex;
1237                 } else {
1238                         ev = MIDI::any;
1239                 }
1240
1241                 int val;
1242                 uint32_t cnt;
1243
1244                 {
1245                         cnt = 0;
1246                         stringstream ss (prop->value());
1247                         ss << hex;
1248
1249                         while (ss >> val) {
1250                                 cnt++;
1251                         }
1252                 }
1253
1254                 if (cnt == 0) {
1255                         return 0;
1256                 }
1257
1258                 data = new MIDI::byte[cnt];
1259                 data_size = cnt;
1260
1261                 {
1262                         stringstream ss (prop->value());
1263                         ss << hex;
1264                         cnt = 0;
1265
1266                         while (ss >> val) {
1267                                 data[cnt++] = (MIDI::byte) val;
1268                         }
1269                 }
1270
1271         } else {
1272                 warning << "Binding ignored - unknown type" << endmsg;
1273                 return 0;
1274         }
1275
1276         if (data_size == 0) {
1277                 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1278                         return 0;
1279                 }
1280
1281                 detail = (MIDI::byte) intval;
1282
1283                 if ((prop = node.property (X_("channel"))) == 0) {
1284                         return 0;
1285                 }
1286
1287                 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1288                         return 0;
1289                 }
1290                 channel = (MIDI::channel_t) intval;
1291                 /* adjust channel to zero-based counting */
1292                 if (channel > 0) {
1293                         channel -= 1;
1294                 }
1295         }
1296
1297         if ((prop = node.property (X_("arg"))) != 0 || (prop = node.property (X_("argument"))) != 0 || (prop = node.property (X_("arguments"))) != 0) {
1298                 argument = prop->value ();
1299         }
1300
1301         prop = node.property (X_("function"));
1302
1303         MIDIFunction* mf = new MIDIFunction (*_input_port->parser());
1304
1305         if (mf->setup (*this, prop->value(), argument, data, data_size)) {
1306                 delete mf;
1307                 return 0;
1308         }
1309
1310         mf->bind_midi (channel, ev, detail);
1311
1312         return mf;
1313 }
1314
1315 MIDIAction*
1316 GenericMidiControlProtocol::create_action (const XMLNode& node)
1317 {
1318         const XMLProperty* prop;
1319         int intval;
1320         MIDI::byte detail = 0;
1321         MIDI::channel_t channel = 0;
1322         string uri;
1323         MIDI::eventType ev;
1324         MIDI::byte* data = 0;
1325         uint32_t data_size = 0;
1326
1327         if ((prop = node.property (X_("ctl"))) != 0) {
1328                 ev = MIDI::controller;
1329         } else if ((prop = node.property (X_("note"))) != 0) {
1330                 ev = MIDI::on;
1331         } else if ((prop = node.property (X_("pgm"))) != 0) {
1332                 ev = MIDI::program;
1333         } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
1334
1335                 if (prop->name() == X_("sysex")) {
1336                         ev = MIDI::sysex;
1337                 } else {
1338                         ev = MIDI::any;
1339                 }
1340
1341                 int val;
1342                 uint32_t cnt;
1343
1344                 {
1345                         cnt = 0;
1346                         stringstream ss (prop->value());
1347                         ss << hex;
1348
1349                         while (ss >> val) {
1350                                 cnt++;
1351                         }
1352                 }
1353
1354                 if (cnt == 0) {
1355                         return 0;
1356                 }
1357
1358                 data = new MIDI::byte[cnt];
1359                 data_size = cnt;
1360
1361                 {
1362                         stringstream ss (prop->value());
1363                         ss << hex;
1364                         cnt = 0;
1365
1366                         while (ss >> val) {
1367                                 data[cnt++] = (MIDI::byte) val;
1368                         }
1369                 }
1370
1371         } else {
1372                 warning << "Binding ignored - unknown type" << endmsg;
1373                 return 0;
1374         }
1375
1376         if (data_size == 0) {
1377                 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1378                         return 0;
1379                 }
1380
1381                 detail = (MIDI::byte) intval;
1382
1383                 if ((prop = node.property (X_("channel"))) == 0) {
1384                         return 0;
1385                 }
1386
1387                 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1388                         return 0;
1389                 }
1390                 channel = (MIDI::channel_t) intval;
1391                 /* adjust channel to zero-based counting */
1392                 if (channel > 0) {
1393                         channel -= 1;
1394                 }
1395         }
1396
1397         prop = node.property (X_("action"));
1398
1399         MIDIAction* ma = new MIDIAction (*_input_port->parser());
1400
1401         if (ma->init (*this, prop->value(), data, data_size)) {
1402                 delete ma;
1403                 return 0;
1404         }
1405
1406         ma->bind_midi (channel, ev, detail);
1407
1408         return ma;
1409 }
1410
1411 void
1412 GenericMidiControlProtocol::set_current_bank (uint32_t b)
1413 {
1414         _current_bank = b;
1415         reset_controllables ();
1416 }
1417
1418 void
1419 GenericMidiControlProtocol::next_bank ()
1420 {
1421         _current_bank++;
1422         reset_controllables ();
1423 }
1424
1425 void
1426 GenericMidiControlProtocol::prev_bank()
1427 {
1428         if (_current_bank) {
1429                 _current_bank--;
1430                 reset_controllables ();
1431         }
1432 }
1433
1434 void
1435 GenericMidiControlProtocol::set_motorised (bool m)
1436 {
1437         _motorised = m;
1438 }
1439
1440 void
1441 GenericMidiControlProtocol::set_threshold (int t)
1442 {
1443         _threshold = t;
1444 }
1445
1446 bool
1447 GenericMidiControlProtocol::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1448 {
1449         if (!_input_port || !_output_port) {
1450                 return false;
1451         }
1452
1453         string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_input_port)->name());
1454         string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
1455
1456         if (ni == name1 || ni == name2) {
1457                 if (yn) {
1458                         connection_state |= InputConnected;
1459                 } else {
1460                         connection_state &= ~InputConnected;
1461                 }
1462         } else if (no == name1 || no == name2) {
1463                 if (yn) {
1464                         connection_state |= OutputConnected;
1465                 } else {
1466                         connection_state &= ~OutputConnected;
1467                 }
1468         } else {
1469                 /* not our ports */
1470                 return false;
1471         }
1472
1473         if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1474
1475                 /* XXX this is a horrible hack. Without a short sleep here,
1476                    something prevents the device wakeup messages from being
1477                    sent and/or the responses from being received.
1478                 */
1479
1480                 g_usleep (100000);
1481                 connected ();
1482
1483         } else {
1484
1485         }
1486
1487         ConnectionChange (); /* emit signal for our GUI */
1488
1489         return true; /* connection status changed */
1490 }
1491
1492 void
1493 GenericMidiControlProtocol::connected ()
1494 {
1495 }
1496
1497 boost::shared_ptr<Port>
1498 GenericMidiControlProtocol::output_port() const
1499 {
1500         return _output_port;
1501 }
1502
1503 boost::shared_ptr<Port>
1504 GenericMidiControlProtocol::input_port() const
1505 {
1506         return _input_port;
1507 }
1508
1509 void
1510 GenericMidiControlProtocol::maybe_start_touch (boost::shared_ptr<Controllable> controllable)
1511 {
1512         boost::shared_ptr<AutomationControl> actl = boost::dynamic_pointer_cast<AutomationControl> (controllable);
1513         if (actl) {
1514                 actl->start_touch (session->audible_sample ());
1515         }
1516 }