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