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/error.h"
29 #include "pbd/failed_constructor.h"
30 #include "pbd/file_utils.h"
31 #include "pbd/xml++.h"
32 #include "pbd/compose.h"
34 #include "midi++/port.h"
36 #include "ardour/async_midi_port.h"
37 #include "ardour/audioengine.h"
38 #include "ardour/audioengine.h"
39 #include "ardour/controllable_descriptor.h"
40 #include "ardour/filesystem_paths.h"
41 #include "ardour/session.h"
42 #include "ardour/midi_ui.h"
43 #include "ardour/rc_configuration.h"
44 #include "ardour/midiport_manager.h"
45 #include "ardour/debug.h"
47 #include "generic_midi_control_protocol.h"
48 #include "midicontrollable.h"
49 #include "midifunction.h"
50 #include "midiaction.h"
52 using namespace ARDOUR;
58 #define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
60 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
61 : ControlProtocol (s, _("Generic MIDI"))
62 , connection_state (ConnectionState (0))
67 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort> (s.midi_input_port ());
68 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort> (s.midi_output_port ());
71 _feedback_interval = 10000; // microseconds
72 last_feedback_time = 0;
77 /* these signals are emitted by the MidiControlUI's event loop thread
78 * and we may as well handle them right there in the same the same
82 Controllable::StartLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::start_learning, this, _1));
83 Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1));
84 Controllable::CreateBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::create_binding, this, _1, _2, _3));
85 Controllable::DeleteBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding, this, _1));
87 /* this signal is emitted by the process() callback, and if
88 * send_feedback() is going to do anything, it should do it in the
89 * context of the process() callback itself.
92 Session::SendFeedback.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::send_feedback, this));
93 //Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
95 /* this one is cross-thread */
97 PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), midi_ui_context());
99 /* Catch port connections and disconnections (cross-thread) */
100 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR,
101 boost::bind (&GenericMidiControlProtocol::connection_handler, this, _1, _2, _3, _4, _5),
107 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
113 static const char * const midimap_env_variable_name = "ARDOUR_MIDIMAPS_PATH";
114 static const char* const midi_map_dir_name = "midi_maps";
115 static const char* const midi_map_suffix = ".map";
118 system_midi_map_search_path ()
120 bool midimap_path_defined = false;
121 std::string spath_env (Glib::getenv (midimap_env_variable_name, midimap_path_defined));
123 if (midimap_path_defined) {
127 Searchpath spath (ardour_data_search_path());
128 spath.add_subdirectory_to_paths(midi_map_dir_name);
133 user_midi_map_directory ()
135 return Glib::build_filename (user_config_directory(), midi_map_dir_name);
139 midi_map_filter (const string &str, void* /*arg*/)
141 return (str.length() > strlen(midi_map_suffix) &&
142 str.find (midi_map_suffix) == (str.length() - strlen (midi_map_suffix)));
146 GenericMidiControlProtocol::reload_maps ()
148 vector<string> midi_maps;
149 Searchpath spath (system_midi_map_search_path());
150 spath += user_midi_map_directory ();
152 find_files_matching_filter (midi_maps, spath, midi_map_filter, 0, false, true);
154 if (midi_maps.empty()) {
155 cerr << "No MIDI maps found using " << spath.to_string() << endl;
159 for (vector<string>::iterator i = midi_maps.begin(); i != midi_maps.end(); ++i) {
160 string fullpath = *i;
164 if (!tree.read (fullpath.c_str())) {
170 XMLProperty const * prop = tree.root()->property ("name");
176 mi.name = prop->value ();
179 map_info.push_back (mi);
184 GenericMidiControlProtocol::drop_all ()
186 DEBUG_TRACE (DEBUG::GenericMidi, "Drop all bindings\n");
187 Glib::Threads::Mutex::Lock lm (pending_lock);
188 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
190 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
193 controllables.clear ();
195 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
198 pending_controllables.clear ();
200 for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
205 for (MIDIActions::iterator i = actions.begin(); i != actions.end(); ++i) {
212 GenericMidiControlProtocol::drop_bindings ()
214 DEBUG_TRACE (DEBUG::GenericMidi, "Drop bindings, leave learned\n");
215 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
217 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
218 if (!(*i)->learned()) {
220 i = controllables.erase (i);
226 for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
231 _current_binding = "";
237 GenericMidiControlProtocol::set_active (bool /*yn*/)
239 /* nothing to do here: the MIDI UI thread in libardour handles all our
246 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
248 _feedback_interval = ms;
252 GenericMidiControlProtocol::send_feedback ()
254 /* This is executed in RT "process" context", so no blocking calls
261 microseconds_t now = get_microseconds ();
263 if (last_feedback_time != 0) {
264 if ((now - last_feedback_time) < _feedback_interval) {
271 last_feedback_time = now;
275 GenericMidiControlProtocol::_send_feedback ()
277 /* This is executed in RT "process" context", so no blocking calls
280 const int32_t bufsize = 16 * 1024; /* XXX too big */
281 MIDI::byte buf[bufsize];
282 int32_t bsize = bufsize;
284 /* XXX: due to bugs in some ALSA / JACK MIDI bridges, we have to do separate
285 writes for each controllable here; if we send more than one MIDI message
286 in a single jack_midi_event_write then some bridges will only pass the
290 Glib::Threads::Mutex::Lock lm (controllables_lock, Glib::Threads::TRY_LOCK);
295 for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
296 MIDI::byte* end = (*r)->write_feedback (buf, bsize);
298 _output_port->write (buf, (int32_t) (end - buf), 0);
304 GenericMidiControlProtocol::start_learning (Controllable* c)
310 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
311 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Learn binding: Controlable number: %1\n", c));
313 MIDIControllables::iterator tmp;
314 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
317 if ((*i)->get_controllable() == c) {
319 controllables.erase (i);
325 Glib::Threads::Mutex::Lock lm (pending_lock);
327 MIDIPendingControllables::iterator ptmp;
328 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
331 if (((*i)->first)->get_controllable() == c) {
332 (*i)->second.disconnect();
335 pending_controllables.erase (i);
341 MIDIControllable* mc = 0;
343 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
344 if ((*i)->get_controllable() && ((*i)->get_controllable()->id() == c->id())) {
351 mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
355 Glib::Threads::Mutex::Lock lm (pending_lock);
357 MIDIPendingControllable* element = new MIDIPendingControllable;
359 c->LearningFinished.connect_same_thread (element->second, boost::bind (&GenericMidiControlProtocol::learning_stopped, this, mc));
361 pending_controllables.push_back (element);
363 mc->learn_about_external_control ();
368 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
370 Glib::Threads::Mutex::Lock lm (pending_lock);
371 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
373 MIDIPendingControllables::iterator tmp;
375 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
379 if ( (*i)->first == mc) {
380 (*i)->second.disconnect();
382 pending_controllables.erase(i);
388 controllables.push_back (mc);
392 GenericMidiControlProtocol::stop_learning (Controllable* c)
394 Glib::Threads::Mutex::Lock lm (pending_lock);
395 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
396 MIDIControllable* dptr = 0;
398 /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
399 relevant MIDIControllable and remove it from the pending list.
402 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
403 if (((*i)->first)->get_controllable() == c) {
404 (*i)->first->stop_learning ();
406 (*i)->second.disconnect();
409 pending_controllables.erase (i);
418 GenericMidiControlProtocol::delete_binding (PBD::Controllable* control)
421 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
423 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
424 MIDIControllable* existingBinding = (*iter);
426 if (control == (existingBinding->get_controllable())) {
427 delete existingBinding;
428 iter = controllables.erase (iter);
437 // This next function seems unused
439 GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
441 if (control != NULL) {
442 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
444 MIDI::channel_t channel = (pos & 0xf);
445 MIDI::byte value = control_number;
447 // Create a MIDIControllable
448 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *control, false);
450 // Remove any old binding for this midi channel/type/value pair
451 // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
452 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
453 MIDIControllable* existingBinding = (*iter);
455 if ((existingBinding->get_control_channel() & 0xf ) == channel &&
456 existingBinding->get_control_additional() == value &&
457 (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller) {
459 delete existingBinding;
460 iter = controllables.erase (iter);
467 // Update the MIDI Controllable based on the the pos param
468 // Here is where a table lookup for user mappings could go; for now we'll just wing it...
469 mc->bind_midi(channel, MIDI::controller, value);
470 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Create binding: Channel: %1 Controller: %2 Value: %3 \n", channel, MIDI::controller, value));
471 controllables.push_back (mc);
476 GenericMidiControlProtocol::check_used_event (int pos, int control_number)
478 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
480 MIDI::channel_t channel = (pos & 0xf);
481 MIDI::byte value = control_number;
483 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("checking for used event: Channel: %1 Controller: %2 value: %3\n", (int) channel, (pos & 0xf0), (int) value));
485 // Remove any old binding for this midi channel/type/value pair
486 // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
487 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
488 MIDIControllable* existingBinding = (*iter);
489 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
490 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
491 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
492 delete existingBinding;
493 iter = controllables.erase (iter);
502 for (MIDIFunctions::iterator iter = functions.begin(); iter != functions.end();) {
503 MIDIFunction* existingBinding = (*iter);
504 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
505 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
506 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
507 delete existingBinding;
508 iter = functions.erase (iter);
517 for (MIDIActions::iterator iter = actions.begin(); iter != actions.end();) {
518 MIDIAction* existingBinding = (*iter);
519 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
520 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
521 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
522 delete existingBinding;
523 iter = actions.erase (iter);
535 GenericMidiControlProtocol::get_state ()
537 XMLNode& node (ControlProtocol::get_state());
540 snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
541 node.add_property (X_("feedback_interval"), buf);
542 snprintf (buf, sizeof (buf), "%d", _threshold);
543 node.add_property (X_("threshold"), buf);
545 node.add_property (X_("motorized"), _motorised ? "yes" : "no");
547 if (!_current_binding.empty()) {
548 node.add_property ("binding", _current_binding);
551 XMLNode* children = new XMLNode (X_("Controls"));
553 node.add_child_nocopy (*children);
555 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
556 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
558 /* we don't care about bindings that come from a bindings map, because
559 they will all be reset/recreated when we load the relevant bindings
563 if ((*i)->get_controllable() && (*i)->learned()) {
564 children->add_child_nocopy ((*i)->get_state());
572 GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
575 XMLNodeConstIterator niter;
576 const XMLProperty* prop;
578 if (ControlProtocol::set_state (node, version)) {
582 if ((prop = node.property ("feedback_interval")) != 0) {
583 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
584 _feedback_interval = 10000;
587 _feedback_interval = 10000;
590 if ((prop = node.property ("threshold")) != 0) {
591 if (sscanf (prop->value().c_str(), "%d", &_threshold) != 1) {
598 if ((prop = node.property ("motorized")) != 0) {
599 _motorised = string_is_affirmative (prop->value ());
604 boost::shared_ptr<Controllable> c;
607 Glib::Threads::Mutex::Lock lm (pending_lock);
608 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
611 pending_controllables.clear ();
614 // midi map has to be loaded first so learned binding can go on top
615 if ((prop = node.property ("binding")) != 0) {
616 for (list<MapInfo>::iterator x = map_info.begin(); x != map_info.end(); ++x) {
617 if (prop->value() == (*x).name) {
618 load_bindings ((*x).path);
624 /* Load up specific bindings from the
625 * <Controls><MidiControllable>...</MidiControllable><Controls> section
629 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
630 nlist = node.children(); // "Controls"
632 if (!nlist.empty()) {
633 nlist = nlist.front()->children(); // "MIDIControllable" ...
635 if (!nlist.empty()) {
636 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
638 if ((prop = (*niter)->property ("id")) != 0) {
640 ID id = prop->value ();
641 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Relearned binding for session: Control ID: %1\n", id.to_s()));
642 Controllable* c = Controllable::by_id (id);
645 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
647 if (mc->set_state (**niter, version) == 0) {
648 controllables.push_back (mc);
652 warning << string_compose (
653 _("Generic MIDI control: controllable %1 not found in session (ignored)"),
654 id.to_s()) << endmsg;
666 GenericMidiControlProtocol::set_feedback (bool yn)
669 last_feedback_time = 0;
674 GenericMidiControlProtocol::get_feedback () const
680 GenericMidiControlProtocol::load_bindings (const string& xmlpath)
682 DEBUG_TRACE (DEBUG::GenericMidi, "Load bindings: Reading midi map\n");
685 if (!state_tree.read (xmlpath.c_str())) {
686 error << string_compose(_("Could not understand MIDI bindings file %1"), xmlpath) << endmsg;
690 XMLNode* root = state_tree.root();
692 if (root->name() != X_("ArdourMIDIBindings")) {
693 error << string_compose (_("MIDI Bindings file %1 is not really a MIDI bindings file"), xmlpath) << endmsg;
697 const XMLProperty* prop;
699 if ((prop = root->property ("version")) == 0) {
706 sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, µ);
707 Stateful::loading_state_version = (major * 1000) + minor;
710 const XMLNodeList& children (root->children());
711 XMLNodeConstIterator citer;
712 XMLNodeConstIterator gciter;
714 MIDIControllable* mc;
718 DEBUG_TRACE (DEBUG::GenericMidi, "Loading bindings\n");
719 for (citer = children.begin(); citer != children.end(); ++citer) {
721 if ((*citer)->name() == "DeviceInfo") {
722 const XMLProperty* prop;
724 if ((prop = (*citer)->property ("bank-size")) != 0) {
725 _bank_size = atoi (prop->value());
729 if ((prop = (*citer)->property ("motorized")) != 0) {
730 _motorised = string_is_affirmative (prop->value ());
735 if ((prop = (*citer)->property ("threshold")) != 0) {
736 _threshold = atoi (prop->value ());
743 if ((*citer)->name() == "Binding") {
744 const XMLNode* child = *citer;
746 if (child->property ("uri")) {
749 if ((mc = create_binding (*child)) != 0) {
750 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
751 controllables.push_back (mc);
754 } else if (child->property ("function")) {
759 if ((mf = create_function (*child)) != 0) {
760 functions.push_back (mf);
763 } else if (child->property ("action")) {
766 if ((ma = create_action (*child)) != 0) {
767 actions.push_back (ma);
773 if ((prop = root->property ("name")) != 0) {
774 _current_binding = prop->value ();
777 reset_controllables ();
783 GenericMidiControlProtocol::create_binding (const XMLNode& node)
785 const XMLProperty* prop;
787 MIDI::channel_t channel;
792 MIDIControllable::Encoder encoder = MIDIControllable::No_enc;
793 bool rpn_value = false;
794 bool nrpn_value = false;
795 bool rpn_change = false;
796 bool nrpn_change = false;
798 if ((prop = node.property (X_("ctl"))) != 0) {
799 ev = MIDI::controller;
800 } else if ((prop = node.property (X_("note"))) != 0) {
802 } else if ((prop = node.property (X_("pgm"))) != 0) {
804 } else if ((prop = node.property (X_("pb"))) != 0) {
805 ev = MIDI::pitchbend;
806 } else if ((prop = node.property (X_("enc-l"))) != 0) {
807 encoder = MIDIControllable::Enc_L;
808 ev = MIDI::controller;
809 } else if ((prop = node.property (X_("enc-r"))) != 0) {
810 encoder = MIDIControllable::Enc_R;
811 ev = MIDI::controller;
812 } else if ((prop = node.property (X_("enc-2"))) != 0) {
813 encoder = MIDIControllable::Enc_2;
814 ev = MIDI::controller;
815 } else if ((prop = node.property (X_("enc-b"))) != 0) {
816 encoder = MIDIControllable::Enc_B;
817 ev = MIDI::controller;
818 } else if ((prop = node.property (X_("rpn"))) != 0) {
820 } else if ((prop = node.property (X_("nrpn"))) != 0) {
822 } else if ((prop = node.property (X_("rpn-delta"))) != 0) {
824 } else if ((prop = node.property (X_("nrpn-delta"))) != 0) {
830 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
834 detail = (MIDI::byte) intval;
836 if ((prop = node.property (X_("channel"))) == 0) {
840 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
843 channel = (MIDI::channel_t) intval;
844 /* adjust channel to zero-based counting */
849 if ((prop = node.property (X_("momentary"))) != 0) {
850 momentary = string_is_affirmative (prop->value());
855 prop = node.property (X_("uri"));
858 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), momentary);
860 if (mc->init (uri)) {
866 mc->bind_rpn_value (channel, detail);
867 } else if (nrpn_value) {
868 mc->bind_nrpn_value (channel, detail);
869 } else if (rpn_change) {
870 mc->bind_rpn_change (channel, detail);
871 } else if (nrpn_change) {
872 mc->bind_nrpn_change (channel, detail);
874 mc->set_encoder (encoder);
875 mc->bind_midi (channel, ev, detail);
882 GenericMidiControlProtocol::reset_controllables ()
884 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
886 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ) {
887 MIDIControllable* existingBinding = (*iter);
888 MIDIControllables::iterator next = iter;
891 if (!existingBinding->learned()) {
892 ControllableDescriptor& desc (existingBinding->descriptor());
895 desc.set_bank_offset (_current_bank * _bank_size);
898 /* its entirely possible that the session doesn't have
899 * the specified controllable (e.g. it has too few
900 * tracks). if we find this to be the case, we just leave
901 * the binding around, unbound, and it will do "late
902 * binding" (or "lazy binding") if/when any data arrives.
905 existingBinding->lookup_controllable ();
912 boost::shared_ptr<Controllable>
913 GenericMidiControlProtocol::lookup_controllable (const ControllableDescriptor& desc) const
915 return session->controllable_by_descriptor (desc);
919 GenericMidiControlProtocol::create_function (const XMLNode& node)
921 const XMLProperty* prop;
923 MIDI::byte detail = 0;
924 MIDI::channel_t channel = 0;
927 MIDI::byte* data = 0;
928 uint32_t data_size = 0;
931 if ((prop = node.property (X_("ctl"))) != 0) {
932 ev = MIDI::controller;
933 } else if ((prop = node.property (X_("note"))) != 0) {
935 } else if ((prop = node.property (X_("pgm"))) != 0) {
937 } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
939 if (prop->name() == X_("sysex")) {
950 stringstream ss (prop->value());
962 data = new MIDI::byte[cnt];
966 stringstream ss (prop->value());
971 data[cnt++] = (MIDI::byte) val;
976 warning << "Binding ignored - unknown type" << endmsg;
980 if (data_size == 0) {
981 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
985 detail = (MIDI::byte) intval;
987 if ((prop = node.property (X_("channel"))) == 0) {
991 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
994 channel = (MIDI::channel_t) intval;
995 /* adjust channel to zero-based counting */
1001 if ((prop = node.property (X_("arg"))) != 0 || (prop = node.property (X_("argument"))) != 0 || (prop = node.property (X_("arguments"))) != 0) {
1002 argument = prop->value ();
1005 prop = node.property (X_("function"));
1007 MIDIFunction* mf = new MIDIFunction (*_input_port->parser());
1009 if (mf->setup (*this, prop->value(), argument, data, data_size)) {
1014 mf->bind_midi (channel, ev, detail);
1020 GenericMidiControlProtocol::create_action (const XMLNode& node)
1022 const XMLProperty* prop;
1024 MIDI::byte detail = 0;
1025 MIDI::channel_t channel = 0;
1028 MIDI::byte* data = 0;
1029 uint32_t data_size = 0;
1031 if ((prop = node.property (X_("ctl"))) != 0) {
1032 ev = MIDI::controller;
1033 } else if ((prop = node.property (X_("note"))) != 0) {
1035 } else if ((prop = node.property (X_("pgm"))) != 0) {
1037 } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
1039 if (prop->name() == X_("sysex")) {
1050 stringstream ss (prop->value());
1062 data = new MIDI::byte[cnt];
1066 stringstream ss (prop->value());
1071 data[cnt++] = (MIDI::byte) val;
1076 warning << "Binding ignored - unknown type" << endmsg;
1080 if (data_size == 0) {
1081 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1085 detail = (MIDI::byte) intval;
1087 if ((prop = node.property (X_("channel"))) == 0) {
1091 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1094 channel = (MIDI::channel_t) intval;
1095 /* adjust channel to zero-based counting */
1101 prop = node.property (X_("action"));
1103 MIDIAction* ma = new MIDIAction (*_input_port->parser());
1105 if (ma->init (*this, prop->value(), data, data_size)) {
1110 ma->bind_midi (channel, ev, detail);
1116 GenericMidiControlProtocol::set_current_bank (uint32_t b)
1119 reset_controllables ();
1123 GenericMidiControlProtocol::next_bank ()
1126 reset_controllables ();
1130 GenericMidiControlProtocol::prev_bank()
1132 if (_current_bank) {
1134 reset_controllables ();
1139 GenericMidiControlProtocol::set_motorised (bool m)
1145 GenericMidiControlProtocol::set_threshold (int t)
1151 GenericMidiControlProtocol::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1153 if (!_input_port || !_output_port) {
1157 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_input_port)->name());
1158 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
1160 if (ni == name1 || ni == name2) {
1162 connection_state |= InputConnected;
1164 connection_state &= ~InputConnected;
1166 } else if (no == name1 || no == name2) {
1168 connection_state |= OutputConnected;
1170 connection_state &= ~OutputConnected;
1177 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1179 /* XXX this is a horrible hack. Without a short sleep here,
1180 something prevents the device wakeup messages from being
1181 sent and/or the responses from being received.
1191 ConnectionChange (); /* emit signal for our GUI */
1193 return true; /* connection status changed */
1197 GenericMidiControlProtocol::connected ()
1199 cerr << "Now connected\n";
1202 boost::shared_ptr<Port>
1203 GenericMidiControlProtocol::output_port() const
1205 return _output_port;
1208 boost::shared_ptr<Port>
1209 GenericMidiControlProtocol::input_port() const