MIDI binding maps make their debut

git-svn-id: svn://localhost/ardour2/branches/3.0@6408 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2009-12-28 16:49:44 +00:00
parent cba3ca64b3
commit 109acd1568
9 changed files with 279 additions and 9 deletions

View file

@ -769,6 +769,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
/* Controllables */
boost::shared_ptr<PBD::Controllable> controllable_by_id (const PBD::ID&);
boost::shared_ptr<PBD::Controllable> controllable_by_uri (const std::string&);
void add_controllable (boost::shared_ptr<PBD::Controllable>);
void remove_controllable (PBD::Controllable*);

View file

@ -68,6 +68,12 @@ MidiControlUI::do_request (MidiUIRequest* req)
} else if (req->type == CallSlot) {
#ifndef NDEBUG
if (getenv ("DEBUG_THREADED_SIGNALS")) {
cerr << "MIDI UI calls a slot\n";
}
#endif
req->the_slot ();
} else if (req->type == Quit) {
@ -149,6 +155,12 @@ MidiControlUI::thread_init ()
{
struct sched_param rtparam;
char* c = new char[7];
strcpy (c, X_("midiUI"));
pthread_set_name (c);
cerr << "MIDI UI running\n";
PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self(), X_("MIDI"), 2048);
SessionEvent::create_per_thread_pool (X_("MIDI I/O"), 128);

View file

@ -2678,6 +2678,41 @@ Session::controllable_by_id (const PBD::ID& id)
return boost::shared_ptr<Controllable>();
}
boost::shared_ptr<Controllable>
Session::controllable_by_uri (const std::string& uri)
{
boost::shared_ptr<Controllable> c;
string::size_type last_slash;
string useful_part;
if ((last_slash = uri.find_last_of ('/')) == string::npos) {
return c;
}
useful_part = uri.substr (last_slash+1);
uint32_t rid;
char what[64];
if (sscanf (useful_part.c_str(), "rid=%" PRIu32 "?%63s", &rid, what) != 2) {
return c;
}
boost::shared_ptr<Route> r = route_by_remote_id (rid);
if (!r) {
return c;
}
if (strncmp (what, "gain", 4) == 0) {
c = r->gain_control ();
} else if (strncmp (what, "pan", 3) == 0) {
} else if (strncmp (what, "plugin", 6) == 0) {
}
return c;
}
void
Session::add_instant_xml (XMLNode& node, bool write_to_config)
{

View file

@ -25,6 +25,7 @@
#include <cstring>
#include "pbd/base_ui.h"
#include "pbd/pthread_utils.h"
#include "pbd/error.h"
#include "pbd/compose.h"
#include "pbd/failed_constructor.h"

View file

@ -34,9 +34,11 @@
#include "generic_midi_control_protocol.h"
#include "midicontrollable.h"
#include "midifunction.h"
using namespace ARDOUR;
using namespace PBD;
using namespace std;
#include "i18n.h"
@ -44,7 +46,7 @@ using namespace PBD;
#define ui_bind(x) boost::protect (boost::bind ((x)))
GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
: ControlProtocol (s, _("Generic MIDI"), MidiControlUI::instance())
: ControlProtocol (s, _("Generic MIDI"), midi_ui_context())
{
MIDI::Manager* mm = MIDI::Manager::instance();
@ -71,10 +73,32 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
Controllable::DeleteBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding, this, _1));
Session::SendFeedback.connect (*this, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
std::string xmlpath = "/tmp/midi.map";
load_bindings (xmlpath);
reset_controllables ();
}
GenericMidiControlProtocol::~GenericMidiControlProtocol ()
{
Glib::Mutex::Lock lm (pending_lock);
Glib::Mutex::Lock lm2 (controllables_lock);
for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
delete *i;
}
controllables.clear ();
for (MIDIPendingControllables::iterator i = pending_controllables.begin(); i != pending_controllables.end(); ++i) {
delete *i;
}
pending_controllables.clear ();
for (MIDIFunctions::iterator i = functions.begin(); i != functions.end(); ++i) {
delete *i;
}
functions.clear ();
}
int
@ -136,6 +160,9 @@ GenericMidiControlProtocol::start_learning (Controllable* c)
return false;
}
Glib::Mutex::Lock lm (pending_lock);
Glib::Mutex::Lock lm2 (controllables_lock);
MIDIControllables::iterator tmp;
for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ) {
tmp = i;
@ -392,3 +419,167 @@ GenericMidiControlProtocol::get_feedback () const
return do_feedback;
}
int
GenericMidiControlProtocol::load_bindings (const string& xmlpath)
{
XMLTree state_tree;
if (!state_tree.read (xmlpath.c_str())) {
error << string_compose(_("Could not understand MIDI bindings file %1"), xmlpath) << endmsg;
return -1;
}
XMLNode* root = state_tree.root();
if (root->name() != X_("ArdourMIDIBindings")) {
error << string_compose (_("MIDI Bindings file %1 is not really a MIDI bindings file"), xmlpath) << endmsg;
return -1;
}
const XMLProperty* prop;
if ((prop = root->property ("version")) == 0) {
return -1;
} else {
int major;
int minor;
int micro;
sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, &micro);
Stateful::loading_state_version = (major * 1000) + minor;
}
const XMLNodeList& children (root->children());
XMLNodeConstIterator citer;
XMLNodeConstIterator gciter;
MIDIControllable* mc;
for (citer = children.begin(); citer != children.end(); ++citer) {
if ((*citer)->name() == "Binding") {
const XMLNode* child = *citer;
if (child->property ("uri")) {
/* controllable */
if ((mc = create_binding (*child)) != 0) {
Glib::Mutex::Lock lm2 (controllables_lock);
controllables.insert (mc);
}
} else if (child->property ("function")) {
/* function */
MIDIFunction* mf;
if ((mf = create_function (*child)) != 0) {
functions.push_back (mf);
}
}
}
}
return 0;
}
MIDIControllable*
GenericMidiControlProtocol::create_binding (const XMLNode& node)
{
const XMLProperty* prop;
int detail;
int channel;
string uri;
MIDI::eventType ev;
if ((prop = node.property (X_("channel"))) == 0) {
return 0;
}
if (sscanf (prop->value().c_str(), "%d", &channel) != 1) {
return 0;
}
if ((prop = node.property (X_("ctl"))) != 0) {
ev = MIDI::controller;
} else if ((prop = node.property (X_("note"))) != 0) {
ev = MIDI::on;
} else if ((prop = node.property (X_("pgm"))) != 0) {
ev = MIDI::program;
} else {
return 0;
}
if (sscanf (prop->value().c_str(), "%d", &detail) != 1) {
return 0;
}
prop = node.property (X_("uri"));
uri = prop->value();
MIDIControllable* mc = new MIDIControllable (*_port, uri, false);
mc->bind_midi (channel, ev, detail);
cerr << "New MC with URI = " << uri << endl;
return mc;
}
void
GenericMidiControlProtocol::reset_controllables ()
{
Glib::Mutex::Lock lm2 (controllables_lock);
for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
MIDIControllable* existingBinding = (*iter);
boost::shared_ptr<Controllable> c = session->controllable_by_uri (existingBinding->current_uri());
existingBinding->set_controllable (c.get());
}
}
MIDIFunction*
GenericMidiControlProtocol::create_function (const XMLNode& node)
{
const XMLProperty* prop;
int detail;
int channel;
string uri;
MIDI::eventType ev;
if ((prop = node.property (X_("channel"))) == 0) {
return 0;
}
if (sscanf (prop->value().c_str(), "%d", &channel) != 1) {
return 0;
}
if ((prop = node.property (X_("ctl"))) != 0) {
ev = MIDI::controller;
} else if ((prop = node.property (X_("note"))) != 0) {
ev = MIDI::on;
} else if ((prop = node.property (X_("pgm"))) != 0) {
ev = MIDI::program;
} else {
return 0;
}
if (sscanf (prop->value().c_str(), "%d", &detail) != 1) {
return 0;
}
prop = node.property (X_("function"));
MIDIFunction* mf = new MIDIFunction (*_port);
if (mf->init (*this, prop->value())) {
delete mf;
return 0;
}
mf->bind_midi (channel, ev, detail);
cerr << "New MF with function = " << prop->value() << endl;
return mf;
}

View file

@ -1,7 +1,27 @@
/*
Copyright (C) 2006 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef ardour_generic_midi_control_protocol_h
#define ardour_generic_midi_control_protocol_h
#include <set>
#include <list>
#include <glibmm/thread.h>
#include "ardour/types.h"
@ -20,6 +40,7 @@ namespace ARDOUR {
}
class MIDIControllable;
class MIDIFunction;
class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
public:
@ -50,6 +71,9 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
typedef std::set<MIDIControllable*> MIDIControllables;
MIDIControllables controllables;
typedef std::list<MIDIFunction*> MIDIFunctions;
MIDIFunctions functions;
typedef std::pair<MIDIControllable*,PBD::Connection> MIDIPendingControllable;
typedef std::list<MIDIPendingControllable* > MIDIPendingControllables;
MIDIPendingControllables pending_controllables;
@ -63,6 +87,12 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
void create_binding (PBD::Controllable*, int, int);
void delete_binding (PBD::Controllable*);
int load_bindings (const std::string&);
MIDIControllable* create_binding (const XMLNode&);
MIDIFunction* create_function (const XMLNode&);
void reset_controllables ();
};
#endif /* ardour_generic_midi_control_protocol_h */

View file

@ -91,13 +91,9 @@ MIDIControllable::drop_external_control ()
}
void
MIDIControllable::reacquire_controllable ()
MIDIControllable::set_controllable (Controllable* c)
{
if (!_current_uri.empty()) {
controllable = Controllable::by_uri (_current_uri);
} else {
controllable = 0;
}
controllable = c;
}
void

View file

@ -45,6 +45,8 @@ class MIDIControllable : public PBD::Stateful
MIDIControllable (MIDI::Port&, const std::string& uri, bool bistate = false);
virtual ~MIDIControllable ();
void rediscover_controllable ();
bool ok() const { return !_current_uri.empty(); }
void send_feedback ();
@ -64,6 +66,8 @@ class MIDIControllable : public PBD::Stateful
MIDI::Port& get_port() const { return _port; }
PBD::Controllable* get_controllable() const { return controllable; }
void set_controllable (PBD::Controllable*);
const std::string& current_uri() const { return _current_uri; }
std::string control_description() const { return _control_description; }
@ -92,7 +96,6 @@ class MIDIControllable : public PBD::Stateful
bool feedback;
void init ();
void reacquire_controllable ();
void midi_receiver (MIDI::Parser &p, MIDI::byte *, size_t);
void midi_sense_note (MIDI::Parser &, MIDI::EventTwoBytes *, bool is_on);

View file

@ -24,6 +24,7 @@ def build(bld):
generic_midi_control_protocol.cc
interface.cc
midicontrollable.cc
midifunction.cc
'''
obj.export_incdirs = ['.']
obj.cxxflags = '-DPACKAGE="ardour_genericmidi"'