From 0c4fe26b4111e5c7955380d9a7ab55c4c775d6d0 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 12 Apr 2012 16:41:07 +0000 Subject: [PATCH] MCP: switch devices on the fly; name MIDI ports appropriately; fix active state; move sysex parsing into Surface git-svn-id: svn://localhost/ardour2/branches/3.0@11942 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/surfaces/mackie/device_info.cc | 62 ++++-- libs/surfaces/mackie/device_info.h | 5 + libs/surfaces/mackie/gui.cc | 31 +-- .../mackie/mackie_control_protocol.cc | 53 ++--- .../surfaces/mackie/mackie_control_protocol.h | 2 +- libs/surfaces/mackie/surface.cc | 153 +++++++++++--- libs/surfaces/mackie/surface.h | 23 +- libs/surfaces/mackie/surface_port.cc | 200 +++--------------- libs/surfaces/mackie/surface_port.h | 23 +- mcp_devices/mcpro.xml | 1 + 10 files changed, 268 insertions(+), 285 deletions(-) diff --git a/libs/surfaces/mackie/device_info.cc b/libs/surfaces/mackie/device_info.cc index 446f5dd34d..566a01be9a 100644 --- a/libs/surfaces/mackie/device_info.cc +++ b/libs/surfaces/mackie/device_info.cc @@ -58,45 +58,61 @@ DeviceInfo::set_state (const XMLNode& node, int /* version */) { const XMLProperty* prop; const XMLNode* child; - + + if (node.name() != "MackieProtocolDevice") { + return -1; + } + + /* name is mandatory */ + + if ((child = node.child ("Name")) != 0) { + if ((prop = child->property ("value")) != 0) { + _name = prop->value(); + } else { + return -1; + } + } + if ((child = node.child ("Strips")) != 0) { - if ((prop = node.property ("value")) != 0) { + if ((prop = child->property ("value")) != 0) { if ((_strip_cnt = atoi (prop->value())) == 0) { _strip_cnt = 8; } } } + if ((child = node.child ("Extenders")) != 0) { + if ((prop = child->property ("value")) != 0) { + if ((_extenders = atoi (prop->value())) == 0) { + _extenders = 0; + } + } + } + if ((child = node.child ("TwoCharacterDisplay")) != 0) { - if ((prop = node.property ("value")) != 0) { + if ((prop = child->property ("value")) != 0) { _has_two_character_display = string_is_affirmative (prop->value()); } } if ((child = node.child ("MasterFader")) != 0) { - if ((prop = node.property ("value")) != 0) { + if ((prop = child->property ("value")) != 0) { _has_master_fader = string_is_affirmative (prop->value()); } } if ((child = node.child ("DisplaySegments")) != 0) { - if ((prop = node.property ("value")) != 0) { + if ((prop = child->property ("value")) != 0) { _has_segmented_display = string_is_affirmative (prop->value()); } } if ((child = node.child ("TimecodeDisplay")) != 0) { - if ((prop = node.property ("value")) != 0) { + if ((prop = child->property ("value")) != 0) { _has_timecode_display = string_is_affirmative (prop->value()); } } - if ((child = node.child ("Name")) != 0) { - if ((prop = node.property ("value")) != 0) { - _name = prop->value(); - } - } - return 0; } @@ -112,6 +128,12 @@ DeviceInfo::strip_cnt() const return _strip_cnt; } +uint32_t +DeviceInfo::extenders() const +{ + return _extenders; +} + bool DeviceInfo::has_master_fader() const { @@ -207,6 +229,7 @@ DeviceInfo::reload_device_info () XMLTree tree; + if (!tree.read (fullpath.c_str())) { continue; } @@ -216,11 +239,20 @@ DeviceInfo::reload_device_info () continue; } - di.set_state (*root, 3000); /* version is ignored for now */ - - device_info[di.name()] = di; + if (di.set_state (*root, 3000) == 0) { /* version is ignored for now */ + device_info[di.name()] = di; + std::cerr << di << '\n'; + } } delete devinfos; } +std::ostream& operator<< (std::ostream& os, const Mackie::DeviceInfo& di) +{ + os << di.name() << ' ' + << di.strip_cnt() << ' ' + << di.extenders() << ' ' + ; + return os; +} diff --git a/libs/surfaces/mackie/device_info.h b/libs/surfaces/mackie/device_info.h index c73837843f..f605237510 100644 --- a/libs/surfaces/mackie/device_info.h +++ b/libs/surfaces/mackie/device_info.h @@ -20,6 +20,7 @@ #ifndef __ardour_mackie_control_protocol_device_info_h__ #define __ardour_mackie_control_protocol_device_info_h__ +#include #include #include #include @@ -37,6 +38,7 @@ class DeviceInfo int set_state (const XMLNode&, int version); uint32_t strip_cnt () const; + uint32_t extenders() const; bool has_two_character_display() const; bool has_master_fader () const; bool has_segmented_display() const; @@ -48,6 +50,7 @@ class DeviceInfo private: uint32_t _strip_cnt; + uint32_t _extenders; bool _has_two_character_display; bool _has_master_fader; bool _has_segmented_display; @@ -67,4 +70,6 @@ class DeviceProfile } +std::ostream& operator<< (std::ostream& os, const Mackie::DeviceInfo& di); + #endif /* __ardour_mackie_control_protocol_device_info_h__ */ diff --git a/libs/surfaces/mackie/gui.cc b/libs/surfaces/mackie/gui.cc index 7f49b3e392..76715a4751 100644 --- a/libs/surfaces/mackie/gui.cc +++ b/libs/surfaces/mackie/gui.cc @@ -39,11 +39,9 @@ public: private: void surface_combo_changed (); - void extenders_changed (); MackieControlProtocol& _cp; Gtk::ComboBoxText _surface_combo; - Gtk::SpinButton _extenders; }; void* @@ -78,46 +76,27 @@ MackieControlProtocolGUI::MackieControlProtocolGUI (MackieControlProtocol& p) table->attach (_surface_combo, 1, 2, 0, 1); vector surfaces; - + for (std::map::iterator i = DeviceInfo::device_info.begin(); i != DeviceInfo::device_info.end(); ++i) { + std::cerr << "Dveice known: " << i->first << endl; surfaces.push_back (i->first); } Gtkmm2ext::set_popdown_strings (_surface_combo, surfaces); _surface_combo.set_active_text (p.device_info().name()); - _extenders.set_range (0, 8); - _extenders.set_increments (1, 4); - - Gtk::Label* l = manage (new Gtk::Label (_("Extenders:"))); - l->set_alignment (0, 0.5); - table->attach (*l, 0, 1, 1, 2); - table->attach (_extenders, 1, 2, 1, 2); - pack_start (*table); - Gtk::Label* cop_out = manage (new Gtk::Label (_("You must restart Ardour for changes\nto these settings to take effect."))); - cop_out->set_use_markup (true); - pack_start (*cop_out); - set_spacing (4); + set_border_width (12); show_all (); _surface_combo.signal_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::surface_combo_changed)); - _extenders.signal_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::extenders_changed)); } void MackieControlProtocolGUI::surface_combo_changed () { - if (_surface_combo.get_active_text() == _("Mackie Control")) { - ARDOUR::Config->set_mackie_emulation (X_("mcu")); - } else { - ARDOUR::Config->set_mackie_emulation (X_("bcf")); - } + _cp.set_device (_surface_combo.get_active_text()); } -void -MackieControlProtocolGUI::extenders_changed () -{ - ARDOUR::Config->set_mackie_extenders (_extenders.get_value_as_int ()); -} + diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc index c316121dc5..c4a37499eb 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.cc +++ b/libs/surfaces/mackie/mackie_control_protocol.cc @@ -321,7 +321,6 @@ MackieControlProtocol::switch_banks (uint32_t initial, bool force) for (; r != sorted.end() && added < (*si)->n_strips(); ++r, ++added) { routes.push_back (*r); - cerr << "\t\tadded " << (*r)->name() << endl; } DEBUG_TRACE (DEBUG::MackieControl, string_compose ("give surface %1 routes\n", routes.size())); @@ -497,21 +496,40 @@ MackieControlProtocol::connect_session_signals() } } +void +MackieControlProtocol::set_device (const string& device_name) +{ + map::iterator d = DeviceInfo::device_info.find (device_name); + + if (d == DeviceInfo::device_info.end()) { + return; + } + + _device_info = d->second; + + surfaces.clear (); + create_surfaces (); + switch_banks (0, true); +} + void MackieControlProtocol::create_surfaces () { - string device_name = "mcu"; + string device_name = _device_info.name(); surface_type_t stype = mcu; + char buf[128]; - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces\n", - 1 + ARDOUR::Config->get_mackie_extenders())); + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces\n", 1 + _device_info.extenders())); - for (uint32_t n = 0; n < 1 + ARDOUR::Config->get_mackie_extenders(); ++n) { + for (uint32_t n = 0; n < 1 + _device_info.extenders(); ++n) { - boost::shared_ptr surface (new Surface (*this, session->engine().jack(), device_name, n, stype)); + boost::shared_ptr surface (new Surface (*this, device_name, n, stype)); surfaces.push_back (surface); + + /* next device will be an extender */ - device_name = "mcu_xt"; + snprintf (buf, sizeof (buf), "%s XT%d", _device_info.name().c_str(), n+1); + device_name = buf; stype = ext; _input_bundle->add_channel ( @@ -589,10 +607,6 @@ MackieControlProtocol::set_state (const XMLNode & node, int /*version*/) int retval = 0; const XMLProperty* prop; - if ((prop = node.property (X_("device"))) != 0) { - load_device_info (prop->value()); - } - // fetch current bank if ((prop = node.property (X_("bank"))) != 0) { string bank = prop->value(); @@ -620,6 +634,10 @@ MackieControlProtocol::set_state (const XMLNode & node, int /*version*/) _f_actions[n] = action; } + if ((prop = node.property (X_("device"))) != 0) { + set_device (prop->value ()); + } + return retval; } @@ -1147,19 +1165,6 @@ MackieControlProtocol::force_special_route_to_strip (boost::shared_ptr r, } } -void -MackieControlProtocol::load_device_info (const string& name) -{ - map::iterator i = DeviceInfo::device_info.find (name); - - if (i == DeviceInfo::device_info.end()) { - error << string_compose (_("No device info for Mackie Control device \"%1\" - ignored"), name) << endmsg; - return; - } - - _device_info = i->second; -} - void MackieControlProtocol::gui_track_selection_changed (ARDOUR::RouteNotificationListPtr rl) { diff --git a/libs/surfaces/mackie/mackie_control_protocol.h b/libs/surfaces/mackie/mackie_control_protocol.h index 978fa222c3..8fa37831ec 100644 --- a/libs/surfaces/mackie/mackie_control_protocol.h +++ b/libs/surfaces/mackie/mackie_control_protocol.h @@ -118,6 +118,7 @@ class MackieControlProtocol const Mackie::DeviceInfo& device_info() const { return _device_info; } int set_active (bool yn); + void set_device (const std::string&); FlipMode flip_mode () const { return _flip_mode; } ViewMode view_mode () const { return _view_mode; } @@ -269,7 +270,6 @@ class MackieControlProtocol std::vector _f_actions; ButtonMap button_map; - void load_device_info (const std::string&); void create_surfaces (); void port_connected_or_disconnected (std::string, std::string, bool); bool control_in_use_timeout (Mackie::Surface*, Mackie::Control *, Mackie::Control *); diff --git a/libs/surfaces/mackie/surface.cc b/libs/surfaces/mackie/surface.cc index 55cb573051..b056d7b2ec 100644 --- a/libs/surfaces/mackie/surface.cc +++ b/libs/surfaces/mackie/surface.cc @@ -38,11 +38,15 @@ using ARDOUR::Panner; using ARDOUR::Pannable; using ARDOUR::PannerShell; -// The MCU sysex header -static MidiByteArray mackie_sysex_hdr (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x10); +// The MCU sysex header.4th byte Will be overwritten +// when we get an incoming sysex that identifies +// the device type +static MidiByteArray mackie_sysex_hdr (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x14); -// The MCU extender sysex header -static MidiByteArray mackie_sysex_hdr_xt (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x11); +// The MCU extender sysex header.4th byte Will be overwritten +// when we get an incoming sysex that identifies +// the device type +static MidiByteArray mackie_sysex_hdr_xt (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x15); static MidiByteArray empty_midi_byte_array; @@ -121,33 +125,18 @@ static GlobalControlDefinition mackie_global_controls[] = { { "", 0, Button::factory, "" } }; -Surface::Surface (MackieControlProtocol& mcp, jack_client_t* jack, const std::string& device_name, uint32_t number, surface_type_t stype) +Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, uint32_t number, surface_type_t stype) : _mcp (mcp) , _stype (stype) , _number (number) - , _active (false) + , _name (device_name) + , _active (true) , _connected (false) , _jog_wheel (0) { DEBUG_TRACE (DEBUG::MackieControl, "Surface::init\n"); - MIDI::Manager * mm = MIDI::Manager::instance(); - - MIDI::Port * input = new MIDI::Port (string_compose (_("%1 in"), device_name), MIDI::Port::IsInput, jack); - MIDI::Port * output =new MIDI::Port (string_compose (_("%1 out"), device_name), MIDI::Port::IsOutput, jack); - - input->set_centrally_parsed (false); - output->set_centrally_parsed (false); - - mm->add_port (input); - mm->add_port (output); - - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface has ports named %1 and %2\n", - input->name(), output->name())); - - _port = new SurfacePort (*this, *input, *output); - _port->open(); - _port->inactive_event.connect_same_thread (*this, boost::bind (&Surface::handle_port_inactive, this, _port)); + _port = new SurfacePort (*this); switch (stype) { case mcu: @@ -177,7 +166,12 @@ Surface::~Surface () { DEBUG_TRACE (DEBUG::MackieControl, "Surface: destructor\n"); - zero_all (); + // faders to minimum + write_sysex (0x61); + // All LEDs off + write_sysex (0x62); + // Reset (reboot into offline mode) + // _write_sysex (0x63); // delete groups for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) { @@ -307,11 +301,14 @@ Surface::connect_to_signals () { if (!_connected) { + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n", number(), _port->input_port().name())); MIDI::Parser* p = _port->input_port().parser(); + /* Incoming sysex */ + p->sysex.connect_same_thread (*this, boost::bind (&Surface::handle_midi_sysex, this, _1, _2, _3)); /* V-Pot messages are Controller */ p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2)); /* Button messages are NoteOn */ @@ -435,6 +432,113 @@ Surface::handle_midi_controller_message (MIDI::Parser &, MIDI::EventTwoBytes* ev } } +void +Surface::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count) +{ + MidiByteArray bytes (count, raw_bytes); + + + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes)); + + /* always save the device type ID so that our outgoing sysex messages + * are correct + */ + + if (_stype == mcu) { + mackie_sysex_hdr[3] = bytes[4]; + } else { + mackie_sysex_hdr_xt[3] = bytes[4]; + } + + switch (bytes[5]) { + case 0x01: + /* MCP: Device Ready + LCP: Connection Challenge + */ + if (bytes[4] == 0x10 || bytes[4] == 0x11) { + write_sysex (host_connection_query (bytes)); + } else { + _active = true; + } + break; + + case 0x03: /* LCP Connection Confirmation */ + if (bytes[4] == 0x10 || bytes[4] == 0x11) { + write_sysex (host_connection_confirmation (bytes)); + _active = true; + } + break; + + case 0x04: /* LCP: Confirmation Denied */ + _active = false; + break; + default: + error << "MCP: unknown sysex: " << bytes << endmsg; + } +} + +static MidiByteArray +calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end) +{ + MidiByteArray l; + back_insert_iterator back (l); + copy (begin, end, back); + + MidiByteArray retval; + + // this is how to calculate the response to the challenge. + // from the Logic docs. + retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3])); + retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3]))); + retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1]))); + retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4)))); + + return retval; +} + +// not used right now +MidiByteArray +Surface::host_connection_query (MidiByteArray & bytes) +{ + MidiByteArray response; + + if (bytes[4] != 0x10 && bytes[4] != 0x11) { + /* not a Logic Control device - no response required */ + return response; + } + + // handle host connection query + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes)); + + if (bytes.size() != 18) { + cerr << "expecting 18 bytes, read " << bytes << " from " << _port->input_port().name() << endl; + return response; + } + + // build and send host connection reply + response << 0x02; + copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response)); + response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4); + return response; +} + +// not used right now +MidiByteArray +Surface::host_connection_confirmation (const MidiByteArray & bytes) +{ + DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes)); + + // decode host connection confirmation + if (bytes.size() != 14) { + ostringstream os; + os << "expecting 14 bytes, read " << bytes << " from " << _port->input_port().name(); + throw MackieControlException (os.str()); + } + + // send version request + return MidiByteArray (2, 0x13, 0x00); +} + void Surface::handle_port_inactive (SurfacePort * port) { @@ -737,3 +841,4 @@ Surface::gui_selection_changed (ARDOUR::RouteNotificationListPtr routes) _port->write (msg); } + diff --git a/libs/surfaces/mackie/surface.h b/libs/surfaces/mackie/surface.h index 310eb00c24..a105125a0c 100644 --- a/libs/surfaces/mackie/surface.h +++ b/libs/surfaces/mackie/surface.h @@ -38,13 +38,12 @@ class Led; class Surface : public PBD::ScopedConnectionList { public: - Surface (MackieControlProtocol&, jack_client_t* jack, const std::string& device_name, uint32_t number, surface_type_t stype); + Surface (MackieControlProtocol&, const std::string& name, uint32_t number, surface_type_t stype); virtual ~Surface(); surface_type_t type() const { return _stype; } uint32_t number() const { return _number; } - - MackieControlProtocol& mcp() const { return _mcp; } + const std::string& name() { return _name; } bool active() const { return _active; } void drop_routes (); @@ -148,20 +147,26 @@ public: void gui_selection_changed (ARDOUR::RouteNotificationListPtr); + MackieControlProtocol& mcp() const { return _mcp; } + protected: void init_controls(); void init_strips (); private: MackieControlProtocol& _mcp; - SurfacePort* _port; - surface_type_t _stype; - uint32_t _number; - bool _active; - bool _connected; - Mackie::JogWheel* _jog_wheel; + SurfacePort* _port; + surface_type_t _stype; + uint32_t _number; + std::string _name; + bool _active; + bool _connected; + Mackie::JogWheel* _jog_wheel; void jog_wheel_state_display (Mackie::JogWheel::State state); + void handle_midi_sysex (MIDI::Parser&, MIDI::byte *, size_t count); + MidiByteArray host_connection_query (MidiByteArray& bytes); + MidiByteArray host_connection_confirmation (const MidiByteArray& bytes); }; } diff --git a/libs/surfaces/mackie/surface_port.cc b/libs/surfaces/mackie/surface_port.cc index d35c15da3f..ebc2ab1117 100644 --- a/libs/surfaces/mackie/surface_port.cc +++ b/libs/surfaces/mackie/surface_port.cc @@ -29,9 +29,11 @@ #include "ardour/debug.h" #include "ardour/rc_configuration.h" +#include "ardour/session.h" +#include "ardour/audioengine.h" #include "controls.h" -#include "mackie_control_exception.h" +#include "mackie_control_protocol.h" #include "surface.h" #include "surface_port.h" @@ -42,22 +44,35 @@ using namespace std; using namespace Mackie; using namespace PBD; -/** @param input_port Input MIDI::Port; this object takes responsibility for removing it from - * the MIDI::Manager and destroying it. - * @param output_port Output MIDI::Port; responsibility similarly taken. +/** @param input_port Input MIDI::Port; this object takes responsibility for + * adding & removing it from the MIDI::Manager and destroying it. @param + * output_port Output MIDI::Port; responsibility similarly taken. */ -SurfacePort::SurfacePort (Surface& s, MIDI::Port & input_port, MIDI::Port & output_port) +SurfacePort::SurfacePort (Surface& s) : _surface (&s) - , _input_port (&input_port) - , _output_port (&output_port) - , _active (false) { + jack_client_t* jack = MackieControlProtocol::instance()->get_session().engine().jack(); + + _input_port = new MIDI::Port (string_compose (_("%1 in"), _surface->name()), MIDI::Port::IsInput, jack); + _output_port =new MIDI::Port (string_compose (_("%1 out"), _surface->name()), MIDI::Port::IsOutput, jack); + + /* MackieControl has its own thread for handling input from the input + * port, and we don't want anything handling output from the output + * port. This stops the Generic MIDI UI event loop in ardour from + * attempting to handle these ports. + */ + + _input_port->set_centrally_parsed (false); + _output_port->set_centrally_parsed (false); + + MIDI::Manager * mm = MIDI::Manager::instance(); + + mm->add_port (_input_port); + mm->add_port (_output_port); } SurfacePort::~SurfacePort() { - close (); - MIDI::Manager* mm = MIDI::Manager::instance (); if (_input_port) { @@ -78,183 +93,38 @@ string fetch_errmsg (int error_number) return msg; } -MidiByteArray SurfacePort::read() -{ - const int max_buf_size = 512; - MIDI::byte buf[max_buf_size]; - MidiByteArray retval; - - // check active. Mainly so that the destructor - // doesn't destroy the mutex while it's still locked - if (!active()) { - return retval; - } - - // return nothing read if the lock isn't acquired - - // read port and copy to return value - int nread = input_port().read (buf, sizeof (buf)); - - if (nread >= 0) { - retval.copy (nread, buf); - if ((size_t) nread == sizeof (buf)) { -#ifdef PORT_DEBUG - cout << "SurfacePort::read recursive" << endl; -#endif - retval << read(); - } - } else { - if (errno != EAGAIN) { - ostringstream os; - os << "Surface: error reading from port: " << input_port().name(); - os << ": " << errno << fetch_errmsg (errno); - - cout << os.str() << endl; - inactive_event(); - throw MackieControlException (os.str()); - } - } -#ifdef PORT_DEBUG - cout << "SurfacePort::read: " << retval << endl; -#endif - return retval; -} - -void SurfacePort::write (const MidiByteArray & mba) +int +SurfacePort::write (const MidiByteArray & mba) { if (mba.empty()) { - return; + return 0; } - if (!active()) return; - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("port %1 write %2\n", output_port().name(), mba)); int count = output_port().write (mba.bytes().get(), mba.size(), 0); if (count != (int)mba.size()) { + if (errno == 0) { + cout << "port overflow on " << output_port().name() << ". Did not write all of " << mba << endl; + } else if (errno != EAGAIN) { ostringstream os; os << "Surface: couldn't write to port " << output_port().name(); os << ", error: " << fetch_errmsg (errno) << "(" << errno << ")"; - cout << os.str() << endl; - inactive_event(); } - } -} - -void SurfacePort::open() -{ - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::open %1\n", *this)); - input_port().parser()->sysex.connect_same_thread (sysex_connection, boost::bind (&SurfacePort::handle_midi_sysex, this, _1, _2, _3)); - _active = true; -} - -void SurfacePort::close() -{ - DEBUG_TRACE (DEBUG::MackieControl, "SurfacePort::close\n"); - sysex_connection.disconnect(); - - if (_surface) { - // faders to minimum - _surface->write_sysex (0x61); - // All LEDs off - _surface->write_sysex (0x62); - // Reset (reboot into offline mode) - _surface->write_sysex (0x63); + return -1; } - _active = false; + return 0; } -void -SurfacePort::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count) -{ - MidiByteArray bytes (count, raw_bytes); - - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes)); - - switch (bytes[5]) - { - case 0x01: - _surface->write_sysex (host_connection_query (bytes)); - break; - case 0x03: - // not used right now - _surface->write_sysex (host_connection_confirmation (bytes)); - break; - case 0x04: - inactive_event (); - cout << "host connection error" << bytes << endl; - break; - case 0x14: - // probe_emulation (bytes); - break; - default: - cout << "unknown sysex: " << bytes << endl; - } -} - -MidiByteArray calculate_challenge_response (MidiByteArray::iterator begin, MidiByteArray::iterator end) -{ - MidiByteArray l; - back_insert_iterator back (l); - copy (begin, end, back); - - MidiByteArray retval; - - // this is how to calculate the response to the challenge. - // from the Logic docs. - retval << (0x7f & (l[0] + (l[1] ^ 0xa) - l[3])); - retval << (0x7f & ( (l[2] >> l[3]) ^ (l[0] + l[3]))); - retval << (0x7f & ((l[3] - (l[2] << 2)) ^ (l[0] | l[1]))); - retval << (0x7f & (l[1] - l[2] + (0xf0 ^ (l[3] << 4)))); - - return retval; -} - -// not used right now -MidiByteArray SurfacePort::host_connection_query (MidiByteArray & bytes) -{ - MidiByteArray response; - - // handle host connection query - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes)); - - if (bytes.size() != 18) { - cerr << "expecting 18 bytes, read " << bytes << " from " << input_port().name() << endl; - return response; - } - - // build and send host connection reply - response << 0x02; - copy (bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter (response)); - response << calculate_challenge_response (bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4); - return response; -} - -// not used right now -MidiByteArray SurfacePort::host_connection_confirmation (const MidiByteArray & bytes) -{ - DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes)); - - // decode host connection confirmation - if (bytes.size() != 14) { - ostringstream os; - os << "expecting 14 bytes, read " << bytes << " from " << input_port().name(); - throw MackieControlException (os.str()); - } - - // send version request - return MidiByteArray (2, 0x13, 0x00); -} - - -ostream & Mackie::operator << (ostream & os, const SurfacePort & port) +ostream & +Mackie::operator << (ostream & os, const SurfacePort & port) { os << "{ "; os << "name: " << port.input_port().name() << " " << port.output_port().name(); diff --git a/libs/surfaces/mackie/surface_port.h b/libs/surfaces/mackie/surface_port.h index 8b66073b4a..1a65d8dbcf 100644 --- a/libs/surfaces/mackie/surface_port.h +++ b/libs/surfaces/mackie/surface_port.h @@ -44,42 +44,23 @@ class Surface; class SurfacePort { public: - SurfacePort (Mackie::Surface&, MIDI::Port& input_port, MIDI::Port& output_port); + SurfacePort (Mackie::Surface&); virtual ~SurfacePort(); - void open(); - void close(); - - /// read bytes from the port. They'll either end up in the - /// parser, or if that's not active they'll be returned - MidiByteArray read(); - /// an easier way to output bytes via midi - void write (const MidiByteArray&); + int write (const MidiByteArray&); MIDI::Port& input_port() { return *_input_port; } const MIDI::Port& input_port() const { return *_input_port; } MIDI::Port& output_port() { return *_output_port; } const MIDI::Port& output_port() const { return *_output_port; } - // emitted when the port goes inactive (ie a read or write failed) - PBD::Signal0 inactive_event; - - void handle_midi_sysex (MIDI::Parser&, MIDI::byte *, size_t count); - - bool active() const { return _active; } - protected: - MidiByteArray host_connection_query (MidiByteArray& bytes); - MidiByteArray host_connection_confirmation (const MidiByteArray& bytes); private: Mackie::Surface* _surface; MIDI::Port* _input_port; MIDI::Port* _output_port; - bool _active; - - PBD::ScopedConnection sysex_connection; }; std::ostream& operator << (std::ostream& , const SurfacePort& port); diff --git a/mcp_devices/mcpro.xml b/mcp_devices/mcpro.xml index 54a2f58ac7..6b0c6dfc27 100644 --- a/mcp_devices/mcpro.xml +++ b/mcp_devices/mcpro.xml @@ -6,4 +6,5 @@ +