mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-08 15:54:57 +01:00
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:
parent
cba3ca64b3
commit
109acd1568
9 changed files with 279 additions and 9 deletions
|
|
@ -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*);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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, µ);
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue