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