13
0

track Mackie MIDI port connection status as primary trigger for handshake with device

This commit is contained in:
Paul Davis 2015-10-07 13:17:42 -04:00
parent 7675739f45
commit ce9b271a92
7 changed files with 161 additions and 65 deletions

View File

@ -664,7 +664,7 @@ void
MackieControlProtocolGUI::discover_clicked ()
{
/* this should help to get things started */
_cp.midi_connectivity_established ();
_cp.ping_devices ();
}
void

View File

@ -87,9 +87,9 @@ const int MackieControlProtocol::MODIFIER_CMDALT = 0x8;
const int MackieControlProtocol::MODIFIER_ZOOM = 0x10;
const int MackieControlProtocol::MODIFIER_SCRUB = 0x20;
const int MackieControlProtocol::MAIN_MODIFIER_MASK = (MackieControlProtocol::MODIFIER_OPTION|
MackieControlProtocol::MODIFIER_CONTROL|
MackieControlProtocol::MODIFIER_SHIFT|
MackieControlProtocol::MODIFIER_CMDALT);
MackieControlProtocol::MODIFIER_CONTROL|
MackieControlProtocol::MODIFIER_SHIFT|
MackieControlProtocol::MODIFIER_CMDALT);
MackieControlProtocol* MackieControlProtocol::_instance = 0;
@ -184,16 +184,14 @@ MackieControlProtocol::thread_init ()
}
void
MackieControlProtocol::midi_connectivity_established ()
MackieControlProtocol::ping_devices ()
{
for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) {
(*si)->say_hello ();
}
/* should not be called if surfaces are not connected, but will not
* malfunction if it is.
*/
if (_device_info.no_handshake()) {
for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) {
(*si)->turn_it_on ();
}
for (Surfaces::const_iterator si = surfaces.begin(); si != surfaces.end(); ++si) {
(*si)->connected ();
}
}
@ -725,7 +723,7 @@ gboolean
ArdourSurface::ipmidi_input_handler (GIOChannel*, GIOCondition condition, void *data)
{
ArdourSurface::MackieControlProtocol::ipMIDIHandler* ipm = static_cast<ArdourSurface::MackieControlProtocol::ipMIDIHandler*>(data);
return ipm->mcp->midi_input_handler (Glib::IOCondition (condition), ipm->port);
return ipm->mcp->midi_input_handler (Glib::IOCondition (condition), ipm->port);
}
int
@ -822,24 +820,24 @@ MackieControlProtocol::create_surfaces ()
if ((fd = input_port.selectable ()) >= 0) {
GIOChannel* ioc = g_io_channel_unix_new (fd);
GSource* gsrc = g_io_create_watch (ioc, GIOCondition (G_IO_IN|G_IO_HUP|G_IO_ERR));
GIOChannel* ioc = g_io_channel_unix_new (fd);
GSource* gsrc = g_io_create_watch (ioc, GIOCondition (G_IO_IN|G_IO_HUP|G_IO_ERR));
/* hack up an object so that in the callback from the event loop
we have both the MackieControlProtocol and the input port.
/* hack up an object so that in the callback from the event loop
we have both the MackieControlProtocol and the input port.
If we were using C++ for this stuff we wouldn't need this
but a nasty, not-fixable bug in the binding between C
and C++ makes it necessary to avoid C++ for the IO
callback setup.
*/
If we were using C++ for this stuff we wouldn't need this
but a nasty, not-fixable bug in the binding between C
and C++ makes it necessary to avoid C++ for the IO
callback setup.
*/
ipMIDIHandler* ipm = new ipMIDIHandler (); /* we will leak this sizeof(pointer)*2 sized object */
ipm->mcp = this;
ipm->port = &input_port;
ipMIDIHandler* ipm = new ipMIDIHandler (); /* we will leak this sizeof(pointer)*2 sized object */
ipm->mcp = this;
ipm->port = &input_port;
g_source_set_callback (gsrc, (GSourceFunc) ipmidi_input_handler, ipm, NULL);
g_source_attach (gsrc, main_loop()->get_context()->gobj());
g_source_set_callback (gsrc, (GSourceFunc) ipmidi_input_handler, ipm, NULL);
g_source_attach (gsrc, main_loop()->get_context()->gobj());
}
}
}
@ -1027,10 +1025,10 @@ MackieControlProtocol::update_timecode_display()
void MackieControlProtocol::notify_parameter_changed (std::string const & p)
{
if (p == "punch-in") {
// no such button right now
// no such button right now
// update_global_button (Button::PunchIn, session->config.get_punch_in());
} else if (p == "punch-out") {
// no such button right now
// no such button right now
// update_global_button (Button::PunchOut, session->config.get_punch_out());
} else if (p == "clicking") {
update_global_button (Button::Click, Config->get_clicking());
@ -1314,7 +1312,7 @@ MackieControlProtocol::handle_button_event (Surface& surface, Button& button, Bu
if (action.find ('/') != string::npos) { /* good chance that this is really an action */
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Looked up action for button %1 with modifier %2, got [%3]\n",
button.bid(), _modifier_state, action));
button.bid(), _modifier_state, action));
/* if there is a bound action for this button, and this is a press event,
carry out the action. If its a release event, do nothing since we

View File

@ -220,7 +220,7 @@ class MackieControlProtocol
bool session_load () { return _session_load; }
void not_session_load () { _session_load = false; }
void midi_connectivity_established ();
void ping_devices ();
protected:
// shut down the surface

View File

@ -25,6 +25,7 @@
#include "midi++/port.h"
#include "ardour/audioengine.h"
#include "ardour/automation_control.h"
#include "ardour/debug.h"
#include "ardour/route.h"
@ -88,6 +89,7 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui
, _jog_wheel (0)
, _master_fader (0)
, _last_master_gain_written (-0.0f)
, connection_state (0)
{
DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
@ -120,6 +122,11 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui
DEBUG_TRACE (DEBUG::MackieControl, "init_strips done\n");
}
/*
*/
ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Surface::connection_handler, this, _1, _2, _3, _4, _5), &_mcp);
connect_to_signals ();
DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface done\n");
@ -147,6 +154,60 @@ Surface::~Surface ()
DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface done\n");
}
void
Surface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
{
string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->input_name());
string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (_port->output_name());
if (ni == name1 || ni == name2) {
if (yn) {
connection_state |= InputConnected;
} else {
connection_state &= ~InputConnected;
}
} else if (no == name1 || no == name2) {
if (yn) {
connection_state |= OutputConnected;
} else {
connection_state &= ~OutputConnected;
}
}
if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
/* this will send a device query message, which should
result in a response that will kick off device type
discovery and activation of the surface(s).
The intended order of events is:
- each surface sends a device query message
- devices respond with either MCP or LCP response (sysex in both
cases)
- sysex message causes Surface::turn_it_on() which tells the
MCP object that the surface is ready, and sets up strip
displays and binds faders and buttons for that surface
In the case of LCP, where this is a handshake process that could
fail, the response process to the initial sysex after a device query
will mark the surface inactive, which won't shut anything down
but will stop any writes to the device.
Note: there are no known cases of the handshake process failing.
We actually can't initiate this in this callback, so we have
to queue it with the MCP event loop.
*/
connected ();
} else {
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 disconnected (input or output or both)\n", _name));
_active = false;
}
}
XMLNode&
Surface::get_state()
{
@ -401,7 +462,7 @@ Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uin
*/
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2 (%4)\n",
fader_id, pb, _number, pb/16384.0));
fader_id, pb, _number, pb/16384.0));
if (_mcp.device_info().no_handshake()) {
turn_it_on ();
@ -608,7 +669,6 @@ calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iter
return retval;
}
// not used right now
MidiByteArray
Surface::host_connection_query (MidiByteArray & bytes)
{
@ -634,7 +694,6 @@ Surface::host_connection_query (MidiByteArray & bytes)
return response;
}
// not used right now
MidiByteArray
Surface::host_connection_confirmation (const MidiByteArray & bytes)
{
@ -1102,3 +1161,15 @@ Surface::hui_heartbeat ()
MidiByteArray msg (3, MIDI::on, 0x0, 0x0);
_port->write (msg);
}
void
Surface::connected ()
{
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 now connected, trying to ping device...\n", _name));
say_hello ();
if (_mcp.device_info().no_handshake()) {
turn_it_on ();
}
}

View File

@ -3,6 +3,7 @@
#include <stdint.h>
#include "pbd/signals.h"
#include "pbd/xml++.h"
#include "midi++/types.h"
@ -18,6 +19,7 @@ namespace MIDI {
namespace ARDOUR {
class Route;
class Port;
}
class MidiByteArray;
@ -49,7 +51,7 @@ public:
uint32_t number() const { return _number; }
const std::string& name() { return _name; }
void say_hello ();
void connected ();
bool active() const { return _active; }
@ -174,15 +176,25 @@ public:
Mackie::JogWheel* _jog_wheel;
Fader* _master_fader;
float _last_master_gain_written;
PBD::ScopedConnection port_connection;
void handle_midi_sysex (MIDI::Parser&, MIDI::byte *, size_t count);
MidiByteArray host_connection_query (MidiByteArray& bytes);
MidiByteArray host_connection_confirmation (const MidiByteArray& bytes);
void say_hello ();
void init_controls ();
void init_strips (uint32_t n);
void setup_master ();
void master_gain_changed ();
void connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool);
enum ConnectionState {
InputConnected = 0x1,
OutputConnected = 0x2
};
int connection_state;
};
}

View File

@ -144,6 +144,18 @@ SurfacePort::set_state (const XMLNode& node, int version)
return 0;
}
std::string
SurfacePort::input_name () const
{
return _async_in->name();
}
std::string
SurfacePort::output_name () const
{
return _async_out->name();
}
// wrapper for one day when strerror_r is working properly
string fetch_errmsg (int error_number)
{

View File

@ -1,19 +1,19 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2006,2007 John Anderson
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 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.
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.
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 surface_port_h
#define surface_port_h
@ -47,32 +47,35 @@ namespace Mackie
class Surface;
/**
Make a relationship between a midi port and a Mackie device.
Make a relationship between a midi port and a Mackie device.
*/
class SurfacePort
{
public:
SurfacePort (Mackie::Surface&);
virtual ~SurfacePort();
public:
SurfacePort (Mackie::Surface&);
virtual ~SurfacePort();
/// an easier way to output bytes via midi
int write (const MidiByteArray&);
/// an easier way to output bytes via midi
int write (const MidiByteArray&);
MIDI::Port& input_port() const { return *_input_port; }
MIDI::Port& output_port() const { return *_output_port; }
MIDI::Port& input_port() const { return *_input_port; }
MIDI::Port& output_port() const { return *_output_port; }
XMLNode& get_state ();
int set_state (const XMLNode&, int version);
std::string input_name() const;
std::string output_name() const;
protected:
XMLNode& get_state ();
int set_state (const XMLNode&, int version);
private:
Mackie::Surface* _surface;
MIDI::Port* _input_port;
MIDI::Port* _output_port;
boost::shared_ptr<ARDOUR::Port> _async_in;
boost::shared_ptr<ARDOUR::Port> _async_out;
protected:
private:
Mackie::Surface* _surface;
MIDI::Port* _input_port;
MIDI::Port* _output_port;
boost::shared_ptr<ARDOUR::Port> _async_in;
boost::shared_ptr<ARDOUR::Port> _async_out;
};
std::ostream& operator << (std::ostream& , const SurfacePort& port);