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.
26 #include <io.h> // Microsoft's nearest equivalent to <unistd.h>
27 #include <ardourext/misc.h>
32 #include <glibmm/fileutils.h>
33 #include <glibmm/miscutils.h>
35 #include "pbd/compose.h"
36 #include "pbd/convert.h"
37 #include "pbd/error.h"
38 #include "pbd/failed_constructor.h"
39 #include "pbd/file_utils.h"
40 #include "pbd/strsplit.h"
41 #include "pbd/types_convert.h"
42 #include "pbd/xml++.h"
44 #include "midi++/port.h"
46 #include "ardour/async_midi_port.h"
47 #include "ardour/audioengine.h"
48 #include "ardour/auditioner.h"
49 #include "ardour/filesystem_paths.h"
50 #include "ardour/session.h"
51 #include "ardour/midi_ui.h"
52 #include "ardour/plugin_insert.h"
53 #include "ardour/rc_configuration.h"
54 #include "ardour/midiport_manager.h"
55 #include "ardour/debug.h"
57 #include "generic_midi_control_protocol.h"
58 #include "midicontrollable.h"
59 #include "midifunction.h"
60 #include "midiaction.h"
62 using namespace ARDOUR;
68 #define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
70 GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
71 : ControlProtocol (s, _("Generic MIDI"))
72 , connection_state (ConnectionState (0))
77 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort> (s.midi_input_port ());
78 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort> (s.midi_output_port ());
80 _input_bundle.reset (new ARDOUR::Bundle (_("Generic MIDI Control In"), true));
81 _output_bundle.reset (new ARDOUR::Bundle (_("Generic MIDI Control Out"), false));
83 _input_bundle->add_channel (
84 boost::static_pointer_cast<MidiPort>(_input_port)->name(),
85 ARDOUR::DataType::MIDI,
86 session->engine().make_port_name_non_relative (boost::static_pointer_cast<MidiPort>(_input_port)->name())
89 _output_bundle->add_channel (
90 boost::static_pointer_cast<MidiPort>(_output_port)->name(),
91 ARDOUR::DataType::MIDI,
92 session->engine().make_port_name_non_relative (boost::static_pointer_cast<MidiPort>(_output_port)->name())
95 session->BundleAddedOrRemoved ();
98 _feedback_interval = 10000; // microseconds
99 last_feedback_time = 0;
104 /* these signals are emitted by the MidiControlUI's event loop thread
105 * and we may as well handle them right there in the same the same
109 Controllable::StartLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::start_learning, this, _1));
110 Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1));
112 /* this signal is emitted by the process() callback, and if
113 * send_feedback() is going to do anything, it should do it in the
114 * context of the process() callback itself.
117 Session::SendFeedback.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::send_feedback, this));
118 //Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
120 /* this one is cross-thread */
122 PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), midi_ui_context());
124 /* Catch port connections and disconnections (cross-thread) */
125 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR,
126 boost::bind (&GenericMidiControlProtocol::connection_handler, this, _1, _2, _3, _4, _5),
132 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
138 list<boost::shared_ptr<ARDOUR::Bundle> >
139 GenericMidiControlProtocol::bundles ()
141 list<boost::shared_ptr<ARDOUR::Bundle> > b;
144 b.push_back (_input_bundle);
145 b.push_back (_output_bundle);
152 static const char * const midimap_env_variable_name = "ARDOUR_MIDIMAPS_PATH";
153 static const char* const midi_map_dir_name = "midi_maps";
154 static const char* const midi_map_suffix = ".map";
157 system_midi_map_search_path ()
159 bool midimap_path_defined = false;
160 std::string spath_env (Glib::getenv (midimap_env_variable_name, midimap_path_defined));
162 if (midimap_path_defined) {
166 Searchpath spath (ardour_data_search_path());
167 spath.add_subdirectory_to_paths(midi_map_dir_name);
172 user_midi_map_directory ()
174 return Glib::build_filename (user_config_directory(), midi_map_dir_name);
178 midi_map_filter (const string &str, void* /*arg*/)
180 return (str.length() > strlen(midi_map_suffix) &&
181 str.find (midi_map_suffix) == (str.length() - strlen (midi_map_suffix)));
185 GenericMidiControlProtocol::reload_maps ()
187 vector<string> midi_maps;
188 Searchpath spath (system_midi_map_search_path());
189 spath += user_midi_map_directory ();
191 find_files_matching_filter (midi_maps, spath, midi_map_filter, 0, false, true);
193 if (midi_maps.empty()) {
194 cerr << "No MIDI maps found using " << spath.to_string() << endl;
198 for (vector<string>::iterator i = midi_maps.begin(); i != midi_maps.end(); ++i) {
199 string fullpath = *i;
203 if (!tree.read (fullpath.c_str())) {
210 if (!tree.root()->get_property ("name", str)) {
217 map_info.push_back (mi);
222 GenericMidiControlProtocol::drop_all ()
224 DEBUG_TRACE (DEBUG::GenericMidi, "Drop all bindings\n");
225 Glib::Threads::Mutex::Lock lm (pending_lock);
226 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
228 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
231 controllables.clear ();
233 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
234 (*i)->connection.disconnect();
240 pending_controllables.clear ();
242 for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
247 for (MIDIActions::iterator i = actions.begin(); i != actions.end(); ++i) {
254 GenericMidiControlProtocol::drop_bindings ()
256 DEBUG_TRACE (DEBUG::GenericMidi, "Drop bindings, leave learned\n");
257 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
259 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
260 if (!(*i)->learned()) {
262 i = controllables.erase (i);
268 for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
273 _current_binding = "";
279 GenericMidiControlProtocol::set_active (bool /*yn*/)
281 /* nothing to do here: the MIDI UI thread in libardour handles all our
288 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms)
290 _feedback_interval = ms;
294 GenericMidiControlProtocol::send_feedback ()
296 /* This is executed in RT "process" context", so no blocking calls
303 microseconds_t now = get_microseconds ();
305 if (last_feedback_time != 0) {
306 if ((now - last_feedback_time) < _feedback_interval) {
313 last_feedback_time = now;
317 GenericMidiControlProtocol::_send_feedback ()
319 /* This is executed in RT "process" context", so no blocking calls
322 const int32_t bufsize = 16 * 1024; /* XXX too big */
323 MIDI::byte buf[bufsize];
324 int32_t bsize = bufsize;
326 /* XXX: due to bugs in some ALSA / JACK MIDI bridges, we have to do separate
327 writes for each controllable here; if we send more than one MIDI message
328 in a single jack_midi_event_write then some bridges will only pass the
332 Glib::Threads::Mutex::Lock lm (controllables_lock, Glib::Threads::TRY_LOCK);
337 for (MIDIControllables::iterator r = controllables.begin(); r != controllables.end(); ++r) {
338 MIDI::byte* end = (*r)->write_feedback (buf, bsize);
340 _output_port->write (buf, (int32_t) (end - buf), 0);
346 GenericMidiControlProtocol::start_learning (boost::weak_ptr <Controllable> wc)
348 boost::shared_ptr<Controllable> c = wc.lock ();
353 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
354 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Learn binding: Controlable number: %1\n", c));
356 /* drop any existing mappings for the same controllable for which
357 * learning has just started.
360 MIDIControllables::iterator tmp;
361 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
364 if ((*i)->get_controllable() == c) {
366 controllables.erase (i);
371 /* check pending controllables (those for which a learn is underway) to
372 * see if it is for the same one for which learning has just started.
376 Glib::Threads::Mutex::Lock lm (pending_lock);
378 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
379 if (((*i)->mc)->get_controllable() == c) {
380 (*i)->connection.disconnect();
385 i = pending_controllables.erase (i);
392 MIDIControllable* mc = 0;
395 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
396 if ((*i)->get_controllable() && ((*i)->get_controllable()->id() == c->id())) {
403 mc = new MIDIControllable (this, *_input_port->parser(), c, false);
407 /* stuff the new controllable into pending */
410 Glib::Threads::Mutex::Lock lm (pending_lock);
412 MIDIPendingControllable* element = new MIDIPendingControllable (mc, own_mc);
413 c->LearningFinished.connect_same_thread (element->connection, boost::bind (&GenericMidiControlProtocol::learning_stopped, this, mc));
415 pending_controllables.push_back (element);
417 mc->learn_about_external_control ();
422 GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
424 Glib::Threads::Mutex::Lock lm (pending_lock);
425 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
427 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ) {
428 if ( (*i)->mc == mc) {
429 (*i)->connection.disconnect();
431 i = pending_controllables.erase(i);
437 /* add the controllable for which learning stopped to our list of
441 controllables.push_back (mc);
445 GenericMidiControlProtocol::stop_learning (boost::weak_ptr<PBD::Controllable> wc)
447 boost::shared_ptr<Controllable> c = wc.lock ();
452 Glib::Threads::Mutex::Lock lm (pending_lock);
453 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
454 MIDIControllable* dptr = 0;
456 /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
457 relevant MIDIControllable and remove it from the pending list.
460 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
461 if (((*i)->mc)->get_controllable() == c) {
462 (*i)->mc->stop_learning ();
464 (*i)->connection.disconnect();
467 pending_controllables.erase (i);
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 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
487 MIDIControllable* existingBinding = (*iter);
488 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
489 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
490 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
491 delete existingBinding;
492 iter = controllables.erase (iter);
501 for (MIDIFunctions::iterator iter = functions.begin(); iter != functions.end();) {
502 MIDIFunction* existingBinding = (*iter);
503 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
504 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
505 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
506 delete existingBinding;
507 iter = functions.erase (iter);
516 for (MIDIActions::iterator iter = actions.begin(); iter != actions.end();) {
517 MIDIAction* existingBinding = (*iter);
518 if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
519 if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
520 DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
521 delete existingBinding;
522 iter = actions.erase (iter);
534 GenericMidiControlProtocol::get_state ()
536 XMLNode& node (ControlProtocol::get_state());
538 node.set_property (X_("feedback-interval"), _feedback_interval);
539 node.set_property (X_("threshold"), _threshold);
540 node.set_property (X_("motorized"), _motorised);
542 if (!_current_binding.empty()) {
543 node.set_property ("binding", _current_binding);
546 XMLNode* children = new XMLNode (X_("Controls"));
548 node.add_child_nocopy (*children);
550 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
551 for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
553 /* we don't care about bindings that come from a bindings map, because
554 they will all be reset/recreated when we load the relevant bindings
558 if ((*i)->get_controllable() && (*i)->learned()) {
559 children->add_child_nocopy ((*i)->get_state());
567 GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
570 XMLNodeConstIterator niter;
572 if (ControlProtocol::set_state (node, version)) {
576 if (!node.get_property ("feedback-interval", _feedback_interval)) {
577 _feedback_interval = 10000;
580 if (!node.get_property ("threshold", _threshold)) {
584 if (!node.get_property ("motorized", _motorised)) {
588 boost::shared_ptr<Controllable> c;
591 Glib::Threads::Mutex::Lock lm (pending_lock);
592 for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
593 (*i)->connection.disconnect();
599 pending_controllables.clear ();
603 // midi map has to be loaded first so learned binding can go on top
604 if (node.get_property ("binding", str)) {
605 for (list<MapInfo>::iterator x = map_info.begin(); x != map_info.end(); ++x) {
606 if (str == (*x).name) {
607 load_bindings ((*x).path);
613 /* Load up specific bindings from the
614 * <Controls><MidiControllable>...</MidiControllable><Controls> section
617 bool load_dynamic_bindings = false;
618 node.get_property ("session-state", load_dynamic_bindings);
620 if (load_dynamic_bindings) {
621 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
622 nlist = node.children(); // "Controls"
624 if (!nlist.empty()) {
625 nlist = nlist.front()->children(); // "MIDIControllable" ...
627 if (!nlist.empty()) {
628 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
631 if ((*niter)->get_property ("id", id)) {
633 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Relearned binding for session: Control ID: %1\n", id.to_s()));
634 boost::shared_ptr<PBD::Controllable> c = Controllable::by_id (id);
637 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), c, false);
639 if (mc->set_state (**niter, version) == 0) {
640 controllables.push_back (mc);
642 warning << string_compose ("Generic MIDI control: Failed to set state for Control ID: %1\n", id.to_s());
647 warning << string_compose (
648 _("Generic MIDI control: controllable %1 not found in session (ignored)"),
649 id.to_s()) << endmsg;
661 GenericMidiControlProtocol::set_feedback (bool yn)
664 last_feedback_time = 0;
669 GenericMidiControlProtocol::get_feedback () const
675 GenericMidiControlProtocol::load_bindings (const string& xmlpath)
677 DEBUG_TRACE (DEBUG::GenericMidi, "Load bindings: Reading midi map\n");
680 if (!state_tree.read (xmlpath.c_str())) {
681 error << string_compose(_("Could not understand MIDI bindings file %1"), xmlpath) << endmsg;
685 XMLNode* root = state_tree.root();
687 if (root->name() != X_("ArdourMIDIBindings")) {
688 error << string_compose (_("MIDI Bindings file %1 is not really a MIDI bindings file"), xmlpath) << endmsg;
692 const XMLProperty* prop;
694 if ((prop = root->property ("version")) == 0) {
698 const XMLNodeList& children (root->children());
699 XMLNodeConstIterator citer;
700 XMLNodeConstIterator gciter;
702 MIDIControllable* mc;
706 DEBUG_TRACE (DEBUG::GenericMidi, "Loading bindings\n");
707 for (citer = children.begin(); citer != children.end(); ++citer) {
709 if ((*citer)->name() == "DeviceInfo") {
711 if ((*citer)->get_property ("bank-size", _bank_size)) {
715 if (!(*citer)->get_property ("motorized", _motorised)) {
719 if (!(*citer)->get_property ("threshold", _threshold)) {
724 if ((*citer)->name() == "Binding") {
725 const XMLNode* child = *citer;
727 if (child->property ("uri")) {
730 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
731 if ((mc = create_binding (*child)) != 0) {
732 controllables.push_back (mc);
735 } else if (child->property ("function")) {
740 if ((mf = create_function (*child)) != 0) {
741 functions.push_back (mf);
744 } else if (child->property ("action")) {
747 if ((ma = create_action (*child)) != 0) {
748 actions.push_back (ma);
754 if ((prop = root->property ("name")) != 0) {
755 _current_binding = prop->value ();
758 reset_controllables ();
764 GenericMidiControlProtocol::create_binding (const XMLNode& node)
766 const XMLProperty* prop;
768 MIDI::channel_t channel;
773 MIDIControllable::CtlType ctltype;
774 MIDIControllable::Encoder encoder = MIDIControllable::No_enc;
775 bool rpn_value = false;
776 bool nrpn_value = false;
777 bool rpn_change = false;
778 bool nrpn_change = false;
780 if ((prop = node.property (X_("ctl"))) != 0) {
781 ctltype = MIDIControllable::Ctl_Momentary;
782 ev = MIDI::controller;
783 } else if ((prop = node.property (X_("ctl-toggle"))) !=0) {
784 ctltype = MIDIControllable::Ctl_Toggle;
785 ev = MIDI::controller;
786 } else if ((prop = node.property (X_("ctl-dial"))) !=0) {
787 ctltype = MIDIControllable::Ctl_Dial;
788 ev = MIDI::controller;
789 } else if ((prop = node.property (X_("note"))) != 0) {
791 } else if ((prop = node.property (X_("pgm"))) != 0) {
793 } else if ((prop = node.property (X_("pb"))) != 0) {
794 ev = MIDI::pitchbend;
795 } else if ((prop = node.property (X_("enc-l"))) != 0) {
796 encoder = MIDIControllable::Enc_L;
797 ev = MIDI::controller;
798 } else if ((prop = node.property (X_("enc-r"))) != 0) {
799 encoder = MIDIControllable::Enc_R;
800 ev = MIDI::controller;
801 } else if ((prop = node.property (X_("enc-2"))) != 0) {
802 encoder = MIDIControllable::Enc_2;
803 ev = MIDI::controller;
804 } else if ((prop = node.property (X_("enc-b"))) != 0) {
805 encoder = MIDIControllable::Enc_B;
806 ev = MIDI::controller;
807 } else if ((prop = node.property (X_("rpn"))) != 0) {
809 } else if ((prop = node.property (X_("nrpn"))) != 0) {
811 } else if ((prop = node.property (X_("rpn-delta"))) != 0) {
813 } else if ((prop = node.property (X_("nrpn-delta"))) != 0) {
819 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
823 detail = (MIDI::byte) intval;
825 if ((prop = node.property (X_("channel"))) == 0) {
829 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
832 channel = (MIDI::channel_t) intval;
833 /* adjust channel to zero-based counting */
838 if ((prop = node.property (X_("momentary"))) != 0) {
839 momentary = string_to<bool> (prop->value());
844 prop = node.property (X_("uri"));
847 MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), momentary);
849 if (mc->init (uri)) {
855 mc->bind_rpn_value (channel, detail);
856 } else if (nrpn_value) {
857 mc->bind_nrpn_value (channel, detail);
858 } else if (rpn_change) {
859 mc->bind_rpn_change (channel, detail);
860 } else if (nrpn_change) {
861 mc->bind_nrpn_change (channel, detail);
863 mc->set_ctltype (ctltype);
864 mc->set_encoder (encoder);
865 mc->bind_midi (channel, ev, detail);
872 GenericMidiControlProtocol::reset_controllables ()
874 Glib::Threads::Mutex::Lock lm2 (controllables_lock);
876 for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ) {
877 MIDIControllable* existingBinding = (*iter);
878 MIDIControllables::iterator next = iter;
881 if (!existingBinding->learned()) {
883 /* its entirely possible that the session doesn't have
884 * the specified controllable (e.g. it has too few
885 * tracks). if we find this to be the case, we just leave
886 * the binding around, unbound, and it will do "late
887 * binding" (or "lazy binding") if/when any data arrives.
890 existingBinding->lookup_controllable ();
897 boost::shared_ptr<Controllable>
898 GenericMidiControlProtocol::lookup_controllable (const string & str) const
900 boost::shared_ptr<Controllable> c;
902 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("lookup controllable from \"%1\"\n", str));
905 DEBUG_TRACE (DEBUG::GenericMidi, "no session\n");
909 /* step 1: split string apart */
911 string::size_type first_space = str.find_first_of (" ");
913 if (first_space == string::npos) {
917 string front = str.substr (0, first_space);
919 split (front, path, '/');
921 if (path.size() < 2) {
925 string back = str.substr (first_space);
927 split (back, rest, ' ');
933 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("parsed into path of %1, rest of %1\n", path.size(), rest.size()));
935 /* Step 2: analyse parts of the string to figure out what type of
936 * Stripable we're looking for
948 static regex_t compiled_pattern;
949 static bool compiled = false;
952 const char * const pattern = "^[BS]?[0-9]+";
953 /* this pattern compilation is not going to fail */
954 regcomp (&compiled_pattern, pattern, REG_EXTENDED|REG_NOSUB);
955 /* leak compiled pattern */
959 /* Step 3: identify what "rest" looks like - name, or simple nueric, or
960 * banked/selection specifier
963 bool matched = (regexec (&compiled_pattern, rest[0].c_str(), 0, 0, 0) == 0);
968 if (rest[0][0] == 'B') {
970 /* already matched digits, so we know atoi() will succeed */
971 id = atoi (rest[0].substr (1));
972 type = PresentationOrder;
973 } else if (rest[0][0] == 'S') {
974 /* already matched digits, so we know atoi() will succeed */
975 id = atoi (rest[0].substr (1));
977 } else if (isdigit (rest[0][0])) {
978 /* already matched digits, so we know atoi() will succeed */
980 type = PresentationOrder;
985 id -= 1; /* order is zero-based, but maps use 1-based */
988 id += _current_bank * _bank_size;
997 /* step 4: find the reference Stripable */
999 boost::shared_ptr<Stripable> s;
1001 if (path[0] == X_("route") || path[0] == X_("rid")) {
1006 case PresentationOrder:
1007 s = session->get_remote_nth_stripable (id, PresentationInfo::Route);
1013 if (name == "Master" || name == X_("master")) {
1014 s = session->master_out();
1015 } else if (name == X_("control") || name == X_("listen") || name == X_("monitor") || name == "Monitor") {
1016 s = session->monitor_out();
1017 } else if (name == X_("auditioner")) {
1018 s = session->the_auditioner();
1020 s = session->route_by_name (name);
1025 s = session->route_by_selected_count (id);
1029 } else if (path[0] == X_("vca")) {
1031 s = session->get_remote_nth_stripable (id, PresentationInfo::VCA);
1033 } else if (path[0] == X_("bus")) {
1037 s = session->route_by_name (name);
1040 s = session->get_remote_nth_stripable (id, PresentationInfo::Bus);
1043 } else if (path[0] == X_("track")) {
1047 s = session->route_by_name (name);
1050 s = session->get_remote_nth_stripable (id, PresentationInfo::Track);
1055 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("no stripable found for \"%1\"\n", str));
1059 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("found stripable %1\n", s->name()));
1061 /* step 5: find the referenced controllable for that stripable.
1063 * Some controls exist only for Route, so we need that too
1066 boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (s);
1068 if (path[1] == X_("gain")) {
1069 c = s->gain_control();
1070 } else if (path[1] == X_("trim")) {
1071 c = s->trim_control ();
1072 } else if (path[1] == X_("solo")) {
1073 c = s->solo_control();
1074 } else if (path[1] == X_("mute")) {
1075 c = s->mute_control();
1076 } else if (path[1] == X_("recenable")) {
1077 c = s->rec_enable_control ();
1078 } else if (path[1] == X_("panwidth")) {
1079 c = s->pan_width_control ();
1080 } else if (path[1] == X_("pandirection") || path[1] == X_("balance")) {
1081 c = s->pan_azimuth_control ();
1082 } else if (path[1] == X_("plugin")) {
1084 /* /route/plugin/parameter */
1086 if (path.size() == 3 && rest.size() == 3) {
1087 if (path[2] == X_("parameter")) {
1089 int plugin = atoi (rest[1]);
1090 int parameter_index = atoi (rest[2]);
1092 /* revert to zero based counting */
1096 if (parameter_index > 0) {
1101 boost::shared_ptr<Processor> proc = r->nth_plugin (plugin);
1104 boost::shared_ptr<PluginInsert> p = boost::dynamic_pointer_cast<PluginInsert> (proc);
1108 param = p->plugin()->nth_parameter (parameter_index, ok);
1110 c = boost::dynamic_pointer_cast<Controllable> (proc->control (Evoral::Parameter (PluginAutomation, 0, param)));
1118 } else if (path[1] == X_("send")) {
1120 if (path.size() == 3 && rest.size() == 2) {
1121 if (path[2] == X_("gain")) {
1122 uint32_t send = atoi (rest[1]);
1126 c = s->send_level_controllable (send);
1127 } else if (path[2] == X_("direction")) {
1128 /* XXX not implemented yet */
1130 } else if (path[2] == X_("enable")) {
1131 /* XXX not implemented yet */
1135 } else if (path[1] == X_("eq")) {
1137 /* /route/eq/enable */
1138 /* /route/eq/gain/<band> */
1139 /* /route/eq/freq/<band> */
1140 /* /route/eq/q/<band> */
1141 /* /route/eq/shape/<band> */
1143 if (path.size() == 3) {
1145 if (path[2] == X_("enable")) {
1146 c = s->eq_enable_controllable ();
1149 } else if (path.size() == 4) {
1151 int band = atoi (path[3]); /* band number */
1153 if (path[2] == X_("gain")) {
1154 c = s->eq_gain_controllable (band);
1155 } else if (path[2] == X_("freq")) {
1156 c = s->eq_freq_controllable (band);
1157 } else if (path[2] == X_("q")) {
1158 c = s->eq_q_controllable (band);
1159 } else if (path[2] == X_("shape")) {
1160 c = s->eq_shape_controllable (band);
1164 } else if (path[1] == X_("filter")) {
1166 /* /route/filter/hi/freq */
1168 if (path.size() == 4) {
1172 if (path[2] == X_("hi")) {
1173 filter = 1; /* high pass filter */
1175 filter = 0; /* low pass filter */
1178 if (path[3] == X_("enable")) {
1179 c = s->filter_enable_controllable (filter);
1180 } else if (path[3] == X_("freq")) {
1181 c = s->filter_freq_controllable (filter);
1182 } else if (path[3] == X_("slope")) {
1183 c = s->filter_slope_controllable (filter);
1188 } else if (path[1] == X_("compressor")) {
1190 if (path.size() == 3) {
1191 if (path[2] == X_("enable")) {
1192 c = s->comp_enable_controllable ();
1193 } else if (path[2] == X_("threshold")) {
1194 c = s->comp_threshold_controllable ();
1195 } else if (path[2] == X_("mode")) {
1196 c = s->comp_mode_controllable ();
1197 } else if (path[2] == X_("speed")) {
1198 c = s->comp_speed_controllable ();
1199 } else if (path[2] == X_("makeup")) {
1200 c = s->comp_makeup_controllable ();
1206 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("found controllable \"%1\"\n", c->name()));
1208 DEBUG_TRACE (DEBUG::GenericMidi, "no controllable found\n");
1215 GenericMidiControlProtocol::create_function (const XMLNode& node)
1217 const XMLProperty* prop;
1219 MIDI::byte detail = 0;
1220 MIDI::channel_t channel = 0;
1223 MIDI::byte* data = 0;
1224 uint32_t data_size = 0;
1227 if ((prop = node.property (X_("ctl"))) != 0) {
1228 ev = MIDI::controller;
1229 } else if ((prop = node.property (X_("note"))) != 0) {
1231 } else if ((prop = node.property (X_("pgm"))) != 0) {
1233 } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
1235 if (prop->name() == X_("sysex")) {
1246 stringstream ss (prop->value());
1258 data = new MIDI::byte[cnt];
1262 stringstream ss (prop->value());
1267 data[cnt++] = (MIDI::byte) val;
1272 warning << "Binding ignored - unknown type" << endmsg;
1276 if (data_size == 0) {
1277 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1281 detail = (MIDI::byte) intval;
1283 if ((prop = node.property (X_("channel"))) == 0) {
1287 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1290 channel = (MIDI::channel_t) intval;
1291 /* adjust channel to zero-based counting */
1297 if ((prop = node.property (X_("arg"))) != 0 || (prop = node.property (X_("argument"))) != 0 || (prop = node.property (X_("arguments"))) != 0) {
1298 argument = prop->value ();
1301 prop = node.property (X_("function"));
1303 MIDIFunction* mf = new MIDIFunction (*_input_port->parser());
1305 if (mf->setup (*this, prop->value(), argument, data, data_size)) {
1310 mf->bind_midi (channel, ev, detail);
1316 GenericMidiControlProtocol::create_action (const XMLNode& node)
1318 const XMLProperty* prop;
1320 MIDI::byte detail = 0;
1321 MIDI::channel_t channel = 0;
1324 MIDI::byte* data = 0;
1325 uint32_t data_size = 0;
1327 if ((prop = node.property (X_("ctl"))) != 0) {
1328 ev = MIDI::controller;
1329 } else if ((prop = node.property (X_("note"))) != 0) {
1331 } else if ((prop = node.property (X_("pgm"))) != 0) {
1333 } else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
1335 if (prop->name() == X_("sysex")) {
1346 stringstream ss (prop->value());
1358 data = new MIDI::byte[cnt];
1362 stringstream ss (prop->value());
1367 data[cnt++] = (MIDI::byte) val;
1372 warning << "Binding ignored - unknown type" << endmsg;
1376 if (data_size == 0) {
1377 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1381 detail = (MIDI::byte) intval;
1383 if ((prop = node.property (X_("channel"))) == 0) {
1387 if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
1390 channel = (MIDI::channel_t) intval;
1391 /* adjust channel to zero-based counting */
1397 prop = node.property (X_("action"));
1399 MIDIAction* ma = new MIDIAction (*_input_port->parser());
1401 if (ma->init (*this, prop->value(), data, data_size)) {
1406 ma->bind_midi (channel, ev, detail);
1412 GenericMidiControlProtocol::set_current_bank (uint32_t b)
1415 reset_controllables ();
1419 GenericMidiControlProtocol::next_bank ()
1422 reset_controllables ();
1426 GenericMidiControlProtocol::prev_bank()
1428 if (_current_bank) {
1430 reset_controllables ();
1435 GenericMidiControlProtocol::set_motorised (bool m)
1441 GenericMidiControlProtocol::set_threshold (int t)
1447 GenericMidiControlProtocol::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1449 if (!_input_port || !_output_port) {
1453 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_input_port)->name());
1454 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
1456 if (ni == name1 || ni == name2) {
1458 connection_state |= InputConnected;
1460 connection_state &= ~InputConnected;
1462 } else if (no == name1 || no == name2) {
1464 connection_state |= OutputConnected;
1466 connection_state &= ~OutputConnected;
1473 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1475 /* XXX this is a horrible hack. Without a short sleep here,
1476 something prevents the device wakeup messages from being
1477 sent and/or the responses from being received.
1487 ConnectionChange (); /* emit signal for our GUI */
1489 return true; /* connection status changed */
1493 GenericMidiControlProtocol::connected ()
1497 boost::shared_ptr<Port>
1498 GenericMidiControlProtocol::output_port() const
1500 return _output_port;
1503 boost::shared_ptr<Port>
1504 GenericMidiControlProtocol::input_port() const
1510 GenericMidiControlProtocol::maybe_start_touch (boost::shared_ptr<Controllable> controllable)
1512 boost::shared_ptr<AutomationControl> actl = boost::dynamic_pointer_cast<AutomationControl> (controllable);
1514 actl->start_touch (session->audible_sample ());