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/async_midi_port.h"
38 #include "ardour/audioengine.h"
39 #include "ardour/audioengine.h"
40 #include "ardour/filesystem_paths.h"
41 #include "ardour/session.h"
42 #include "ardour/route.h"
43 #include "ardour/midi_ui.h"
44 #include "ardour/rc_configuration.h"
45 #include "ardour/midiport_manager.h"
46 #include "ardour/debug.h"
48 #include "generic_midi_control_protocol.h"
49 #include "midicontrollable.h"
50 #include "midifunction.h"
51 #include "midiaction.h"
53 using namespace ARDOUR;
59 #define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
61 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
62 : ControlProtocol (s, _("Generic MIDI"))
63 , connection_state (ConnectionState (0))
68 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort> (s.midi_input_port ());
69 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort> (s.midi_output_port ());
72 _feedback_interval = 10000; // microseconds
73 last_feedback_time = 0;
78 /* these signals are emitted by the MidiControlUI's event loop thread
79 * and we may as well handle them right there in the same the same
83 Controllable::StartLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::start_learning, this, _1));
84 Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1));
85 Controllable::CreateBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::create_binding, this, _1, _2, _3));
86 Controllable::DeleteBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding, this, _1));
88 /* this signal is emitted by the process() callback, and if
89 * send_feedback() is going to do anything, it should do it in the
90 * context of the process() callback itself.
93 Session::SendFeedback.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::send_feedback, this));
94 //Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
96 /* this one is cross-thread */
98 Route::RemoteControlIDChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), midi_ui_context());
100 /* Catch port connections and disconnections (cross-thread) */
101 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR,
102 boost::bind (&GenericMidiControlProtocol::connection_handler, this, _1, _2, _3, _4, _5),
108 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
114 static const char * const midimap_env_variable_name = "ARDOUR_MIDIMAPS_PATH";
115 static const char* const midi_map_dir_name = "midi_maps";
116 static const char* const midi_map_suffix = ".map";
119 system_midi_map_search_path ()
121 bool midimap_path_defined = false;
122 std::string spath_env (Glib::getenv (midimap_env_variable_name, midimap_path_defined));
124 if (midimap_path_defined) {
128 Searchpath spath (ardour_data_search_path());
129 spath.add_subdirectory_to_paths(midi_map_dir_name);
134 user_midi_map_directory ()
136 return Glib::build_filename (user_config_directory(), midi_map_dir_name);
140 midi_map_filter (const string &str, void* /*arg*/)
142 return (str.length() > strlen(midi_map_suffix) &&
143 str.find (midi_map_suffix) == (str.length() - strlen (midi_map_suffix)));
147 GenericMidiControlProtocol::reload_maps ()
149 vector<string> midi_maps;
150 Searchpath spath (system_midi_map_search_path());
151 spath += user_midi_map_directory ();
153 find_files_matching_filter (midi_maps, spath, midi_map_filter, 0, false, true);
155 if (midi_maps.empty()) {
156 cerr << "No MIDI maps found using " << spath.to_string() << endl;
160 for (vector<string>::iterator i = midi_maps.begin(); i != midi_maps.end(); ++i) {
161 string fullpath = *i;
165 if (!tree.read (fullpath.c_str())) {
171 XMLProperty const * prop = tree.root()->property ("name");
177 mi.name = prop->value ();
180 map_info.push_back (mi);
185 GenericMidiControlProtocol::drop_all ()
187 DEBUG_TRACE (DEBUG::GenericMidi, "Drop all bindings\n");
188 Glib::Threads::Mutex::Lock lm (pending_lock);
189 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
191 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
194 controllables.clear ();
196 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
199 pending_controllables.clear ();
201 for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
206 for (MIDIActions::iterator i = actions.begin(); i != actions.end(); ++i) {
213 GenericMidiControlProtocol::drop_bindings ()
215 DEBUG_TRACE (DEBUG::GenericMidi, "Drop bindings, leave learned\n");
216 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
218 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
219 if (!(*i)->learned()) {
221 i = controllables.erase (i);
227 for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
232 _current_binding = "";
238 GenericMidiControlProtocol::set_active (bool /*yn*/)
240 /* nothing to do here: the MIDI UI thread in libardour handles all our
247 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
249 _feedback_interval = ms;
253 GenericMidiControlProtocol::send_feedback ()
255 /* This is executed in RT "process" context", so no blocking calls
262 microseconds_t now = get_microseconds ();
264 if (last_feedback_time != 0) {
265 if ((now - last_feedback_time) < _feedback_interval) {
272 last_feedback_time = now;
276 GenericMidiControlProtocol::_send_feedback ()
278 /* This is executed in RT "process" context", so no blocking calls
281 const int32_t bufsize = 16 * 1024; /* XXX too big */
282 MIDI::byte buf[bufsize];
283 int32_t bsize = bufsize;
285 /* XXX: due to bugs in some ALSA / JACK MIDI bridges, we have to do separate
286 writes for each controllable here; if we send more than one MIDI message
287 in a single jack_midi_event_write then some bridges will only pass the
291 Glib::Threads::Mutex::Lock lm (controllables_lock, Glib::Threads::TRY_LOCK);
296 for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
297 MIDI::byte* end = (*r)->write_feedback (buf, bsize);
299 _output_port->write (buf, (int32_t) (end - buf), 0);
305 GenericMidiControlProtocol::start_learning (Controllable* c)
311 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
312 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Learn binding: Controlable number: %1\n", c));
314 MIDIControllables::iterator tmp;
315 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
318 if ((*i)->get_controllable() == c) {
320 controllables.erase (i);
326 Glib::Threads::Mutex::Lock lm (pending_lock);
328 MIDIPendingControllables::iterator ptmp;
329 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
332 if (((*i)->first)->get_controllable() == c) {
333 (*i)->second.disconnect();
336 pending_controllables.erase (i);
342 MIDIControllable* mc = 0;
344 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
345 if ((*i)->get_controllable() && ((*i)->get_controllable()->id() == c->id())) {
352 mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
356 Glib::Threads::Mutex::Lock lm (pending_lock);
358 MIDIPendingControllable* element = new MIDIPendingControllable;
360 c->LearningFinished.connect_same_thread (element->second, boost::bind (&GenericMidiControlProtocol::learning_stopped, this, mc));
362 pending_controllables.push_back (element);
364 mc->learn_about_external_control ();
369 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
371 Glib::Threads::Mutex::Lock lm (pending_lock);
372 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
374 MIDIPendingControllables::iterator tmp;
376 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
380 if ( (*i)->first == mc) {
381 (*i)->second.disconnect();
383 pending_controllables.erase(i);
389 controllables.push_back (mc);
393 GenericMidiControlProtocol::stop_learning (Controllable* c)
395 Glib::Threads::Mutex::Lock lm (pending_lock);
396 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
397 MIDIControllable* dptr = 0;
399 /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
400 relevant MIDIControllable and remove it from the pending list.
403 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
404 if (((*i)->first)->get_controllable() == c) {
405 (*i)->first->stop_learning ();
407 (*i)->second.disconnect();
410 pending_controllables.erase (i);
419 GenericMidiControlProtocol::delete_binding (PBD::Controllable* control)
422 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
424 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
425 MIDIControllable* existingBinding = (*iter);
427 if (control == (existingBinding->get_controllable())) {
428 delete existingBinding;
429 iter = controllables.erase (iter);
438 // This next function seems unused
440 GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
442 if (control != NULL) {
443 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
445 MIDI::channel_t channel = (pos & 0xf);
446 MIDI::byte value = control_number;
448 // Create a MIDIControllable
449 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *control, false);
451 // Remove any old binding for this midi channel/type/value pair
452 // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
453 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
454 MIDIControllable* existingBinding = (*iter);
456 if ((existingBinding->get_control_channel() & 0xf ) == channel &&
457 existingBinding->get_control_additional() == value &&
458 (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller) {
460 delete existingBinding;
461 iter = controllables.erase (iter);
468 // Update the MIDI Controllable based on the the pos param
469 // Here is where a table lookup for user mappings could go; for now we'll just wing it...
470 mc->bind_midi(channel, MIDI::controller, value);
471 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Create binding: Channel: %1 Controller: %2 Value: %3 \n", channel, MIDI::controller, value));
472 controllables.push_back (mc);
477 GenericMidiControlProtocol::check_used_event (int pos, int control_number)
479 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
481 MIDI::channel_t channel = (pos & 0xf);
482 MIDI::byte value = control_number;
484 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("checking for used event: Channel: %1 Controller: %2 value: %3\n", (int) channel, (pos & 0xf0), (int) value));
486 // Remove any old binding for this midi channel/type/value pair
487 // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
488 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
489 MIDIControllable* existingBinding = (*iter);
490 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
491 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
492 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
493 delete existingBinding;
494 iter = controllables.erase (iter);
503 for (MIDIFunctions::iterator iter = functions.begin(); iter != functions.end();) {
504 MIDIFunction* existingBinding = (*iter);
505 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
506 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
507 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
508 delete existingBinding;
509 iter = functions.erase (iter);
518 for (MIDIActions::iterator iter = actions.begin(); iter != actions.end();) {
519 MIDIAction* existingBinding = (*iter);
520 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
521 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
522 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
523 delete existingBinding;
524 iter = actions.erase (iter);
536 GenericMidiControlProtocol::get_state ()
538 XMLNode& node (ControlProtocol::get_state());
541 snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
542 node.add_property (X_("feedback_interval"), buf);
543 snprintf (buf, sizeof (buf), "%d", _threshold);
544 node.add_property (X_("threshold"), buf);
546 node.add_property (X_("motorized"), _motorised ? "yes" : "no");
548 if (!_current_binding.empty()) {
549 node.add_property ("binding", _current_binding);
552 XMLNode* children = new XMLNode (X_("Controls"));
554 node.add_child_nocopy (*children);
556 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
557 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
559 /* we don't care about bindings that come from a bindings map, because
560 they will all be reset/recreated when we load the relevant bindings
564 if ((*i)->get_controllable() && (*i)->learned()) {
565 children->add_child_nocopy ((*i)->get_state());
573 GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
576 XMLNodeConstIterator niter;
577 const XMLProperty* prop;
579 if (ControlProtocol::set_state (node, version)) {
583 if ((prop = node.property ("feedback_interval")) != 0) {
584 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
585 _feedback_interval = 10000;
588 _feedback_interval = 10000;
591 if ((prop = node.property ("threshold")) != 0) {
592 if (sscanf (prop->value().c_str(), "%d", &_threshold) != 1) {
599 if ((prop = node.property ("motorized")) != 0) {
600 _motorised = string_is_affirmative (prop->value ());
605 boost::shared_ptr<Controllable> c;
608 Glib::Threads::Mutex::Lock lm (pending_lock);
609 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
612 pending_controllables.clear ();
615 // midi map has to be loaded first so learned binding can go on top
616 if ((prop = node.property ("binding")) != 0) {
617 for (list<MapInfo>::iterator x = map_info.begin(); x != map_info.end(); ++x) {
618 if (prop->value() == (*x).name) {
619 load_bindings ((*x).path);
625 /* Load up specific bindings from the
626 * <Controls><MidiControllable>...</MidiControllable><Controls> section
630 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
631 nlist = node.children(); // "Controls"
633 if (!nlist.empty()) {
634 nlist = nlist.front()->children(); // "MIDIControllable" ...
636 if (!nlist.empty()) {
637 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
639 if ((prop = (*niter)->property ("id")) != 0) {
641 ID id = prop->value ();
642 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Relearned binding for session: Control ID: %1\n", id.to_s()));
643 Controllable* c = Controllable::by_id (id);
646 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
648 if (mc->set_state (**niter, version) == 0) {
649 controllables.push_back (mc);
653 warning << string_compose (
654 _("Generic MIDI control: controllable %1 not found in session (ignored)"),
655 id.to_s()) << endmsg;
667 GenericMidiControlProtocol::set_feedback (bool yn)
670 last_feedback_time = 0;
675 GenericMidiControlProtocol::get_feedback () const
681 GenericMidiControlProtocol::load_bindings (const string& xmlpath)
683 DEBUG_TRACE (DEBUG::GenericMidi, "Load bindings: Reading midi map\n");
686 if (!state_tree.read (xmlpath.c_str())) {
687 error << string_compose(_("Could not understand MIDI bindings file %1"), xmlpath) << endmsg;
691 XMLNode* root = state_tree.root();
693 if (root->name() != X_("ArdourMIDIBindings")) {
694 error << string_compose (_("MIDI Bindings file %1 is not really a MIDI bindings file"), xmlpath) << endmsg;
698 const XMLProperty* prop;
700 if ((prop = root->property ("version")) == 0) {
707 sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, µ);
708 Stateful::loading_state_version = (major * 1000) + minor;
711 const XMLNodeList& children (root->children());
712 XMLNodeConstIterator citer;
713 XMLNodeConstIterator gciter;
715 MIDIControllable* mc;
719 DEBUG_TRACE (DEBUG::GenericMidi, "Loading bindings\n");
720 for (citer = children.begin(); citer != children.end(); ++citer) {
722 if ((*citer)->name() == "DeviceInfo") {
723 const XMLProperty* prop;
725 if ((prop = (*citer)->property ("bank-size")) != 0) {
726 _bank_size = atoi (prop->value());
730 if ((prop = (*citer)->property ("motorized")) != 0) {
731 _motorised = string_is_affirmative (prop->value ());
736 if ((prop = (*citer)->property ("threshold")) != 0) {
737 _threshold = atoi (prop->value ());
744 if ((*citer)->name() == "Binding") {
745 const XMLNode* child = *citer;
747 if (child->property ("uri")) {
750 if ((mc = create_binding (*child)) != 0) {
751 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
752 controllables.push_back (mc);
755 } else if (child->property ("function")) {
760 if ((mf = create_function (*child)) != 0) {
761 functions.push_back (mf);
764 } else if (child->property ("action")) {
767 if ((ma = create_action (*child)) != 0) {
768 actions.push_back (ma);
774 if ((prop = root->property ("name")) != 0) {
775 _current_binding = prop->value ();
778 reset_controllables ();
784 GenericMidiControlProtocol::create_binding (const XMLNode& node)
786 const XMLProperty* prop;
788 MIDI::channel_t channel;
793 MIDIControllable::Encoder encoder = MIDIControllable::No_enc;
794 bool rpn_value = false;
795 bool nrpn_value = false;
796 bool rpn_change = false;
797 bool nrpn_change = false;
799 if ((prop = node.property (X_("ctl"))) != 0) {
800 ev = MIDI::controller;
801 } else if ((prop = node.property (X_("note"))) != 0) {
803 } else if ((prop = node.property (X_("pgm"))) != 0) {
805 } else if ((prop = node.property (X_("pb"))) != 0) {
806 ev = MIDI::pitchbend;
807 } else if ((prop = node.property (X_("enc-l"))) != 0) {
808 encoder = MIDIControllable::Enc_L;
809 ev = MIDI::controller;
810 } else if ((prop = node.property (X_("enc-r"))) != 0) {
811 encoder = MIDIControllable::Enc_R;
812 ev = MIDI::controller;
813 } else if ((prop = node.property (X_("enc-2"))) != 0) {
814 encoder = MIDIControllable::Enc_2;
815 ev = MIDI::controller;
816 } else if ((prop = node.property (X_("enc-b"))) != 0) {
817 encoder = MIDIControllable::Enc_B;
818 ev = MIDI::controller;
819 } else if ((prop = node.property (X_("rpn"))) != 0) {
821 } else if ((prop = node.property (X_("nrpn"))) != 0) {
823 } else if ((prop = node.property (X_("rpn-delta"))) != 0) {
825 } else if ((prop = node.property (X_("nrpn-delta"))) != 0) {
831 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
835 detail = (MIDI::byte) intval;
837 if ((prop = node.property (X_("channel"))) == 0) {
841 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
844 channel = (MIDI::channel_t) intval;
845 /* adjust channel to zero-based counting */
850 if ((prop = node.property (X_("momentary"))) != 0) {
851 momentary = string_is_affirmative (prop->value());
856 prop = node.property (X_("uri"));
859 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), momentary);
861 if (mc->init (uri)) {
867 mc->bind_rpn_value (channel, detail);
868 } else if (nrpn_value) {
869 mc->bind_nrpn_value (channel, detail);
870 } else if (rpn_change) {
871 mc->bind_rpn_change (channel, detail);
872 } else if (nrpn_change) {
873 mc->bind_nrpn_change (channel, detail);
875 mc->set_encoder (encoder);
876 mc->bind_midi (channel, ev, detail);
883 GenericMidiControlProtocol::reset_controllables ()
885 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
887 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ) {
888 MIDIControllable* existingBinding = (*iter);
889 MIDIControllables::iterator next = iter;
892 if (!existingBinding->learned()) {
893 ControllableDescriptor& desc (existingBinding->descriptor());
896 desc.set_bank_offset (_current_bank * _bank_size);
899 /* its entirely possible that the session doesn't have
900 * the specified controllable (e.g. it has too few
901 * tracks). if we find this to be the case, we just leave
902 * the binding around, unbound, and it will do "late
903 * binding" (or "lazy binding") if/when any data arrives.
906 existingBinding->lookup_controllable ();
913 boost::shared_ptr<Controllable>
914 GenericMidiControlProtocol::lookup_controllable (const ControllableDescriptor& desc) const
916 return session->controllable_by_descriptor (desc);
920 GenericMidiControlProtocol::create_function (const XMLNode& node)
922 const XMLProperty* prop;
924 MIDI::byte detail = 0;
925 MIDI::channel_t channel = 0;
928 MIDI::byte* data = 0;
929 uint32_t data_size = 0;
932 if ((prop = node.property (X_("ctl"))) != 0) {
933 ev = MIDI::controller;
934 } else if ((prop = node.property (X_("note"))) != 0) {
936 } else if ((prop = node.property (X_("pgm"))) != 0) {
938 } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
940 if (prop->name() == X_("sysex")) {
951 stringstream ss (prop->value());
963 data = new MIDI::byte[cnt];
967 stringstream ss (prop->value());
972 data[cnt++] = (MIDI::byte) val;
977 warning << "Binding ignored - unknown type" << endmsg;
981 if (data_size == 0) {
982 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
986 detail = (MIDI::byte) intval;
988 if ((prop = node.property (X_("channel"))) == 0) {
992 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
995 channel = (MIDI::channel_t) intval;
996 /* adjust channel to zero-based counting */
1002 if ((prop = node.property (X_("arg"))) != 0 || (prop = node.property (X_("argument"))) != 0 || (prop = node.property (X_("arguments"))) != 0) {
1003 argument = prop->value ();
1006 prop = node.property (X_("function"));
1008 MIDIFunction* mf = new MIDIFunction (*_input_port->parser());
1010 if (mf->setup (*this, prop->value(), argument, data, data_size)) {
1015 mf->bind_midi (channel, ev, detail);
1021 GenericMidiControlProtocol::create_action (const XMLNode& node)
1023 const XMLProperty* prop;
1025 MIDI::byte detail = 0;
1026 MIDI::channel_t channel = 0;
1029 MIDI::byte* data = 0;
1030 uint32_t data_size = 0;
1032 if ((prop = node.property (X_("ctl"))) != 0) {
1033 ev = MIDI::controller;
1034 } else if ((prop = node.property (X_("note"))) != 0) {
1036 } else if ((prop = node.property (X_("pgm"))) != 0) {
1038 } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
1040 if (prop->name() == X_("sysex")) {
1051 stringstream ss (prop->value());
1063 data = new MIDI::byte[cnt];
1067 stringstream ss (prop->value());
1072 data[cnt++] = (MIDI::byte) val;
1077 warning << "Binding ignored - unknown type" << endmsg;
1081 if (data_size == 0) {
1082 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1086 detail = (MIDI::byte) intval;
1088 if ((prop = node.property (X_("channel"))) == 0) {
1092 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1095 channel = (MIDI::channel_t) intval;
1096 /* adjust channel to zero-based counting */
1102 prop = node.property (X_("action"));
1104 MIDIAction* ma = new MIDIAction (*_input_port->parser());
1106 if (ma->init (*this, prop->value(), data, data_size)) {
1111 ma->bind_midi (channel, ev, detail);
1117 GenericMidiControlProtocol::set_current_bank (uint32_t b)
1120 reset_controllables ();
1124 GenericMidiControlProtocol::next_bank ()
1127 reset_controllables ();
1131 GenericMidiControlProtocol::prev_bank()
1133 if (_current_bank) {
1135 reset_controllables ();
1140 GenericMidiControlProtocol::set_motorised (bool m)
1146 GenericMidiControlProtocol::set_threshold (int t)
1152 GenericMidiControlProtocol::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1154 if (!_input_port || !_output_port) {
1158 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_input_port)->name());
1159 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
1161 if (ni == name1 || ni == name2) {
1163 connection_state |= InputConnected;
1165 connection_state &= ~InputConnected;
1167 } else if (no == name1 || no == name2) {
1169 connection_state |= OutputConnected;
1171 connection_state &= ~OutputConnected;
1178 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1180 /* XXX this is a horrible hack. Without a short sleep here,
1181 something prevents the device wakeup messages from being
1182 sent and/or the responses from being received.
1192 ConnectionChange (); /* emit signal for our GUI */
1194 return true; /* connection status changed */
1198 GenericMidiControlProtocol::connected ()
1200 cerr << "Now connected\n";
1203 boost::shared_ptr<Port>
1204 GenericMidiControlProtocol::output_port() const
1206 return _output_port;
1209 boost::shared_ptr<Port>
1210 GenericMidiControlProtocol::input_port() const