13
0

add support for "msg=" bindings and also action="SomeGroup/Action"

git-svn-id: svn://localhost/ardour2/branches/3.0@6876 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2010-04-08 21:05:55 +00:00
parent 5970212b87
commit 77c09fc824
9 changed files with 526 additions and 157 deletions

View File

@ -41,6 +41,7 @@
#include "generic_midi_control_protocol.h"
#include "midicontrollable.h"
#include "midifunction.h"
#include "midiaction.h"
using namespace ARDOUR;
using namespace PBD;
@ -193,6 +194,11 @@ GenericMidiControlProtocol::drop_all ()
delete *i;
}
functions.clear ();
for (MIDIActions::iterator i = actions.begin(); i != actions.end(); ++i) {
delete *i;
}
actions.clear ();
}
void
@ -640,7 +646,14 @@ GenericMidiControlProtocol::load_bindings (const string& xmlpath)
if ((mf = create_function (*child)) != 0) {
functions.push_back (mf);
}
}
} else if (child->property ("action")) {
MIDIAction* ma;
if ((ma = create_action (*child)) != 0) {
actions.push_back (ma);
}
}
}
}
@ -744,8 +757,8 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
MIDI::channel_t channel = 0;
string uri;
MIDI::eventType ev;
MIDI::byte* sysex = 0;
uint32_t sysex_size = 0;
MIDI::byte* data = 0;
uint32_t data_size = 0;
if ((prop = node.property (X_("ctl"))) != 0) {
ev = MIDI::controller;
@ -753,9 +766,14 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
ev = MIDI::on;
} else if ((prop = node.property (X_("pgm"))) != 0) {
ev = MIDI::program;
} else if ((prop = node.property (X_("sysex"))) != 0) {
} else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
if (prop->name() == X_("sysex")) {
ev = MIDI::sysex;
} else {
ev = MIDI::any;
}
ev = MIDI::sysex;
int val;
uint32_t cnt;
@ -773,8 +791,8 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
return 0;
}
sysex = new MIDI::byte[cnt];
sysex_size = cnt;
data = new MIDI::byte[cnt];
data_size = cnt;
{
stringstream ss (prop->value());
@ -782,16 +800,16 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
cnt = 0;
while (ss >> val) {
sysex[cnt++] = (MIDI::byte) val;
data[cnt++] = (MIDI::byte) val;
}
}
} else {
warning << "Binding ignored - unknown type" << endmsg;
return 0;
}
if (sysex_size == 0) {
if (data_size == 0) {
if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
return 0;
}
@ -816,7 +834,7 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
MIDIFunction* mf = new MIDIFunction (*_port);
if (mf->init (*this, prop->value(), sysex, sysex_size)) {
if (mf->init (*this, prop->value(), data, data_size)) {
delete mf;
return 0;
}
@ -826,6 +844,102 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
return mf;
}
MIDIAction*
GenericMidiControlProtocol::create_action (const XMLNode& node)
{
const XMLProperty* prop;
int intval;
MIDI::byte detail = 0;
MIDI::channel_t channel = 0;
string uri;
MIDI::eventType ev;
MIDI::byte* data = 0;
uint32_t data_size = 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 if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
if (prop->name() == X_("sysex")) {
ev = MIDI::sysex;
} else {
ev = MIDI::any;
}
int val;
uint32_t cnt;
{
cnt = 0;
stringstream ss (prop->value());
ss << hex;
while (ss >> val) {
cnt++;
}
}
if (cnt == 0) {
return 0;
}
data = new MIDI::byte[cnt];
data_size = cnt;
{
stringstream ss (prop->value());
ss << hex;
cnt = 0;
while (ss >> val) {
data[cnt++] = (MIDI::byte) val;
}
}
} else {
warning << "Binding ignored - unknown type" << endmsg;
return 0;
}
if (data_size == 0) {
if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
return 0;
}
detail = (MIDI::byte) intval;
if ((prop = node.property (X_("channel"))) == 0) {
return 0;
}
if (sscanf (prop->value().c_str(), "%d", &intval) != 1) {
return 0;
}
channel = (MIDI::channel_t) intval;
/* adjust channel to zero-based counting */
if (channel > 0) {
channel -= 1;
}
}
prop = node.property (X_("action"));
MIDIAction* ma = new MIDIAction (*_port);
if (ma->init (*this, prop->value(), data, data_size)) {
delete ma;
return 0;
}
ma->bind_midi (channel, ev, detail);
return ma;
}
void
GenericMidiControlProtocol::set_current_bank (uint32_t b)
{

View File

@ -40,6 +40,7 @@ namespace ARDOUR {
class MIDIControllable;
class MIDIFunction;
class MIDIAction;
class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
public:
@ -94,6 +95,9 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
typedef std::list<MIDIFunction*> MIDIFunctions;
MIDIFunctions functions;
typedef std::list<MIDIAction*> MIDIActions;
MIDIActions actions;
typedef std::pair<MIDIControllable*,PBD::ScopedConnection> MIDIPendingControllable;
typedef std::list<MIDIPendingControllable* > MIDIPendingControllables;
MIDIPendingControllables pending_controllables;
@ -110,6 +114,7 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
MIDIControllable* create_binding (const XMLNode&);
MIDIFunction* create_function (const XMLNode&);
MIDIAction* create_action (const XMLNode&);
void reset_controllables ();
void drop_all ();

View File

@ -0,0 +1,62 @@
/*
Copyright (C) 2009-2010 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.
*/
#include <cstring>
#include "midi++/port.h"
#include "midiaction.h"
#include "generic_midi_control_protocol.h"
using namespace MIDI;
MIDIAction::MIDIAction (MIDI::Port& p)
: MIDIInvokable (p)
{
}
MIDIAction::~MIDIAction ()
{
}
int
MIDIAction::init (GenericMidiControlProtocol& ui, const std::string& invokable_name, MIDI::byte* msg_data, size_t data_sz)
{
MIDIInvokable::init (ui, invokable_name, msg_data, data_sz);
return 0;
}
void
MIDIAction::execute ()
{
_ui->access_action (_invokable_name);
}
XMLNode&
MIDIAction::get_state ()
{
XMLNode* node = new XMLNode ("MIDIAction");
return *node;
}
int
MIDIAction::set_state (const XMLNode& node, int version)
{
return 0;
}

View File

@ -0,0 +1,65 @@
/*
Copyright (C) 2009 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 __gm_midiaction_h__
#define __gm_midiaction_h__
#include <string>
#include "midi++/types.h"
#include "pbd/signals.h"
#include "pbd/stateful.h"
#include "ardour/types.h"
#include "midiinvokable.h"
namespace Gtk {
class Action;
}
namespace MIDI {
class Channel;
class Port;
class Parser;
}
class GenericMidiControlProtocol;
class MIDIAction : public MIDIInvokable
{
public:
MIDIAction (MIDI::Port&);
virtual ~MIDIAction ();
int init (GenericMidiControlProtocol&, const std::string& action_name, MIDI::byte* sysex = 0, size_t ssize = 0);
const std::string& action_name() const { return _invokable_name; }
XMLNode& get_state (void);
int set_state (const XMLNode&, int version);
private:
Gtk::Action* _action;
void execute ();
};
#endif // __gm_midicontrollable_h__

View File

@ -26,52 +26,43 @@
using namespace MIDI;
MIDIFunction::MIDIFunction (MIDI::Port& p)
: _port (p)
: MIDIInvokable (p)
{
sysex_size = 0;
sysex = 0;
}
MIDIFunction::~MIDIFunction ()
{
delete [] sysex;
}
int
MIDIFunction::init (GenericMidiControlProtocol& ui, const std::string& function_name, MIDI::byte* sysex_data, size_t sysex_sz)
MIDIFunction::init (GenericMidiControlProtocol& ui, const std::string& invokable_name, MIDI::byte* msg_data, size_t data_sz)
{
if (strcasecmp (function_name.c_str(), "transport-stop") == 0) {
MIDIInvokable::init (ui, invokable_name, msg_data, data_sz);
if (strcasecmp (_invokable_name.c_str(), "transport-stop") == 0) {
_function = TransportStop;
} else if (strcasecmp (function_name.c_str(), "transport-roll") == 0) {
} else if (strcasecmp (_invokable_name.c_str(), "transport-roll") == 0) {
_function = TransportRoll;
} else if (strcasecmp (function_name.c_str(), "transport-zero") == 0) {
} else if (strcasecmp (_invokable_name.c_str(), "transport-zero") == 0) {
_function = TransportZero;
} else if (strcasecmp (function_name.c_str(), "transport-start") == 0) {
} else if (strcasecmp (_invokable_name.c_str(), "transport-start") == 0) {
_function = TransportStart;
} else if (strcasecmp (function_name.c_str(), "transport-end") == 0) {
} else if (strcasecmp (_invokable_name.c_str(), "transport-end") == 0) {
_function = TransportEnd;
} else if (strcasecmp (function_name.c_str(), "loop-toggle") == 0) {
} else if (strcasecmp (_invokable_name.c_str(), "loop-toggle") == 0) {
_function = TransportLoopToggle;
} else if (strcasecmp (function_name.c_str(), "rec-enable") == 0) {
} else if (strcasecmp (_invokable_name.c_str(), "rec-enable") == 0) {
_function = TransportRecordEnable;
} else if (strcasecmp (function_name.c_str(), "rec-disable") == 0) {
} else if (strcasecmp (_invokable_name.c_str(), "rec-disable") == 0) {
_function = TransportRecordDisable;
} else if (strcasecmp (function_name.c_str(), "next-bank") == 0) {
} else if (strcasecmp (_invokable_name.c_str(), "next-bank") == 0) {
_function = NextBank;
} else if (strcasecmp (function_name.c_str(), "prev-bank") == 0) {
} else if (strcasecmp (_invokable_name.c_str(), "prev-bank") == 0) {
_function = PrevBank;
} else {
return -1;
}
_ui = &ui;
if (sysex_sz) {
/* we take ownership of the sysex data */
sysex = sysex_data;
sysex_size = sysex_sz;
}
return 0;
}
@ -121,104 +112,6 @@ MIDIFunction::execute ()
}
}
void
MIDIFunction::midi_sense_note_on (Parser &p, EventTwoBytes *tb)
{
midi_sense_note (p, tb, true);
}
void
MIDIFunction::midi_sense_note_off (Parser &p, EventTwoBytes *tb)
{
midi_sense_note (p, tb, false);
}
void
MIDIFunction::midi_sense_note (Parser &, EventTwoBytes *msg, bool /* is_on */)
{
if (msg->note_number == control_additional) {
execute ();
}
}
void
MIDIFunction::midi_sense_controller (Parser &, EventTwoBytes *msg)
{
if (control_additional == msg->controller_number) {
execute ();
}
}
void
MIDIFunction::midi_sense_program_change (Parser &, byte msg)
{
if (msg == control_additional) {
execute ();
}
}
void
MIDIFunction::midi_sense_sysex (Parser &, byte* msg, size_t sz)
{
if (sz != sysex_size) {
return;
}
if (memcmp (msg, sysex, sysex_size) != 0) {
return;
}
execute ();
}
void
MIDIFunction::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
{
midi_sense_connection[0].disconnect ();
midi_sense_connection[1].disconnect ();
control_type = ev;
control_channel = chn;
control_additional = additional;
if (_port.input() == 0) {
return;
}
Parser& p = *_port.input();
int chn_i = chn;
/* incoming MIDI is parsed by Ardour' MidiUI event loop/thread, and we want our handlers to execute in that context, so we use
Signal::connect_same_thread() here.
*/
switch (ev) {
case MIDI::off:
p.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIFunction::midi_sense_note_off, this, _1, _2));
break;
case MIDI::on:
p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIFunction::midi_sense_note_on, this, _1, _2));
break;
case MIDI::controller:
p.channel_controller[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIFunction::midi_sense_controller, this, _1, _2));
break;
case MIDI::program:
p.channel_program_change[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIFunction::midi_sense_program_change, this, _1, _2));
break;
case MIDI::sysex:
p.sysex.connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIFunction::midi_sense_sysex, this, _1, _2, _3));
break;
default:
break;
}
}
XMLNode&
MIDIFunction::get_state ()
{

View File

@ -29,6 +29,8 @@
#include "ardour/types.h"
#include "midiinvokable.h"
namespace MIDI {
class Channel;
class Port;
@ -37,7 +39,7 @@ namespace MIDI {
class GenericMidiControlProtocol;
class MIDIFunction : public PBD::Stateful
class MIDIFunction : public MIDIInvokable
{
public:
enum Function {
@ -58,37 +60,14 @@ class MIDIFunction : public PBD::Stateful
int init (GenericMidiControlProtocol&, const std::string& function_name, MIDI::byte* sysex = 0, size_t ssize = 0);
MIDI::Port& get_port() const { return _port; }
const std::string& function_name() const { return _function_name; }
const std::string& function_name() const { return _invokable_name; }
XMLNode& get_state (void);
int set_state (const XMLNode&, int version);
void bind_midi (MIDI::channel_t, MIDI::eventType, MIDI::byte);
MIDI::channel_t get_control_channel () { return control_channel; }
MIDI::eventType get_control_type () { return control_type; }
MIDI::byte get_control_additional () { return control_additional; }
private:
Function _function;
GenericMidiControlProtocol* _ui;
std::string _function_name;
MIDI::Port& _port;
PBD::ScopedConnection midi_sense_connection[2];
MIDI::eventType control_type;
MIDI::byte control_additional;
MIDI::channel_t control_channel;
MIDI::byte* sysex;
size_t sysex_size;
void execute ();
void midi_sense_note (MIDI::Parser &, MIDI::EventTwoBytes *, bool is_on);
void midi_sense_note_on (MIDI::Parser &p, MIDI::EventTwoBytes *tb);
void midi_sense_note_off (MIDI::Parser &p, MIDI::EventTwoBytes *tb);
void midi_sense_controller (MIDI::Parser &, MIDI::EventTwoBytes *);
void midi_sense_program_change (MIDI::Parser &, MIDI::byte);
void midi_sense_sysex (MIDI::Parser &, MIDI::byte*, size_t);
};
#endif // __gm_midicontrollable_h__

View File

@ -0,0 +1,171 @@
/*
Copyright (C) 2009 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.
*/
#include <cstring>
#include "midi++/port.h"
#include "midifunction.h"
#include "generic_midi_control_protocol.h"
using namespace MIDI;
MIDIInvokable::MIDIInvokable (MIDI::Port& p)
: _port (p)
{
data_size = 0;
data = 0;
}
MIDIInvokable::~MIDIInvokable ()
{
delete [] data;
}
int
MIDIInvokable::init (GenericMidiControlProtocol& ui, const std::string& name, MIDI::byte* msg_data, size_t data_sz)
{
_ui = &ui;
_invokable_name = name;
if (data_sz) {
/* we take ownership of the sysex data */
data = msg_data;
data_size = data_sz;
}
return 0;
}
void
MIDIInvokable::midi_sense_note_on (Parser &p, EventTwoBytes *tb)
{
midi_sense_note (p, tb, true);
}
void
MIDIInvokable::midi_sense_note_off (Parser &p, EventTwoBytes *tb)
{
midi_sense_note (p, tb, false);
}
void
MIDIInvokable::midi_sense_note (Parser &, EventTwoBytes *msg, bool /* is_on */)
{
if (msg->note_number == control_additional) {
execute ();
}
}
void
MIDIInvokable::midi_sense_controller (Parser &, EventTwoBytes *msg)
{
if (control_additional == msg->controller_number) {
execute ();
}
}
void
MIDIInvokable::midi_sense_program_change (Parser &, byte msg)
{
if (msg == control_additional) {
execute ();
}
}
void
MIDIInvokable::midi_sense_sysex (Parser &, byte* msg, size_t sz)
{
if (sz != data_size) {
return;
}
if (memcmp (msg, data, data_size) != 0) {
return;
}
execute ();
}
void
MIDIInvokable::midi_sense_any (Parser &, byte* msg, size_t sz)
{
if (sz != data_size) {
return;
}
if (memcmp (msg, data, data_size) != 0) {
return;
}
execute ();
}
void
MIDIInvokable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
{
midi_sense_connection[0].disconnect ();
midi_sense_connection[1].disconnect ();
control_type = ev;
control_channel = chn;
control_additional = additional;
if (_port.input() == 0) {
return;
}
Parser& p = *_port.input();
int chn_i = chn;
/* incoming MIDI is parsed by Ardour' MidiUI event loop/thread, and we want our handlers to execute in that context, so we use
Signal::connect_same_thread() here.
*/
switch (ev) {
case MIDI::off:
p.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_note_off, this, _1, _2));
break;
case MIDI::on:
p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_note_on, this, _1, _2));
break;
case MIDI::controller:
p.channel_controller[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_controller, this, _1, _2));
break;
case MIDI::program:
p.channel_program_change[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_program_change, this, _1, _2));
break;
case MIDI::sysex:
p.sysex.connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_sysex, this, _1, _2, _3));
break;
case MIDI::any:
p.any.connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIInvokable::midi_sense_any, this, _1, _2, _3));
break;
default:
break;
}
}

View File

@ -0,0 +1,78 @@
/*
Copyright (C) 2009 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 __gm_midiinvokable_h__
#define __gm_midiinvokable_h__
#include <string>
#include "midi++/types.h"
#include "pbd/signals.h"
#include "pbd/stateful.h"
#include "ardour/types.h"
namespace MIDI {
class Channel;
class Port;
class Parser;
}
class GenericMidiControlProtocol;
class MIDIInvokable : public PBD::Stateful
{
public:
MIDIInvokable (MIDI::Port&);
virtual ~MIDIInvokable ();
virtual int init (GenericMidiControlProtocol&, const std::string&, MIDI::byte* data = 0, size_t dsize = 0);
MIDI::Port& get_port() const { return _port; }
void bind_midi (MIDI::channel_t, MIDI::eventType, MIDI::byte);
MIDI::channel_t get_control_channel () { return control_channel; }
MIDI::eventType get_control_type () { return control_type; }
MIDI::byte get_control_additional () { return control_additional; }
protected:
GenericMidiControlProtocol* _ui;
std::string _invokable_name;
MIDI::Port& _port;
PBD::ScopedConnection midi_sense_connection[2];
MIDI::eventType control_type;
MIDI::byte control_additional;
MIDI::channel_t control_channel;
MIDI::byte* data;
size_t data_size;
void midi_sense_note (MIDI::Parser &, MIDI::EventTwoBytes *, bool is_on);
void midi_sense_note_on (MIDI::Parser &p, MIDI::EventTwoBytes *tb);
void midi_sense_note_off (MIDI::Parser &p, MIDI::EventTwoBytes *tb);
void midi_sense_controller (MIDI::Parser &, MIDI::EventTwoBytes *);
void midi_sense_program_change (MIDI::Parser &, MIDI::byte);
void midi_sense_sysex (MIDI::Parser &, MIDI::byte*, size_t);
void midi_sense_any (MIDI::Parser &, MIDI::byte*, size_t);
virtual void execute () = 0;
};
#endif // __gm_midicontrollable_h__

View File

@ -24,8 +24,10 @@ def build(bld):
generic_midi_control_protocol.cc
gmcp_gui.cc
interface.cc
midiinvokable.cc
midicontrollable.cc
midifunction.cc
midiaction.cc
'''
obj.export_incdirs = ['.']
obj.cxxflags = '-DPACKAGE="ardour_genericmidi"'