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)->mc)->get_controllable() == c) {
335 (*i)->connection.disconnect();
337 pending_controllables.erase (i);
343 MIDIControllable* mc = 0;
346 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
347 if ((*i)->get_controllable() && ((*i)->get_controllable()->id() == c->id())) {
354 mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
359 Glib::Threads::Mutex::Lock lm (pending_lock);
361 MIDIPendingControllable* element = new MIDIPendingControllable (mc, own_mc);
362 c->LearningFinished.connect_same_thread (element->connection, boost::bind (&GenericMidiControlProtocol::learning_stopped, this, mc));
364 pending_controllables.push_back (element);
366 mc->learn_about_external_control ();
371 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
373 Glib::Threads::Mutex::Lock lm (pending_lock);
374 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
376 MIDIPendingControllables::iterator tmp;
378 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
382 if ( (*i)->mc == mc) {
383 (*i)->connection.disconnect();
385 pending_controllables.erase(i);
391 controllables.push_back (mc);
395 GenericMidiControlProtocol::stop_learning (Controllable* c)
397 Glib::Threads::Mutex::Lock lm (pending_lock);
398 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
399 MIDIControllable* dptr = 0;
401 /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
402 relevant MIDIControllable and remove it from the pending list.
405 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
406 if (((*i)->mc)->get_controllable() == c) {
407 (*i)->mc->stop_learning ();
409 (*i)->connection.disconnect();
412 pending_controllables.erase (i);
421 GenericMidiControlProtocol::delete_binding (PBD::Controllable* control)
424 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
426 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
427 MIDIControllable* existingBinding = (*iter);
429 if (control == (existingBinding->get_controllable())) {
430 delete existingBinding;
431 iter = controllables.erase (iter);
440 // This next function seems unused
442 GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
444 if (control != NULL) {
445 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
447 MIDI::channel_t channel = (pos & 0xf);
448 MIDI::byte value = control_number;
450 // Create a MIDIControllable
451 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *control, false);
453 // Remove any old binding for this midi channel/type/value pair
454 // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
455 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
456 MIDIControllable* existingBinding = (*iter);
458 if ((existingBinding->get_control_channel() & 0xf ) == channel &&
459 existingBinding->get_control_additional() == value &&
460 (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller) {
462 delete existingBinding;
463 iter = controllables.erase (iter);
470 // Update the MIDI Controllable based on the the pos param
471 // Here is where a table lookup for user mappings could go; for now we'll just wing it...
472 mc->bind_midi(channel, MIDI::controller, value);
473 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Create binding: Channel: %1 Controller: %2 Value: %3 \n", channel, MIDI::controller, value));
474 controllables.push_back (mc);
479 GenericMidiControlProtocol::check_used_event (int pos, int control_number)
481 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
483 MIDI::channel_t channel = (pos & 0xf);
484 MIDI::byte value = control_number;
486 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("checking for used event: Channel: %1 Controller: %2 value: %3\n", (int) channel, (pos & 0xf0), (int) value));
488 // Remove any old binding for this midi channel/type/value pair
489 // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
490 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
491 MIDIControllable* existingBinding = (*iter);
492 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
493 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
494 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
495 delete existingBinding;
496 iter = controllables.erase (iter);
505 for (MIDIFunctions::iterator iter = functions.begin(); iter != functions.end();) {
506 MIDIFunction* existingBinding = (*iter);
507 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
508 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
509 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
510 delete existingBinding;
511 iter = functions.erase (iter);
520 for (MIDIActions::iterator iter = actions.begin(); iter != actions.end();) {
521 MIDIAction* existingBinding = (*iter);
522 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
523 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
524 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
525 delete existingBinding;
526 iter = actions.erase (iter);
538 GenericMidiControlProtocol::get_state ()
540 XMLNode& node (ControlProtocol::get_state());
543 snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
544 node.add_property (X_("feedback_interval"), buf);
545 snprintf (buf, sizeof (buf), "%d", _threshold);
546 node.add_property (X_("threshold"), buf);
548 node.add_property (X_("motorized"), _motorised ? "yes" : "no");
550 if (!_current_binding.empty()) {
551 node.add_property ("binding", _current_binding);
554 XMLNode* children = new XMLNode (X_("Controls"));
556 node.add_child_nocopy (*children);
558 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
559 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
561 /* we don't care about bindings that come from a bindings map, because
562 they will all be reset/recreated when we load the relevant bindings
566 if ((*i)->get_controllable() && (*i)->learned()) {
567 children->add_child_nocopy ((*i)->get_state());
575 GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
578 XMLNodeConstIterator niter;
579 const XMLProperty* prop;
581 if (ControlProtocol::set_state (node, version)) {
585 if ((prop = node.property ("feedback_interval")) != 0) {
586 if (sscanf (prop->value().c_str(), "%" PRIu64, &_feedback_interval) != 1) {
587 _feedback_interval = 10000;
590 _feedback_interval = 10000;
593 if ((prop = node.property ("threshold")) != 0) {
594 if (sscanf (prop->value().c_str(), "%d", &_threshold) != 1) {
601 if ((prop = node.property ("motorized")) != 0) {
602 _motorised = string_is_affirmative (prop->value ());
607 boost::shared_ptr<Controllable> c;
610 Glib::Threads::Mutex::Lock lm (pending_lock);
611 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
614 pending_controllables.clear ();
617 // midi map has to be loaded first so learned binding can go on top
618 if ((prop = node.property ("binding")) != 0) {
619 for (list<MapInfo>::iterator x = map_info.begin(); x != map_info.end(); ++x) {
620 if (prop->value() == (*x).name) {
621 load_bindings ((*x).path);
627 /* Load up specific bindings from the
628 * <Controls><MidiControllable>...</MidiControllable><Controls> section
632 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
633 nlist = node.children(); // "Controls"
635 if (!nlist.empty()) {
636 nlist = nlist.front()->children(); // "MIDIControllable" ...
638 if (!nlist.empty()) {
639 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
641 if ((prop = (*niter)->property ("id")) != 0) {
643 ID id = prop->value ();
644 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Relearned binding for session: Control ID: %1\n", id.to_s()));
645 Controllable* c = Controllable::by_id (id);
648 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
650 if (mc->set_state (**niter, version) == 0) {
651 controllables.push_back (mc);
655 warning << string_compose (
656 _("Generic MIDI control: controllable %1 not found in session (ignored)"),
657 id.to_s()) << endmsg;
669 GenericMidiControlProtocol::set_feedback (bool yn)
672 last_feedback_time = 0;
677 GenericMidiControlProtocol::get_feedback () const
683 GenericMidiControlProtocol::load_bindings (const string& xmlpath)
685 DEBUG_TRACE (DEBUG::GenericMidi, "Load bindings: Reading midi map\n");
688 if (!state_tree.read (xmlpath.c_str())) {
689 error << string_compose(_("Could not understand MIDI bindings file %1"), xmlpath) << endmsg;
693 XMLNode* root = state_tree.root();
695 if (root->name() != X_("ArdourMIDIBindings")) {
696 error << string_compose (_("MIDI Bindings file %1 is not really a MIDI bindings file"), xmlpath) << endmsg;
700 const XMLProperty* prop;
702 if ((prop = root->property ("version")) == 0) {
709 sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, µ);
710 Stateful::loading_state_version = (major * 1000) + minor;
713 const XMLNodeList& children (root->children());
714 XMLNodeConstIterator citer;
715 XMLNodeConstIterator gciter;
717 MIDIControllable* mc;
721 DEBUG_TRACE (DEBUG::GenericMidi, "Loading bindings\n");
722 for (citer = children.begin(); citer != children.end(); ++citer) {
724 if ((*citer)->name() == "DeviceInfo") {
725 const XMLProperty* prop;
727 if ((prop = (*citer)->property ("bank-size")) != 0) {
728 _bank_size = atoi (prop->value());
732 if ((prop = (*citer)->property ("motorized")) != 0) {
733 _motorised = string_is_affirmative (prop->value ());
738 if ((prop = (*citer)->property ("threshold")) != 0) {
739 _threshold = atoi (prop->value ());
746 if ((*citer)->name() == "Binding") {
747 const XMLNode* child = *citer;
749 if (child->property ("uri")) {
752 if ((mc = create_binding (*child)) != 0) {
753 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
754 controllables.push_back (mc);
757 } else if (child->property ("function")) {
762 if ((mf = create_function (*child)) != 0) {
763 functions.push_back (mf);
766 } else if (child->property ("action")) {
769 if ((ma = create_action (*child)) != 0) {
770 actions.push_back (ma);
776 if ((prop = root->property ("name")) != 0) {
777 _current_binding = prop->value ();
780 reset_controllables ();
786 GenericMidiControlProtocol::create_binding (const XMLNode& node)
788 const XMLProperty* prop;
790 MIDI::channel_t channel;
795 MIDIControllable::Encoder encoder = MIDIControllable::No_enc;
796 bool rpn_value = false;
797 bool nrpn_value = false;
798 bool rpn_change = false;
799 bool nrpn_change = false;
801 if ((prop = node.property (X_("ctl"))) != 0) {
802 ev = MIDI::controller;
803 } else if ((prop = node.property (X_("note"))) != 0) {
805 } else if ((prop = node.property (X_("pgm"))) != 0) {
807 } else if ((prop = node.property (X_("pb"))) != 0) {
808 ev = MIDI::pitchbend;
809 } else if ((prop = node.property (X_("enc-l"))) != 0) {
810 encoder = MIDIControllable::Enc_L;
811 ev = MIDI::controller;
812 } else if ((prop = node.property (X_("enc-r"))) != 0) {
813 encoder = MIDIControllable::Enc_R;
814 ev = MIDI::controller;
815 } else if ((prop = node.property (X_("enc-2"))) != 0) {
816 encoder = MIDIControllable::Enc_2;
817 ev = MIDI::controller;
818 } else if ((prop = node.property (X_("enc-b"))) != 0) {
819 encoder = MIDIControllable::Enc_B;
820 ev = MIDI::controller;
821 } else if ((prop = node.property (X_("rpn"))) != 0) {
823 } else if ((prop = node.property (X_("nrpn"))) != 0) {
825 } else if ((prop = node.property (X_("rpn-delta"))) != 0) {
827 } else if ((prop = node.property (X_("nrpn-delta"))) != 0) {
833 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
837 detail = (MIDI::byte) intval;
839 if ((prop = node.property (X_("channel"))) == 0) {
843 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
846 channel = (MIDI::channel_t) intval;
847 /* adjust channel to zero-based counting */
852 if ((prop = node.property (X_("momentary"))) != 0) {
853 momentary = string_is_affirmative (prop->value());
858 prop = node.property (X_("uri"));
861 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), momentary);
863 if (mc->init (uri)) {
869 mc->bind_rpn_value (channel, detail);
870 } else if (nrpn_value) {
871 mc->bind_nrpn_value (channel, detail);
872 } else if (rpn_change) {
873 mc->bind_rpn_change (channel, detail);
874 } else if (nrpn_change) {
875 mc->bind_nrpn_change (channel, detail);
877 mc->set_encoder (encoder);
878 mc->bind_midi (channel, ev, detail);
885 GenericMidiControlProtocol::reset_controllables ()
887 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
889 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ) {
890 MIDIControllable* existingBinding = (*iter);
891 MIDIControllables::iterator next = iter;
894 if (!existingBinding->learned()) {
895 ControllableDescriptor& desc (existingBinding->descriptor());
898 desc.set_bank_offset (_current_bank * _bank_size);
901 /* its entirely possible that the session doesn't have
902 * the specified controllable (e.g. it has too few
903 * tracks). if we find this to be the case, we just leave
904 * the binding around, unbound, and it will do "late
905 * binding" (or "lazy binding") if/when any data arrives.
908 existingBinding->lookup_controllable ();
915 boost::shared_ptr<Controllable>
916 GenericMidiControlProtocol::lookup_controllable (const ControllableDescriptor& desc) const
918 return session->controllable_by_descriptor (desc);
922 GenericMidiControlProtocol::create_function (const XMLNode& node)
924 const XMLProperty* prop;
926 MIDI::byte detail = 0;
927 MIDI::channel_t channel = 0;
930 MIDI::byte* data = 0;
931 uint32_t data_size = 0;
934 if ((prop = node.property (X_("ctl"))) != 0) {
935 ev = MIDI::controller;
936 } else if ((prop = node.property (X_("note"))) != 0) {
938 } else if ((prop = node.property (X_("pgm"))) != 0) {
940 } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
942 if (prop->name() == X_("sysex")) {
953 stringstream ss (prop->value());
965 data = new MIDI::byte[cnt];
969 stringstream ss (prop->value());
974 data[cnt++] = (MIDI::byte) val;
979 warning << "Binding ignored - unknown type" << endmsg;
983 if (data_size == 0) {
984 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
988 detail = (MIDI::byte) intval;
990 if ((prop = node.property (X_("channel"))) == 0) {
994 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
997 channel = (MIDI::channel_t) intval;
998 /* adjust channel to zero-based counting */
1004 if ((prop = node.property (X_("arg"))) != 0 || (prop = node.property (X_("argument"))) != 0 || (prop = node.property (X_("arguments"))) != 0) {
1005 argument = prop->value ();
1008 prop = node.property (X_("function"));
1010 MIDIFunction* mf = new MIDIFunction (*_input_port->parser());
1012 if (mf->setup (*this, prop->value(), argument, data, data_size)) {
1017 mf->bind_midi (channel, ev, detail);
1023 GenericMidiControlProtocol::create_action (const XMLNode& node)
1025 const XMLProperty* prop;
1027 MIDI::byte detail = 0;
1028 MIDI::channel_t channel = 0;
1031 MIDI::byte* data = 0;
1032 uint32_t data_size = 0;
1034 if ((prop = node.property (X_("ctl"))) != 0) {
1035 ev = MIDI::controller;
1036 } else if ((prop = node.property (X_("note"))) != 0) {
1038 } else if ((prop = node.property (X_("pgm"))) != 0) {
1040 } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
1042 if (prop->name() == X_("sysex")) {
1053 stringstream ss (prop->value());
1065 data = new MIDI::byte[cnt];
1069 stringstream ss (prop->value());
1074 data[cnt++] = (MIDI::byte) val;
1079 warning << "Binding ignored - unknown type" << endmsg;
1083 if (data_size == 0) {
1084 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1088 detail = (MIDI::byte) intval;
1090 if ((prop = node.property (X_("channel"))) == 0) {
1094 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1097 channel = (MIDI::channel_t) intval;
1098 /* adjust channel to zero-based counting */
1104 prop = node.property (X_("action"));
1106 MIDIAction* ma = new MIDIAction (*_input_port->parser());
1108 if (ma->init (*this, prop->value(), data, data_size)) {
1113 ma->bind_midi (channel, ev, detail);
1119 GenericMidiControlProtocol::set_current_bank (uint32_t b)
1122 reset_controllables ();
1126 GenericMidiControlProtocol::next_bank ()
1129 reset_controllables ();
1133 GenericMidiControlProtocol::prev_bank()
1135 if (_current_bank) {
1137 reset_controllables ();
1142 GenericMidiControlProtocol::set_motorised (bool m)
1148 GenericMidiControlProtocol::set_threshold (int t)
1154 GenericMidiControlProtocol::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1156 if (!_input_port || !_output_port) {
1160 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_input_port)->name());
1161 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
1163 if (ni == name1 || ni == name2) {
1165 connection_state |= InputConnected;
1167 connection_state &= ~InputConnected;
1169 } else if (no == name1 || no == name2) {
1171 connection_state |= OutputConnected;
1173 connection_state &= ~OutputConnected;
1180 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1182 /* XXX this is a horrible hack. Without a short sleep here,
1183 something prevents the device wakeup messages from being
1184 sent and/or the responses from being received.
1194 ConnectionChange (); /* emit signal for our GUI */
1196 return true; /* connection status changed */
1200 GenericMidiControlProtocol::connected ()
1202 cerr << "Now connected\n";
1205 boost::shared_ptr<Port>
1206 GenericMidiControlProtocol::output_port() const
1208 return _output_port;
1211 boost::shared_ptr<Port>
1212 GenericMidiControlProtocol::input_port() const