13
0

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
This commit is contained in:
Paul Davis 2012-04-12 16:41:07 +00:00
parent 62620122a9
commit 0c4fe26b41
10 changed files with 268 additions and 285 deletions

View File

@ -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;
}

View File

@ -20,6 +20,7 @@
#ifndef __ardour_mackie_control_protocol_device_info_h__
#define __ardour_mackie_control_protocol_device_info_h__
#include <iostream>
#include <stdint.h>
#include <string>
#include <map>
@ -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__ */

View File

@ -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<string> surfaces;
for (std::map<std::string,DeviceInfo>::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 (_("<i>You must restart Ardour for changes\nto these settings to take effect.</i>")));
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 ());
}

View File

@ -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<string,DeviceInfo>::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> surface (new Surface (*this, session->engine().jack(), device_name, n, stype));
boost::shared_ptr<Surface> 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<Route> r,
}
}
void
MackieControlProtocol::load_device_info (const string& name)
{
map<string,DeviceInfo>::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)
{

View File

@ -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<std::string> _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 *);

View File

@ -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<MidiByteArray> 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);
}

View File

@ -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);
};
}

View File

@ -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<MidiByteArray> 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();

View File

@ -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<void> 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);

View File

@ -6,4 +6,5 @@
<SegmentedDisplay value="no"/>
<TimecodeDisplay value="yes"/>
<TwoCharacterDisplay value="yes"/>
<Extenders value="0"/>
</MackieProtocolDevice>