mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-16 19:56:31 +01:00
convert GenericMIDI into a real control protocol module, with its own event loop and ports
This commit is contained in:
parent
1ced4067aa
commit
d100c92731
2 changed files with 202 additions and 41 deletions
|
|
@ -41,6 +41,7 @@
|
|||
#include "pbd/error.h"
|
||||
#include "pbd/failed_constructor.h"
|
||||
#include "pbd/file_utils.h"
|
||||
#include "pbd/i18n.h"
|
||||
#include "pbd/strsplit.h"
|
||||
#include "pbd/types_convert.h"
|
||||
#include "pbd/xml++.h"
|
||||
|
|
@ -63,37 +64,47 @@
|
|||
#include "midifunction.h"
|
||||
#include "midiaction.h"
|
||||
|
||||
#include "pbd/abstract_ui.cc" // instantiate template
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
using namespace Glib;
|
||||
using namespace std;
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
#define midi_ui_context() MidiControlUI::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
|
||||
|
||||
GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
|
||||
: ControlProtocol (s, _("Generic MIDI"))
|
||||
, AbstractUI<GenericMIDIRequest> (name())
|
||||
, connection_state (ConnectionState (0))
|
||||
, _motorised (false)
|
||||
, _threshold (10)
|
||||
, gui (0)
|
||||
{
|
||||
// _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort> (s.midi_input_port ());
|
||||
// _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort> (s.midi_output_port ());
|
||||
boost::shared_ptr<ARDOUR::Port> inp;
|
||||
boost::shared_ptr<ARDOUR::Port> outp;
|
||||
|
||||
inp = AudioEngine::instance()->register_input_port (DataType::MIDI, _("MIDI Control In"), true);
|
||||
outp = AudioEngine::instance()->register_output_port (DataType::MIDI, _("MIDI Control Out"), true);
|
||||
|
||||
if (inp == 0 || outp == 0) {
|
||||
throw failed_constructor();
|
||||
}
|
||||
|
||||
_input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(inp);
|
||||
_output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(outp);
|
||||
|
||||
_input_bundle.reset (new ARDOUR::Bundle (_("Generic MIDI Control In"), true));
|
||||
_output_bundle.reset (new ARDOUR::Bundle (_("Generic MIDI Control Out"), false));
|
||||
|
||||
_input_bundle->add_channel (
|
||||
boost::static_pointer_cast<MidiPort>(_input_port)->name(),
|
||||
inp->name(),
|
||||
ARDOUR::DataType::MIDI,
|
||||
session->engine().make_port_name_non_relative (boost::static_pointer_cast<MidiPort>(_input_port)->name())
|
||||
session->engine().make_port_name_non_relative (inp->name())
|
||||
);
|
||||
|
||||
_output_bundle->add_channel (
|
||||
boost::static_pointer_cast<MidiPort>(_output_port)->name(),
|
||||
outp->name(),
|
||||
ARDOUR::DataType::MIDI,
|
||||
session->engine().make_port_name_non_relative (boost::static_pointer_cast<MidiPort>(_output_port)->name())
|
||||
session->engine().make_port_name_non_relative (outp->name())
|
||||
);
|
||||
|
||||
session->BundleAddedOrRemoved ();
|
||||
|
|
@ -105,7 +116,7 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
|
|||
_current_bank = 0;
|
||||
_bank_size = 0;
|
||||
|
||||
/* these signals are emitted by the MidiControlUI's event loop thread
|
||||
/* these signals are emitted by our event loop thread
|
||||
* and we may as well handle them right there in the same the same
|
||||
* thread
|
||||
*/
|
||||
|
|
@ -119,22 +130,36 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
|
|||
*/
|
||||
|
||||
Session::SendFeedback.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::send_feedback, this));
|
||||
//Session::SendFeedback.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::send_feedback, this), midi_ui_context());;
|
||||
|
||||
/* this one is cross-thread */
|
||||
|
||||
PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), midi_ui_context());
|
||||
PresentationInfo::Change.connect (*this, MISSING_INVALIDATOR, boost::bind (&GenericMidiControlProtocol::reset_controllables, this), this);
|
||||
|
||||
/* Catch port connections and disconnections (cross-thread) */
|
||||
ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR,
|
||||
boost::bind (&GenericMidiControlProtocol::connection_handler, this, _1, _2, _3, _4, _5),
|
||||
midi_ui_context());
|
||||
this);
|
||||
|
||||
reload_maps ();
|
||||
}
|
||||
|
||||
GenericMidiControlProtocol::~GenericMidiControlProtocol ()
|
||||
{
|
||||
if (_input_port) {
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("unregistering input port %1\n", boost::shared_ptr<ARDOUR::Port>(_input_port)->name()));
|
||||
Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
|
||||
AudioEngine::instance()->unregister_port (_input_port);
|
||||
_input_port.reset ();
|
||||
}
|
||||
|
||||
if (_output_port) {
|
||||
_output_port->drain (10000, 250000); /* check every 10 msecs, wait up to 1/4 second for the port to drain */
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("unregistering output port %1\n", boost::shared_ptr<ARDOUR::Port>(_output_port)->name()));
|
||||
Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
|
||||
AudioEngine::instance()->unregister_port (_output_port);
|
||||
_output_port.reset ();
|
||||
}
|
||||
|
||||
drop_all ();
|
||||
tear_down_gui ();
|
||||
}
|
||||
|
|
@ -279,12 +304,57 @@ GenericMidiControlProtocol::drop_bindings ()
|
|||
_current_bank = 0;
|
||||
}
|
||||
|
||||
int
|
||||
GenericMidiControlProtocol::set_active (bool /*yn*/)
|
||||
void
|
||||
GenericMidiControlProtocol::do_request (GenericMIDIRequest* req)
|
||||
{
|
||||
/* nothing to do here: the MIDI UI thread in libardour handles all our
|
||||
I/O needs.
|
||||
*/
|
||||
if (req->type == CallSlot) {
|
||||
|
||||
call_slot (MISSING_INVALIDATOR, req->the_slot);
|
||||
|
||||
} else if (req->type == Quit) {
|
||||
|
||||
stop ();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
GenericMidiControlProtocol::stop ()
|
||||
{
|
||||
BaseUI::quit ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
GenericMidiControlProtocol::thread_init ()
|
||||
{
|
||||
pthread_set_name (event_loop_name().c_str());
|
||||
|
||||
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
|
||||
ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
|
||||
|
||||
set_thread_priority ();
|
||||
}
|
||||
|
||||
int
|
||||
GenericMidiControlProtocol::set_active (bool yn)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose("GenericMIDI::set_active init with yn: '%1'\n", yn));
|
||||
|
||||
if (yn == active()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (yn) {
|
||||
BaseUI::run ();
|
||||
} else {
|
||||
BaseUI::quit ();
|
||||
}
|
||||
|
||||
ControlProtocol::set_active (yn);
|
||||
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose("GenericMIDI::set_active done with yn: '%1'\n", yn));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -539,6 +609,17 @@ GenericMidiControlProtocol::get_state ()
|
|||
{
|
||||
XMLNode& node (ControlProtocol::get_state());
|
||||
|
||||
|
||||
XMLNode* child;
|
||||
|
||||
child = new XMLNode (X_("Input"));
|
||||
child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_input_port)->get_state());
|
||||
node.add_child_nocopy (*child);
|
||||
|
||||
child = new XMLNode (X_("Output"));
|
||||
child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_output_port)->get_state());
|
||||
node.add_child_nocopy (*child);
|
||||
|
||||
node.set_property (X_("feedback-interval"), _feedback_interval);
|
||||
node.set_property (X_("threshold"), _threshold);
|
||||
node.set_property (X_("motorized"), _motorised);
|
||||
|
|
@ -572,11 +653,26 @@ GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
|
|||
{
|
||||
XMLNodeList nlist;
|
||||
XMLNodeConstIterator niter;
|
||||
XMLNode const* child;
|
||||
|
||||
if (ControlProtocol::set_state (node, version)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((child = node.child (X_("Input"))) != 0) {
|
||||
XMLNode* portnode = child->child (Port::state_node_name.c_str());
|
||||
if (portnode) {
|
||||
boost::shared_ptr<ARDOUR::Port>(_input_port)->set_state (*portnode, version);
|
||||
}
|
||||
}
|
||||
|
||||
if ((child = node.child (X_("Output"))) != 0) {
|
||||
XMLNode* portnode = child->child (Port::state_node_name.c_str());
|
||||
if (portnode) {
|
||||
boost::shared_ptr<ARDOUR::Port>(_output_port)->set_state (*portnode, version);
|
||||
}
|
||||
}
|
||||
|
||||
if (!node.get_property ("feedback-interval", _feedback_interval)) {
|
||||
_feedback_interval = 10000;
|
||||
}
|
||||
|
|
@ -623,15 +719,18 @@ GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
|
|||
|
||||
if (load_dynamic_bindings) {
|
||||
Glib::Threads::Mutex::Lock lm2 (controllables_lock);
|
||||
nlist = node.children(); // "Controls"
|
||||
XMLNode* controls_node = node.child (X_("Controls"));
|
||||
|
||||
if (controls_node) {
|
||||
|
||||
nlist = controls_node->children();
|
||||
|
||||
if (!nlist.empty()) {
|
||||
nlist = nlist.front()->children(); // "MIDIControllable" ...
|
||||
|
||||
if (!nlist.empty()) {
|
||||
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
||||
|
||||
PBD::ID id;
|
||||
|
||||
if ((*niter)->get_property ("id", id)) {
|
||||
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Relearned binding for session: Control ID: %1\n", id.to_s()));
|
||||
|
|
@ -1450,10 +1549,14 @@ GenericMidiControlProtocol::set_threshold (int t)
|
|||
bool
|
||||
GenericMidiControlProtocol::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
|
||||
{
|
||||
bool input_was_connected = (connection_state & InputConnected);
|
||||
|
||||
if (!_input_port || !_output_port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("connection change: %1 and %2 connected ? %3\n", name1, name2, yn));
|
||||
|
||||
string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_input_port)->name());
|
||||
string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
|
||||
|
||||
|
|
@ -1474,18 +1577,14 @@ GenericMidiControlProtocol::connection_handler (boost::weak_ptr<ARDOUR::Port>, s
|
|||
return false;
|
||||
}
|
||||
|
||||
if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
|
||||
|
||||
/* XXX this is a horrible hack. Without a short sleep here,
|
||||
something prevents the device wakeup messages from being
|
||||
sent and/or the responses from being received.
|
||||
*/
|
||||
|
||||
g_usleep (100000);
|
||||
connected ();
|
||||
|
||||
if (connection_state & InputConnected) {
|
||||
if (!input_was_connected) {
|
||||
start_midi_handling ();
|
||||
}
|
||||
} else {
|
||||
|
||||
if (input_was_connected) {
|
||||
stop_midi_handling ();
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionChange (); /* emit signal for our GUI */
|
||||
|
|
@ -1493,11 +1592,6 @@ GenericMidiControlProtocol::connection_handler (boost::weak_ptr<ARDOUR::Port>, s
|
|||
return true; /* connection status changed */
|
||||
}
|
||||
|
||||
void
|
||||
GenericMidiControlProtocol::connected ()
|
||||
{
|
||||
}
|
||||
|
||||
boost::shared_ptr<Port>
|
||||
GenericMidiControlProtocol::output_port() const
|
||||
{
|
||||
|
|
@ -1518,3 +1612,52 @@ GenericMidiControlProtocol::maybe_start_touch (boost::shared_ptr<Controllable> c
|
|||
actl->start_touch (session->audible_sample ());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GenericMidiControlProtocol::start_midi_handling ()
|
||||
{
|
||||
/* This connection means that whenever data is ready from the input
|
||||
* port, the relevant thread will invoke our ::midi_input_handler()
|
||||
* method, which will read the data, and invoke the parser.
|
||||
*/
|
||||
|
||||
_input_port->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &GenericMidiControlProtocol::midi_input_handler), boost::weak_ptr<AsyncMIDIPort> (_input_port)));
|
||||
_input_port->xthread().attach (main_loop()->get_context());
|
||||
}
|
||||
|
||||
void
|
||||
GenericMidiControlProtocol::stop_midi_handling ()
|
||||
{
|
||||
midi_connections.drop_connections ();
|
||||
|
||||
/* Note: the input handler is still active at this point, but we're no
|
||||
* longer connected to any of the parser signals
|
||||
*/
|
||||
}
|
||||
|
||||
bool
|
||||
GenericMidiControlProtocol::midi_input_handler (Glib::IOCondition ioc, boost::weak_ptr<ARDOUR::AsyncMIDIPort> wport)
|
||||
{
|
||||
boost::shared_ptr<AsyncMIDIPort> port (wport.lock());
|
||||
|
||||
if (!port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("something happend on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
|
||||
|
||||
if (ioc & ~IO_IN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ioc & IO_IN) {
|
||||
|
||||
port->clear ();
|
||||
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("data available on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
|
||||
samplepos_t now = session->engine().sample_time();
|
||||
port->parse (now);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@
|
|||
#include <list>
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#define ABSTRACT_UI_EXPORTS
|
||||
#include "pbd/abstract_ui.h"
|
||||
|
||||
#include "ardour/types.h"
|
||||
#include "ardour/port.h"
|
||||
|
||||
|
|
@ -48,11 +51,23 @@ class MIDIControllable;
|
|||
class MIDIFunction;
|
||||
class MIDIAction;
|
||||
|
||||
class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
|
||||
struct GenericMIDIRequest : public BaseUI::BaseRequestObject {
|
||||
public:
|
||||
GenericMIDIRequest () {}
|
||||
~GenericMIDIRequest () {}
|
||||
};
|
||||
|
||||
|
||||
class GenericMidiControlProtocol : public ARDOUR::ControlProtocol, public AbstractUI<GenericMIDIRequest> {
|
||||
public:
|
||||
GenericMidiControlProtocol (ARDOUR::Session&);
|
||||
virtual ~GenericMidiControlProtocol();
|
||||
|
||||
void do_request (GenericMIDIRequest*);
|
||||
int stop ();
|
||||
|
||||
void thread_init ();
|
||||
|
||||
int set_active (bool yn);
|
||||
static bool probe() { return true; }
|
||||
|
||||
|
|
@ -169,7 +184,6 @@ private:
|
|||
int connection_state;
|
||||
bool connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn);
|
||||
PBD::ScopedConnection port_connection;
|
||||
void connected();
|
||||
|
||||
std::string _current_binding;
|
||||
uint32_t _bank_size;
|
||||
|
|
@ -185,7 +199,11 @@ private:
|
|||
mutable void *gui;
|
||||
void build_gui ();
|
||||
|
||||
PBD::ScopedConnectionList midi_connections;
|
||||
|
||||
bool midi_input_handler (Glib::IOCondition ioc, boost::weak_ptr<ARDOUR::AsyncMIDIPort> port);
|
||||
void start_midi_handling ();
|
||||
void stop_midi_handling ();
|
||||
};
|
||||
|
||||
#endif /* ardour_generic_midi_control_protocol_h */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue