restore excess calls to sync-order stuff (for now); allow MIDI controllers to use...
[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 #define __STDC_FORMAT_MACROS 1
21 #include <stdint.h>
22
23 #include <sstream>
24 #include <algorithm>
25
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
39 #include "generic_midi_control_protocol.h"
40 #include "midicontrollable.h"
41 #include "midifunction.h"
42
43 using namespace ARDOUR;
44 using namespace PBD;
45 using namespace std;
46
47 #include "i18n.h"
48
49 #define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
50 #define ui_bind(x) boost::protect (boost::bind ((x)))
51
52 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
53         : ControlProtocol (s, _("Generic MIDI"), midi_ui_context())
54         , gui (0)
55 {
56
57         MIDI::Manager* mm = MIDI::Manager::instance();
58         
59         /* XXX it might be nice to run "control" through i18n, but thats a bit tricky because
60            the name is defined in ardour.rc which is likely not internationalized.
61         */
62         
63         _port = mm->port (X_("control"));
64
65         if (_port == 0) {
66                 error << _("no MIDI port named \"control\" exists - generic MIDI control disabled") << endmsg;
67                 throw failed_constructor();
68         }
69
70         do_feedback = false;
71         _feedback_interval = 10000; // microseconds
72         last_feedback_time = 0;
73
74         _current_bank = 0;
75         _bank_size = 0;
76
77         /* XXX is it right to do all these in the same thread as whatever emits the signal? */
78
79         Controllable::StartLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::start_learning, this, _1));
80         Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1));
81         Controllable::CreateBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::create_binding, this, _1, _2, _3));
82         Controllable::DeleteBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding, this, _1));
83
84         Session::SendFeedback.connect (*this, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
85         Route::RemoteControlIDChange.connect (*this, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), midi_ui_context());
86
87         reload_maps ();
88 }
89
90 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
91 {
92         drop_all ();
93         tear_down_gui ();
94 }
95
96 static const char* const midi_map_dir_name = "midi_maps";
97 static const char* const midi_map_suffix = ".map";
98
99 static sys::path
100 system_midi_map_search_path ()
101 {
102         SearchPath spath(system_data_search_path());
103         spath.add_subdirectory_to_paths(midi_map_dir_name);
104
105         // just return the first directory in the search path that exists
106         SearchPath::const_iterator i = std::find_if(spath.begin(), spath.end(), sys::exists);
107
108         if (i == spath.end()) return sys::path();
109
110         return *i;
111 }
112
113 static sys::path
114 user_midi_map_directory ()
115 {
116         sys::path p(user_config_directory());
117         p /= midi_map_dir_name;
118
119         return p;
120 }
121
122 static bool
123 midi_map_filter (const string &str, void */*arg*/)
124 {
125         return (str.length() > strlen(midi_map_suffix) &&
126                 str.find (midi_map_suffix) == (str.length() - strlen (midi_map_suffix)));
127 }
128
129 void
130 GenericMidiControlProtocol::reload_maps ()
131 {
132         vector<string *> *midi_maps;
133         PathScanner scanner;
134         SearchPath spath (system_midi_map_search_path());
135         spath += user_midi_map_directory ();
136
137         midi_maps = scanner (spath.to_string(), midi_map_filter, 0, false, true);
138
139         if (!midi_maps) {
140                 cerr << "No MIDI maps found using " << spath.to_string() << endl;
141                 return;
142         }
143
144         cerr << "Found " << midi_maps->size() << " MIDI maps along " << spath.to_string() << endl;
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
194 void
195 GenericMidiControlProtocol::drop_bindings ()
196 {
197         Glib::Mutex::Lock lm2 (controllables_lock);
198
199         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
200                 if (!(*i)->learned()) {
201                         delete *i;
202                         i = controllables.erase (i);
203                 } else {
204                         ++i;
205                 }
206         }
207
208         for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
209                 delete *i;
210         }
211         functions.clear ();
212
213         _current_binding = "";
214         _bank_size = 0;
215         _current_bank = 0;
216 }
217
218 int
219 GenericMidiControlProtocol::set_active (bool /*yn*/)
220 {
221         /* start/stop delivery/outbound thread */
222         return 0;
223 }
224
225 void
226 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
227 {
228         _feedback_interval = ms;
229 }
230
231 void 
232 GenericMidiControlProtocol::send_feedback ()
233 {
234         if (!do_feedback) {
235                 return;
236         }
237
238         microseconds_t now = get_microseconds ();
239
240         if (last_feedback_time != 0) {
241                 if ((now - last_feedback_time) < _feedback_interval) {
242                         return;
243                 }
244         }
245
246         _send_feedback ();
247         
248         last_feedback_time = now;
249 }
250
251 void 
252 GenericMidiControlProtocol::_send_feedback ()
253 {
254         const int32_t bufsize = 16 * 1024; /* XXX too big */
255         MIDI::byte buf[bufsize];
256         int32_t bsize = bufsize;
257         MIDI::byte* end = buf;
258         
259         for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
260                 end = (*r)->write_feedback (end, bsize);
261         }
262         
263         if (end == buf) {
264                 return;
265         } 
266
267         _port->write (buf, (int32_t) (end - buf), 0);
268 }
269
270 bool
271 GenericMidiControlProtocol::start_learning (Controllable* c)
272 {
273         if (c == 0) {
274                 return false;
275         }
276
277         Glib::Mutex::Lock lm2 (controllables_lock);
278
279         MIDIControllables::iterator tmp;
280         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
281                 tmp = i;
282                 ++tmp;
283                 if ((*i)->get_controllable() == c) {
284                         delete (*i);
285                         controllables.erase (i);
286                 }
287                 i = tmp;
288         }
289
290         {
291                 Glib::Mutex::Lock lm (pending_lock);
292                 
293                 MIDIPendingControllables::iterator ptmp;
294                 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
295                         ptmp = i;
296                         ++ptmp;
297                         if (((*i)->first)->get_controllable() == c) {
298                                 (*i)->second.disconnect();
299                                 delete (*i)->first;
300                                 delete *i;
301                                 pending_controllables.erase (i);
302                         }
303                         i = ptmp;
304                 }
305         }
306
307         MIDIControllable* mc = 0;
308
309         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
310                 if ((*i)->get_controllable()->id() == c->id()) {
311                         mc = *i;
312                         break;
313                 }
314         }
315
316         if (!mc) {
317                 mc = new MIDIControllable (*_port, *c);
318         }
319         
320         {
321                 Glib::Mutex::Lock lm (pending_lock);
322
323                 MIDIPendingControllable* element = new MIDIPendingControllable;
324                 element->first = mc;
325                 c->LearningFinished.connect_same_thread (element->second, boost::bind (&GenericMidiControlProtocol::learning_stopped, this, mc));
326
327                 pending_controllables.push_back (element);
328         }
329
330         mc->learn_about_external_control ();
331         return true;
332 }
333
334 void
335 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
336 {
337         Glib::Mutex::Lock lm (pending_lock);
338         Glib::Mutex::Lock lm2 (controllables_lock);
339         
340         MIDIPendingControllables::iterator tmp;
341
342         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
343                 tmp = i;
344                 ++tmp;
345
346                 if ( (*i)->first == mc) {
347                         (*i)->second.disconnect();
348                         delete *i;
349                         pending_controllables.erase(i);
350                 }
351
352                 i = tmp;
353         }
354
355         controllables.push_back (mc);
356 }
357
358 void
359 GenericMidiControlProtocol::stop_learning (Controllable* c)
360 {
361         Glib::Mutex::Lock lm (pending_lock);
362         Glib::Mutex::Lock lm2 (controllables_lock);
363         MIDIControllable* dptr = 0;
364
365         /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
366            relevant MIDIControllable and remove it from the pending list.
367         */
368
369         for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
370                 if (((*i)->first)->get_controllable() == c) {
371                         (*i)->first->stop_learning ();
372                         dptr = (*i)->first;
373                         (*i)->second.disconnect();
374
375                         delete *i;
376                         pending_controllables.erase (i);
377                         break;
378                 }
379         }
380         
381         delete dptr;
382 }
383
384 void
385 GenericMidiControlProtocol::delete_binding (PBD::Controllable* control)
386 {
387         if (control != 0) {
388                 Glib::Mutex::Lock lm2 (controllables_lock);
389                 
390                 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
391                         MIDIControllable* existingBinding = (*iter);
392                         
393                         if (control == (existingBinding->get_controllable())) {
394                                 delete existingBinding;
395                                 controllables.erase (iter);
396                         }
397                         
398                 }
399         }
400 }
401
402 void
403 GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
404 {
405         if (control != NULL) {
406                 Glib::Mutex::Lock lm2 (controllables_lock);
407                 
408                 MIDI::channel_t channel = (pos & 0xf);
409                 MIDI::byte value = control_number;
410                 
411                 // Create a MIDIControllable
412                 MIDIControllable* mc = new MIDIControllable (*_port, *control);
413
414                 // Remove any old binding for this midi channel/type/value pair
415                 // Note:  can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
416                 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
417                         MIDIControllable* existingBinding = (*iter);
418                         
419                         if ((existingBinding->get_control_channel() & 0xf ) == channel &&
420                             existingBinding->get_control_additional() == value &&
421                             (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller) {
422                                 
423                                 delete existingBinding;
424                                 controllables.erase (iter);
425                         }
426                         
427                 }
428                 
429                 // Update the MIDI Controllable based on the the pos param
430                 // Here is where a table lookup for user mappings could go; for now we'll just wing it...
431                 mc->bind_midi(channel, MIDI::controller, value);
432
433                 controllables.push_back (mc);
434         }
435 }
436
437 XMLNode&
438 GenericMidiControlProtocol::get_state () 
439 {
440         XMLNode* node = new XMLNode ("Protocol"); 
441         char buf[32];
442
443         node->add_property (X_("name"), _name);
444         node->add_property (X_("feedback"), do_feedback ? "1" : "0");
445         snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
446         node->add_property (X_("feedback_interval"), buf);
447
448         if (!_current_binding.empty()) {
449                 node->add_property ("binding", _current_binding);
450         }
451
452         XMLNode* children = new XMLNode (X_("controls"));
453
454         node->add_child_nocopy (*children);
455
456         Glib::Mutex::Lock lm2 (controllables_lock);
457         for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
458
459                 /* we don't care about bindings that come from a bindings map, because
460                    they will all be reset/recreated when we load the relevant bindings
461                    file.
462                 */
463
464                 if ((*i)->learned()) {
465                         children->add_child_nocopy ((*i)->get_state());
466                 }
467         }
468
469         return *node;
470 }
471
472 int
473 GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
474 {
475         XMLNodeList nlist;
476         XMLNodeConstIterator niter;
477         const XMLProperty* prop;
478
479         if ((prop = node.property ("feedback")) != 0) {
480                 do_feedback = (bool) atoi (prop->value().c_str());
481         } else {
482                 do_feedback = false;
483         }
484
485         if ((prop = node.property ("feedback_interval")) != 0) {
486                 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
487                         _feedback_interval = 10000;
488                 }
489         } else {
490                 _feedback_interval = 10000;
491         }
492
493         boost::shared_ptr<Controllable> c;
494         
495         {
496                 Glib::Mutex::Lock lm (pending_lock);
497                 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
498                         delete *i;
499                 }
500                 pending_controllables.clear ();
501         }
502
503         {
504                 Glib::Mutex::Lock lm2 (controllables_lock);
505                 controllables.clear ();
506                 nlist = node.children(); // "controls"
507                 
508                 if (nlist.empty()) {
509                         return 0;
510                 }
511                 
512                 nlist = nlist.front()->children ();
513                 
514                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
515                         
516                         if ((prop = (*niter)->property ("id")) != 0) {
517                                 
518                                 ID id = prop->value ();
519                                 c = session->controllable_by_id (id);
520                                 
521                                 if (c) {
522                                         MIDIControllable* mc = new MIDIControllable (*_port, *c);
523
524                                         if (mc->set_state (**niter, version) == 0) {
525                                                 controllables.push_back (mc);
526                                         }
527                                         
528                                 } else {
529                                         warning << string_compose (
530                                                 _("Generic MIDI control: controllable %1 not found in session (ignored)"),
531                                                 id) << endmsg;
532                                 }
533                         }
534                 }
535
536         }
537
538         if ((prop = node.property ("binding")) != 0) {
539                 for (list<MapInfo>::iterator x = map_info.begin(); x != map_info.end(); ++x) {
540                         if (prop->value() == (*x).name) {
541                                 load_bindings ((*x).path);
542                                 break;
543                         }
544                 }
545         }
546
547         return 0;
548 }
549
550 int
551 GenericMidiControlProtocol::set_feedback (bool yn)
552 {
553         do_feedback = yn;
554         last_feedback_time = 0;
555         return 0;
556 }
557
558 bool
559 GenericMidiControlProtocol::get_feedback () const
560 {
561         return do_feedback;
562 }
563
564
565
566
567 int
568 GenericMidiControlProtocol::load_bindings (const string& xmlpath)
569 {
570         XMLTree state_tree;
571
572         if (!state_tree.read (xmlpath.c_str())) {
573                 error << string_compose(_("Could not understand MIDI bindings file %1"), xmlpath) << endmsg;
574                 return -1;
575         }
576
577         XMLNode* root = state_tree.root();
578
579         if (root->name() != X_("ArdourMIDIBindings")) {
580                 error << string_compose (_("MIDI Bindings file %1 is not really a MIDI bindings file"), xmlpath) << endmsg;
581                 return -1;
582         }
583
584         const XMLProperty* prop;
585
586         if ((prop = root->property ("version")) == 0) {
587                 return -1;
588         } else {
589                 int major;
590                 int minor;
591                 int micro;
592
593                 sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, &micro);
594                 Stateful::loading_state_version = (major * 1000) + minor;
595         }
596         
597         const XMLNodeList& children (root->children());
598         XMLNodeConstIterator citer;
599         XMLNodeConstIterator gciter;
600
601         MIDIControllable* mc;
602
603         drop_all ();
604
605         for (citer = children.begin(); citer != children.end(); ++citer) {
606                 
607                 if ((*citer)->name() == "DeviceInfo") {
608                         const XMLProperty* prop;
609
610                         if ((prop = (*citer)->property ("bank-size")) != 0) {
611                                 _bank_size = atoi (prop->value());
612                                 _current_bank = 0;
613                         }
614                 }
615
616                 if ((*citer)->name() == "Binding") {
617                         const XMLNode* child = *citer;
618
619                         if (child->property ("uri")) {
620                                 /* controllable */
621                                 
622                                 if ((mc = create_binding (*child)) != 0) {
623                                         Glib::Mutex::Lock lm2 (controllables_lock);
624                                         controllables.push_back (mc);
625                                 }
626
627                         } else if (child->property ("function")) {
628
629                                 /* function */
630                                 MIDIFunction* mf;
631
632                                 if ((mf = create_function (*child)) != 0) {
633                                         functions.push_back (mf);
634                                 }
635                         }
636                 }
637         }
638         
639         if ((prop = root->property ("name")) != 0) {
640                 _current_binding = prop->value ();
641         }
642
643         reset_controllables ();
644
645         return 0;
646 }
647
648 MIDIControllable*
649 GenericMidiControlProtocol::create_binding (const XMLNode& node)
650 {
651         const XMLProperty* prop;
652         MIDI::byte detail;
653         MIDI::channel_t channel;
654         string uri;
655         MIDI::eventType ev;
656         int intval;
657
658         if ((prop = node.property (X_("ctl"))) != 0) {
659                 ev = MIDI::controller;
660         } else if ((prop = node.property (X_("note"))) != 0) {
661                 ev = MIDI::on;
662         } else if ((prop = node.property (X_("pgm"))) != 0) {
663                 ev = MIDI::program;
664         } else {
665                 return 0;
666         }
667         
668         if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
669                 return 0;
670         }
671         
672         detail = (MIDI::byte) intval;
673
674         if ((prop = node.property (X_("channel"))) == 0) {
675                 return 0;
676         }
677         
678         if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
679                 return 0;
680         }
681         channel = (MIDI::channel_t) intval;
682         /* adjust channel to zero-based counting */
683         if (channel > 0) {
684                 channel -= 1;
685         }
686         
687         prop = node.property (X_("uri"));
688         uri = prop->value();
689
690         MIDIControllable* mc = new MIDIControllable (*_port, false);
691
692         if (mc->init (uri)) {
693                 delete mc;
694                 return 0;
695         }
696
697         mc->bind_midi (channel, ev, detail);
698
699         cerr << "New MC with URI " << uri << " on channel " << (int) channel << " detail = " << (int) detail << endl;
700
701         return mc;
702 }
703
704 void
705 GenericMidiControlProtocol::reset_controllables ()
706 {
707         cerr << "GM::RC\n";
708         Glib::Mutex::Lock lm2 (controllables_lock);
709         
710         for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
711                 MIDIControllable* existingBinding = (*iter);
712
713                 if (!existingBinding->learned()) {
714                         uint32_t rid = existingBinding->rid();
715                         if (existingBinding->bank_relative()) {
716                                 rid += _current_bank * _bank_size;
717                         }
718                         boost::shared_ptr<Controllable> c = session->controllable_by_rid_and_name (rid, existingBinding->what().c_str());
719                         existingBinding->set_controllable (c.get());
720                 }
721         }
722 }
723
724 MIDIFunction*
725 GenericMidiControlProtocol::create_function (const XMLNode& node)
726 {
727         const XMLProperty* prop;
728         int intval;
729         MIDI::byte detail = 0;
730         MIDI::channel_t channel = 0;
731         string uri;
732         MIDI::eventType ev;
733         MIDI::byte* sysex = 0;
734         uint32_t sysex_size = 0;
735
736         if ((prop = node.property (X_("ctl"))) != 0) {
737                 ev = MIDI::controller;
738         } else if ((prop = node.property (X_("note"))) != 0) {
739                 ev = MIDI::on;
740         } else if ((prop = node.property (X_("pgm"))) != 0) {
741                 ev = MIDI::program;
742         } else if ((prop = node.property (X_("sysex"))) != 0) {
743
744                 ev = MIDI::sysex;
745                 int val;
746                 uint32_t cnt;
747
748                 {
749                         cnt = 0;
750                         stringstream ss (prop->value());
751                         ss << hex;
752                         
753                         while (ss >> val) {
754                                 cnt++;
755                         }
756                 }
757
758                 if (cnt == 0) {
759                         return 0;
760                 }
761
762                 sysex = new MIDI::byte[cnt];
763                 sysex_size = cnt;
764                 
765                 {
766                         stringstream ss (prop->value());
767                         ss << hex;
768                         cnt = 0;
769                         
770                         while (ss >> val) {
771                                 sysex[cnt++] = (MIDI::byte) val;
772                         }
773                 }
774                 
775         } else {
776                 warning << "Binding ignored - unknown type" << endmsg;
777                 return 0;
778         }
779
780         if (sysex_size == 0) {
781                 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
782                         return 0;
783                 }
784                 
785                 detail = (MIDI::byte) intval;
786
787                 if ((prop = node.property (X_("channel"))) == 0) {
788                         return 0;
789                 }
790         
791                 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
792                         return 0;
793                 }
794                 channel = (MIDI::channel_t) intval;
795                 /* adjust channel to zero-based counting */
796                 if (channel > 0) {
797                         channel -= 1;
798                 }
799         }
800
801         prop = node.property (X_("function"));
802         
803         MIDIFunction* mf = new MIDIFunction (*_port);
804         
805         if (mf->init (*this, prop->value(), sysex, sysex_size)) {
806                 delete mf;
807                 return 0;
808         }
809
810         cerr << "New MF with function = " << prop->value() << " on channel " << (int) channel << " detail = " << (int) detail << endl;
811         mf->bind_midi (channel, ev, detail);
812
813
814         return mf;
815 }
816
817 void
818 GenericMidiControlProtocol::set_current_bank (uint32_t b)
819 {
820         _current_bank = b;
821         reset_controllables ();
822 }
823
824 void
825 GenericMidiControlProtocol::next_bank ()
826 {
827         _current_bank++;
828         reset_controllables ();
829 }
830
831 void
832 GenericMidiControlProtocol::prev_bank()
833 {
834         if (_current_bank) {
835                 _current_bank--;
836                 reset_controllables ();
837         }
838 }