track changes to config parameters for MMC device id's correctly (from roy vegard)
[ardour2.git] / libs / surfaces / generic_midi / generic_midi_control_protocol.cc
blob1a615de4249d8558640d17662454c58faa0b702d
1 /*
2 Copyright (C) 2006 Paul Davis
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.
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.
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.
20 #include <stdint.h>
22 #include <sstream>
23 #include <algorithm>
25 #include <glibmm/miscutils.h>
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"
33 #include "midi++/port.h"
34 #include "midi++/manager.h"
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"
42 #include "generic_midi_control_protocol.h"
43 #include "midicontrollable.h"
44 #include "midifunction.h"
45 #include "midiaction.h"
47 using namespace ARDOUR;
48 using namespace PBD;
49 using namespace std;
51 #include "i18n.h"
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)))
56 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
57 : ControlProtocol (s, _("Generic MIDI"), midi_ui_context())
58 , gui (0)
61 _input_port = MIDI::Manager::instance()->midi_input_port ();
62 _output_port = MIDI::Manager::instance()->midi_output_port ();
64 do_feedback = false;
65 _feedback_interval = 10000; // microseconds
66 last_feedback_time = 0;
68 _current_bank = 0;
69 _bank_size = 0;
71 /* XXX is it right to do all these in the same thread as whatever emits the signal? */
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));
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());
81 reload_maps ();
84 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
86 drop_all ();
87 tear_down_gui ();
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";
94 static sys::path
95 system_midi_map_search_path ()
97 bool midimap_path_defined = false;
98 sys::path spath_env (Glib::getenv (midimap_env_variable_name, midimap_path_defined));
100 if (midimap_path_defined) {
101 return spath_env;
104 SearchPath spath (system_data_search_path());
105 spath.add_subdirectory_to_paths(midi_map_dir_name);
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);
110 if (i == spath.end()) return sys::path();
112 return *i;
115 static sys::path
116 user_midi_map_directory ()
118 sys::path p(user_config_directory());
119 p /= midi_map_dir_name;
121 return p;
124 static bool
125 midi_map_filter (const string &str, void */*arg*/)
127 return (str.length() > strlen(midi_map_suffix) &&
128 str.find (midi_map_suffix) == (str.length() - strlen (midi_map_suffix)));
131 void
132 GenericMidiControlProtocol::reload_maps ()
134 vector<string *> *midi_maps;
135 PathScanner scanner;
136 SearchPath spath (system_midi_map_search_path());
137 spath += user_midi_map_directory ();
139 midi_maps = scanner (spath.to_string(), midi_map_filter, 0, false, true);
141 if (!midi_maps) {
142 cerr << "No MIDI maps found using " << spath.to_string() << endl;
143 return;
146 for (vector<string*>::iterator i = midi_maps->begin(); i != midi_maps->end(); ++i) {
147 string fullpath = *(*i);
149 XMLTree tree;
151 if (!tree.read (fullpath.c_str())) {
152 continue;
155 MapInfo mi;
157 XMLProperty* prop = tree.root()->property ("name");
159 if (!prop) {
160 continue;
163 mi.name = prop->value ();
164 mi.path = fullpath;
166 map_info.push_back (mi);
169 delete midi_maps;
172 void
173 GenericMidiControlProtocol::drop_all ()
175 Glib::Mutex::Lock lm (pending_lock);
176 Glib::Mutex::Lock lm2 (controllables_lock);
178 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
179 delete *i;
181 controllables.clear ();
183 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
184 delete *i;
186 pending_controllables.clear ();
188 for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
189 delete *i;
191 functions.clear ();
193 for (MIDIActions::iterator i = actions.begin(); i != actions.end(); ++i) {
194 delete *i;
196 actions.clear ();
199 void
200 GenericMidiControlProtocol::drop_bindings ()
202 Glib::Mutex::Lock lm2 (controllables_lock);
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;
213 for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
214 delete *i;
216 functions.clear ();
218 _current_binding = "";
219 _bank_size = 0;
220 _current_bank = 0;
224 GenericMidiControlProtocol::set_active (bool /*yn*/)
226 /* start/stop delivery/outbound thread */
227 return 0;
230 void
231 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
233 _feedback_interval = ms;
236 void
237 GenericMidiControlProtocol::send_feedback ()
239 if (!do_feedback) {
240 return;
243 microseconds_t now = get_microseconds ();
245 if (last_feedback_time != 0) {
246 if ((now - last_feedback_time) < _feedback_interval) {
247 return;
251 _send_feedback ();
253 last_feedback_time = now;
256 void
257 GenericMidiControlProtocol::_send_feedback ()
259 const int32_t bufsize = 16 * 1024; /* XXX too big */
260 MIDI::byte buf[bufsize];
261 int32_t bsize = bufsize;
262 MIDI::byte* end = buf;
264 for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
265 end = (*r)->write_feedback (end, bsize);
268 if (end == buf) {
269 return;
272 _output_port->write (buf, (int32_t) (end - buf), 0);
275 bool
276 GenericMidiControlProtocol::start_learning (Controllable* c)
278 if (c == 0) {
279 return false;
282 Glib::Mutex::Lock lm2 (controllables_lock);
284 MIDIControllables::iterator tmp;
285 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
286 tmp = i;
287 ++tmp;
288 if ((*i)->get_controllable() == c) {
289 delete (*i);
290 controllables.erase (i);
292 i = tmp;
296 Glib::Mutex::Lock lm (pending_lock);
298 MIDIPendingControllables::iterator ptmp;
299 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
300 ptmp = i;
301 ++ptmp;
302 if (((*i)->first)->get_controllable() == c) {
303 (*i)->second.disconnect();
304 delete (*i)->first;
305 delete *i;
306 pending_controllables.erase (i);
308 i = ptmp;
312 MIDIControllable* mc = 0;
314 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
315 if ((*i)->get_controllable() && ((*i)->get_controllable()->id() == c->id())) {
316 mc = *i;
317 break;
321 if (!mc) {
322 mc = new MIDIControllable (*_input_port, *c, false);
326 Glib::Mutex::Lock lm (pending_lock);
328 MIDIPendingControllable* element = new MIDIPendingControllable;
329 element->first = mc;
330 c->LearningFinished.connect_same_thread (element->second, boost::bind (&GenericMidiControlProtocol::learning_stopped, this, mc));
332 pending_controllables.push_back (element);
335 mc->learn_about_external_control ();
336 return true;
339 void
340 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
342 Glib::Mutex::Lock lm (pending_lock);
343 Glib::Mutex::Lock lm2 (controllables_lock);
345 MIDIPendingControllables::iterator tmp;
347 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
348 tmp = i;
349 ++tmp;
351 if ( (*i)->first == mc) {
352 (*i)->second.disconnect();
353 delete *i;
354 pending_controllables.erase(i);
357 i = tmp;
360 controllables.push_back (mc);
363 void
364 GenericMidiControlProtocol::stop_learning (Controllable* c)
366 Glib::Mutex::Lock lm (pending_lock);
367 Glib::Mutex::Lock lm2 (controllables_lock);
368 MIDIControllable* dptr = 0;
370 /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
371 relevant MIDIControllable and remove it from the pending list.
374 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
375 if (((*i)->first)->get_controllable() == c) {
376 (*i)->first->stop_learning ();
377 dptr = (*i)->first;
378 (*i)->second.disconnect();
380 delete *i;
381 pending_controllables.erase (i);
382 break;
386 delete dptr;
389 void
390 GenericMidiControlProtocol::delete_binding (PBD::Controllable* control)
392 if (control != 0) {
393 Glib::Mutex::Lock lm2 (controllables_lock);
395 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
396 MIDIControllable* existingBinding = (*iter);
398 if (control == (existingBinding->get_controllable())) {
399 delete existingBinding;
400 iter = controllables.erase (iter);
401 } else {
402 ++iter;
409 void
410 GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
412 if (control != NULL) {
413 Glib::Mutex::Lock lm2 (controllables_lock);
415 MIDI::channel_t channel = (pos & 0xf);
416 MIDI::byte value = control_number;
418 // Create a MIDIControllable
419 MIDIControllable* mc = new MIDIControllable (*_input_port, *control, false);
421 // Remove any old binding for this midi channel/type/value pair
422 // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
423 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
424 MIDIControllable* existingBinding = (*iter);
426 if ((existingBinding->get_control_channel() & 0xf ) == channel &&
427 existingBinding->get_control_additional() == value &&
428 (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller) {
430 delete existingBinding;
431 iter = controllables.erase (iter);
432 } else {
433 ++iter;
438 // Update the MIDI Controllable based on the the pos param
439 // Here is where a table lookup for user mappings could go; for now we'll just wing it...
440 mc->bind_midi(channel, MIDI::controller, value);
442 controllables.push_back (mc);
446 XMLNode&
447 GenericMidiControlProtocol::get_state ()
449 XMLNode* node = new XMLNode ("Protocol");
450 char buf[32];
452 node->add_property (X_("name"), _name);
453 node->add_property (X_("feedback"), do_feedback ? "1" : "0");
454 snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
455 node->add_property (X_("feedback_interval"), buf);
457 if (!_current_binding.empty()) {
458 node->add_property ("binding", _current_binding);
461 XMLNode* children = new XMLNode (X_("Controls"));
463 node->add_child_nocopy (*children);
465 Glib::Mutex::Lock lm2 (controllables_lock);
466 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
468 /* we don't care about bindings that come from a bindings map, because
469 they will all be reset/recreated when we load the relevant bindings
470 file.
473 if ((*i)->learned()) {
474 children->add_child_nocopy ((*i)->get_state());
478 return *node;
482 GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
484 XMLNodeList nlist;
485 XMLNodeConstIterator niter;
486 const XMLProperty* prop;
488 if ((prop = node.property ("feedback")) != 0) {
489 do_feedback = (bool) atoi (prop->value().c_str());
490 } else {
491 do_feedback = false;
494 if ((prop = node.property ("feedback_interval")) != 0) {
495 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
496 _feedback_interval = 10000;
498 } else {
499 _feedback_interval = 10000;
502 boost::shared_ptr<Controllable> c;
505 Glib::Mutex::Lock lm (pending_lock);
506 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
507 delete *i;
509 pending_controllables.clear ();
513 Glib::Mutex::Lock lm2 (controllables_lock);
514 controllables.clear ();
515 nlist = node.children(); // "Controls"
517 if (nlist.empty()) {
518 return 0;
521 nlist = nlist.front()->children ();
523 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
525 if ((prop = (*niter)->property ("id")) != 0) {
527 cerr << "Looking for MIDI Controllable with ID " << prop->value() << endl;
529 ID id = prop->value ();
530 Controllable* c = Controllable::by_id (id);
532 cerr << "\tresult = " << c << endl;
534 if (c) {
535 MIDIControllable* mc = new MIDIControllable (*_input_port, *c, false);
537 if (mc->set_state (**niter, version) == 0) {
538 controllables.push_back (mc);
541 } else {
542 warning << string_compose (
543 _("Generic MIDI control: controllable %1 not found in session (ignored)"),
544 id) << endmsg;
551 if ((prop = node.property ("binding")) != 0) {
552 for (list<MapInfo>::iterator x = map_info.begin(); x != map_info.end(); ++x) {
553 if (prop->value() == (*x).name) {
554 load_bindings ((*x).path);
555 break;
560 return 0;
564 GenericMidiControlProtocol::set_feedback (bool yn)
566 do_feedback = yn;
567 last_feedback_time = 0;
568 return 0;
571 bool
572 GenericMidiControlProtocol::get_feedback () const
574 return do_feedback;
581 GenericMidiControlProtocol::load_bindings (const string& xmlpath)
583 XMLTree state_tree;
585 if (!state_tree.read (xmlpath.c_str())) {
586 error << string_compose(_("Could not understand MIDI bindings file %1"), xmlpath) << endmsg;
587 return -1;
590 XMLNode* root = state_tree.root();
592 if (root->name() != X_("ArdourMIDIBindings")) {
593 error << string_compose (_("MIDI Bindings file %1 is not really a MIDI bindings file"), xmlpath) << endmsg;
594 return -1;
597 const XMLProperty* prop;
599 if ((prop = root->property ("version")) == 0) {
600 return -1;
601 } else {
602 int major;
603 int minor;
604 int micro;
606 sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, &micro);
607 Stateful::loading_state_version = (major * 1000) + minor;
610 const XMLNodeList& children (root->children());
611 XMLNodeConstIterator citer;
612 XMLNodeConstIterator gciter;
614 MIDIControllable* mc;
616 drop_all ();
618 for (citer = children.begin(); citer != children.end(); ++citer) {
620 if ((*citer)->name() == "DeviceInfo") {
621 const XMLProperty* prop;
623 if ((prop = (*citer)->property ("bank-size")) != 0) {
624 _bank_size = atoi (prop->value());
625 _current_bank = 0;
629 if ((*citer)->name() == "Binding") {
630 const XMLNode* child = *citer;
632 if (child->property ("uri")) {
633 /* controllable */
635 if ((mc = create_binding (*child)) != 0) {
636 Glib::Mutex::Lock lm2 (controllables_lock);
637 controllables.push_back (mc);
640 } else if (child->property ("function")) {
642 /* function */
643 MIDIFunction* mf;
645 if ((mf = create_function (*child)) != 0) {
646 functions.push_back (mf);
649 } else if (child->property ("action")) {
650 MIDIAction* ma;
652 if ((ma = create_action (*child)) != 0) {
653 actions.push_back (ma);
659 if ((prop = root->property ("name")) != 0) {
660 _current_binding = prop->value ();
663 reset_controllables ();
665 return 0;
668 MIDIControllable*
669 GenericMidiControlProtocol::create_binding (const XMLNode& node)
671 const XMLProperty* prop;
672 MIDI::byte detail;
673 MIDI::channel_t channel;
674 string uri;
675 MIDI::eventType ev;
676 int intval;
677 bool momentary;
679 if ((prop = node.property (X_("ctl"))) != 0) {
680 ev = MIDI::controller;
681 } else if ((prop = node.property (X_("note"))) != 0) {
682 ev = MIDI::on;
683 } else if ((prop = node.property (X_("pgm"))) != 0) {
684 ev = MIDI::program;
685 } else {
686 return 0;
689 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
690 return 0;
693 detail = (MIDI::byte) intval;
695 if ((prop = node.property (X_("channel"))) == 0) {
696 return 0;
699 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
700 return 0;
702 channel = (MIDI::channel_t) intval;
703 /* adjust channel to zero-based counting */
704 if (channel > 0) {
705 channel -= 1;
708 if ((prop = node.property (X_("momentary"))) != 0) {
709 momentary = string_is_affirmative (prop->value());
710 } else {
711 momentary = false;
714 prop = node.property (X_("uri"));
715 uri = prop->value();
717 MIDIControllable* mc = new MIDIControllable (*_input_port, momentary);
719 if (mc->init (uri)) {
720 delete mc;
721 return 0;
724 mc->bind_midi (channel, ev, detail);
726 return mc;
729 void
730 GenericMidiControlProtocol::reset_controllables ()
732 Glib::Mutex::Lock lm2 (controllables_lock);
734 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ) {
735 MIDIControllable* existingBinding = (*iter);
736 MIDIControllables::iterator next = iter;
737 ++next;
739 if (!existingBinding->learned()) {
740 ControllableDescriptor& desc (existingBinding->descriptor());
742 if (desc.banked()) {
743 desc.set_bank_offset (_current_bank * _bank_size);
746 /* its entirely possible that the session doesn't have
747 * the specified controllable (e.g. it has too few
748 * tracks). if we find this to be the case, drop any
749 * bindings that would be left without controllables.
752 boost::shared_ptr<Controllable> c = session->controllable_by_descriptor (desc);
753 if (c) {
754 existingBinding->set_controllable (c.get());
755 } else {
756 controllables.erase (iter);
760 iter = next;
764 MIDIFunction*
765 GenericMidiControlProtocol::create_function (const XMLNode& node)
767 const XMLProperty* prop;
768 int intval;
769 MIDI::byte detail = 0;
770 MIDI::channel_t channel = 0;
771 string uri;
772 MIDI::eventType ev;
773 MIDI::byte* data = 0;
774 uint32_t data_size = 0;
775 string argument;
777 if ((prop = node.property (X_("ctl"))) != 0) {
778 ev = MIDI::controller;
779 } else if ((prop = node.property (X_("note"))) != 0) {
780 ev = MIDI::on;
781 } else if ((prop = node.property (X_("pgm"))) != 0) {
782 ev = MIDI::program;
783 } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
785 if (prop->name() == X_("sysex")) {
786 ev = MIDI::sysex;
787 } else {
788 ev = MIDI::any;
791 int val;
792 uint32_t cnt;
795 cnt = 0;
796 stringstream ss (prop->value());
797 ss << hex;
799 while (ss >> val) {
800 cnt++;
804 if (cnt == 0) {
805 return 0;
808 data = new MIDI::byte[cnt];
809 data_size = cnt;
812 stringstream ss (prop->value());
813 ss << hex;
814 cnt = 0;
816 while (ss >> val) {
817 data[cnt++] = (MIDI::byte) val;
821 } else {
822 warning << "Binding ignored - unknown type" << endmsg;
823 return 0;
826 if (data_size == 0) {
827 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
828 return 0;
831 detail = (MIDI::byte) intval;
833 if ((prop = node.property (X_("channel"))) == 0) {
834 return 0;
837 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
838 return 0;
840 channel = (MIDI::channel_t) intval;
841 /* adjust channel to zero-based counting */
842 if (channel > 0) {
843 channel -= 1;
847 if ((prop = node.property (X_("arg"))) != 0) {
848 argument = prop->value ();
851 prop = node.property (X_("function"));
853 MIDIFunction* mf = new MIDIFunction (*_input_port);
855 if (mf->setup (*this, prop->value(), argument, data, data_size)) {
856 delete mf;
857 return 0;
860 mf->bind_midi (channel, ev, detail);
862 return mf;
865 MIDIAction*
866 GenericMidiControlProtocol::create_action (const XMLNode& node)
868 const XMLProperty* prop;
869 int intval;
870 MIDI::byte detail = 0;
871 MIDI::channel_t channel = 0;
872 string uri;
873 MIDI::eventType ev;
874 MIDI::byte* data = 0;
875 uint32_t data_size = 0;
877 if ((prop = node.property (X_("ctl"))) != 0) {
878 ev = MIDI::controller;
879 } else if ((prop = node.property (X_("note"))) != 0) {
880 ev = MIDI::on;
881 } else if ((prop = node.property (X_("pgm"))) != 0) {
882 ev = MIDI::program;
883 } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
885 if (prop->name() == X_("sysex")) {
886 ev = MIDI::sysex;
887 } else {
888 ev = MIDI::any;
891 int val;
892 uint32_t cnt;
895 cnt = 0;
896 stringstream ss (prop->value());
897 ss << hex;
899 while (ss >> val) {
900 cnt++;
904 if (cnt == 0) {
905 return 0;
908 data = new MIDI::byte[cnt];
909 data_size = cnt;
912 stringstream ss (prop->value());
913 ss << hex;
914 cnt = 0;
916 while (ss >> val) {
917 data[cnt++] = (MIDI::byte) val;
921 } else {
922 warning << "Binding ignored - unknown type" << endmsg;
923 return 0;
926 if (data_size == 0) {
927 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
928 return 0;
931 detail = (MIDI::byte) intval;
933 if ((prop = node.property (X_("channel"))) == 0) {
934 return 0;
937 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
938 return 0;
940 channel = (MIDI::channel_t) intval;
941 /* adjust channel to zero-based counting */
942 if (channel > 0) {
943 channel -= 1;
947 prop = node.property (X_("action"));
949 MIDIAction* ma = new MIDIAction (*_input_port);
951 if (ma->init (*this, prop->value(), data, data_size)) {
952 delete ma;
953 return 0;
956 ma->bind_midi (channel, ev, detail);
958 return ma;
961 void
962 GenericMidiControlProtocol::set_current_bank (uint32_t b)
964 _current_bank = b;
965 reset_controllables ();
968 void
969 GenericMidiControlProtocol::next_bank ()
971 _current_bank++;
972 reset_controllables ();
975 void
976 GenericMidiControlProtocol::prev_bank()
978 if (_current_bank) {
979 _current_bank--;
980 reset_controllables ();