#include "pbd/controllable_descriptor.h"
#include "pbd/error.h"
#include "pbd/failed_constructor.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
#include "pbd/xml++.h"
+#include "pbd/compose.h"
#include "midi++/port.h"
#include "ardour/midi_ui.h"
#include "ardour/rc_configuration.h"
#include "ardour/midiport_manager.h"
+#include "ardour/debug.h"
#include "generic_midi_control_protocol.h"
#include "midicontrollable.h"
Controllable::CreateBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::create_binding, this, _1, _2, _3));
Controllable::DeleteBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding, this, _1));
- Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
-#if 0
- /* XXXX SOMETHING GOES WRONG HERE (april 2012) - STILL DEBUGGING */
/* this signal is emitted by the process() callback, and if
* send_feedback() is going to do anything, it should do it in the
* context of the process() callback itself.
*/
Session::SendFeedback.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::send_feedback, this));
-#endif
+ //Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
+
/* this one is cross-thread */
Route::RemoteControlIDChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), midi_ui_context());
void
GenericMidiControlProtocol::reload_maps ()
{
- vector<string *> *midi_maps;
- PathScanner scanner;
+ vector<string> midi_maps;
Searchpath spath (system_midi_map_search_path());
spath += user_midi_map_directory ();
- midi_maps = scanner (spath.to_string(), midi_map_filter, 0, false, true);
+ find_files_matching_filter (midi_maps, spath, midi_map_filter, 0, false, true);
- if (!midi_maps) {
+ if (midi_maps.empty()) {
cerr << "No MIDI maps found using " << spath.to_string() << endl;
return;
}
- for (vector<string*>::iterator i = midi_maps->begin(); i != midi_maps->end(); ++i) {
- string fullpath = *(*i);
+ for (vector<string>::iterator i = midi_maps.begin(); i != midi_maps.end(); ++i) {
+ string fullpath = *i;
XMLTree tree;
map_info.push_back (mi);
}
-
- delete midi_maps;
}
void
GenericMidiControlProtocol::drop_all ()
{
+ DEBUG_TRACE (DEBUG::GenericMidi, "Drop all bindings\n");
Glib::Threads::Mutex::Lock lm (pending_lock);
Glib::Threads::Mutex::Lock lm2 (controllables_lock);
void
GenericMidiControlProtocol::drop_bindings ()
{
+ DEBUG_TRACE (DEBUG::GenericMidi, "Drop bindings, leave learned\n");
Glib::Threads::Mutex::Lock lm2 (controllables_lock);
for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
}
Glib::Threads::Mutex::Lock lm2 (controllables_lock);
+ DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Learn binding: Controlable number: %1\n", c));
MIDIControllables::iterator tmp;
for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
pending_controllables.push_back (element);
}
-
mc->learn_about_external_control ();
return true;
}
}
}
+// This next function seems unused
void
GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
{
// Update the MIDI Controllable based on the the pos param
// Here is where a table lookup for user mappings could go; for now we'll just wing it...
mc->bind_midi(channel, MIDI::controller, value);
-
+ DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Create binding: Channel: %1 Controller: %2 Value: %3 \n", channel, MIDI::controller, value));
controllables.push_back (mc);
}
}
+void
+GenericMidiControlProtocol::check_used_event (int pos, int control_number)
+{
+ Glib::Threads::Mutex::Lock lm2 (controllables_lock);
+
+ MIDI::channel_t channel = (pos & 0xf);
+ MIDI::byte value = control_number;
+
+ DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("checking for used event: Channel: %1 Controller: %2 value: %3\n", (int) channel, (pos & 0xf0), (int) value));
+
+ // Remove any old binding for this midi channel/type/value pair
+ // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
+ for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
+ MIDIControllable* existingBinding = (*iter);
+ if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
+ if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
+ DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
+ delete existingBinding;
+ iter = controllables.erase (iter);
+ } else {
+ ++iter;
+ }
+ } else {
+ ++iter;
+ }
+ }
+
+ for (MIDIFunctions::iterator iter = functions.begin(); iter != functions.end();) {
+ MIDIFunction* existingBinding = (*iter);
+ if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
+ if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
+ DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
+ delete existingBinding;
+ iter = functions.erase (iter);
+ } else {
+ ++iter;
+ }
+ } else {
+ ++iter;
+ }
+ }
+
+ for (MIDIActions::iterator iter = actions.begin(); iter != actions.end();) {
+ MIDIAction* existingBinding = (*iter);
+ if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
+ if ( ((int) existingBinding->get_control_additional() == (int) value) || ((pos & 0xf0) == MIDI::pitchbend)) {
+ DEBUG_TRACE (DEBUG::GenericMidi, "checking: found match, delete old binding.\n");
+ delete existingBinding;
+ iter = actions.erase (iter);
+ } else {
+ ++iter;
+ }
+ } else {
+ ++iter;
+ }
+ }
+
+}
+
XMLNode&
GenericMidiControlProtocol::get_state ()
{
- XMLNode* node = new XMLNode ("Protocol");
+ XMLNode& node (ControlProtocol::get_state());
char buf[32];
- node->add_property (X_("name"), _name);
- node->add_property (X_("feedback"), do_feedback ? "1" : "0");
snprintf (buf, sizeof (buf), "%" PRIu64, _feedback_interval);
- node->add_property (X_("feedback_interval"), buf);
+ node.add_property (X_("feedback_interval"), buf);
snprintf (buf, sizeof (buf), "%d", _threshold);
- node->add_property (X_("threshold"), buf);
+ node.add_property (X_("threshold"), buf);
+
+ node.add_property (X_("motorized"), _motorised ? "yes" : "no");
if (!_current_binding.empty()) {
- node->add_property ("binding", _current_binding);
+ node.add_property ("binding", _current_binding);
}
XMLNode* children = new XMLNode (X_("Controls"));
- node->add_child_nocopy (*children);
+ node.add_child_nocopy (*children);
Glib::Threads::Mutex::Lock lm2 (controllables_lock);
for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
}
}
- return *node;
+ return node;
}
int
XMLNodeConstIterator niter;
const XMLProperty* prop;
- if ((prop = node.property ("feedback")) != 0) {
- do_feedback = (bool) atoi (prop->value().c_str());
- } else {
- do_feedback = false;
+ if (ControlProtocol::set_state (node, version)) {
+ return -1;
}
if ((prop = node.property ("feedback_interval")) != 0) {
_threshold = 10;
}
+ if ((prop = node.property ("motorized")) != 0) {
+ _motorised = string_is_affirmative (prop->value ());
+ } else {
+ _motorised = false;
+ }
+
boost::shared_ptr<Controllable> c;
{
pending_controllables.clear ();
}
+ // midi map has to be loaded first so learned binding can go on top
+ if ((prop = node.property ("binding")) != 0) {
+ for (list<MapInfo>::iterator x = map_info.begin(); x != map_info.end(); ++x) {
+ if (prop->value() == (*x).name) {
+ load_bindings ((*x).path);
+ break;
+ }
+ }
+ }
+
/* Load up specific bindings from the
* <Controls><MidiControllable>...</MidiControllable><Controls> section
*/
{
Glib::Threads::Mutex::Lock lm2 (controllables_lock);
- controllables.clear ();
nlist = node.children(); // "Controls"
if (!nlist.empty()) {
if ((prop = (*niter)->property ("id")) != 0) {
ID id = prop->value ();
+ DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Relearned binding for session: Control ID: %1\n", id.to_s()));
Controllable* c = Controllable::by_id (id);
if (c) {
} else {
warning << string_compose (
_("Generic MIDI control: controllable %1 not found in session (ignored)"),
- id) << endmsg;
+ id.to_s()) << endmsg;
}
}
}
}
}
- if ((prop = node.property ("binding")) != 0) {
- for (list<MapInfo>::iterator x = map_info.begin(); x != map_info.end(); ++x) {
- if (prop->value() == (*x).name) {
- load_bindings ((*x).path);
- break;
- }
- }
- }
-
return 0;
}
int
GenericMidiControlProtocol::load_bindings (const string& xmlpath)
{
+ DEBUG_TRACE (DEBUG::GenericMidi, "Load bindings: Reading midi map\n");
XMLTree state_tree;
if (!state_tree.read (xmlpath.c_str())) {
drop_all ();
+ DEBUG_TRACE (DEBUG::GenericMidi, "Loading bindings\n");
for (citer = children.begin(); citer != children.end(); ++citer) {
if ((*citer)->name() == "DeviceInfo") {
_current_bank = 0;
}
- if ((prop = (*citer)->property ("motorised")) != 0 || ((prop = (*citer)->property ("motorized")) != 0)) {
+ if ((prop = (*citer)->property ("motorized")) != 0) {
_motorised = string_is_affirmative (prop->value ());
} else {
_motorised = false;
MIDI::eventType ev;
int intval;
bool momentary;
+ MIDIControllable::Encoder encoder = MIDIControllable::No_enc;
if ((prop = node.property (X_("ctl"))) != 0) {
ev = MIDI::controller;
ev = MIDI::program;
} else if ((prop = node.property (X_("pb"))) != 0) {
ev = MIDI::pitchbend;
+ } else if ((prop = node.property (X_("enc-l"))) != 0) {
+ encoder = MIDIControllable::Enc_L;
+ ev = MIDI::controller;
+ } else if ((prop = node.property (X_("enc-r"))) != 0) {
+ encoder = MIDIControllable::Enc_R;
+ ev = MIDI::controller;
+ } else if ((prop = node.property (X_("enc-2"))) != 0) {
+ encoder = MIDIControllable::Enc_2;
+ ev = MIDI::controller;
+ } else if ((prop = node.property (X_("enc-b"))) != 0) {
+ encoder = MIDIControllable::Enc_B;
+ ev = MIDI::controller;
} else {
return 0;
}
return 0;
}
+ mc->set_encoder (encoder);
mc->bind_midi (channel, ev, detail);
return mc;