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