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