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