Setup fixed ports for MIDI control data; hence remove configuration of those ports...
[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 #define __STDC_FORMAT_MACROS 1
21 #include <stdint.h>
22
23 #include <sstream>
24 #include <algorithm>
25
26 #include "pbd/controllable_descriptor.h"
27 #include "pbd/error.h"
28 #include "pbd/failed_constructor.h"
29 #include "pbd/pathscanner.h"
30 #include "pbd/xml++.h"
31
32 #include "midi++/port.h"
33
34 #include "ardour/filesystem_paths.h"
35 #include "ardour/session.h"
36 #include "ardour/route.h"
37 #include "ardour/midi_ui.h"
38 #include "ardour/rc_configuration.h"
39
40 #include "generic_midi_control_protocol.h"
41 #include "midicontrollable.h"
42 #include "midifunction.h"
43 #include "midiaction.h"
44
45 using namespace ARDOUR;
46 using namespace PBD;
47 using namespace std;
48
49 #include "i18n.h"
50
51 #define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
52 #define ui_bind(x) boost::protect (boost::bind ((x)))
53
54 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
55         : ControlProtocol (s, _("Generic MIDI"), midi_ui_context())
56         , gui (0)
57 {
58
59         _input_port = s.midi_input_port ();
60         _output_port = s.midi_output_port ();
61
62         do_feedback = false;
63         _feedback_interval = 10000; // microseconds
64         last_feedback_time = 0;
65
66         _current_bank = 0;
67         _bank_size = 0;
68
69         /* XXX is it right to do all these in the same thread as whatever emits the signal? */
70
71         Controllable::StartLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::start_learning, this, _1));
72         Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1));
73         Controllable::CreateBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::create_binding, this, _1, _2, _3));
74         Controllable::DeleteBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding, this, _1));
75
76         Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
77         Route::RemoteControlIDChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), midi_ui_context());
78
79         reload_maps ();
80 }
81
82 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
83 {
84         drop_all ();
85         tear_down_gui ();
86 }
87
88 static const char* const midi_map_dir_name = "midi_maps";
89 static const char* const midi_map_suffix = ".map";
90
91 static sys::path
92 system_midi_map_search_path ()
93 {
94         SearchPath spath(system_data_search_path());
95         spath.add_subdirectory_to_paths(midi_map_dir_name);
96
97         // just return the first directory in the search path that exists
98         SearchPath::const_iterator i = std::find_if(spath.begin(), spath.end(), sys::exists);
99
100         if (i == spath.end()) return sys::path();
101
102         return *i;
103 }
104
105 static sys::path
106 user_midi_map_directory ()
107 {
108         sys::path p(user_config_directory());
109         p /= midi_map_dir_name;
110
111         return p;
112 }
113
114 static bool
115 midi_map_filter (const string &str, void */*arg*/)
116 {
117         return (str.length() > strlen(midi_map_suffix) &&
118                 str.find (midi_map_suffix) == (str.length() - strlen (midi_map_suffix)));
119 }
120
121 void
122 GenericMidiControlProtocol::reload_maps ()
123 {
124         vector<string *> *midi_maps;
125         PathScanner scanner;
126         SearchPath spath (system_midi_map_search_path());
127         spath += user_midi_map_directory ();
128
129         midi_maps = scanner (spath.to_string(), midi_map_filter, 0, false, true);
130
131         if (!midi_maps) {
132                 cerr << "No MIDI maps found using " << spath.to_string() << endl;
133                 return;
134         }
135
136         cerr << "Found " << midi_maps->size() << " MIDI maps along " << spath.to_string() << endl;
137
138         for (vector<string*>::iterator i = midi_maps->begin(); i != midi_maps->end(); ++i) {
139                 string fullpath = *(*i);
140
141                 XMLTree tree;
142
143                 if (!tree.read (fullpath.c_str())) {
144                         continue;
145                 }
146
147                 MapInfo mi;
148
149                 XMLProperty* prop = tree.root()->property ("name");
150
151                 if (!prop) {
152                         continue;
153                 }
154
155                 mi.name = prop->value ();
156                 mi.path = fullpath;
157                 
158                 map_info.push_back (mi);
159         }
160
161         delete midi_maps;
162 }
163         
164 void
165 GenericMidiControlProtocol::drop_all ()
166 {
167         Glib::Mutex::Lock lm (pending_lock);
168         Glib::Mutex::Lock lm2 (controllables_lock);
169
170         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
171                 delete *i;
172         }
173         controllables.clear ();
174
175         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
176                 delete *i;
177         }
178         pending_controllables.clear ();
179
180         for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
181                 delete *i;
182         }
183         functions.clear ();
184
185         for (MIDIActions::iterator i = actions.begin(); i != actions.end(); ++i) {
186                 delete *i;
187         }
188         actions.clear ();
189 }
190
191 void
192 GenericMidiControlProtocol::drop_bindings ()
193 {
194         Glib::Mutex::Lock lm2 (controllables_lock);
195
196         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
197                 if (!(*i)->learned()) {
198                         delete *i;
199                         i = controllables.erase (i);
200                 } else {
201                         ++i;
202                 }
203         }
204
205         for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
206                 delete *i;
207         }
208         functions.clear ();
209
210         _current_binding = "";
211         _bank_size = 0;
212         _current_bank = 0;
213 }
214
215 int
216 GenericMidiControlProtocol::set_active (bool /*yn*/)
217 {
218         /* start/stop delivery/outbound thread */
219         return 0;
220 }
221
222 void
223 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
224 {
225         _feedback_interval = ms;
226 }
227
228 void 
229 GenericMidiControlProtocol::send_feedback ()
230 {
231         if (!do_feedback) {
232                 return;
233         }
234
235         microseconds_t now = get_microseconds ();
236
237         if (last_feedback_time != 0) {
238                 if ((now - last_feedback_time) < _feedback_interval) {
239                         return;
240                 }
241         }
242
243         _send_feedback ();
244         
245         last_feedback_time = now;
246 }
247
248 void 
249 GenericMidiControlProtocol::_send_feedback ()
250 {
251         const int32_t bufsize = 16 * 1024; /* XXX too big */
252         MIDI::byte buf[bufsize];
253         int32_t bsize = bufsize;
254         MIDI::byte* end = buf;
255         
256         for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
257                 end = (*r)->write_feedback (end, bsize);
258         }
259         
260         if (end == buf) {
261                 return;
262         } 
263
264         _output_port->write (buf, (int32_t) (end - buf), 0);
265 }
266
267 bool
268 GenericMidiControlProtocol::start_learning (Controllable* c)
269 {
270         if (c == 0) {
271                 return false;
272         }
273
274         Glib::Mutex::Lock lm2 (controllables_lock);
275
276         MIDIControllables::iterator tmp;
277         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
278                 tmp = i;
279                 ++tmp;
280                 if ((*i)->get_controllable() == c) {
281                         delete (*i);
282                         controllables.erase (i);
283                 }
284                 i = tmp;
285         }
286
287         {
288                 Glib::Mutex::Lock lm (pending_lock);
289                 
290                 MIDIPendingControllables::iterator ptmp;
291                 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
292                         ptmp = i;
293                         ++ptmp;
294                         if (((*i)->first)->get_controllable() == c) {
295                                 (*i)->second.disconnect();
296                                 delete (*i)->first;
297                                 delete *i;
298                                 pending_controllables.erase (i);
299                         }
300                         i = ptmp;
301                 }
302         }
303
304         MIDIControllable* mc = 0;
305
306         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
307                 if ((*i)->get_controllable() && ((*i)->get_controllable()->id() == c->id())) {
308                         mc = *i;
309                         break;
310                 }
311         }
312
313         if (!mc) {
314                 mc = new MIDIControllable (*_input_port, *c, false);
315         }
316         
317         {
318                 Glib::Mutex::Lock lm (pending_lock);
319
320                 MIDIPendingControllable* element = new MIDIPendingControllable;
321                 element->first = mc;
322                 c->LearningFinished.connect_same_thread (element->second, boost::bind (&GenericMidiControlProtocol::learning_stopped, this, mc));
323
324                 pending_controllables.push_back (element);
325         }
326
327         mc->learn_about_external_control ();
328         return true;
329 }
330
331 void
332 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
333 {
334         Glib::Mutex::Lock lm (pending_lock);
335         Glib::Mutex::Lock lm2 (controllables_lock);
336         
337         MIDIPendingControllables::iterator tmp;
338
339         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
340                 tmp = i;
341                 ++tmp;
342
343                 if ( (*i)->first == mc) {
344                         (*i)->second.disconnect();
345                         delete *i;
346                         pending_controllables.erase(i);
347                 }
348
349                 i = tmp;
350         }
351
352         controllables.push_back (mc);
353 }
354
355 void
356 GenericMidiControlProtocol::stop_learning (Controllable* c)
357 {
358         Glib::Mutex::Lock lm (pending_lock);
359         Glib::Mutex::Lock lm2 (controllables_lock);
360         MIDIControllable* dptr = 0;
361
362         /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
363            relevant MIDIControllable and remove it from the pending list.
364         */
365
366         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
367                 if (((*i)->first)->get_controllable() == c) {
368                         (*i)->first->stop_learning ();
369                         dptr = (*i)->first;
370                         (*i)->second.disconnect();
371
372                         delete *i;
373                         pending_controllables.erase (i);
374                         break;
375                 }
376         }
377         
378         delete dptr;
379 }
380
381 void
382 GenericMidiControlProtocol::delete_binding (PBD::Controllable* control)
383 {
384         if (control != 0) {
385                 Glib::Mutex::Lock lm2 (controllables_lock);
386                 
387                 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
388                         MIDIControllable* existingBinding = (*iter);
389                         
390                         if (control == (existingBinding->get_controllable())) {
391                                 delete existingBinding;
392                                 iter = controllables.erase (iter);
393                         } else {
394                                 ++iter;
395                         }
396                         
397                 }
398         }
399 }
400
401 void
402 GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
403 {
404         if (control != NULL) {
405                 Glib::Mutex::Lock lm2 (controllables_lock);
406                 
407                 MIDI::channel_t channel = (pos & 0xf);
408                 MIDI::byte value = control_number;
409                 
410                 // Create a MIDIControllable
411                 MIDIControllable* mc = new MIDIControllable (*_input_port, *control, false);
412
413                 // Remove any old binding for this midi channel/type/value pair
414                 // Note:  can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
415                 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
416                         MIDIControllable* existingBinding = (*iter);
417                         
418                         if ((existingBinding->get_control_channel() & 0xf ) == channel &&
419                             existingBinding->get_control_additional() == value &&
420                             (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller) {
421                                 
422                                 delete existingBinding;
423                                 iter = controllables.erase (iter);
424                         } else {
425                                 ++iter;
426                         }
427                         
428                 }
429                 
430                 // Update the MIDI Controllable based on the the pos param
431                 // Here is where a table lookup for user mappings could go; for now we'll just wing it...
432                 mc->bind_midi(channel, MIDI::controller, value);
433
434                 controllables.push_back (mc);
435         }
436 }
437
438 XMLNode&
439 GenericMidiControlProtocol::get_state () 
440 {
441         XMLNode* node = new XMLNode ("Protocol"); 
442         char buf[32];
443
444         node->add_property (X_("name"), _name);
445         node->add_property (X_("feedback"), do_feedback ? "1" : "0");
446         snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
447         node->add_property (X_("feedback_interval"), buf);
448
449         if (!_current_binding.empty()) {
450                 node->add_property ("binding", _current_binding);
451         }
452
453         XMLNode* children = new XMLNode (X_("controls"));
454
455         node->add_child_nocopy (*children);
456
457         Glib::Mutex::Lock lm2 (controllables_lock);
458         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
459
460                 /* we don't care about bindings that come from a bindings map, because
461                    they will all be reset/recreated when we load the relevant bindings
462                    file.
463                 */
464
465                 if ((*i)->learned()) {
466                         children->add_child_nocopy ((*i)->get_state());
467                 }
468         }
469
470         return *node;
471 }
472
473 int
474 GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
475 {
476         XMLNodeList nlist;
477         XMLNodeConstIterator niter;
478         const XMLProperty* prop;
479
480         if ((prop = node.property ("feedback")) != 0) {
481                 do_feedback = (bool) atoi (prop->value().c_str());
482         } else {
483                 do_feedback = false;
484         }
485
486         if ((prop = node.property ("feedback_interval")) != 0) {
487                 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
488                         _feedback_interval = 10000;
489                 }
490         } else {
491                 _feedback_interval = 10000;
492         }
493
494         boost::shared_ptr<Controllable> c;
495         
496         {
497                 Glib::Mutex::Lock lm (pending_lock);
498                 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
499                         delete *i;
500                 }
501                 pending_controllables.clear ();
502         }
503
504         {
505                 Glib::Mutex::Lock lm2 (controllables_lock);
506                 controllables.clear ();
507                 nlist = node.children(); // "controls"
508                 
509                 if (nlist.empty()) {
510                         return 0;
511                 }
512                 
513                 nlist = nlist.front()->children ();
514                 
515                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
516                         
517                         if ((prop = (*niter)->property ("id")) != 0) {
518                                 
519                                 ID id = prop->value ();
520                                 c = session->controllable_by_id (id);
521                                 
522                                 if (c) {
523                                         MIDIControllable* mc = new MIDIControllable (*_input_port, *c, false);
524
525                                         if (mc->set_state (**niter, version) == 0) {
526                                                 controllables.push_back (mc);
527                                         }
528                                         
529                                 } else {
530                                         warning << string_compose (
531                                                 _("Generic MIDI control: controllable %1 not found in session (ignored)"),
532                                                 id) << endmsg;
533                                 }
534                         }
535                 }
536
537         }
538
539         if ((prop = node.property ("binding")) != 0) {
540                 for (list<MapInfo>::iterator x = map_info.begin(); x != map_info.end(); ++x) {
541                         if (prop->value() == (*x).name) {
542                                 load_bindings ((*x).path);
543                                 break;
544                         }
545                 }
546         }
547
548         return 0;
549 }
550
551 int
552 GenericMidiControlProtocol::set_feedback (bool yn)
553 {
554         do_feedback = yn;
555         last_feedback_time = 0;
556         return 0;
557 }
558
559 bool
560 GenericMidiControlProtocol::get_feedback () const
561 {
562         return do_feedback;
563 }
564
565
566
567
568 int
569 GenericMidiControlProtocol::load_bindings (const string& xmlpath)
570 {
571         XMLTree state_tree;
572
573         if (!state_tree.read (xmlpath.c_str())) {
574                 error << string_compose(_("Could not understand MIDI bindings file %1"), xmlpath) << endmsg;
575                 return -1;
576         }
577
578         XMLNode* root = state_tree.root();
579
580         if (root->name() != X_("ArdourMIDIBindings")) {
581                 error << string_compose (_("MIDI Bindings file %1 is not really a MIDI bindings file"), xmlpath) << endmsg;
582                 return -1;
583         }
584
585         const XMLProperty* prop;
586
587         if ((prop = root->property ("version")) == 0) {
588                 return -1;
589         } else {
590                 int major;
591                 int minor;
592                 int micro;
593
594                 sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, &micro);
595                 Stateful::loading_state_version = (major * 1000) + minor;
596         }
597         
598         const XMLNodeList& children (root->children());
599         XMLNodeConstIterator citer;
600         XMLNodeConstIterator gciter;
601
602         MIDIControllable* mc;
603
604         drop_all ();
605
606         for (citer = children.begin(); citer != children.end(); ++citer) {
607                 
608                 if ((*citer)->name() == "DeviceInfo") {
609                         const XMLProperty* prop;
610
611                         if ((prop = (*citer)->property ("bank-size")) != 0) {
612                                 _bank_size = atoi (prop->value());
613                                 _current_bank = 0;
614                         }
615                 }
616
617                 if ((*citer)->name() == "Binding") {
618                         const XMLNode* child = *citer;
619
620                         if (child->property ("uri")) {
621                                 /* controllable */
622                                 
623                                 if ((mc = create_binding (*child)) != 0) {
624                                         Glib::Mutex::Lock lm2 (controllables_lock);
625                                         controllables.push_back (mc);
626                                 }
627
628                         } else if (child->property ("function")) {
629
630                                 /* function */
631                                 MIDIFunction* mf;
632
633                                 if ((mf = create_function (*child)) != 0) {
634                                         functions.push_back (mf);
635                                 }
636
637                         } else if (child->property ("action")) {
638                                 MIDIAction* ma;
639
640                                 if ((ma = create_action (*child)) != 0) {
641                                         actions.push_back (ma);
642                                 }
643                         }
644                 }
645         }
646         
647         if ((prop = root->property ("name")) != 0) {
648                 _current_binding = prop->value ();
649         }
650
651         reset_controllables ();
652
653         return 0;
654 }
655
656 MIDIControllable*
657 GenericMidiControlProtocol::create_binding (const XMLNode& node)
658 {
659         const XMLProperty* prop;
660         MIDI::byte detail;
661         MIDI::channel_t channel;
662         string uri;
663         MIDI::eventType ev;
664         int intval;
665         bool momentary;
666
667         if ((prop = node.property (X_("ctl"))) != 0) {
668                 ev = MIDI::controller;
669         } else if ((prop = node.property (X_("note"))) != 0) {
670                 ev = MIDI::on;
671         } else if ((prop = node.property (X_("pgm"))) != 0) {
672                 ev = MIDI::program;
673         } else {
674                 return 0;
675         }
676         
677         if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
678                 return 0;
679         }
680         
681         detail = (MIDI::byte) intval;
682
683         if ((prop = node.property (X_("channel"))) == 0) {
684                 return 0;
685         }
686         
687         if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
688                 return 0;
689         }
690         channel = (MIDI::channel_t) intval;
691         /* adjust channel to zero-based counting */
692         if (channel > 0) {
693                 channel -= 1;
694         }
695
696         if ((prop = node.property (X_("momentary"))) != 0) {
697                 momentary = string_is_affirmative (prop->value());
698         } else {
699                 momentary = false;
700         }
701         
702         prop = node.property (X_("uri"));
703         uri = prop->value();
704
705         MIDIControllable* mc = new MIDIControllable (*_input_port, momentary);
706
707         if (mc->init (uri)) {
708                 delete mc;
709                 return 0;
710         }
711
712         mc->bind_midi (channel, ev, detail);
713
714         return mc;
715 }
716
717 void
718 GenericMidiControlProtocol::reset_controllables ()
719 {
720         Glib::Mutex::Lock lm2 (controllables_lock);
721         
722         for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
723                 MIDIControllable* existingBinding = (*iter);
724
725                 if (!existingBinding->learned()) {
726                         ControllableDescriptor& desc (existingBinding->descriptor());
727
728                         if (desc.banked()) {
729                                 desc.set_bank_offset (_current_bank * _bank_size);
730                         }
731
732                         boost::shared_ptr<Controllable> c = session->controllable_by_descriptor (desc);
733                         existingBinding->set_controllable (c.get());
734                 }
735         }
736 }
737
738 MIDIFunction*
739 GenericMidiControlProtocol::create_function (const XMLNode& node)
740 {
741         const XMLProperty* prop;
742         int intval;
743         MIDI::byte detail = 0;
744         MIDI::channel_t channel = 0;
745         string uri;
746         MIDI::eventType ev;
747         MIDI::byte* data = 0;
748         uint32_t data_size = 0;
749
750         if ((prop = node.property (X_("ctl"))) != 0) {
751                 ev = MIDI::controller;
752         } else if ((prop = node.property (X_("note"))) != 0) {
753                 ev = MIDI::on;
754         } else if ((prop = node.property (X_("pgm"))) != 0) {
755                 ev = MIDI::program;
756         } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
757
758                 if (prop->name() == X_("sysex")) {
759                         ev = MIDI::sysex;
760                 } else {
761                         ev = MIDI::any;
762                 }
763
764                 int val;
765                 uint32_t cnt;
766
767                 {
768                         cnt = 0;
769                         stringstream ss (prop->value());
770                         ss << hex;
771                         
772                         while (ss >> val) {
773                                 cnt++;
774                         }
775                 }
776
777                 if (cnt == 0) {
778                         return 0;
779                 }
780
781                 data = new MIDI::byte[cnt];
782                 data_size = cnt;
783                 
784                 {
785                         stringstream ss (prop->value());
786                         ss << hex;
787                         cnt = 0;
788                         
789                         while (ss >> val) {
790                                 data[cnt++] = (MIDI::byte) val;
791                         }
792                 }
793
794         } else {
795                 warning << "Binding ignored - unknown type" << endmsg;
796                 return 0;
797         }
798
799         if (data_size == 0) {
800                 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
801                         return 0;
802                 }
803                 
804                 detail = (MIDI::byte) intval;
805
806                 if ((prop = node.property (X_("channel"))) == 0) {
807                         return 0;
808                 }
809         
810                 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
811                         return 0;
812                 }
813                 channel = (MIDI::channel_t) intval;
814                 /* adjust channel to zero-based counting */
815                 if (channel > 0) {
816                         channel -= 1;
817                 }
818         }
819
820         prop = node.property (X_("function"));
821         
822         MIDIFunction* mf = new MIDIFunction (*_input_port);
823         
824         if (mf->init (*this, prop->value(), data, data_size)) {
825                 delete mf;
826                 return 0;
827         }
828
829         mf->bind_midi (channel, ev, detail);
830
831         return mf;
832 }
833
834 MIDIAction*
835 GenericMidiControlProtocol::create_action (const XMLNode& node)
836 {
837         const XMLProperty* prop;
838         int intval;
839         MIDI::byte detail = 0;
840         MIDI::channel_t channel = 0;
841         string uri;
842         MIDI::eventType ev;
843         MIDI::byte* data = 0;
844         uint32_t data_size = 0;
845
846         if ((prop = node.property (X_("ctl"))) != 0) {
847                 ev = MIDI::controller;
848         } else if ((prop = node.property (X_("note"))) != 0) {
849                 ev = MIDI::on;
850         } else if ((prop = node.property (X_("pgm"))) != 0) {
851                 ev = MIDI::program;
852         } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
853
854                 if (prop->name() == X_("sysex")) {
855                         ev = MIDI::sysex;
856                 } else {
857                         ev = MIDI::any;
858                 }
859
860                 int val;
861                 uint32_t cnt;
862
863                 {
864                         cnt = 0;
865                         stringstream ss (prop->value());
866                         ss << hex;
867                         
868                         while (ss >> val) {
869                                 cnt++;
870                         }
871                 }
872
873                 if (cnt == 0) {
874                         return 0;
875                 }
876
877                 data = new MIDI::byte[cnt];
878                 data_size = cnt;
879                 
880                 {
881                         stringstream ss (prop->value());
882                         ss << hex;
883                         cnt = 0;
884                         
885                         while (ss >> val) {
886                                 data[cnt++] = (MIDI::byte) val;
887                         }
888                 }
889
890         } else {
891                 warning << "Binding ignored - unknown type" << endmsg;
892                 return 0;
893         }
894
895         if (data_size == 0) {
896                 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
897                         return 0;
898                 }
899                 
900                 detail = (MIDI::byte) intval;
901
902                 if ((prop = node.property (X_("channel"))) == 0) {
903                         return 0;
904                 }
905         
906                 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
907                         return 0;
908                 }
909                 channel = (MIDI::channel_t) intval;
910                 /* adjust channel to zero-based counting */
911                 if (channel > 0) {
912                         channel -= 1;
913                 }
914         }
915
916         prop = node.property (X_("action"));
917         
918         MIDIAction* ma = new MIDIAction (*_input_port);
919         
920         if (ma->init (*this, prop->value(), data, data_size)) {
921                 delete ma;
922                 return 0;
923         }
924
925         ma->bind_midi (channel, ev, detail);
926
927         return ma;
928 }
929
930 void
931 GenericMidiControlProtocol::set_current_bank (uint32_t b)
932 {
933         _current_bank = b;
934         reset_controllables ();
935 }
936
937 void
938 GenericMidiControlProtocol::next_bank ()
939 {
940         _current_bank++;
941         reset_controllables ();
942 }
943
944 void
945 GenericMidiControlProtocol::prev_bank()
946 {
947         if (_current_bank) {
948                 _current_bank--;
949                 reset_controllables ();
950         }
951 }