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