From ce9b271a92c8c78c3eace6a0da3438ecdae57107 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 7 Oct 2015 13:17:42 -0400 Subject: [PATCH] track Mackie MIDI port connection status as primary trigger for handshake with device --- libs/surfaces/mackie/gui.cc | 2 +- .../mackie/mackie_control_protocol.cc | 56 +++++++------- .../surfaces/mackie/mackie_control_protocol.h | 2 +- libs/surfaces/mackie/surface.cc | 77 ++++++++++++++++++- libs/surfaces/mackie/surface.h | 16 +++- libs/surfaces/mackie/surface_port.cc | 12 +++ libs/surfaces/mackie/surface_port.h | 61 ++++++++------- 7 files changed, 161 insertions(+), 65 deletions(-) diff --git a/libs/surfaces/mackie/gui.cc b/libs/surfaces/mackie/gui.cc index 156070dbc8..a4907fe7a3 100644 --- a/libs/surfaces/mackie/gui.cc +++ b/libs/surfaces/mackie/gui.cc @@ -664,7 +664,7 @@ void MackieControlProtocolGUI::discover_clicked () { /* this should help to get things started */ - _cp.midi_connectivity_established (); + _cp.ping_devices (); } void diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index b1a7e2a976..b20cbfa1d9 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -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(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 diff --git a/libs/surfaces/mackie/mackie_control_protocol.h b/libs/surfaces/mackie/mackie_control_protocol.h index 0536a0c764..93e917b1b5 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.h +++ b/libs/surfaces/mackie/mackie_control_protocol.h @@ -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 diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc index 5bbb081487..65bb25e01c 100644 --- a/libs/surfaces/mackie/surface.cc +++ b/libs/surfaces/mackie/surface.cc @@ -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, std::string name1, boost::weak_ptr, 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 (); + } +} diff --git a/libs/surfaces/mackie/surface.h b/libs/surfaces/mackie/surface.h index 1c989ec518..1afa1c0de7 100644 --- a/libs/surfaces/mackie/surface.h +++ b/libs/surfaces/mackie/surface.h @@ -3,6 +3,7 @@ #include +#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, std::string name1, boost::weak_ptr, std::string name2, bool); + + enum ConnectionState { + InputConnected = 0x1, + OutputConnected = 0x2 + }; + + int connection_state; }; } diff --git a/libs/surfaces/mackie/surface_port.cc b/libs/surfaces/mackie/surface_port.cc index 3129b5a1fd..50bb206cdf 100644 --- a/libs/surfaces/mackie/surface_port.cc +++ b/libs/surfaces/mackie/surface_port.cc @@ -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) { diff --git a/libs/surfaces/mackie/surface_port.h b/libs/surfaces/mackie/surface_port.h index 2c78e692a7..8b052ec65e 100644 --- a/libs/surfaces/mackie/surface_port.h +++ b/libs/surfaces/mackie/surface_port.h @@ -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 _async_in; - boost::shared_ptr _async_out; + protected: + + private: + Mackie::Surface* _surface; + MIDI::Port* _input_port; + MIDI::Port* _output_port; + boost::shared_ptr _async_in; + boost::shared_ptr _async_out; }; std::ostream& operator << (std::ostream& , const SurfacePort& port);