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.
25 #include <glibmm/fileutils.h>
26 #include <glibmm/miscutils.h>
28 #include "pbd/controllable_descriptor.h"
29 #include "pbd/error.h"
30 #include "pbd/failed_constructor.h"
31 #include "pbd/file_utils.h"
32 #include "pbd/xml++.h"
33 #include "pbd/compose.h"
35 #include "midi++/port.h"
37 #include "ardour/audioengine.h"
38 #include "ardour/filesystem_paths.h"
39 #include "ardour/session.h"
40 #include "ardour/route.h"
41 #include "ardour/midi_ui.h"
42 #include "ardour/rc_configuration.h"
43 #include "ardour/midiport_manager.h"
44 #include "ardour/debug.h"
46 #include "generic_midi_control_protocol.h"
47 #include "midicontrollable.h"
48 #include "midifunction.h"
49 #include "midiaction.h"
51 using namespace ARDOUR;
57 #define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
59 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
60 : ControlProtocol (s, _("Generic MIDI"))
65 _input_port = s.midi_input_port ();
66 _output_port = s.midi_output_port ();
69 _feedback_interval = 10000; // microseconds
70 last_feedback_time = 0;
75 /* these signals are emitted by the MidiControlUI's event loop thread
76 * and we may as well handle them right there in the same the same
80 Controllable::StartLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::start_learning, this, _1));
81 Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1));
82 Controllable::CreateBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::create_binding, this, _1, _2, _3));
83 Controllable::DeleteBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding, this, _1));
85 /* this signal is emitted by the process() callback, and if
86 * send_feedback() is going to do anything, it should do it in the
87 * context of the process() callback itself.
90 Session::SendFeedback.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::send_feedback, this));
91 //Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
93 /* this one is cross-thread */
95 Route::RemoteControlIDChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), midi_ui_context());
100 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
106 static const char * const midimap_env_variable_name = "ARDOUR_MIDIMAPS_PATH";
107 static const char* const midi_map_dir_name = "midi_maps";
108 static const char* const midi_map_suffix = ".map";
111 system_midi_map_search_path ()
113 bool midimap_path_defined = false;
114 std::string spath_env (Glib::getenv (midimap_env_variable_name, midimap_path_defined));
116 if (midimap_path_defined) {
120 Searchpath spath (ardour_data_search_path());
121 spath.add_subdirectory_to_paths(midi_map_dir_name);
126 user_midi_map_directory ()
128 return Glib::build_filename (user_config_directory(), midi_map_dir_name);
132 midi_map_filter (const string &str, void* /*arg*/)
134 return (str.length() > strlen(midi_map_suffix) &&
135 str.find (midi_map_suffix) == (str.length() - strlen (midi_map_suffix)));
139 GenericMidiControlProtocol::reload_maps ()
141 vector<string> midi_maps;
142 Searchpath spath (system_midi_map_search_path());
143 spath += user_midi_map_directory ();
145 find_files_matching_filter (midi_maps, spath, midi_map_filter, 0, false, true);
147 if (midi_maps.empty()) {
148 cerr << "No MIDI maps found using " << spath.to_string() << endl;
152 for (vector<string>::iterator i = midi_maps.begin(); i != midi_maps.end(); ++i) {
153 string fullpath = *i;
157 if (!tree.read (fullpath.c_str())) {
163 XMLProperty* prop = tree.root()->property ("name");
169 mi.name = prop->value ();
172 map_info.push_back (mi);
177 GenericMidiControlProtocol::drop_all ()
179 DEBUG_TRACE (DEBUG::GenericMidi, "Drop all bindings\n");
180 Glib::Threads::Mutex::Lock lm (pending_lock);
181 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
183 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
186 controllables.clear ();
188 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
191 pending_controllables.clear ();
193 for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
198 for (MIDIActions::iterator i = actions.begin(); i != actions.end(); ++i) {
205 GenericMidiControlProtocol::drop_bindings ()
207 DEBUG_TRACE (DEBUG::GenericMidi, "Drop bindings, leave learned\n");
208 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
210 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
211 if (!(*i)->learned()) {
213 i = controllables.erase (i);
219 for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
224 _current_binding = "";
230 GenericMidiControlProtocol::set_active (bool /*yn*/)
232 /* start/stop delivery/outbound thread */
237 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
239 _feedback_interval = ms;
243 GenericMidiControlProtocol::send_feedback ()
245 /* This is executed in RT "process" context", so no blocking calls
252 microseconds_t now = get_microseconds ();
254 if (last_feedback_time != 0) {
255 if ((now - last_feedback_time) < _feedback_interval) {
262 last_feedback_time = now;
266 GenericMidiControlProtocol::_send_feedback ()
268 /* This is executed in RT "process" context", so no blocking calls
271 const int32_t bufsize = 16 * 1024; /* XXX too big */
272 MIDI::byte buf[bufsize];
273 int32_t bsize = bufsize;
275 /* XXX: due to bugs in some ALSA / JACK MIDI bridges, we have to do separate
276 writes for each controllable here; if we send more than one MIDI message
277 in a single jack_midi_event_write then some bridges will only pass the
281 Glib::Threads::Mutex::Lock lm (controllables_lock, Glib::Threads::TRY_LOCK);
286 for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
287 MIDI::byte* end = (*r)->write_feedback (buf, bsize);
289 _output_port->write (buf, (int32_t) (end - buf), 0);
295 GenericMidiControlProtocol::start_learning (Controllable* c)
301 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
302 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Learn binding: Controlable number: %1\n", c));
304 MIDIControllables::iterator tmp;
305 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
308 if ((*i)->get_controllable() == c) {
310 controllables.erase (i);
316 Glib::Threads::Mutex::Lock lm (pending_lock);
318 MIDIPendingControllables::iterator ptmp;
319 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
322 if (((*i)->first)->get_controllable() == c) {
323 (*i)->second.disconnect();
326 pending_controllables.erase (i);
332 MIDIControllable* mc = 0;
334 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
335 if ((*i)->get_controllable() && ((*i)->get_controllable()->id() == c->id())) {
342 mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
346 Glib::Threads::Mutex::Lock lm (pending_lock);
348 MIDIPendingControllable* element = new MIDIPendingControllable;
350 c->LearningFinished.connect_same_thread (element->second, boost::bind (&GenericMidiControlProtocol::learning_stopped, this, mc));
352 pending_controllables.push_back (element);
354 mc->learn_about_external_control ();
359 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
361 Glib::Threads::Mutex::Lock lm (pending_lock);
362 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
364 MIDIPendingControllables::iterator tmp;
366 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
370 if ( (*i)->first == mc) {
371 (*i)->second.disconnect();
373 pending_controllables.erase(i);
379 controllables.push_back (mc);
383 GenericMidiControlProtocol::stop_learning (Controllable* c)
385 Glib::Threads::Mutex::Lock lm (pending_lock);
386 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
387 MIDIControllable* dptr = 0;
389 /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
390 relevant MIDIControllable and remove it from the pending list.
393 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
394 if (((*i)->first)->get_controllable() == c) {
395 (*i)->first->stop_learning ();
397 (*i)->second.disconnect();
400 pending_controllables.erase (i);
409 GenericMidiControlProtocol::delete_binding (PBD::Controllable* control)
412 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
414 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
415 MIDIControllable* existingBinding = (*iter);
417 if (control == (existingBinding->get_controllable())) {
418 delete existingBinding;
419 iter = controllables.erase (iter);
428 // This next function seems unused
430 GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
432 if (control != NULL) {
433 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
435 MIDI::channel_t channel = (pos & 0xf);
436 MIDI::byte value = control_number;
438 // Create a MIDIControllable
439 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *control, false);
441 // Remove any old binding for this midi channel/type/value pair
442 // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
443 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
444 MIDIControllable* existingBinding = (*iter);
446 if ((existingBinding->get_control_channel() & 0xf ) == channel &&
447 existingBinding->get_control_additional() == value &&
448 (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller) {
450 delete existingBinding;
451 iter = controllables.erase (iter);
458 // Update the MIDI Controllable based on the the pos param
459 // Here is where a table lookup for user mappings could go; for now we'll just wing it...
460 mc->bind_midi(channel, MIDI::controller, value);
461 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Create binding: Channel: %1 Controller: %2 Value: %3 \n", channel, MIDI::controller, value));
462 controllables.push_back (mc);
467 GenericMidiControlProtocol::check_used_event (int pos, int control_number)
469 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
471 MIDI::channel_t channel = (pos & 0xf);
472 MIDI::byte value = control_number;
474 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("checking for used event: Channel: %1 Controller: %2 value: %3\n", (int) channel, (pos & 0xf0), (int) value));
476 // Remove any old binding for this midi channel/type/value pair
477 // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
478 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
479 MIDIControllable* existingBinding = (*iter);
480 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
481 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
482 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
483 delete existingBinding;
484 iter = controllables.erase (iter);
493 for (MIDIFunctions::iterator iter = functions.begin(); iter != functions.end();) {
494 MIDIFunction* existingBinding = (*iter);
495 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
496 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
497 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
498 delete existingBinding;
499 iter = functions.erase (iter);
508 for (MIDIActions::iterator iter = actions.begin(); iter != actions.end();) {
509 MIDIAction* existingBinding = (*iter);
510 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
511 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
512 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
513 delete existingBinding;
514 iter = actions.erase (iter);
526 GenericMidiControlProtocol::get_state ()
528 XMLNode& node (ControlProtocol::get_state());
531 snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
532 node.add_property (X_("feedback_interval"), buf);
533 snprintf (buf, sizeof (buf), "%d", _threshold);
534 node.add_property (X_("threshold"), buf);
536 node.add_property (X_("motorized"), _motorised ? "yes" : "no");
538 if (!_current_binding.empty()) {
539 node.add_property ("binding", _current_binding);
542 XMLNode* children = new XMLNode (X_("Controls"));
544 node.add_child_nocopy (*children);
546 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
547 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
549 /* we don't care about bindings that come from a bindings map, because
550 they will all be reset/recreated when we load the relevant bindings
554 if ((*i)->get_controllable() && (*i)->learned()) {
555 children->add_child_nocopy ((*i)->get_state());
563 GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
566 XMLNodeConstIterator niter;
567 const XMLProperty* prop;
569 if (ControlProtocol::set_state (node, version)) {
573 if ((prop = node.property ("feedback_interval")) != 0) {
574 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
575 _feedback_interval = 10000;
578 _feedback_interval = 10000;
581 if ((prop = node.property ("threshold")) != 0) {
582 if (sscanf (prop->value().c_str(), "%d", &_threshold) != 1) {
589 if ((prop = node.property ("motorized")) != 0) {
590 _motorised = string_is_affirmative (prop->value ());
595 boost::shared_ptr<Controllable> c;
598 Glib::Threads::Mutex::Lock lm (pending_lock);
599 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
602 pending_controllables.clear ();
605 // midi map has to be loaded first so learned binding can go on top
606 if ((prop = node.property ("binding")) != 0) {
607 for (list<MapInfo>::iterator x = map_info.begin(); x != map_info.end(); ++x) {
608 if (prop->value() == (*x).name) {
609 load_bindings ((*x).path);
615 /* Load up specific bindings from the
616 * <Controls><MidiControllable>...</MidiControllable><Controls> section
620 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
621 nlist = node.children(); // "Controls"
623 if (!nlist.empty()) {
624 nlist = nlist.front()->children(); // "MIDIControllable" ...
626 if (!nlist.empty()) {
627 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
629 if ((prop = (*niter)->property ("id")) != 0) {
631 ID id = prop->value ();
632 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Relearned binding for session: Control ID: %1\n", id.to_s()));
633 Controllable* c = Controllable::by_id (id);
636 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
638 if (mc->set_state (**niter, version) == 0) {
639 controllables.push_back (mc);
643 warning << string_compose (
644 _("Generic MIDI control: controllable %1 not found in session (ignored)"),
645 id.to_s()) << endmsg;
657 GenericMidiControlProtocol::set_feedback (bool yn)
660 last_feedback_time = 0;
665 GenericMidiControlProtocol::get_feedback () const
671 GenericMidiControlProtocol::load_bindings (const string& xmlpath)
673 DEBUG_TRACE (DEBUG::GenericMidi, "Load bindings: Reading midi map\n");
676 if (!state_tree.read (xmlpath.c_str())) {
677 error << string_compose(_("Could not understand MIDI bindings file %1"), xmlpath) << endmsg;
681 XMLNode* root = state_tree.root();
683 if (root->name() != X_("ArdourMIDIBindings")) {
684 error << string_compose (_("MIDI Bindings file %1 is not really a MIDI bindings file"), xmlpath) << endmsg;
688 const XMLProperty* prop;
690 if ((prop = root->property ("version")) == 0) {
697 sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, µ);
698 Stateful::loading_state_version = (major * 1000) + minor;
701 const XMLNodeList& children (root->children());
702 XMLNodeConstIterator citer;
703 XMLNodeConstIterator gciter;
705 MIDIControllable* mc;
709 DEBUG_TRACE (DEBUG::GenericMidi, "Loading bindings\n");
710 for (citer = children.begin(); citer != children.end(); ++citer) {
712 if ((*citer)->name() == "DeviceInfo") {
713 const XMLProperty* prop;
715 if ((prop = (*citer)->property ("bank-size")) != 0) {
716 _bank_size = atoi (prop->value());
720 if ((prop = (*citer)->property ("motorized")) != 0) {
721 _motorised = string_is_affirmative (prop->value ());
726 if ((prop = (*citer)->property ("threshold")) != 0) {
727 _threshold = atoi (prop->value ());
734 if ((*citer)->name() == "Binding") {
735 const XMLNode* child = *citer;
737 if (child->property ("uri")) {
740 if ((mc = create_binding (*child)) != 0) {
741 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
742 controllables.push_back (mc);
745 } else if (child->property ("function")) {
750 if ((mf = create_function (*child)) != 0) {
751 functions.push_back (mf);
754 } else if (child->property ("action")) {
757 if ((ma = create_action (*child)) != 0) {
758 actions.push_back (ma);
764 if ((prop = root->property ("name")) != 0) {
765 _current_binding = prop->value ();
768 reset_controllables ();
774 GenericMidiControlProtocol::create_binding (const XMLNode& node)
776 const XMLProperty* prop;
778 MIDI::channel_t channel;
783 MIDIControllable::Encoder encoder = MIDIControllable::No_enc;
785 if ((prop = node.property (X_("ctl"))) != 0) {
786 ev = MIDI::controller;
787 } else if ((prop = node.property (X_("note"))) != 0) {
789 } else if ((prop = node.property (X_("pgm"))) != 0) {
791 } else if ((prop = node.property (X_("pb"))) != 0) {
792 ev = MIDI::pitchbend;
793 } else if ((prop = node.property (X_("enc-l"))) != 0) {
794 encoder = MIDIControllable::Enc_L;
795 ev = MIDI::controller;
796 } else if ((prop = node.property (X_("enc-r"))) != 0) {
797 encoder = MIDIControllable::Enc_R;
798 ev = MIDI::controller;
799 } else if ((prop = node.property (X_("enc-2"))) != 0) {
800 encoder = MIDIControllable::Enc_2;
801 ev = MIDI::controller;
802 } else if ((prop = node.property (X_("enc-b"))) != 0) {
803 encoder = MIDIControllable::Enc_B;
804 ev = MIDI::controller;
809 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
813 detail = (MIDI::byte) intval;
815 if ((prop = node.property (X_("channel"))) == 0) {
819 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
822 channel = (MIDI::channel_t) intval;
823 /* adjust channel to zero-based counting */
828 if ((prop = node.property (X_("momentary"))) != 0) {
829 momentary = string_is_affirmative (prop->value());
834 prop = node.property (X_("uri"));
837 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), momentary);
839 if (mc->init (uri)) {
844 mc->set_encoder (encoder);
845 mc->bind_midi (channel, ev, detail);
851 GenericMidiControlProtocol::reset_controllables ()
853 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
855 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ) {
856 MIDIControllable* existingBinding = (*iter);
857 MIDIControllables::iterator next = iter;
860 if (!existingBinding->learned()) {
861 ControllableDescriptor& desc (existingBinding->descriptor());
864 desc.set_bank_offset (_current_bank * _bank_size);
867 /* its entirely possible that the session doesn't have
868 * the specified controllable (e.g. it has too few
869 * tracks). if we find this to be the case, we just leave
870 * the binding around, unbound, and it will do "late
871 * binding" (or "lazy binding") if/when any data arrives.
874 existingBinding->lookup_controllable ();
881 boost::shared_ptr<Controllable>
882 GenericMidiControlProtocol::lookup_controllable (const ControllableDescriptor& desc) const
884 return session->controllable_by_descriptor (desc);
888 GenericMidiControlProtocol::create_function (const XMLNode& node)
890 const XMLProperty* prop;
892 MIDI::byte detail = 0;
893 MIDI::channel_t channel = 0;
896 MIDI::byte* data = 0;
897 uint32_t data_size = 0;
900 if ((prop = node.property (X_("ctl"))) != 0) {
901 ev = MIDI::controller;
902 } else if ((prop = node.property (X_("note"))) != 0) {
904 } else if ((prop = node.property (X_("pgm"))) != 0) {
906 } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
908 if (prop->name() == X_("sysex")) {
919 stringstream ss (prop->value());
931 data = new MIDI::byte[cnt];
935 stringstream ss (prop->value());
940 data[cnt++] = (MIDI::byte) val;
945 warning << "Binding ignored - unknown type" << endmsg;
949 if (data_size == 0) {
950 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
954 detail = (MIDI::byte) intval;
956 if ((prop = node.property (X_("channel"))) == 0) {
960 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
963 channel = (MIDI::channel_t) intval;
964 /* adjust channel to zero-based counting */
970 if ((prop = node.property (X_("arg"))) != 0 || (prop = node.property (X_("argument"))) != 0 || (prop = node.property (X_("arguments"))) != 0) {
971 argument = prop->value ();
974 prop = node.property (X_("function"));
976 MIDIFunction* mf = new MIDIFunction (*_input_port->parser());
978 if (mf->setup (*this, prop->value(), argument, data, data_size)) {
983 mf->bind_midi (channel, ev, detail);
989 GenericMidiControlProtocol::create_action (const XMLNode& node)
991 const XMLProperty* prop;
993 MIDI::byte detail = 0;
994 MIDI::channel_t channel = 0;
997 MIDI::byte* data = 0;
998 uint32_t data_size = 0;
1000 if ((prop = node.property (X_("ctl"))) != 0) {
1001 ev = MIDI::controller;
1002 } else if ((prop = node.property (X_("note"))) != 0) {
1004 } else if ((prop = node.property (X_("pgm"))) != 0) {
1006 } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
1008 if (prop->name() == X_("sysex")) {
1019 stringstream ss (prop->value());
1031 data = new MIDI::byte[cnt];
1035 stringstream ss (prop->value());
1040 data[cnt++] = (MIDI::byte) val;
1045 warning << "Binding ignored - unknown type" << endmsg;
1049 if (data_size == 0) {
1050 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1054 detail = (MIDI::byte) intval;
1056 if ((prop = node.property (X_("channel"))) == 0) {
1060 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1063 channel = (MIDI::channel_t) intval;
1064 /* adjust channel to zero-based counting */
1070 prop = node.property (X_("action"));
1072 MIDIAction* ma = new MIDIAction (*_input_port->parser());
1074 if (ma->init (*this, prop->value(), data, data_size)) {
1079 ma->bind_midi (channel, ev, detail);
1085 GenericMidiControlProtocol::set_current_bank (uint32_t b)
1088 reset_controllables ();
1092 GenericMidiControlProtocol::next_bank ()
1095 reset_controllables ();
1099 GenericMidiControlProtocol::prev_bank()
1101 if (_current_bank) {
1103 reset_controllables ();
1108 GenericMidiControlProtocol::set_motorised (bool m)
1114 GenericMidiControlProtocol::set_threshold (int t)