mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-16 11:46:25 +01:00
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:
parent
62620122a9
commit
0c4fe26b41
10 changed files with 268 additions and 285 deletions
|
|
@ -58,45 +58,61 @@ DeviceInfo::set_state (const XMLNode& node, int /* version */)
|
||||||
{
|
{
|
||||||
const XMLProperty* prop;
|
const XMLProperty* prop;
|
||||||
const XMLNode* child;
|
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 ((child = node.child ("Strips")) != 0) {
|
||||||
if ((prop = node.property ("value")) != 0) {
|
if ((prop = child->property ("value")) != 0) {
|
||||||
if ((_strip_cnt = atoi (prop->value())) == 0) {
|
if ((_strip_cnt = atoi (prop->value())) == 0) {
|
||||||
_strip_cnt = 8;
|
_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 ((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());
|
_has_two_character_display = string_is_affirmative (prop->value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((child = node.child ("MasterFader")) != 0) {
|
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());
|
_has_master_fader = string_is_affirmative (prop->value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((child = node.child ("DisplaySegments")) != 0) {
|
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());
|
_has_segmented_display = string_is_affirmative (prop->value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((child = node.child ("TimecodeDisplay")) != 0) {
|
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());
|
_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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,6 +128,12 @@ DeviceInfo::strip_cnt() const
|
||||||
return _strip_cnt;
|
return _strip_cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
DeviceInfo::extenders() const
|
||||||
|
{
|
||||||
|
return _extenders;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
DeviceInfo::has_master_fader() const
|
DeviceInfo::has_master_fader() const
|
||||||
{
|
{
|
||||||
|
|
@ -207,6 +229,7 @@ DeviceInfo::reload_device_info ()
|
||||||
|
|
||||||
XMLTree tree;
|
XMLTree tree;
|
||||||
|
|
||||||
|
|
||||||
if (!tree.read (fullpath.c_str())) {
|
if (!tree.read (fullpath.c_str())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -216,11 +239,20 @@ DeviceInfo::reload_device_info ()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
di.set_state (*root, 3000); /* version is ignored for now */
|
if (di.set_state (*root, 3000) == 0) { /* version is ignored for now */
|
||||||
|
device_info[di.name()] = di;
|
||||||
device_info[di.name()] = di;
|
std::cerr << di << '\n';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete devinfos;
|
delete devinfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<< (std::ostream& os, const Mackie::DeviceInfo& di)
|
||||||
|
{
|
||||||
|
os << di.name() << ' '
|
||||||
|
<< di.strip_cnt() << ' '
|
||||||
|
<< di.extenders() << ' '
|
||||||
|
;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#ifndef __ardour_mackie_control_protocol_device_info_h__
|
#ifndef __ardour_mackie_control_protocol_device_info_h__
|
||||||
#define __ardour_mackie_control_protocol_device_info_h__
|
#define __ardour_mackie_control_protocol_device_info_h__
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
@ -37,6 +38,7 @@ class DeviceInfo
|
||||||
int set_state (const XMLNode&, int version);
|
int set_state (const XMLNode&, int version);
|
||||||
|
|
||||||
uint32_t strip_cnt () const;
|
uint32_t strip_cnt () const;
|
||||||
|
uint32_t extenders() const;
|
||||||
bool has_two_character_display() const;
|
bool has_two_character_display() const;
|
||||||
bool has_master_fader () const;
|
bool has_master_fader () const;
|
||||||
bool has_segmented_display() const;
|
bool has_segmented_display() const;
|
||||||
|
|
@ -48,6 +50,7 @@ class DeviceInfo
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t _strip_cnt;
|
uint32_t _strip_cnt;
|
||||||
|
uint32_t _extenders;
|
||||||
bool _has_two_character_display;
|
bool _has_two_character_display;
|
||||||
bool _has_master_fader;
|
bool _has_master_fader;
|
||||||
bool _has_segmented_display;
|
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__ */
|
#endif /* __ardour_mackie_control_protocol_device_info_h__ */
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,9 @@ public:
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void surface_combo_changed ();
|
void surface_combo_changed ();
|
||||||
void extenders_changed ();
|
|
||||||
|
|
||||||
MackieControlProtocol& _cp;
|
MackieControlProtocol& _cp;
|
||||||
Gtk::ComboBoxText _surface_combo;
|
Gtk::ComboBoxText _surface_combo;
|
||||||
Gtk::SpinButton _extenders;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void*
|
void*
|
||||||
|
|
@ -78,46 +76,27 @@ MackieControlProtocolGUI::MackieControlProtocolGUI (MackieControlProtocol& p)
|
||||||
table->attach (_surface_combo, 1, 2, 0, 1);
|
table->attach (_surface_combo, 1, 2, 0, 1);
|
||||||
|
|
||||||
vector<string> surfaces;
|
vector<string> surfaces;
|
||||||
|
|
||||||
for (std::map<std::string,DeviceInfo>::iterator i = DeviceInfo::device_info.begin(); i != DeviceInfo::device_info.end(); ++i) {
|
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);
|
surfaces.push_back (i->first);
|
||||||
}
|
}
|
||||||
Gtkmm2ext::set_popdown_strings (_surface_combo, surfaces);
|
Gtkmm2ext::set_popdown_strings (_surface_combo, surfaces);
|
||||||
_surface_combo.set_active_text (p.device_info().name());
|
_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);
|
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_spacing (4);
|
||||||
|
set_border_width (12);
|
||||||
show_all ();
|
show_all ();
|
||||||
|
|
||||||
_surface_combo.signal_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::surface_combo_changed));
|
_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
|
void
|
||||||
MackieControlProtocolGUI::surface_combo_changed ()
|
MackieControlProtocolGUI::surface_combo_changed ()
|
||||||
{
|
{
|
||||||
if (_surface_combo.get_active_text() == _("Mackie Control")) {
|
_cp.set_device (_surface_combo.get_active_text());
|
||||||
ARDOUR::Config->set_mackie_emulation (X_("mcu"));
|
|
||||||
} else {
|
|
||||||
ARDOUR::Config->set_mackie_emulation (X_("bcf"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
MackieControlProtocolGUI::extenders_changed ()
|
|
||||||
{
|
|
||||||
ARDOUR::Config->set_mackie_extenders (_extenders.get_value_as_int ());
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,6 @@ MackieControlProtocol::switch_banks (uint32_t initial, bool force)
|
||||||
|
|
||||||
for (; r != sorted.end() && added < (*si)->n_strips(); ++r, ++added) {
|
for (; r != sorted.end() && added < (*si)->n_strips(); ++r, ++added) {
|
||||||
routes.push_back (*r);
|
routes.push_back (*r);
|
||||||
cerr << "\t\tadded " << (*r)->name() << endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("give surface %1 routes\n", routes.size()));
|
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
|
void
|
||||||
MackieControlProtocol::create_surfaces ()
|
MackieControlProtocol::create_surfaces ()
|
||||||
{
|
{
|
||||||
string device_name = "mcu";
|
string device_name = _device_info.name();
|
||||||
surface_type_t stype = mcu;
|
surface_type_t stype = mcu;
|
||||||
|
char buf[128];
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces\n",
|
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Create %1 surfaces\n", 1 + _device_info.extenders()));
|
||||||
1 + ARDOUR::Config->get_mackie_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);
|
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;
|
stype = ext;
|
||||||
|
|
||||||
_input_bundle->add_channel (
|
_input_bundle->add_channel (
|
||||||
|
|
@ -589,10 +607,6 @@ MackieControlProtocol::set_state (const XMLNode & node, int /*version*/)
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
const XMLProperty* prop;
|
const XMLProperty* prop;
|
||||||
|
|
||||||
if ((prop = node.property (X_("device"))) != 0) {
|
|
||||||
load_device_info (prop->value());
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch current bank
|
// fetch current bank
|
||||||
if ((prop = node.property (X_("bank"))) != 0) {
|
if ((prop = node.property (X_("bank"))) != 0) {
|
||||||
string bank = prop->value();
|
string bank = prop->value();
|
||||||
|
|
@ -620,6 +634,10 @@ MackieControlProtocol::set_state (const XMLNode & node, int /*version*/)
|
||||||
_f_actions[n] = action;
|
_f_actions[n] = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((prop = node.property (X_("device"))) != 0) {
|
||||||
|
set_device (prop->value ());
|
||||||
|
}
|
||||||
|
|
||||||
return retval;
|
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
|
void
|
||||||
MackieControlProtocol::gui_track_selection_changed (ARDOUR::RouteNotificationListPtr rl)
|
MackieControlProtocol::gui_track_selection_changed (ARDOUR::RouteNotificationListPtr rl)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ class MackieControlProtocol
|
||||||
const Mackie::DeviceInfo& device_info() const { return _device_info; }
|
const Mackie::DeviceInfo& device_info() const { return _device_info; }
|
||||||
|
|
||||||
int set_active (bool yn);
|
int set_active (bool yn);
|
||||||
|
void set_device (const std::string&);
|
||||||
|
|
||||||
FlipMode flip_mode () const { return _flip_mode; }
|
FlipMode flip_mode () const { return _flip_mode; }
|
||||||
ViewMode view_mode () const { return _view_mode; }
|
ViewMode view_mode () const { return _view_mode; }
|
||||||
|
|
@ -269,7 +270,6 @@ class MackieControlProtocol
|
||||||
std::vector<std::string> _f_actions;
|
std::vector<std::string> _f_actions;
|
||||||
ButtonMap button_map;
|
ButtonMap button_map;
|
||||||
|
|
||||||
void load_device_info (const std::string&);
|
|
||||||
void create_surfaces ();
|
void create_surfaces ();
|
||||||
void port_connected_or_disconnected (std::string, std::string, bool);
|
void port_connected_or_disconnected (std::string, std::string, bool);
|
||||||
bool control_in_use_timeout (Mackie::Surface*, Mackie::Control *, Mackie::Control *);
|
bool control_in_use_timeout (Mackie::Surface*, Mackie::Control *, Mackie::Control *);
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,15 @@ using ARDOUR::Panner;
|
||||||
using ARDOUR::Pannable;
|
using ARDOUR::Pannable;
|
||||||
using ARDOUR::PannerShell;
|
using ARDOUR::PannerShell;
|
||||||
|
|
||||||
// The MCU sysex header
|
// The MCU sysex header.4th byte Will be overwritten
|
||||||
static MidiByteArray mackie_sysex_hdr (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x10);
|
// 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
|
// The MCU extender sysex header.4th byte Will be overwritten
|
||||||
static MidiByteArray mackie_sysex_hdr_xt (5, MIDI::sysex, 0x0, 0x0, 0x66, 0x11);
|
// 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;
|
static MidiByteArray empty_midi_byte_array;
|
||||||
|
|
||||||
|
|
@ -121,33 +125,18 @@ static GlobalControlDefinition mackie_global_controls[] = {
|
||||||
{ "", 0, Button::factory, "" }
|
{ "", 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)
|
: _mcp (mcp)
|
||||||
, _stype (stype)
|
, _stype (stype)
|
||||||
, _number (number)
|
, _number (number)
|
||||||
, _active (false)
|
, _name (device_name)
|
||||||
|
, _active (true)
|
||||||
, _connected (false)
|
, _connected (false)
|
||||||
, _jog_wheel (0)
|
, _jog_wheel (0)
|
||||||
{
|
{
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, "Surface::init\n");
|
DEBUG_TRACE (DEBUG::MackieControl, "Surface::init\n");
|
||||||
|
|
||||||
MIDI::Manager * mm = MIDI::Manager::instance();
|
_port = new SurfacePort (*this);
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
switch (stype) {
|
switch (stype) {
|
||||||
case mcu:
|
case mcu:
|
||||||
|
|
@ -177,7 +166,12 @@ Surface::~Surface ()
|
||||||
{
|
{
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, "Surface: destructor\n");
|
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
|
// delete groups
|
||||||
for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
|
for (Groups::iterator it = groups.begin(); it != groups.end(); ++it) {
|
||||||
|
|
@ -307,11 +301,14 @@ Surface::connect_to_signals ()
|
||||||
{
|
{
|
||||||
if (!_connected) {
|
if (!_connected) {
|
||||||
|
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
|
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface %1 connecting to signals on port %2\n",
|
||||||
number(), _port->input_port().name()));
|
number(), _port->input_port().name()));
|
||||||
|
|
||||||
MIDI::Parser* p = _port->input_port().parser();
|
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 */
|
/* V-Pot messages are Controller */
|
||||||
p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
|
p->controller.connect_same_thread (*this, boost::bind (&Surface::handle_midi_controller_message, this, _1, _2));
|
||||||
/* Button messages are NoteOn */
|
/* 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
|
void
|
||||||
Surface::handle_port_inactive (SurfacePort * port)
|
Surface::handle_port_inactive (SurfacePort * port)
|
||||||
{
|
{
|
||||||
|
|
@ -737,3 +841,4 @@ Surface::gui_selection_changed (ARDOUR::RouteNotificationListPtr routes)
|
||||||
|
|
||||||
_port->write (msg);
|
_port->write (msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,13 +38,12 @@ class Led;
|
||||||
class Surface : public PBD::ScopedConnectionList
|
class Surface : public PBD::ScopedConnectionList
|
||||||
{
|
{
|
||||||
public:
|
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();
|
virtual ~Surface();
|
||||||
|
|
||||||
surface_type_t type() const { return _stype; }
|
surface_type_t type() const { return _stype; }
|
||||||
uint32_t number() const { return _number; }
|
uint32_t number() const { return _number; }
|
||||||
|
const std::string& name() { return _name; }
|
||||||
MackieControlProtocol& mcp() const { return _mcp; }
|
|
||||||
|
|
||||||
bool active() const { return _active; }
|
bool active() const { return _active; }
|
||||||
void drop_routes ();
|
void drop_routes ();
|
||||||
|
|
@ -148,20 +147,26 @@ public:
|
||||||
|
|
||||||
void gui_selection_changed (ARDOUR::RouteNotificationListPtr);
|
void gui_selection_changed (ARDOUR::RouteNotificationListPtr);
|
||||||
|
|
||||||
|
MackieControlProtocol& mcp() const { return _mcp; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void init_controls();
|
void init_controls();
|
||||||
void init_strips ();
|
void init_strips ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MackieControlProtocol& _mcp;
|
MackieControlProtocol& _mcp;
|
||||||
SurfacePort* _port;
|
SurfacePort* _port;
|
||||||
surface_type_t _stype;
|
surface_type_t _stype;
|
||||||
uint32_t _number;
|
uint32_t _number;
|
||||||
bool _active;
|
std::string _name;
|
||||||
bool _connected;
|
bool _active;
|
||||||
Mackie::JogWheel* _jog_wheel;
|
bool _connected;
|
||||||
|
Mackie::JogWheel* _jog_wheel;
|
||||||
|
|
||||||
void jog_wheel_state_display (Mackie::JogWheel::State state);
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,11 @@
|
||||||
|
|
||||||
#include "ardour/debug.h"
|
#include "ardour/debug.h"
|
||||||
#include "ardour/rc_configuration.h"
|
#include "ardour/rc_configuration.h"
|
||||||
|
#include "ardour/session.h"
|
||||||
|
#include "ardour/audioengine.h"
|
||||||
|
|
||||||
#include "controls.h"
|
#include "controls.h"
|
||||||
#include "mackie_control_exception.h"
|
#include "mackie_control_protocol.h"
|
||||||
#include "surface.h"
|
#include "surface.h"
|
||||||
#include "surface_port.h"
|
#include "surface_port.h"
|
||||||
|
|
||||||
|
|
@ -42,22 +44,35 @@ using namespace std;
|
||||||
using namespace Mackie;
|
using namespace Mackie;
|
||||||
using namespace PBD;
|
using namespace PBD;
|
||||||
|
|
||||||
/** @param input_port Input MIDI::Port; this object takes responsibility for removing it from
|
/** @param input_port Input MIDI::Port; this object takes responsibility for
|
||||||
* the MIDI::Manager and destroying it.
|
* adding & removing it from the MIDI::Manager and destroying it. @param
|
||||||
* @param output_port Output MIDI::Port; responsibility similarly taken.
|
* 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)
|
: _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()
|
SurfacePort::~SurfacePort()
|
||||||
{
|
{
|
||||||
close ();
|
|
||||||
|
|
||||||
MIDI::Manager* mm = MIDI::Manager::instance ();
|
MIDI::Manager* mm = MIDI::Manager::instance ();
|
||||||
|
|
||||||
if (_input_port) {
|
if (_input_port) {
|
||||||
|
|
@ -78,183 +93,38 @@ string fetch_errmsg (int error_number)
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
MidiByteArray SurfacePort::read()
|
int
|
||||||
{
|
SurfacePort::write (const MidiByteArray & mba)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
if (mba.empty()) {
|
if (mba.empty()) {
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!active()) return;
|
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("port %1 write %2\n", output_port().name(), mba));
|
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);
|
int count = output_port().write (mba.bytes().get(), mba.size(), 0);
|
||||||
|
|
||||||
if (count != (int)mba.size()) {
|
if (count != (int)mba.size()) {
|
||||||
|
|
||||||
if (errno == 0) {
|
if (errno == 0) {
|
||||||
|
|
||||||
cout << "port overflow on " << output_port().name() << ". Did not write all of " << mba << endl;
|
cout << "port overflow on " << output_port().name() << ". Did not write all of " << mba << endl;
|
||||||
|
|
||||||
} else if (errno != EAGAIN) {
|
} else if (errno != EAGAIN) {
|
||||||
ostringstream os;
|
ostringstream os;
|
||||||
os << "Surface: couldn't write to port " << output_port().name();
|
os << "Surface: couldn't write to port " << output_port().name();
|
||||||
os << ", error: " << fetch_errmsg (errno) << "(" << errno << ")";
|
os << ", error: " << fetch_errmsg (errno) << "(" << errno << ")";
|
||||||
|
|
||||||
cout << os.str() << endl;
|
cout << os.str() << endl;
|
||||||
inactive_event();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return -1;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_active = false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
ostream &
|
||||||
SurfacePort::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count)
|
Mackie::operator << (ostream & os, const SurfacePort & port)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
os << "{ ";
|
os << "{ ";
|
||||||
os << "name: " << port.input_port().name() << " " << port.output_port().name();
|
os << "name: " << port.input_port().name() << " " << port.output_port().name();
|
||||||
|
|
|
||||||
|
|
@ -44,42 +44,23 @@ class Surface;
|
||||||
class SurfacePort
|
class SurfacePort
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SurfacePort (Mackie::Surface&, MIDI::Port& input_port, MIDI::Port& output_port);
|
SurfacePort (Mackie::Surface&);
|
||||||
virtual ~SurfacePort();
|
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
|
/// an easier way to output bytes via midi
|
||||||
void write (const MidiByteArray&);
|
int write (const MidiByteArray&);
|
||||||
|
|
||||||
MIDI::Port& input_port() { return *_input_port; }
|
MIDI::Port& input_port() { return *_input_port; }
|
||||||
const MIDI::Port& input_port() const { return *_input_port; }
|
const MIDI::Port& input_port() const { return *_input_port; }
|
||||||
MIDI::Port& output_port() { return *_output_port; }
|
MIDI::Port& output_port() { return *_output_port; }
|
||||||
const MIDI::Port& output_port() const { 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:
|
protected:
|
||||||
MidiByteArray host_connection_query (MidiByteArray& bytes);
|
|
||||||
MidiByteArray host_connection_confirmation (const MidiByteArray& bytes);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Mackie::Surface* _surface;
|
Mackie::Surface* _surface;
|
||||||
MIDI::Port* _input_port;
|
MIDI::Port* _input_port;
|
||||||
MIDI::Port* _output_port;
|
MIDI::Port* _output_port;
|
||||||
bool _active;
|
|
||||||
|
|
||||||
PBD::ScopedConnection sysex_connection;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream& operator << (std::ostream& , const SurfacePort& port);
|
std::ostream& operator << (std::ostream& , const SurfacePort& port);
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,5 @@
|
||||||
<SegmentedDisplay value="no"/>
|
<SegmentedDisplay value="no"/>
|
||||||
<TimecodeDisplay value="yes"/>
|
<TimecodeDisplay value="yes"/>
|
||||||
<TwoCharacterDisplay value="yes"/>
|
<TwoCharacterDisplay value="yes"/>
|
||||||
|
<Extenders value="0"/>
|
||||||
</MackieProtocolDevice>
|
</MackieProtocolDevice>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue