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/miscutils.h>
27 #include "pbd/controllable_descriptor.h"
28 #include "pbd/error.h"
29 #include "pbd/failed_constructor.h"
30 #include "pbd/pathscanner.h"
31 #include "pbd/xml++.h"
33 #include "midi++/port.h"
34 #include "midi++/manager.h"
36 #include "ardour/filesystem_paths.h"
37 #include "ardour/session.h"
38 #include "ardour/route.h"
39 #include "ardour/midi_ui.h"
40 #include "ardour/rc_configuration.h"
42 #include "generic_midi_control_protocol.h"
43 #include "midicontrollable.h"
44 #include "midifunction.h"
45 #include "midiaction.h"
47 using namespace ARDOUR
;
53 #define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
54 #define ui_bind(x) boost::protect (boost::bind ((x)))
56 GenericMidiControlProtocol::GenericMidiControlProtocol (Session
& s
)
57 : ControlProtocol (s
, _("Generic MIDI"), midi_ui_context())
61 _input_port
= MIDI::Manager::instance()->midi_input_port ();
62 _output_port
= MIDI::Manager::instance()->midi_output_port ();
65 _feedback_interval
= 10000; // microseconds
66 last_feedback_time
= 0;
71 /* XXX is it right to do all these in the same thread as whatever emits the signal? */
73 Controllable::StartLearning
.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::start_learning
, this, _1
));
74 Controllable::StopLearning
.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning
, this, _1
));
75 Controllable::CreateBinding
.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::create_binding
, this, _1
, _2
, _3
));
76 Controllable::DeleteBinding
.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding
, this, _1
));
78 Session::SendFeedback
.connect (*this, MISSING_INVALIDATOR
, boost::bind (&GenericMidiControlProtocol::send_feedback
, this), midi_ui_context());;
79 Route::RemoteControlIDChange
.connect (*this, MISSING_INVALIDATOR
, boost::bind (&GenericMidiControlProtocol::reset_controllables
, this), midi_ui_context());
84 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
90 static const char * const midimap_env_variable_name
= "ARDOUR_MIDIMAPS_PATH";
91 static const char* const midi_map_dir_name
= "midi_maps";
92 static const char* const midi_map_suffix
= ".map";
95 system_midi_map_search_path ()
97 bool midimap_path_defined
= false;
98 sys::path
spath_env (Glib::getenv (midimap_env_variable_name
, midimap_path_defined
));
100 if (midimap_path_defined
) {
104 SearchPath
spath (system_data_search_path());
105 spath
.add_subdirectory_to_paths(midi_map_dir_name
);
107 // just return the first directory in the search path that exists
108 SearchPath::const_iterator i
= std::find_if(spath
.begin(), spath
.end(), sys::exists
);
110 if (i
== spath
.end()) return sys::path();
116 user_midi_map_directory ()
118 sys::path
p(user_config_directory());
119 p
/= midi_map_dir_name
;
125 midi_map_filter (const string
&str
, void */
*arg*/
)
127 return (str
.length() > strlen(midi_map_suffix
) &&
128 str
.find (midi_map_suffix
) == (str
.length() - strlen (midi_map_suffix
)));
132 GenericMidiControlProtocol::reload_maps ()
134 vector
<string
*> *midi_maps
;
136 SearchPath
spath (system_midi_map_search_path());
137 spath
+= user_midi_map_directory ();
139 midi_maps
= scanner (spath
.to_string(), midi_map_filter
, 0, false, true);
142 cerr
<< "No MIDI maps found using " << spath
.to_string() << endl
;
146 for (vector
<string
*>::iterator i
= midi_maps
->begin(); i
!= midi_maps
->end(); ++i
) {
147 string fullpath
= *(*i
);
151 if (!tree
.read (fullpath
.c_str())) {
157 XMLProperty
* prop
= tree
.root()->property ("name");
163 mi
.name
= prop
->value ();
166 map_info
.push_back (mi
);
173 GenericMidiControlProtocol::drop_all ()
175 Glib::Mutex::Lock
lm (pending_lock
);
176 Glib::Mutex::Lock
lm2 (controllables_lock
);
178 for (MIDIControllables::iterator i
= controllables
.begin(); i
!= controllables
.end(); ++i
) {
181 controllables
.clear ();
183 for (MIDIPendingControllables::iterator i
= pending_controllables
.begin(); i
!= pending_controllables
.end(); ++i
) {
186 pending_controllables
.clear ();
188 for (MIDIFunctions::iterator i
= functions
.begin(); i
!= functions
.end(); ++i
) {
193 for (MIDIActions::iterator i
= actions
.begin(); i
!= actions
.end(); ++i
) {
200 GenericMidiControlProtocol::drop_bindings ()
202 Glib::Mutex::Lock
lm2 (controllables_lock
);
204 for (MIDIControllables::iterator i
= controllables
.begin(); i
!= controllables
.end(); ) {
205 if (!(*i
)->learned()) {
207 i
= controllables
.erase (i
);
213 for (MIDIFunctions::iterator i
= functions
.begin(); i
!= functions
.end(); ++i
) {
218 _current_binding
= "";
224 GenericMidiControlProtocol::set_active (bool /*yn*/)
226 /* start/stop delivery/outbound thread */
231 GenericMidiControlProtocol::set_feedback_interval (microseconds_t ms
)
233 _feedback_interval
= ms
;
237 GenericMidiControlProtocol::send_feedback ()
243 microseconds_t now
= get_microseconds ();
245 if (last_feedback_time
!= 0) {
246 if ((now
- last_feedback_time
) < _feedback_interval
) {
253 last_feedback_time
= now
;
257 GenericMidiControlProtocol::_send_feedback ()
259 const int32_t bufsize
= 16 * 1024; /* XXX too big */
260 MIDI::byte buf
[bufsize
];
261 int32_t bsize
= bufsize
;
262 MIDI::byte
* end
= buf
;
264 for (MIDIControllables::iterator r
= controllables
.begin(); r
!= controllables
.end(); ++r
) {
265 end
= (*r
)->write_feedback (end
, bsize
);
272 _output_port
->write (buf
, (int32_t) (end
- buf
), 0);
276 GenericMidiControlProtocol::start_learning (Controllable
* c
)
282 Glib::Mutex::Lock
lm2 (controllables_lock
);
284 MIDIControllables::iterator tmp
;
285 for (MIDIControllables::iterator i
= controllables
.begin(); i
!= controllables
.end(); ) {
288 if ((*i
)->get_controllable() == c
) {
290 controllables
.erase (i
);
296 Glib::Mutex::Lock
lm (pending_lock
);
298 MIDIPendingControllables::iterator ptmp
;
299 for (MIDIPendingControllables::iterator i
= pending_controllables
.begin(); i
!= pending_controllables
.end(); ) {
302 if (((*i
)->first
)->get_controllable() == c
) {
303 (*i
)->second
.disconnect();
306 pending_controllables
.erase (i
);
312 MIDIControllable
* mc
= 0;
314 for (MIDIControllables::iterator i
= controllables
.begin(); i
!= controllables
.end(); ++i
) {
315 if ((*i
)->get_controllable() && ((*i
)->get_controllable()->id() == c
->id())) {
322 mc
= new MIDIControllable (*_input_port
, *c
, false);
326 Glib::Mutex::Lock
lm (pending_lock
);
328 MIDIPendingControllable
* element
= new MIDIPendingControllable
;
330 c
->LearningFinished
.connect_same_thread (element
->second
, boost::bind (&GenericMidiControlProtocol::learning_stopped
, this, mc
));
332 pending_controllables
.push_back (element
);
335 mc
->learn_about_external_control ();
340 GenericMidiControlProtocol::learning_stopped (MIDIControllable
* mc
)
342 Glib::Mutex::Lock
lm (pending_lock
);
343 Glib::Mutex::Lock
lm2 (controllables_lock
);
345 MIDIPendingControllables::iterator tmp
;
347 for (MIDIPendingControllables::iterator i
= pending_controllables
.begin(); i
!= pending_controllables
.end(); ) {
351 if ( (*i
)->first
== mc
) {
352 (*i
)->second
.disconnect();
354 pending_controllables
.erase(i
);
360 controllables
.push_back (mc
);
364 GenericMidiControlProtocol::stop_learning (Controllable
* c
)
366 Glib::Mutex::Lock
lm (pending_lock
);
367 Glib::Mutex::Lock
lm2 (controllables_lock
);
368 MIDIControllable
* dptr
= 0;
370 /* learning timed out, and we've been told to consider this attempt to learn to be cancelled. find the
371 relevant MIDIControllable and remove it from the pending list.
374 for (MIDIPendingControllables::iterator i
= pending_controllables
.begin(); i
!= pending_controllables
.end(); ++i
) {
375 if (((*i
)->first
)->get_controllable() == c
) {
376 (*i
)->first
->stop_learning ();
378 (*i
)->second
.disconnect();
381 pending_controllables
.erase (i
);
390 GenericMidiControlProtocol::delete_binding (PBD::Controllable
* control
)
393 Glib::Mutex::Lock
lm2 (controllables_lock
);
395 for (MIDIControllables::iterator iter
= controllables
.begin(); iter
!= controllables
.end();) {
396 MIDIControllable
* existingBinding
= (*iter
);
398 if (control
== (existingBinding
->get_controllable())) {
399 delete existingBinding
;
400 iter
= controllables
.erase (iter
);
410 GenericMidiControlProtocol::create_binding (PBD::Controllable
* control
, int pos
, int control_number
)
412 if (control
!= NULL
) {
413 Glib::Mutex::Lock
lm2 (controllables_lock
);
415 MIDI::channel_t channel
= (pos
& 0xf);
416 MIDI::byte value
= control_number
;
418 // Create a MIDIControllable
419 MIDIControllable
* mc
= new MIDIControllable (*_input_port
, *control
, false);
421 // Remove any old binding for this midi channel/type/value pair
422 // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
423 for (MIDIControllables::iterator iter
= controllables
.begin(); iter
!= controllables
.end();) {
424 MIDIControllable
* existingBinding
= (*iter
);
426 if ((existingBinding
->get_control_channel() & 0xf ) == channel
&&
427 existingBinding
->get_control_additional() == value
&&
428 (existingBinding
->get_control_type() & 0xf0 ) == MIDI::controller
) {
430 delete existingBinding
;
431 iter
= controllables
.erase (iter
);
438 // Update the MIDI Controllable based on the the pos param
439 // Here is where a table lookup for user mappings could go; for now we'll just wing it...
440 mc
->bind_midi(channel
, MIDI::controller
, value
);
442 controllables
.push_back (mc
);
447 GenericMidiControlProtocol::get_state ()
449 XMLNode
* node
= new XMLNode ("Protocol");
452 node
->add_property (X_("name"), _name
);
453 node
->add_property (X_("feedback"), do_feedback
? "1" : "0");
454 snprintf (buf
, sizeof (buf
), "%" PRIu64
, _feedback_interval
);
455 node
->add_property (X_("feedback_interval"), buf
);
457 if (!_current_binding
.empty()) {
458 node
->add_property ("binding", _current_binding
);
461 XMLNode
* children
= new XMLNode (X_("Controls"));
463 node
->add_child_nocopy (*children
);
465 Glib::Mutex::Lock
lm2 (controllables_lock
);
466 for (MIDIControllables::iterator i
= controllables
.begin(); i
!= controllables
.end(); ++i
) {
468 /* we don't care about bindings that come from a bindings map, because
469 they will all be reset/recreated when we load the relevant bindings
473 if ((*i
)->learned()) {
474 children
->add_child_nocopy ((*i
)->get_state());
482 GenericMidiControlProtocol::set_state (const XMLNode
& node
, int version
)
485 XMLNodeConstIterator niter
;
486 const XMLProperty
* prop
;
488 if ((prop
= node
.property ("feedback")) != 0) {
489 do_feedback
= (bool) atoi (prop
->value().c_str());
494 if ((prop
= node
.property ("feedback_interval")) != 0) {
495 if (sscanf (prop
->value().c_str(), "%" PRIu64
, &_feedback_interval
) != 1) {
496 _feedback_interval
= 10000;
499 _feedback_interval
= 10000;
502 boost::shared_ptr
<Controllable
> c
;
505 Glib::Mutex::Lock
lm (pending_lock
);
506 for (MIDIPendingControllables::iterator i
= pending_controllables
.begin(); i
!= pending_controllables
.end(); ++i
) {
509 pending_controllables
.clear ();
513 Glib::Mutex::Lock
lm2 (controllables_lock
);
514 controllables
.clear ();
515 nlist
= node
.children(); // "Controls"
521 nlist
= nlist
.front()->children ();
523 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
525 if ((prop
= (*niter
)->property ("id")) != 0) {
527 cerr
<< "Looking for MIDI Controllable with ID " << prop
->value() << endl
;
529 ID id
= prop
->value ();
530 Controllable
* c
= Controllable::by_id (id
);
532 cerr
<< "\tresult = " << c
<< endl
;
535 MIDIControllable
* mc
= new MIDIControllable (*_input_port
, *c
, false);
537 if (mc
->set_state (**niter
, version
) == 0) {
538 controllables
.push_back (mc
);
542 warning
<< string_compose (
543 _("Generic MIDI control: controllable %1 not found in session (ignored)"),
551 if ((prop
= node
.property ("binding")) != 0) {
552 for (list
<MapInfo
>::iterator x
= map_info
.begin(); x
!= map_info
.end(); ++x
) {
553 if (prop
->value() == (*x
).name
) {
554 load_bindings ((*x
).path
);
564 GenericMidiControlProtocol::set_feedback (bool yn
)
567 last_feedback_time
= 0;
572 GenericMidiControlProtocol::get_feedback () const
581 GenericMidiControlProtocol::load_bindings (const string
& xmlpath
)
585 if (!state_tree
.read (xmlpath
.c_str())) {
586 error
<< string_compose(_("Could not understand MIDI bindings file %1"), xmlpath
) << endmsg
;
590 XMLNode
* root
= state_tree
.root();
592 if (root
->name() != X_("ArdourMIDIBindings")) {
593 error
<< string_compose (_("MIDI Bindings file %1 is not really a MIDI bindings file"), xmlpath
) << endmsg
;
597 const XMLProperty
* prop
;
599 if ((prop
= root
->property ("version")) == 0) {
606 sscanf (prop
->value().c_str(), "%d.%d.%d", &major
, &minor
, µ
);
607 Stateful::loading_state_version
= (major
* 1000) + minor
;
610 const XMLNodeList
& children (root
->children());
611 XMLNodeConstIterator citer
;
612 XMLNodeConstIterator gciter
;
614 MIDIControllable
* mc
;
618 for (citer
= children
.begin(); citer
!= children
.end(); ++citer
) {
620 if ((*citer
)->name() == "DeviceInfo") {
621 const XMLProperty
* prop
;
623 if ((prop
= (*citer
)->property ("bank-size")) != 0) {
624 _bank_size
= atoi (prop
->value());
629 if ((*citer
)->name() == "Binding") {
630 const XMLNode
* child
= *citer
;
632 if (child
->property ("uri")) {
635 if ((mc
= create_binding (*child
)) != 0) {
636 Glib::Mutex::Lock
lm2 (controllables_lock
);
637 controllables
.push_back (mc
);
640 } else if (child
->property ("function")) {
645 if ((mf
= create_function (*child
)) != 0) {
646 functions
.push_back (mf
);
649 } else if (child
->property ("action")) {
652 if ((ma
= create_action (*child
)) != 0) {
653 actions
.push_back (ma
);
659 if ((prop
= root
->property ("name")) != 0) {
660 _current_binding
= prop
->value ();
663 reset_controllables ();
669 GenericMidiControlProtocol::create_binding (const XMLNode
& node
)
671 const XMLProperty
* prop
;
673 MIDI::channel_t channel
;
679 if ((prop
= node
.property (X_("ctl"))) != 0) {
680 ev
= MIDI::controller
;
681 } else if ((prop
= node
.property (X_("note"))) != 0) {
683 } else if ((prop
= node
.property (X_("pgm"))) != 0) {
689 if (sscanf (prop
->value().c_str(), "%d", &intval
) != 1) {
693 detail
= (MIDI::byte
) intval
;
695 if ((prop
= node
.property (X_("channel"))) == 0) {
699 if (sscanf (prop
->value().c_str(), "%d", &intval
) != 1) {
702 channel
= (MIDI::channel_t
) intval
;
703 /* adjust channel to zero-based counting */
708 if ((prop
= node
.property (X_("momentary"))) != 0) {
709 momentary
= string_is_affirmative (prop
->value());
714 prop
= node
.property (X_("uri"));
717 MIDIControllable
* mc
= new MIDIControllable (*_input_port
, momentary
);
719 if (mc
->init (uri
)) {
724 mc
->bind_midi (channel
, ev
, detail
);
730 GenericMidiControlProtocol::reset_controllables ()
732 Glib::Mutex::Lock
lm2 (controllables_lock
);
734 for (MIDIControllables::iterator iter
= controllables
.begin(); iter
!= controllables
.end(); ) {
735 MIDIControllable
* existingBinding
= (*iter
);
736 MIDIControllables::iterator next
= iter
;
739 if (!existingBinding
->learned()) {
740 ControllableDescriptor
& desc (existingBinding
->descriptor());
743 desc
.set_bank_offset (_current_bank
* _bank_size
);
746 /* its entirely possible that the session doesn't have
747 * the specified controllable (e.g. it has too few
748 * tracks). if we find this to be the case, drop any
749 * bindings that would be left without controllables.
752 boost::shared_ptr
<Controllable
> c
= session
->controllable_by_descriptor (desc
);
754 existingBinding
->set_controllable (c
.get());
756 controllables
.erase (iter
);
765 GenericMidiControlProtocol::create_function (const XMLNode
& node
)
767 const XMLProperty
* prop
;
769 MIDI::byte detail
= 0;
770 MIDI::channel_t channel
= 0;
773 MIDI::byte
* data
= 0;
774 uint32_t data_size
= 0;
777 if ((prop
= node
.property (X_("ctl"))) != 0) {
778 ev
= MIDI::controller
;
779 } else if ((prop
= node
.property (X_("note"))) != 0) {
781 } else if ((prop
= node
.property (X_("pgm"))) != 0) {
783 } else if ((prop
= node
.property (X_("sysex"))) != 0 || (prop
= node
.property (X_("msg"))) != 0) {
785 if (prop
->name() == X_("sysex")) {
796 stringstream
ss (prop
->value());
808 data
= new MIDI::byte
[cnt
];
812 stringstream
ss (prop
->value());
817 data
[cnt
++] = (MIDI::byte
) val
;
822 warning
<< "Binding ignored - unknown type" << endmsg
;
826 if (data_size
== 0) {
827 if (sscanf (prop
->value().c_str(), "%d", &intval
) != 1) {
831 detail
= (MIDI::byte
) intval
;
833 if ((prop
= node
.property (X_("channel"))) == 0) {
837 if (sscanf (prop
->value().c_str(), "%d", &intval
) != 1) {
840 channel
= (MIDI::channel_t
) intval
;
841 /* adjust channel to zero-based counting */
847 if ((prop
= node
.property (X_("arg"))) != 0) {
848 argument
= prop
->value ();
851 prop
= node
.property (X_("function"));
853 MIDIFunction
* mf
= new MIDIFunction (*_input_port
);
855 if (mf
->setup (*this, prop
->value(), argument
, data
, data_size
)) {
860 mf
->bind_midi (channel
, ev
, detail
);
866 GenericMidiControlProtocol::create_action (const XMLNode
& node
)
868 const XMLProperty
* prop
;
870 MIDI::byte detail
= 0;
871 MIDI::channel_t channel
= 0;
874 MIDI::byte
* data
= 0;
875 uint32_t data_size
= 0;
877 if ((prop
= node
.property (X_("ctl"))) != 0) {
878 ev
= MIDI::controller
;
879 } else if ((prop
= node
.property (X_("note"))) != 0) {
881 } else if ((prop
= node
.property (X_("pgm"))) != 0) {
883 } else if ((prop
= node
.property (X_("sysex"))) != 0 || (prop
= node
.property (X_("msg"))) != 0) {
885 if (prop
->name() == X_("sysex")) {
896 stringstream
ss (prop
->value());
908 data
= new MIDI::byte
[cnt
];
912 stringstream
ss (prop
->value());
917 data
[cnt
++] = (MIDI::byte
) val
;
922 warning
<< "Binding ignored - unknown type" << endmsg
;
926 if (data_size
== 0) {
927 if (sscanf (prop
->value().c_str(), "%d", &intval
) != 1) {
931 detail
= (MIDI::byte
) intval
;
933 if ((prop
= node
.property (X_("channel"))) == 0) {
937 if (sscanf (prop
->value().c_str(), "%d", &intval
) != 1) {
940 channel
= (MIDI::channel_t
) intval
;
941 /* adjust channel to zero-based counting */
947 prop
= node
.property (X_("action"));
949 MIDIAction
* ma
= new MIDIAction (*_input_port
);
951 if (ma
->init (*this, prop
->value(), data
, data_size
)) {
956 ma
->bind_midi (channel
, ev
, detail
);
962 GenericMidiControlProtocol::set_current_bank (uint32_t b
)
965 reset_controllables ();
969 GenericMidiControlProtocol::next_bank ()
972 reset_controllables ();
976 GenericMidiControlProtocol::prev_bank()
980 reset_controllables ();