US2400: add us2400 files to repository.

This commit is contained in:
Ben Loftis
2017-10-05 10:54:46 -05:00
parent 32c725115d
commit 2107d09454
43 changed files with 10137 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
MB: allow control of strip params when a mix-bus or master is selected

View File

@@ -0,0 +1,142 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
Copyright (C) 2017 Ben Loftis
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.
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.
*/
#include <glib.h>
#include "ardour/ardour.h"
#include "button.h"
#include "surface.h"
#include "control_group.h"
using namespace ArdourSurface;
using namespace US2400;
Control*
Button::factory (Surface& surface, Button::ID bid, int id, const std::string& name, Group& group)
{
Button* b = new Button (surface, bid, id, name, group);
/* store button with the device-specific ID */
surface.buttons[id] = b;
surface.controls.push_back (b);
group.add (*b);
return b;
}
void
Button::pressed ()
{
press_time = ARDOUR::get_microseconds ();
}
void
Button::released ()
{
press_time = 0;
}
int32_t
Button::long_press_count ()
{
if (press_time == 0) {
return -1; /* button is not pressed */
}
const ARDOUR::microseconds_t delta = ARDOUR::get_microseconds () - press_time;
if (delta < 500000) {
return 0;
} else if (delta < 1000000) {
return 1;
}
return 2;
}
int
Button::name_to_id (const std::string& name)
{
if (!g_ascii_strcasecmp (name.c_str(), "Send")) { return Send; }
if (!g_ascii_strcasecmp (name.c_str(), "Pan")) { return Pan; }
if (!g_ascii_strcasecmp (name.c_str(), "Bank Left")) { return Left; }
if (!g_ascii_strcasecmp (name.c_str(), "Bank Right")) { return Right; }
if (!g_ascii_strcasecmp (name.c_str(), "Flip")) { return Flip; }
if (!g_ascii_strcasecmp (name.c_str(), "F1")) { return F1; }
if (!g_ascii_strcasecmp (name.c_str(), "F2")) { return F2; }
if (!g_ascii_strcasecmp (name.c_str(), "F3")) { return F3; }
if (!g_ascii_strcasecmp (name.c_str(), "F4")) { return F4; }
if (!g_ascii_strcasecmp (name.c_str(), "F5")) { return F5; }
if (!g_ascii_strcasecmp (name.c_str(), "F6")) { return F6; }
if (!g_ascii_strcasecmp (name.c_str(), "Shift")) { return Shift; }
if (!g_ascii_strcasecmp (name.c_str(), "Drop")) { return Drop; }
if (!g_ascii_strcasecmp (name.c_str(), "Clear Solo")) { return ClearSolo; }
if (!g_ascii_strcasecmp (name.c_str(), "Rewind")) { return Rewind; }
if (!g_ascii_strcasecmp (name.c_str(), "Ffwd")) { return Ffwd; }
if (!g_ascii_strcasecmp (name.c_str(), "Stop")) { return Stop; }
if (!g_ascii_strcasecmp (name.c_str(), "Play")) { return Play; }
if (!g_ascii_strcasecmp (name.c_str(), "Record")) { return Record; }
if (!g_ascii_strcasecmp (name.c_str(), "Scrub")) { return Scrub; }
/* Strip buttons */
if (!g_ascii_strcasecmp (name.c_str(), "Solo")) { return Solo; }
if (!g_ascii_strcasecmp (name.c_str(), "Mute")) { return Mute; }
if (!g_ascii_strcasecmp (name.c_str(), "Select")) { return Select; }
if (!g_ascii_strcasecmp (name.c_str(), "Fader Touch")) { return FaderTouch; }
/* Master Fader button */
if (!g_ascii_strcasecmp (name.c_str(), "Master Fader Touch")) { return MasterFaderTouch; }
return -1;
}
std::string
Button::id_to_name (Button::ID id)
{
if (id == Send) { return "Send"; }
if (id == Pan) { return "Pan"; }
if (id == Left) { return "Bank Left"; }
if (id == Right) { return "Bank Right"; }
if (id == Flip) { return "Flip"; }
if (id == F1) { return "F1"; }
if (id == F2) { return "F2"; }
if (id == F3) { return "F3"; }
if (id == F4) { return "F4"; }
if (id == F5) { return "F5"; }
if (id == F6) { return "F6"; }
if (id == Shift) { return "Shift"; }
if (id == Drop) { return "Drop"; }
if (id == ClearSolo) { return "Clear Solo"; }
if (id == Rewind) { return "Rewind"; }
if (id == Ffwd) { return "FFwd"; }
if (id == Stop) { return "Stop"; }
if (id == Play) { return "Play"; }
if (id == Record) { return "Record"; }
if (id == Scrub) { return "Scrub"; }
if (id == Solo) { return "Solo"; }
if (id == Mute) { return "Mute"; }
if (id == Select) { return "Select"; }
if (id == FaderTouch) { return "Fader Touch"; }
if (id == MasterFaderTouch) { return "Master Fader Touch"; }
return "???";
}

View File

@@ -0,0 +1,121 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
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.
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 __ardour_us2400_control_protocol_button_h__
#define __ardour_us2400_control_protocol_button_h__
#include "ardour/types.h"
#include "controls.h"
#include "led.h"
namespace ArdourSurface {
namespace US2400 {
class Surface;
class Button : public Control
{
public:
/* These values uniquely identify each possible button that an MCP device may
send. Each DeviceInfo object contains its own set of button definitions that
define what device ID will be sent for each button, and there is no reason
for them to be the same. */
enum ID {
/* Global Buttons */
Scrub,
F1,
F2,
F3,
F4,
F5,
F6,
Rewind,
Ffwd,
Stop,
Play,
Record,
Left,
Right,
Flip,
FinalGlobalButton,
/* Global buttons that users should not redefine */
Drop,
Send,
Pan,
ClearSolo,
Shift,
Option,
Ctrl,
CmdAlt,
/* Strip buttons */
Solo,
Mute,
Select,
FaderTouch,
/* Master fader */
MasterFaderTouch,
};
Button (Surface& s, ID bid, int did, std::string name, Group & group)
: Control (did, name, group)
, _surface (s)
, _bid (bid)
, _led (did, name + "_led", group)
, press_time (0) {}
MidiByteArray zero() { return _led.zero (); }
MidiByteArray set_state (LedState ls) { return _led.set_state (ls); }
ID bid() const { return _bid; }
static Control* factory (Surface& surface, Button::ID bid, int id, const std::string&, Group& group);
static int name_to_id (const std::string& name);
static std::string id_to_name (Button::ID);
Surface& surface() const { return _surface; }
void pressed ();
void released ();
int32_t long_press_count ();
private:
Surface& _surface;
ID _bid; /* device independent button ID */
Led _led;
ARDOUR::microseconds_t press_time;
};
} // US2400 namespace
} // ArdourSurface namespace
#endif

View File

@@ -0,0 +1,44 @@
#ifndef __ardour_us2400_control_protocol_control_group_h__
#define __ardour_us2400_control_protocol_control_group_h__
#include <vector>
namespace ArdourSurface {
namespace US2400 {
class Control;
/**
This is a loose group of controls, eg cursor buttons,
transport buttons, functions buttons etc.
*/
class Group
{
public:
Group (const std::string & name)
: _name (name) {}
virtual ~Group() {}
virtual bool is_strip() const { return false; }
virtual bool is_master() const { return false; }
virtual void add (Control & control);
const std::string & name() const { return _name; }
void set_name (const std::string & rhs) { _name = rhs; }
typedef std::vector<Control*> Controls;
const Controls & controls() const { return _controls; }
protected:
Controls _controls;
private:
std::string _name;
};
}
}
#endif

View File

@@ -0,0 +1,127 @@
/*
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 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.
*/
#include <iostream>
#include <iomanip>
#include <sstream>
#include "ardour/automation_control.h"
#include "pbd/enumwriter.h"
#include "controls.h"
#include "types.h"
#include "surface.h"
#include "control_group.h"
#include "button.h"
#include "led.h"
#include "pot.h"
#include "fader.h"
#include "jog.h"
#include "meter.h"
using namespace std;
using namespace ArdourSurface;
using namespace US2400;
using ARDOUR::AutomationControl;
void Group::add (Control& control)
{
_controls.push_back (&control);
}
Control::Control (int id, std::string name, Group & group)
: _id (id)
, _name (name)
, _group (group)
, _in_use (false)
{
}
/** @return true if the control is in use, or false otherwise.
Buttons are `in use' when they are held down.
Faders with touch support are `in use' when they are being touched.
Pots, or faders without touch support, are `in use' from the first move
event until a timeout after the last move event.
*/
bool
Control::in_use () const
{
return _in_use;
}
void
Control::set_in_use (bool in_use)
{
_in_use = in_use;
}
void
Control::set_control (boost::shared_ptr<AutomationControl> ac)
{
normal_ac = ac;
}
void
Control::set_value (float val, PBD::Controllable::GroupControlDisposition group_override)
{
if (normal_ac) {
normal_ac->set_value (normal_ac->interface_to_internal (val), group_override);
}
}
float
Control::get_value ()
{
if (!normal_ac) {
return 0.0f;
}
return normal_ac->internal_to_interface (normal_ac->get_value());
}
void
Control::start_touch (double when)
{
if (normal_ac) {
return normal_ac->start_touch (when);
}
}
void
Control::stop_touch (double when)
{
if (normal_ac) {
return normal_ac->stop_touch (when);
}
}
ostream & operator << (ostream & os, const ArdourSurface::US2400::Control & control)
{
os << typeid (control).name();
os << " { ";
os << "name: " << control.name();
os << ", ";
os << "id: " << "0x" << setw(2) << setfill('0') << hex << control.id() << setfill(' ');
os << ", ";
os << "group: " << control.group().name();
os << " }";
return os;
}

View File

@@ -0,0 +1,95 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
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.
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 __us2400_controls_h__
#define __us2400_controls_h__
#include <map>
#include <vector>
#include <string>
#include <stdint.h>
#include <boost/smart_ptr.hpp>
#include "pbd/controllable.h"
#include "pbd/signals.h"
#include "us2400_control_exception.h"
#include "midi_byte_array.h"
namespace ARDOUR {
class AutomationControl;
}
namespace ArdourSurface {
namespace US2400 {
class Strip;
class Group;
class Surface;
class Control {
public:
Control (int id, std::string name, Group& group);
virtual ~Control() {}
int id() const { return _id; }
const std::string & name() const { return _name; }
Group & group() const { return _group; }
bool in_use () const;
void set_in_use (bool);
// Keep track of the timeout so it can be updated with more incoming events
sigc::connection in_use_connection;
virtual MidiByteArray zero() = 0;
/** If we are doing an in_use timeout for a fader without touch, this
* is its touch button control; otherwise 0.
*/
Control* in_use_touch_control;
boost::shared_ptr<ARDOUR::AutomationControl> control () const { return normal_ac; }
virtual void set_control (boost::shared_ptr<ARDOUR::AutomationControl>);
virtual void reset_control () { normal_ac.reset(); }
float get_value ();
void set_value (float val, PBD::Controllable::GroupControlDisposition gcd = PBD::Controllable::UseGroup);
virtual void start_touch (double when);
virtual void stop_touch (double when);
protected:
boost::shared_ptr<ARDOUR::AutomationControl> normal_ac;
private:
int _id; /* possibly device-dependent ID */
std::string _name;
Group& _group;
bool _in_use;
};
}
}
std::ostream & operator << (std::ostream & os, const ArdourSurface::US2400::Control & control);
#endif /* __us2400_controls_h__ */

View File

@@ -0,0 +1,366 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
Copyright (C) 2017 Ben Loftis
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.
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.
*/
#include <cstdlib>
#include <cstring>
#include <glibmm/miscutils.h>
#include "pbd/xml++.h"
#include "pbd/error.h"
#include "pbd/file_utils.h"
#include "pbd/convert.h"
#include "pbd/stl_delete.h"
#include "ardour/filesystem_paths.h"
#include "device_info.h"
#include "pbd/i18n.h"
using namespace PBD;
using namespace ARDOUR;
using namespace ArdourSurface;
using namespace US2400;
using std::string;
using std::vector;
std::map<std::string,DeviceInfo> DeviceInfo::device_info;
DeviceInfo::DeviceInfo()
: _strip_cnt (8)
, _extenders (3)
, _master_position (0)
, _has_two_character_display (false)
, _has_master_fader (true)
, _has_timecode_display (false)
, _has_global_controls (true)
, _has_jog_wheel (true)
, _has_touch_sense_faders (true)
, _uses_logic_control_buttons (false)
, _no_handshake (false)
, _has_meters (true)
, _has_separate_meters (true)
, _device_type (MCU)
, _name (X_("US2400"))
{
us2400_control_buttons ();
}
DeviceInfo::~DeviceInfo()
{
}
GlobalButtonInfo&
DeviceInfo::get_global_button(Button::ID id)
{
GlobalButtonsInfo::iterator it;
it = _global_buttons.find (id);
return it->second;
}
std::string&
DeviceInfo::get_global_button_name(Button::ID id)
{
GlobalButtonsInfo::iterator it;
it = _global_buttons.find (id);
if (it == _global_buttons.end ()) {
_global_button_name = "";
return _global_button_name;
} else {
return it->second.label;
}
}
void
DeviceInfo::us2400_control_buttons ()
{
_global_buttons.clear ();
shared_buttons ();
}
void
DeviceInfo::logic_control_buttons ()
{
_global_buttons.clear ();
shared_buttons ();
}
void
DeviceInfo::shared_buttons ()
{
// US-2499 button notes:
// CHAN button sends nothing. it inititates a dumb 0..127 knob mode for the 24 knobs
// PAN sends the regular pan/surround message. this tells our strips to send the pan knob position
// AUX1-6 all send the same 0x29 + 0x21 message, I believe the surface uses this to captures knob info, somehow
_global_buttons[Button::Pan] = GlobalButtonInfo ("Pan/Surround", "assignment", 0x2a); // US-2400: this is sent (on&off in one msg) from the Pan button
_global_buttons[Button::Left] = GlobalButtonInfo ("Bank Left", "bank", 0x2e);
_global_buttons[Button::Right] = GlobalButtonInfo ("Bank Right", "bank", 0x2f);
_global_buttons[Button::Flip] = GlobalButtonInfo ("Flip", "assignment", 0x32);
_global_buttons[Button::F1] = GlobalButtonInfo ("F1", "function select", 0x36);
_global_buttons[Button::F2] = GlobalButtonInfo ("F2", "function select", 0x37);
_global_buttons[Button::F3] = GlobalButtonInfo ("F3", "function select", 0x38);
_global_buttons[Button::F4] = GlobalButtonInfo ("F4", "function select", 0x39);
_global_buttons[Button::F5] = GlobalButtonInfo ("F5", "function select", 0x3a);
_global_buttons[Button::F6] = GlobalButtonInfo ("F6", "function select", 0x3b);
_global_buttons[Button::Shift] = GlobalButtonInfo ("Shift", "modifiers", 0x46);
_global_buttons[Button::Option] = GlobalButtonInfo ("Option", "modifiers", 0x47); //There is no physical Option button, but US2400 sends Option+ track Solo == solo clear
_global_buttons[Button::Drop] = GlobalButtonInfo ("Drop", "transport", 0x57); // US-2400: combined with ffwd/rew to call IN/OUT
_global_buttons[Button::Rewind] = GlobalButtonInfo ("Rewind", "transport", 0x5b); // US-2400: if "Drop" 0x57 is held, this is IN
_global_buttons[Button::Ffwd] = GlobalButtonInfo ("Fast Fwd", "transport", 0x5c); // US-2400: if "Drop 0x57 is held, this is OUT
_global_buttons[Button::Stop] = GlobalButtonInfo ("Stop", "transport", 0x5d);
_global_buttons[Button::Play] = GlobalButtonInfo ("Play", "transport", 0x5e);
_global_buttons[Button::Record] = GlobalButtonInfo ("Record", "transport", 0x5f);
_global_buttons[Button::Scrub] = GlobalButtonInfo ("Scrub", "cursor", 0x65);
_strip_buttons[Button::Solo] = StripButtonInfo (0x08, "Solo"); //combined with Option" to do solo clear
_strip_buttons[Button::Mute] = StripButtonInfo (0x10, "Mute");
_strip_buttons[Button::Select] = StripButtonInfo (0x18, "Select");
_strip_buttons[Button::FaderTouch] = StripButtonInfo (0x68, "Fader Touch");
_global_buttons[Button::MasterFaderTouch] = GlobalButtonInfo ("Master Fader Touch", "master", 0x70);
}
int
DeviceInfo::set_state (const XMLNode& node, int /* version */)
{
const XMLProperty* prop;
const XMLNode* child;
if (node.name() != "US-2400Device") {
return -1;
}
if ((child = node.child ("LogicControlButtons")) != 0) {
if (child->get_property ("value", _uses_logic_control_buttons)) {
if (_uses_logic_control_buttons) {
logic_control_buttons ();
} else {
us2400_control_buttons ();
}
}
}
if ((child = node.child ("Buttons")) != 0) {
XMLNodeConstIterator i;
const XMLNodeList& nlist (child->children());
std::string name;
for (i = nlist.begin(); i != nlist.end(); ++i) {
if ((*i)->name () == "GlobalButton") {
if ((*i)->get_property ("name", name)) {
int id = Button::name_to_id (name);
if (id >= 0) {
Button::ID bid = (Button::ID)id;
int32_t id;
if ((*i)->get_property ("id", id)) {
std::map<Button::ID, GlobalButtonInfo>::iterator b = _global_buttons.find (bid);
if (b != _global_buttons.end ()) {
b->second.id = id;
(*i)->get_property ("label", b->second.label);
}
}
}
}
} else if ((*i)->name () == "StripButton") {
if ((*i)->get_property ("name", name)) {
int id = Button::name_to_id (name);
if (id >= 0) {
Button::ID bid = (Button::ID)id;
int32_t base_id;
if ((*i)->get_property ("baseid", base_id)) {
std::map<Button::ID, StripButtonInfo>::iterator b = _strip_buttons.find (bid);
if (b != _strip_buttons.end ()) {
b->second.base_id = base_id;
}
}
}
}
}
}
}
return 0;
}
const string&
DeviceInfo::name() const
{
return _name;
}
uint32_t
DeviceInfo::strip_cnt() const
{
return _strip_cnt;
}
uint32_t
DeviceInfo::extenders() const
{
return _extenders;
}
uint32_t
DeviceInfo::master_position() const
{
return _master_position;
}
bool
DeviceInfo::has_master_fader() const
{
return _has_master_fader;
}
bool
DeviceInfo::has_meters() const
{
return _has_meters;
}
bool
DeviceInfo::has_separate_meters() const
{
return _has_separate_meters;
}
bool
DeviceInfo::has_two_character_display() const
{
return _has_two_character_display;
}
bool
DeviceInfo::has_timecode_display () const
{
return _has_timecode_display;
}
bool
DeviceInfo::has_global_controls () const
{
return _has_global_controls;
}
bool
DeviceInfo::has_jog_wheel () const
{
return _has_jog_wheel;
}
bool
DeviceInfo::no_handshake () const
{
return _no_handshake;
}
bool
DeviceInfo::has_touch_sense_faders () const
{
return _has_touch_sense_faders;
}
static const char * const devinfo_env_variable_name = "ARDOUR_MCP_PATH";
static const char* const devinfo_dir_name = "mcp";
static const char* const devinfo_suffix = ".device";
static Searchpath
devinfo_search_path ()
{
bool devinfo_path_defined = false;
std::string spath_env (Glib::getenv (devinfo_env_variable_name, devinfo_path_defined));
if (devinfo_path_defined) {
return spath_env;
}
Searchpath spath (ardour_data_search_path());
spath.add_subdirectory_to_paths(devinfo_dir_name);
return spath;
}
static bool
devinfo_filter (const string &str, void* /*arg*/)
{
return (str.length() > strlen(devinfo_suffix) &&
str.find (devinfo_suffix) == (str.length() - strlen (devinfo_suffix)));
}
void
DeviceInfo::reload_device_info ()
{
vector<string> s;
vector<string> devinfos;
Searchpath spath (devinfo_search_path());
find_files_matching_filter (devinfos, spath, devinfo_filter, 0, false, true);
device_info.clear ();
if (devinfos.empty()) {
error << "No MCP device info files found using " << spath.to_string() << endmsg;
std::cerr << "No MCP device info files found using " << spath.to_string() << std::endl;
return;
}
for (vector<string>::iterator i = devinfos.begin(); i != devinfos.end(); ++i) {
string fullpath = *i;
DeviceInfo di; // has to be initial every loop or info from last added.
XMLTree tree;
if (!tree.read (fullpath.c_str())) {
continue;
}
XMLNode* root = tree.root ();
if (!root) {
continue;
}
if (di.set_state (*root, 3000) == 0) { /* version is ignored for now */
device_info[di.name()] = di;
}
}
}
std::ostream& operator<< (std::ostream& os, const US2400::DeviceInfo& di)
{
os << di.name() << ' '
<< di.strip_cnt() << ' '
<< di.extenders() << ' '
<< di.master_position() << ' '
;
return os;
}

View File

@@ -0,0 +1,132 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
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.
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 __ardour_us2400_control_protocol_device_info_h__
#define __ardour_us2400_control_protocol_device_info_h__
#include <iostream>
#include <stdint.h>
#include <string>
#include <map>
#include "button.h"
class XMLNode;
namespace ArdourSurface {
namespace US2400 {
struct GlobalButtonInfo {
std::string label; // visible to user
std::string group; // in case we want to present in a GUI
int32_t id; // value sent by device
GlobalButtonInfo () : id (-1) {}
GlobalButtonInfo (const std::string& l, const std::string& g, uint32_t i)
: label (l), group (g), id (i) {}
};
struct StripButtonInfo {
int32_t base_id;
std::string name;
StripButtonInfo () : base_id (-1) {}
StripButtonInfo (uint32_t i, const std::string& n)
: base_id (i), name (n) {}
};
class DeviceInfo
{
public:
enum DeviceType {
MCU = 0x14,
MCXT = 0x15,
LC = 0x10,
LCXT = 0x11,
HUI = 0x5
};
DeviceInfo();
~DeviceInfo();
int set_state (const XMLNode&, int version);
DeviceType device_type() const { return _device_type; }
uint32_t strip_cnt () const;
uint32_t extenders() const;
uint32_t master_position() const;
bool has_two_character_display() const;
bool has_master_fader () const;
bool has_timecode_display() const;
bool has_global_controls() const;
bool has_jog_wheel () const;
bool has_touch_sense_faders() const;
bool no_handshake() const;
bool has_meters() const;
bool has_separate_meters() const;
bool us2400() const { return _us2400; }
const std::string& name() const;
static std::map<std::string,DeviceInfo> device_info;
static void reload_device_info();
std::string& get_global_button_name(Button::ID);
GlobalButtonInfo& get_global_button(Button::ID);
typedef std::map<Button::ID,GlobalButtonInfo> GlobalButtonsInfo;
typedef std::map<Button::ID,StripButtonInfo> StripButtonsInfo;
const GlobalButtonsInfo& global_buttons() const { return _global_buttons; }
const StripButtonsInfo& strip_buttons() const { return _strip_buttons; }
private:
uint32_t _strip_cnt;
uint32_t _extenders;
uint32_t _master_position;
bool _has_two_character_display;
bool _has_master_fader;
bool _has_timecode_display;
bool _has_global_controls;
bool _has_jog_wheel;
bool _has_touch_sense_faders;
bool _uses_logic_control_buttons;
bool _no_handshake;
bool _has_meters;
bool _has_separate_meters;
bool _us2400;
DeviceType _device_type;
std::string _name;
std::string _global_button_name;
GlobalButtonsInfo _global_buttons;
StripButtonsInfo _strip_buttons;
void logic_control_buttons ();
void us2400_control_buttons ();
void shared_buttons ();
};
} // US2400 namespace
} // ArdourSurface namespace
std::ostream& operator<< (std::ostream& os, const ArdourSurface::US2400::DeviceInfo& di);
#endif /* __ardour_us2400_control_protocol_device_info_h__ */

View File

@@ -0,0 +1,329 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
Copyright (C) 2017 Ben Loftis
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.
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.
*/
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <glibmm/miscutils.h>
#include "pbd/xml++.h"
#include "pbd/error.h"
#include "pbd/file_utils.h"
#include "pbd/stl_delete.h"
#include "pbd/replace_all.h"
#include "ardour/filesystem_paths.h"
#include "us2400_control_protocol.h"
#include "device_profile.h"
#include "pbd/i18n.h"
using namespace PBD;
using namespace ARDOUR;
using namespace ArdourSurface;
using namespace US2400;
using std::string;
using std::vector;
std::map<std::string,DeviceProfile> DeviceProfile::device_profiles;
const std::string DeviceProfile::edited_indicator (" (edited)");
const std::string DeviceProfile::default_profile_name ("User");
DeviceProfile::DeviceProfile (const string& n)
: _name (n)
, edited (false)
{
}
DeviceProfile::~DeviceProfile()
{
}
static const char * const devprofile_env_variable_name = "ARDOUR_MCP_PATH";
static const char* const devprofile_dir_name = "us2400";
static const char* const devprofile_suffix = ".profile";
static Searchpath
devprofile_search_path ()
{
bool devprofile_path_defined = false;
std::string spath_env (Glib::getenv (devprofile_env_variable_name, devprofile_path_defined));
if (devprofile_path_defined) {
return spath_env;
}
Searchpath spath (ardour_data_search_path());
spath.add_subdirectory_to_paths(devprofile_dir_name);
return spath;
}
static std::string
user_devprofile_directory ()
{
return Glib::build_filename (user_config_directory(), devprofile_dir_name);
}
static bool
devprofile_filter (const string &str, void* /*arg*/)
{
return (str.length() > strlen(devprofile_suffix) &&
str.find (devprofile_suffix) == (str.length() - strlen (devprofile_suffix)));
}
void
DeviceProfile::reload_device_profiles ()
{
vector<string> s;
vector<string> devprofiles;
Searchpath spath (devprofile_search_path());
find_files_matching_filter (devprofiles, spath, devprofile_filter, 0, false, true);
device_profiles.clear ();
if (devprofiles.empty()) {
error << "No MCP device info files found using " << spath.to_string() << endmsg;
return;
}
for (vector<string>::iterator i = devprofiles.begin(); i != devprofiles.end(); ++i) {
string fullpath = *i;
DeviceProfile dp; // has to be initial every loop or info from last added.
XMLTree tree;
if (!tree.read (fullpath.c_str())) {
continue;
}
XMLNode* root = tree.root ();
if (!root) {
continue;
}
if (dp.set_state (*root, 3000) == 0) { /* version is ignored for now */
dp.set_path (fullpath);
device_profiles[dp.name()] = dp;
}
}
}
int
DeviceProfile::set_state (const XMLNode& node, int /* version */)
{
const XMLProperty* prop;
const XMLNode* child;
if (node.name() != "US2400DeviceProfile") {
return -1;
}
/* name is mandatory */
if ((child = node.child ("Name")) == 0 || (prop = child->property ("value")) == 0) {
return -1;
} else {
_name = prop->value();
}
if ((child = node.child ("Buttons")) != 0) {
XMLNodeConstIterator i;
const XMLNodeList& nlist (child->children());
for (i = nlist.begin(); i != nlist.end(); ++i) {
if ((*i)->name() == "Button") {
if ((prop = (*i)->property ("name")) == 0) {
error << string_compose ("Button without name in device profile \"%1\" - ignored", _name) << endmsg;
continue;
}
int id = Button::name_to_id (prop->value());
if (id < 0) {
error << string_compose ("Unknown button ID \"%1\"", prop->value()) << endmsg;
continue;
}
Button::ID bid = (Button::ID) id;
ButtonActionMap::iterator b = _button_map.find (bid);
if (b == _button_map.end()) {
b = _button_map.insert (_button_map.end(), std::pair<Button::ID,ButtonActions> (bid, ButtonActions()));
}
(*i)->get_property ("plain", b->second.plain);
(*i)->get_property ("shift", b->second.shift);
}
}
}
edited = false;
return 0;
}
XMLNode&
DeviceProfile::get_state () const
{
XMLNode* node = new XMLNode ("US2400DeviceProfile");
XMLNode* child = new XMLNode ("Name");
child->set_property ("value", name());
node->add_child_nocopy (*child);
if (_button_map.empty()) {
return *node;
}
XMLNode* buttons = new XMLNode ("Buttons");
node->add_child_nocopy (*buttons);
for (ButtonActionMap::const_iterator b = _button_map.begin(); b != _button_map.end(); ++b) {
XMLNode* n = new XMLNode ("Button");
n->set_property ("name", Button::id_to_name (b->first));
if (!b->second.plain.empty()) {
n->set_property ("plain", b->second.plain);
}
if (!b->second.shift.empty()) {
n->set_property ("shift", b->second.shift);
}
buttons->add_child_nocopy (*n);
}
return *node;
}
string
DeviceProfile::get_button_action (Button::ID id, int modifier_state) const
{
ButtonActionMap::const_iterator i = _button_map.find (id);
if (i == _button_map.end()) {
return string();
}
if (modifier_state == US2400Protocol::MODIFIER_SHIFT) {
return i->second.shift;
}
return i->second.plain;
}
void
DeviceProfile::set_button_action (Button::ID id, int modifier_state, const string& act)
{
ButtonActionMap::iterator i = _button_map.find (id);
if (i == _button_map.end()) {
i = _button_map.insert (std::make_pair (id, ButtonActions())).first;
}
string action (act);
replace_all (action, "<Actions>/", "");
if (modifier_state == US2400Protocol::MODIFIER_SHIFT) {
i->second.shift = action;
}
if (modifier_state == 0) {
i->second.plain = action;
}
edited = true;
save ();
}
string
DeviceProfile::name_when_edited (string const& base)
{
return string_compose ("%1 %2", base, edited_indicator);
}
string
DeviceProfile::name() const
{
if (edited) {
if (_name.find (edited_indicator) == string::npos) {
/* modify name to included edited indicator */
return name_when_edited (_name);
} else {
/* name already contains edited indicator */
return _name;
}
} else {
return _name;
}
}
void
DeviceProfile::set_path (const string& p)
{
_path = p;
}
/* XXX copied from libs/ardour/utils.cc */
static string
legalize_for_path (const string& str)
{
string::size_type pos;
string illegal_chars = "/\\"; /* DOS, POSIX. Yes, we're going to ignore HFS */
string legal;
legal = str;
pos = 0;
while ((pos = legal.find_first_of (illegal_chars, pos)) != string::npos) {
legal.replace (pos, 1, "_");
pos += 1;
}
return string (legal);
}
void
DeviceProfile::save ()
{
std::string fullpath = user_devprofile_directory();
if (g_mkdir_with_parents (fullpath.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create user MCP profile folder \"%1\" (%2)"), fullpath, strerror (errno)) << endmsg;
return;
}
fullpath = Glib::build_filename (fullpath, string_compose ("%1%2", legalize_for_path (name()), devprofile_suffix));
XMLTree tree;
tree.set_root (&get_state());
if (!tree.write (fullpath)) {
error << string_compose ("MCP profile not saved to %1", fullpath) << endmsg;
}
}

View File

@@ -0,0 +1,80 @@
/*
Copyright (C) 2012 Paul Davis
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.
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 __ardour_us2400_control_protocol_device_profile_h__
#define __ardour_us2400_control_protocol_device_profile_h__
#include <iostream>
#include <stdint.h>
#include <string>
#include <map>
#include "button.h"
class XMLNode;
namespace ArdourSurface {
namespace US2400 {
class DeviceProfile
{
public:
DeviceProfile (const std::string& name = "");
~DeviceProfile();
std::string get_button_action (Button::ID, int modifier_state) const;
void set_button_action (Button::ID, int modifier_state, const std::string&);
std::string name() const;
void set_path (const std::string&);
static void reload_device_profiles ();
static std::map<std::string,DeviceProfile> device_profiles;
static std::string name_when_edited (std::string const& name);
static const std::string default_profile_name;
private:
struct ButtonActions {
std::string plain;
std::string control;
std::string shift;
std::string option;
std::string cmdalt;
std::string shiftcontrol;
};
typedef std::map<Button::ID,ButtonActions> ButtonActionMap;
std::string _name;
std::string _path;
ButtonActionMap _button_map;
bool edited;
static const std::string edited_indicator;
int set_state (const XMLNode&, int version);
XMLNode& get_state () const;
void save ();
};
}
}
#endif /* __ardour_us2400_control_protocol_device_profile_h__ */

View File

@@ -0,0 +1,70 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
Copyright (C) 2017 Ben Loftis
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.
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.
*/
#include <cmath>
#include "pbd/compose.h"
#include "ardour/debug.h"
#include "fader.h"
#include "surface.h"
#include "control_group.h"
#include "us2400_control_protocol.h"
using namespace ArdourSurface;
using namespace US2400;
using namespace PBD;
Control*
Fader::factory (Surface& surface, int id, const char* name, Group& group)
{
Fader* f = new Fader (id, name, group);
surface.faders[id] = f;
surface.controls.push_back (f);
group.add (*f);
return f;
}
MidiByteArray
Fader::set_position (float normalized)
{
position = normalized;
return update_message ();
}
MidiByteArray
Fader::update_message ()
{
int posi = lrintf (16384.0 * position);
if (posi == last_update_position) {
if (posi == llast_update_position) {
return MidiByteArray();
}
}
llast_update_position = last_update_position;
last_update_position = posi;
DEBUG_TRACE (DEBUG::US2400, string_compose ("generate fader message for position %1 (%2)\n", position, posi));
return MidiByteArray (3, 0xe0 + id(), posi & 0x7f, posi >> 7);
}

View File

@@ -0,0 +1,38 @@
#ifndef __ardour_us2400_control_protocol_fader_h__
#define __ardour_us2400_control_protocol_fader_h__
#include "controls.h"
namespace ArdourSurface {
namespace US2400 {
class Fader : public Control
{
public:
Fader (int id, std::string name, Group & group)
: Control (id, name, group)
, position (0.0)
, last_update_position (-1)
, llast_update_position (-1)
{
}
MidiByteArray set_position (float);
MidiByteArray zero() { return set_position (0.0); }
MidiByteArray update_message ();
static Control* factory (Surface&, int id, const char*, Group&);
private:
float position;
int last_update_position;
int llast_update_position;
};
}
}
#endif

824
libs/surfaces/us2400/gui.cc Normal file
View File

@@ -0,0 +1,824 @@
/*
Copyright (C) 2010 Paul Davis
Copyright (C) 2017 Ben Loftis
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.
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.
*/
#include <gtkmm/comboboxtext.h>
#include <gtkmm/box.h>
#include <gtkmm/spinbutton.h>
#include <gtkmm/table.h>
#include <gtkmm/treeview.h>
#include <gtkmm/liststore.h>
#include <gtkmm/treestore.h>
#include <gtkmm/notebook.h>
#include <gtkmm/cellrenderercombo.h>
#include <gtkmm/scale.h>
#include <gtkmm/alignment.h>
#include "pbd/error.h"
#include "pbd/unwind.h"
#include "pbd/strsplit.h"
#include "pbd/stacktrace.h"
#include "gtkmm2ext/actions.h"
#include "gtkmm2ext/bindings.h"
#include "gtkmm2ext/gui_thread.h"
#include "gtkmm2ext/utils.h"
#include "ardour/audioengine.h"
#include "ardour/port.h"
#include "ardour/rc_configuration.h"
#include "us2400_control_protocol.h"
#include "device_info.h"
#include "gui.h"
#include "surface.h"
#include "surface_port.h"
#include "pbd/i18n.h"
using namespace std;
using namespace Gtk;
using namespace ArdourSurface;
using namespace US2400;
void*
US2400Protocol::get_gui () const
{
if (!_gui) {
const_cast<US2400Protocol*>(this)->build_gui ();
}
static_cast<Gtk::Notebook*>(_gui)->show_all();
return _gui;
}
void
US2400Protocol::tear_down_gui ()
{
if (_gui) {
Gtk::Widget *w = static_cast<Gtk::Widget*>(_gui)->get_parent();
if (w) {
w->hide();
delete w;
}
}
delete (US2400ProtocolGUI*) _gui;
_gui = 0;
}
void
US2400Protocol::build_gui ()
{
_gui = (void *) new US2400ProtocolGUI (*this);
}
US2400ProtocolGUI::US2400ProtocolGUI (US2400Protocol& p)
: _cp (p)
, table (2, 9)
, _device_dependent_widget (0)
, _ignore_profile_changed (false)
, ignore_active_change (false)
{
Gtk::Label* l;
Gtk::Alignment* align;
int row = 0;
set_border_width (12);
table.set_row_spacings (4);
table.set_col_spacings (6);
table.set_border_width (12);
table.set_homogeneous (false);
_cp.DeviceChanged.connect (device_change_connection, invalidator (*this), boost::bind (&US2400ProtocolGUI::device_changed, this), gui_context());
_cp.ConnectionChange.connect (connection_change_connection, invalidator (*this), boost::bind (&US2400ProtocolGUI::connection_handler, this), gui_context());
/* device-dependent part */
device_dependent_row = row;
if (_device_dependent_widget) {
table.remove (*_device_dependent_widget);
_device_dependent_widget = 0;
}
_device_dependent_widget = device_dependent_widget ();
table.attach (*_device_dependent_widget, 0, 12, row, row+1, AttachOptions(0), AttachOptions(0), 0, 0);
row++;
/* back to the boilerplate */
vector<string> profiles;
for (std::map<std::string,DeviceProfile>::iterator i = DeviceProfile::device_profiles.begin(); i != DeviceProfile::device_profiles.end(); ++i) {
cerr << "add discovered profile " << i->first << endl;
profiles.push_back (i->first);
}
Gtkmm2ext::set_popdown_strings (_profile_combo, profiles);
cerr << "set active profile from " << p.device_profile().name() << endl;
_profile_combo.set_active_text (p.device_profile().name());
_profile_combo.signal_changed().connect (sigc::mem_fun (*this, &US2400ProtocolGUI::profile_combo_changed));
append_page (table, _("Device Setup"));
table.show_all();
/* function key editor */
VBox* fkey_packer = manage (new VBox);
HBox* profile_packer = manage (new HBox);
HBox* observation_packer = manage (new HBox);
l = manage (new Gtk::Label (_("Profile/Settings:")));
profile_packer->pack_start (*l, false, false);
profile_packer->pack_start (_profile_combo, true, true);
profile_packer->set_spacing (12);
profile_packer->set_border_width (12);
fkey_packer->pack_start (*profile_packer, false, false);
fkey_packer->pack_start (function_key_scroller, true, true);
fkey_packer->pack_start (*observation_packer, false, false);
fkey_packer->set_spacing (12);
function_key_scroller.property_shadow_type() = Gtk::SHADOW_NONE;
function_key_scroller.add (function_key_editor);
append_page (*fkey_packer, _("Function Keys"));
build_available_action_menu ();
build_function_key_editor ();
refresh_function_key_editor ();
fkey_packer->show_all();
}
void
US2400ProtocolGUI::connection_handler ()
{
/* ignore all changes to combobox active strings here, because we're
updating them to match a new ("external") reality - we were called
because port connections have changed.
*/
PBD::Unwinder<bool> ici (ignore_active_change, true);
vector<Gtk::ComboBox*>::iterator ic;
vector<Gtk::ComboBox*>::iterator oc;
vector<string> midi_inputs;
vector<string> midi_outputs;
ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsOutput|ARDOUR::IsTerminal), midi_inputs);
ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsInput|ARDOUR::IsTerminal), midi_outputs);
for (ic = input_combos.begin(), oc = output_combos.begin(); ic != input_combos.end() && oc != output_combos.end(); ++ic, ++oc) {
boost::shared_ptr<Surface> surface = _cp.get_surface_by_raw_pointer ((*ic)->get_data ("surface"));
if (surface) {
update_port_combos (midi_inputs, midi_outputs, *ic, *oc, surface);
}
}
}
void
US2400ProtocolGUI::update_port_combos (vector<string> const& midi_inputs, vector<string> const& midi_outputs,
Gtk::ComboBox* input_combo,
Gtk::ComboBox* output_combo,
boost::shared_ptr<Surface> surface)
{
Glib::RefPtr<Gtk::ListStore> input = build_midi_port_list (midi_inputs, true);
Glib::RefPtr<Gtk::ListStore> output = build_midi_port_list (midi_outputs, false);
bool input_found = false;
bool output_found = false;
int n;
input_combo->set_model (input);
output_combo->set_model (output);
Gtk::TreeModel::Children children = input->children();
Gtk::TreeModel::Children::iterator i;
i = children.begin();
++i; /* skip "Disconnected" */
for (n = 1; i != children.end(); ++i, ++n) {
string port_name = (*i)[midi_port_columns.full_name];
if (surface->port().input().connected_to (port_name)) {
input_combo->set_active (n);
input_found = true;
break;
}
}
if (!input_found) {
input_combo->set_active (0); /* disconnected */
}
children = output->children();
i = children.begin();
++i; /* skip "Disconnected" */
for (n = 1; i != children.end(); ++i, ++n) {
string port_name = (*i)[midi_port_columns.full_name];
if (surface->port().output().connected_to (port_name)) {
output_combo->set_active (n);
output_found = true;
break;
}
}
if (!output_found) {
output_combo->set_active (0); /* disconnected */
}
}
Gtk::Widget*
US2400ProtocolGUI::device_dependent_widget ()
{
Gtk::Table* dd_table;
Gtk::Label* l;
int row = 0;
uint32_t n_surfaces = 1 + _cp.device_info().extenders();
uint32_t main_pos = _cp.device_info().master_position();
dd_table = Gtk::manage (new Gtk::Table (2, n_surfaces));
dd_table->set_row_spacings (4);
dd_table->set_col_spacings (6);
dd_table->set_border_width (12);
vector<string> midi_inputs;
vector<string> midi_outputs;
ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsOutput|ARDOUR::IsPhysical), midi_inputs);
ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsInput|ARDOUR::IsPhysical), midi_outputs);
input_combos.clear ();
output_combos.clear ();
int portcount = n_surfaces;
for (uint32_t n = 0; n < portcount; ++n) {
boost::shared_ptr<Surface> surface = _cp.nth_surface (n);
if (!surface) {
PBD::fatal << string_compose (_("programming error: %1\n"), string_compose ("n=%1 surface not found!", n)) << endmsg;
/*NOTREACHED*/
}
Gtk::ComboBox* input_combo = manage (new Gtk::ComboBox);
Gtk::ComboBox* output_combo = manage (new Gtk::ComboBox);
update_port_combos (midi_inputs, midi_outputs, input_combo, output_combo, surface);
input_combo->pack_start (midi_port_columns.short_name);
input_combo->set_data ("surface", surface.get());
input_combos.push_back (input_combo);
output_combo->pack_start (midi_port_columns.short_name);
output_combo->set_data ("surface", surface.get());
output_combos.push_back (output_combo);
boost::weak_ptr<Surface> ws (surface);
input_combo->signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &US2400ProtocolGUI::active_port_changed), input_combo, ws, true));
output_combo->signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &US2400ProtocolGUI::active_port_changed), output_combo, ws, false));
string send_string;
string receive_string;
//port 1,2,3 are faders & pan knobs. like mackie MCU
//port 4 is the joystick
//port 5 sends "chan" knobs (24 of them )
//port 6 --- ??? ( could be send to knobs... ? )
send_string = string_compose(_("US-2400 send port #%1 (faders %2 to %3):"), n + 1, n*8+1, n*8+8);
receive_string = string_compose(_("US-2400 receive port #%1 (faders %2 to %3):"), n + 1, n*8+1, n*8+8);
if (n==3) {
send_string = string_compose(_("US-2400 send port #%1 (joystick):"), n + 1);
receive_string = string_compose(_("US-2400 receive port #%1 (joystick):"), n + 1);
}
l = manage (new Gtk::Label (send_string));
l->set_alignment (1.0, 0.5);
dd_table->attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
dd_table->attach (*input_combo, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
row++;
l = manage (new Gtk::Label (receive_string));
l->set_alignment (1.0, 0.5);
dd_table->attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
dd_table->attach (*output_combo, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
row++;
}
row++;
l = manage (new Gtk::Label ("US-2400 Port #5 is reserved for use as a generic USB device. (click the CHAN button to activate)"));
l->set_alignment (1.0, 0.5);
dd_table->attach (*l, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
row++;
row++;
l = manage (new Gtk::Label ("US-2400 Port #6 is unused."));
l->set_alignment (1.0, 0.5);
dd_table->attach (*l, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
row++;
row++;
l = manage (new Gtk::Label ("NOTE: you must select mode 4 on the US-2400 unit."));
l->set_alignment (1.0, 0.5);
dd_table->attach (*l, 0, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
row++;
return dd_table;
}
CellRendererCombo*
US2400ProtocolGUI::make_action_renderer (Glib::RefPtr<TreeStore> model, Gtk::TreeModelColumnBase column)
{
CellRendererCombo* renderer = manage (new CellRendererCombo);
renderer->property_model() = model;
renderer->property_editable() = true;
renderer->property_text_column() = 0;
renderer->property_has_entry() = false;
renderer->signal_edited().connect (sigc::bind (sigc::mem_fun(*this, &US2400ProtocolGUI::action_changed), column));
return renderer;
}
void
US2400ProtocolGUI::build_available_action_menu ()
{
/* build a model of all available actions (needs to be tree structured
* more)
*/
available_action_model = TreeStore::create (available_action_columns);
vector<string> paths;
vector<string> labels;
vector<string> tooltips;
vector<string> keys;
vector<Glib::RefPtr<Gtk::Action> > actions;
typedef std::map<string,TreeIter> NodeMap;
NodeMap nodes;
NodeMap::iterator r;
Gtkmm2ext::ActionMap::get_all_actions (paths, labels, tooltips, keys, actions);
vector<string>::iterator k;
vector<string>::iterator p;
vector<string>::iterator t;
vector<string>::iterator l;
available_action_model->clear ();
/* Because there are button bindings built in that are not
in the key binding map, there needs to be a way to undo
a profile edit.
*/
TreeIter rowp;
TreeModel::Row parent;
rowp = available_action_model->append();
parent = *(rowp);
parent[available_action_columns.name] = _("Remove Binding");
/* Key aliasing */
rowp = available_action_model->append();
parent = *(rowp);
parent[available_action_columns.name] = _("Shift");
rowp = available_action_model->append();
parent = *(rowp);
parent[available_action_columns.name] = _("Control");
rowp = available_action_model->append();
parent = *(rowp);
parent[available_action_columns.name] = _("Option");
rowp = available_action_model->append();
parent = *(rowp);
parent[available_action_columns.name] = _("CmdAlt");
for (l = labels.begin(), k = keys.begin(), p = paths.begin(), t = tooltips.begin(); l != labels.end(); ++k, ++p, ++t, ++l) {
TreeModel::Row row;
vector<string> parts;
parts.clear ();
split (*p, parts, '/');
if (parts.empty()) {
continue;
}
//kinda kludgy way to avoid displaying menu items as mappable
if ( parts[1] == _("Main_menu") )
continue;
if ( parts[1] == _("JACK") )
continue;
if ( parts[1] == _("redirectmenu") )
continue;
if ( parts[1] == _("Editor_menus") )
continue;
if ( parts[1] == _("RegionList") )
continue;
if ( parts[1] == _("ProcessorMenu") )
continue;
if ((r = nodes.find (parts[1])) == nodes.end()) {
/* top level is missing */
TreeIter rowp;
TreeModel::Row parent;
rowp = available_action_model->append();
nodes[parts[1]] = rowp;
parent = *(rowp);
parent[available_action_columns.name] = parts[1];
row = *(available_action_model->append (parent.children()));
} else {
row = *(available_action_model->append ((*r->second)->children()));
}
/* add this action */
if (l->empty ()) {
row[available_action_columns.name] = *t;
action_map[*t] = *p;
} else {
row[available_action_columns.name] = *l;
action_map[*l] = *p;
}
row[available_action_columns.path] = (*p);
}
}
void
US2400ProtocolGUI::build_function_key_editor ()
{
function_key_editor.append_column (_("Key"), function_key_columns.name);
TreeViewColumn* col;
CellRendererCombo* renderer;
renderer = make_action_renderer (available_action_model, function_key_columns.plain);
col = manage (new TreeViewColumn (_("Plain"), *renderer));
col->add_attribute (renderer->property_text(), function_key_columns.plain);
function_key_editor.append_column (*col);
renderer = make_action_renderer (available_action_model, function_key_columns.shift);
col = manage (new TreeViewColumn (_("Shift"), *renderer));
col->add_attribute (renderer->property_text(), function_key_columns.shift);
function_key_editor.append_column (*col);
/*
* renderer = make_action_renderer (available_action_model, function_key_columns.control);
col = manage (new TreeViewColumn (_("Control"), *renderer));
col->add_attribute (renderer->property_text(), function_key_columns.control);
function_key_editor.append_column (*col);
renderer = make_action_renderer (available_action_model, function_key_columns.option);
col = manage (new TreeViewColumn (_("Option"), *renderer));
col->add_attribute (renderer->property_text(), function_key_columns.option);
function_key_editor.append_column (*col);
renderer = make_action_renderer (available_action_model, function_key_columns.cmdalt);
col = manage (new TreeViewColumn (_("Cmd/Alt"), *renderer));
col->add_attribute (renderer->property_text(), function_key_columns.cmdalt);
function_key_editor.append_column (*col);
renderer = make_action_renderer (available_action_model, function_key_columns.shiftcontrol);
col = manage (new TreeViewColumn (_("Shift+Control"), *renderer));
col->add_attribute (renderer->property_text(), function_key_columns.shiftcontrol);
function_key_editor.append_column (*col);
*/
function_key_model = ListStore::create (function_key_columns);
function_key_editor.set_model (function_key_model);
}
void
US2400ProtocolGUI::refresh_function_key_editor ()
{
function_key_editor.set_model (Glib::RefPtr<TreeModel>());
function_key_model->clear ();
/* now fill with data */
TreeModel::Row row;
DeviceProfile dp (_cp.device_profile());
DeviceInfo di;
for (int n = 0; n < US2400::Button::FinalGlobalButton; ++n) {
US2400::Button::ID bid = (US2400::Button::ID) n;
row = *(function_key_model->append());
if (di.global_buttons().find (bid) == di.global_buttons().end()) {
row[function_key_columns.name] = US2400::Button::id_to_name (bid);
} else {
row[function_key_columns.name] = di.get_global_button_name (bid) + "*";
}
row[function_key_columns.id] = bid;
Glib::RefPtr<Gtk::Action> act;
string action;
const string defstring = "\u2022";
/* We only allow plain bindings for Fn keys. All others are
* reserved for hard-coded actions.
*/
if (bid >= US2400::Button::F1 && bid <= US2400::Button::F6) {
action = dp.get_button_action (bid, 0);
if (action.empty()) {
row[function_key_columns.plain] = defstring;
} else {
if (action.find ('/') == string::npos) {
/* Probably a key alias */
row[function_key_columns.plain] = action;
} else {
act = ActionManager::get_action (action.c_str());
if (act) {
row[function_key_columns.plain] = act->get_label();
} else {
row[function_key_columns.plain] = defstring;
}
}
}
}
//~ /* We only allow plain bindings for Fn keys. All others are
//~ * reserved for hard-coded actions.
//~ */
//~
//~ if (bid >= US2400::Button::F1 && bid <= US2400::Button::F8) {
//~
//~ action = dp.get_button_action (bid, US2400Protocol::MODIFIER_SHIFT);
//~ if (action.empty()) {
//~ row[function_key_columns.shift] = defstring;
//~ } else {
//~ if (action.find ('/') == string::npos) {
//~ /* Probably a key alias */
//~ row[function_key_columns.shift] = action;
//~ } else {
//~ act = ActionManager::get_action (action.c_str());
//~ if (act) {
//~ row[function_key_columns.shift] = act->get_label();
//~ } else {
//~ row[function_key_columns.shift] = defstring;
//~ }
//~ }
//~ }
//~ }
//~ action = dp.get_button_action (bid, US2400Protocol::MODIFIER_CONTROL);
//~ if (action.empty()) {
//~ row[function_key_columns.control] = defstring;
//~ } else {
//~ if (action.find ('/') == string::npos) {
//~ /* Probably a key alias */
//~ row[function_key_columns.control] = action;
//~ } else {
//~ act = ActionManager::get_action (action.c_str());
//~ if (act) {
//~ row[function_key_columns.control] = act->get_label();
//~ } else {
//~ row[function_key_columns.control] = defstring;
//~ }
//~ }
//~ }
//~
//~ action = dp.get_button_action (bid, US2400Protocol::MODIFIER_OPTION);
//~ if (action.empty()) {
//~ row[function_key_columns.option] = defstring;
//~ } else {
//~ if (action.find ('/') == string::npos) {
//~ /* Probably a key alias */
//~ row[function_key_columns.option] = action;
//~ } else {
//~ act = ActionManager::get_action (action.c_str());
//~ if (act) {
//~ row[function_key_columns.option] = act->get_label();
//~ } else {
//~ row[function_key_columns.option] = defstring;
//~ }
//~ }
//~ }
//~
//~ action = dp.get_button_action (bid, US2400Protocol::MODIFIER_CMDALT);
//~ if (action.empty()) {
//~ row[function_key_columns.cmdalt] = defstring;
//~ } else {
//~ if (action.find ('/') == string::npos) {
//~ /* Probably a key alias */
//~ row[function_key_columns.cmdalt] = action;
//~ } else {
//~ act = ActionManager::get_action (action.c_str());
//~ if (act) {
//~ row[function_key_columns.cmdalt] = act->get_label();
//~ } else {
//~ row[function_key_columns.cmdalt] = defstring;
//~ }
//~ }
//~ }
//~
//~ action = dp.get_button_action (bid, (US2400Protocol::MODIFIER_SHIFT|US2400Protocol::MODIFIER_CONTROL));
//~ if (action.empty()) {
//~ row[function_key_columns.shiftcontrol] = defstring;
//~ } else {
//~ act = ActionManager::get_action (action.c_str());
//~ if (act) {
//~ row[function_key_columns.shiftcontrol] = act->get_label();
//~ } else {
//~ row[function_key_columns.shiftcontrol] = defstring;
//~ }
//~ }
}
function_key_editor.set_model (function_key_model);
}
void
US2400ProtocolGUI::action_changed (const Glib::ustring &sPath, const Glib::ustring &text, TreeModelColumnBase col)
{
// Remove Binding is not in the action map but still valid
bool remove (false);
if ( text == "Remove Binding") {
remove = true;
}
Gtk::TreePath path(sPath);
Gtk::TreeModel::iterator row = function_key_model->get_iter(path);
if (row) {
std::map<std::string,std::string>::iterator i = action_map.find (text);
if (i == action_map.end()) {
if (!remove) {
return;
}
}
Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (i->second.c_str());
if (act || remove) {
/* update visible text, using string supplied by
available action model so that it matches and is found
within the model.
*/
if (remove) {
Glib::ustring dot = "\u2022";
(*row).set_value (col.index(), dot);
} else {
(*row).set_value (col.index(), text);
}
/* update the current DeviceProfile, using the full
* path
*/
int modifier;
switch (col.index()) {
case 3:
modifier = US2400Protocol::MODIFIER_SHIFT;
break;
case 4:
modifier = US2400Protocol::MODIFIER_CONTROL;
break;
case 5:
modifier = US2400Protocol::MODIFIER_OPTION;
break;
case 6:
modifier = US2400Protocol::MODIFIER_CMDALT;
break;
case 7:
modifier = (US2400Protocol::MODIFIER_SHIFT|US2400Protocol::MODIFIER_CONTROL);
break;
default:
modifier = 0;
}
if (remove) {
_cp.device_profile().set_button_action ((*row)[function_key_columns.id], modifier, "");
} else {
_cp.device_profile().set_button_action ((*row)[function_key_columns.id], modifier, i->second);
}
_ignore_profile_changed = true;
_profile_combo.set_active_text ( _cp.device_profile().name() );
_ignore_profile_changed = false;
} else {
std::cerr << "no such action\n";
}
}
}
void
US2400ProtocolGUI::device_changed ()
{
if (_device_dependent_widget) {
table.remove (*_device_dependent_widget);
_device_dependent_widget = 0;
}
_device_dependent_widget = device_dependent_widget ();
_device_dependent_widget->show_all ();
table.attach (*_device_dependent_widget, 0, 12, device_dependent_row, device_dependent_row+1, AttachOptions(0), AttachOptions(0), 0, 0);
}
void
US2400ProtocolGUI::profile_combo_changed ()
{
if (!_ignore_profile_changed) {
string profile = _profile_combo.get_active_text();
_cp.set_profile (profile);
refresh_function_key_editor ();
}
}
Glib::RefPtr<Gtk::ListStore>
US2400ProtocolGUI::build_midi_port_list (vector<string> const & ports, bool for_input)
{
Glib::RefPtr<Gtk::ListStore> store = ListStore::create (midi_port_columns);
TreeModel::Row row;
row = *store->append ();
row[midi_port_columns.full_name] = string();
row[midi_port_columns.short_name] = _("Disconnected");
for (vector<string>::const_iterator p = ports.begin(); p != ports.end(); ++p) {
row = *store->append ();
row[midi_port_columns.full_name] = *p;
std::string pn = ARDOUR::AudioEngine::instance()->get_pretty_name_by_name (*p);
if (pn.empty ()) {
pn = (*p).substr ((*p).find (':') + 1);
}
row[midi_port_columns.short_name] = pn;
}
return store;
}
void
US2400ProtocolGUI::active_port_changed (Gtk::ComboBox* combo, boost::weak_ptr<Surface> ws, bool for_input)
{
if (ignore_active_change) {
return;
}
boost::shared_ptr<Surface> surface = ws.lock();
if (!surface) {
return;
}
TreeModel::iterator active = combo->get_active ();
string new_port = (*active)[midi_port_columns.full_name];
if (new_port.empty()) {
if (for_input) {
surface->port().input().disconnect_all ();
} else {
surface->port().output().disconnect_all ();
}
return;
}
if (for_input) {
if (!surface->port().input().connected_to (new_port)) {
surface->port().input().disconnect_all ();
surface->port().input().connect (new_port);
}
} else {
if (!surface->port().output().connected_to (new_port)) {
surface->port().output().disconnect_all ();
surface->port().output().connect (new_port);
}
}
}

142
libs/surfaces/us2400/gui.h Normal file
View File

@@ -0,0 +1,142 @@
/*
Copyright (C) 2010-2012 Paul Davis
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.
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.
*/
#include <vector>
#include <gtkmm/combobox.h>
#include <gtkmm/box.h>
#include <gtkmm/spinbutton.h>
#include <gtkmm/table.h>
#include <gtkmm/treeview.h>
#include <gtkmm/liststore.h>
#include <gtkmm/notebook.h>
#include <gtkmm/scrolledwindow.h>
namespace Gtk {
class CellRendererCombo;
}
#include "button.h"
#include "pbd/i18n.h"
namespace ArdourSurface {
class US2400Protocol;
namespace US2400 {
class Surface;
}
class US2400ProtocolGUI : public Gtk::Notebook
{
public:
US2400ProtocolGUI (US2400Protocol &);
private:
US2400Protocol& _cp;
Gtk::Table table;
Gtk::ComboBoxText _profile_combo;
typedef std::vector<Gtk::ComboBox*> PortCombos;
PortCombos input_combos;
PortCombos output_combos;
struct MidiPortColumns : public Gtk::TreeModel::ColumnRecord {
MidiPortColumns() {
add (short_name);
add (full_name);
}
Gtk::TreeModelColumn<std::string> short_name;
Gtk::TreeModelColumn<std::string> full_name;
};
struct AvailableActionColumns : public Gtk::TreeModel::ColumnRecord {
AvailableActionColumns() {
add (name);
add (path);
}
Gtk::TreeModelColumn<std::string> name;
Gtk::TreeModelColumn<std::string> path;
};
struct FunctionKeyColumns : public Gtk::TreeModel::ColumnRecord {
FunctionKeyColumns() {
add (name);
add (id);
add (plain);
add (shift);
add (control);
add (option);
add (cmdalt);
add (shiftcontrol);
};
Gtk::TreeModelColumn<std::string> name;
Gtk::TreeModelColumn<US2400::Button::ID> id;
Gtk::TreeModelColumn<std::string> plain;
Gtk::TreeModelColumn<std::string> shift;
Gtk::TreeModelColumn<std::string> control;
Gtk::TreeModelColumn<std::string> option;
Gtk::TreeModelColumn<std::string> cmdalt;
Gtk::TreeModelColumn<std::string> shiftcontrol;
};
AvailableActionColumns available_action_columns;
FunctionKeyColumns function_key_columns;
MidiPortColumns midi_port_columns;
Gtk::ScrolledWindow function_key_scroller;
Gtk::TreeView function_key_editor;
Glib::RefPtr<Gtk::ListStore> function_key_model;
Glib::RefPtr<Gtk::TreeStore> available_action_model;
Glib::RefPtr<Gtk::ListStore> build_midi_port_list (bool for_input);
void build_available_action_menu ();
void refresh_function_key_editor ();
void build_function_key_editor ();
void action_changed (const Glib::ustring &sPath, const Glib::ustring &text, Gtk::TreeModelColumnBase);
Gtk::CellRendererCombo* make_action_renderer (Glib::RefPtr<Gtk::TreeStore> model, Gtk::TreeModelColumnBase);
void profile_combo_changed ();
std::map<std::string,std::string> action_map; // map from action names to paths
Gtk::Widget* device_dependent_widget ();
Gtk::Widget* _device_dependent_widget;
int device_dependent_row;
PBD::ScopedConnection device_change_connection;
void device_changed ();
void update_port_combos (std::vector<std::string> const&, std::vector<std::string> const&,
Gtk::ComboBox* input_combo,
Gtk::ComboBox* output_combo,
boost::shared_ptr<US2400::Surface> surface);
PBD::ScopedConnection connection_change_connection;
void connection_handler ();
Glib::RefPtr<Gtk::ListStore> build_midi_port_list (std::vector<std::string> const & ports, bool for_input);
bool _ignore_profile_changed;
bool ignore_active_change;
void active_port_changed (Gtk::ComboBox* combo, boost::weak_ptr<US2400::Surface> ws, bool for_input);
};
}

View File

@@ -0,0 +1,102 @@
/*
Copyright (C) 2006,2007 Paul Davis
Copyright (C) 2017 Ben Loftis
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.
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.
*/
#include <stdexcept>
#include "pbd/error.h"
#include "ardour/rc_configuration.h"
#include "control_protocol/control_protocol.h"
#include "us2400_control_protocol.h"
using namespace ARDOUR;
using namespace PBD;
using namespace std;
using namespace ArdourSurface;
using namespace US2400;
static ControlProtocol*
new_us2400_protocol (ControlProtocolDescriptor*, Session* s)
{
US2400Protocol* mcp = 0;
try {
mcp = new US2400Protocol (*s);
/* do not set active here - wait for set_state() */
}
catch (exception & e) {
error << "Error instantiating US-2400: " << e.what() << endmsg;
delete mcp;
mcp = 0;
}
return mcp;
}
static void
delete_us2400_protocol (ControlProtocolDescriptor*, ControlProtocol* cp)
{
try
{
delete cp;
}
catch ( exception & e )
{
cout << "Exception caught trying to destroy US-2400: " << e.what() << endl;
}
}
/**
This is called on startup to check whether the lib should be loaded.
So anything that can be changed in the UI should not be used here to
prevent loading of the lib.
*/
static bool
probe_us2400_protocol (ControlProtocolDescriptor*)
{
return US2400Protocol::probe();
}
static void*
us2400_request_buffer_factory (uint32_t num_requests)
{
return US2400Protocol::request_factory (num_requests);
}
// Field names commented out by JE - 06-01-2010
static ControlProtocolDescriptor us2400_descriptor = {
/*name : */ "Tascam US-2400",
/*id : */ "uri://ardour.org/surfaces/us2400:0",
/*ptr : */ 0,
/*module : */ 0,
/*mandatory : */ 0,
// actually, the surface does support feedback, but all this
// flag does is show a submenu on the UI, which is useless for the mackie
// because feedback is always on. In any case, who'd want to use the
// mcu without the motorised sliders doing their thing?
/*supports_feedback : */ false,
/*probe : */ probe_us2400_protocol,
/*initialize : */ new_us2400_protocol,
/*destroy : */ delete_us2400_protocol,
/*request_buffer_factory */ us2400_request_buffer_factory
};
extern "C" ARDOURSURFACE_API ControlProtocolDescriptor* protocol_descriptor () { return &us2400_descriptor; }

View File

@@ -0,0 +1,37 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
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.
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.
*/
#include "jog.h"
#include "surface.h"
#include "control_group.h"
using namespace ArdourSurface;
using namespace US2400;
const int Jog::ID = 0x3c;
Control*
Jog::factory (Surface& surface, int id, const char* name, Group& group)
{
Jog* j = new Jog (id, name, group);
surface.pots[id] = j;
surface.controls.push_back (j);
group.add (*j);
return j;
}

View File

@@ -0,0 +1,49 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
Copyright (C) 2017 Ben Loftis
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.
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 __ardour_us2400_control_protocol_jog_h__
#define __ardour_us2400_control_protocol_jog_h__
#include "controls.h"
#include "pot.h"
namespace ArdourSurface {
namespace US2400 {
class Jog : public Pot
{
public:
static const int ID;
Jog (int id, std::string name, Group & group)
: Pot (id, name, group)
{
}
MidiByteArray zero() { return MidiByteArray(); }
static Control* factory (Surface&, int id, const char*, Group&);
};
}
}
#endif /* __ardour_us2400_control_protocol_jog_h__ */

View File

@@ -0,0 +1,70 @@
/*
Copyright (C) 2012 Paul Davis
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.
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.
*/
#include <cmath>
#include "ardour/session.h"
#include "jog_wheel.h"
#include "us2400_control_protocol.h"
#include "surface_port.h"
#include "controls.h"
#include "surface.h"
#include <algorithm>
using namespace ArdourSurface;
using namespace US2400;
JogWheel::JogWheel (US2400Protocol & mcp)
: _mcp (mcp)
, _mode (scroll)
{
}
void
JogWheel::set_mode (Mode m)
{
_mode = m;
}
void JogWheel::jog_event (float delta)
{
if (_mcp.zoom_mode()) {
if (delta > 0) {
for (unsigned int i = 0; i < fabs (delta); ++i) {
_mcp.ZoomIn();
}
} else {
for (unsigned int i = 0; i < fabs (delta); ++i) {
_mcp.ZoomOut();
}
}
return;
}
switch (_mode) {
case scroll:
_mcp.ScrollTimeline (delta/4.0);
break;
default:
break;
}
}

View File

@@ -0,0 +1,37 @@
#ifndef mackie_jog_wheel
#define mackie_jog_wheel
#include "timer.h"
#include <stack>
#include <deque>
#include <queue>
namespace ArdourSurface {
class US2400Protocol;
namespace US2400
{
class JogWheel
{
public:
enum Mode { scroll };
JogWheel (US2400Protocol & mcp);
/// As the wheel turns...
void jog_event (float delta);
void set_mode (Mode m);
Mode mode() const { return _mode; }
private:
US2400Protocol & _mcp;
Mode _mode;
};
}
}
#endif

View File

@@ -0,0 +1,75 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
Copyright (C) 2017 Ben Loftis
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.
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.
*/
#include "led.h"
#include "surface.h"
#include "control_group.h"
using namespace ArdourSurface;
using namespace US2400;
const int Led::FaderTouch = 0x70;
const int Led::Timecode = 0x71;
const int Led::Beats = 0x72;
const int Led::RudeSolo = 0x73;
const int Led::RelayClick = 0x74;
Control*
Led::factory (Surface& surface, int id, const char* name, Group& group)
{
Led* l = new Led (id, name, group);
surface.leds[id] = l;
surface.controls.push_back (l);
group.add (*l);
return l;
}
MidiByteArray
Led::set_state (LedState new_state)
{
if (new_state == last_state) {
if (new_state == llast_state) {
return MidiByteArray ();
}
}
llast_state = last_state;
last_state = new_state;
state = new_state;
MIDI::byte msg = 0;
switch (state.state()) {
case LedState::on:
msg = 0x7f;
break;
case LedState::off:
msg = 0x00;
break;
case LedState::flashing:
msg = 0x01;
break;
case LedState::none:
return MidiByteArray ();
}
return MidiByteArray (3, 0x90, id(), msg);
}

View File

@@ -0,0 +1,66 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
Copyright (C) 2017 Ben Loftis
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.
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 __ardour_us2400_control_protocol_led_h__
#define __ardour_us2400_control_protocol_led_h__
#include "controls.h"
#include "midi_byte_array.h"
#include "types.h"
namespace ArdourSurface {
namespace US2400 {
class Led : public Control
{
public:
static const int FaderTouch;
static const int Timecode;
static const int Beats;
static const int RudeSolo;
static const int RelayClick;
Led (int id, std::string name, Group & group)
: Control (id, name, group)
, state (off)
, last_state (off)
, llast_state (off)
{
}
Led & led() { return *this; }
MidiByteArray set_state (LedState);
MidiByteArray zero() { return set_state (off); }
static Control* factory (Surface&, int id, const char*, Group&);
private:
LedState state;
LedState last_state;
LedState llast_state;
};
}
}
#endif /* __ardour_us2400_control_protocol_led_h__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,137 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
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.
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.
*/
#include <cmath>
#include "pbd/compose.h"
#include "ardour/debug.h"
#include "meter.h"
#include "surface.h"
#include "surface_port.h"
#include "control_group.h"
#include "us2400_control_protocol.h"
using namespace PBD;
using namespace ArdourSurface;
using namespace US2400;
Control*
Meter::factory (Surface& surface, int id, const char* name, Group& group)
{
Meter* m = new Meter (id, name, group);
surface.meters[id] = m;
surface.controls.push_back (m);
group.add (*m);
return m;
}
void
Meter::notify_metering_state_changed(Surface& surface, bool transport_is_rolling, bool metering_active)
{
return;
MidiByteArray msg;
// sysex header
msg << surface.sysex_hdr();
// code for Channel Meter Enable Message
msg << 0x20;
// Channel identification
msg << id();
// Enable (0x07) / Disable (0x00) level meter on LCD, peak hold display on horizontal meter and signal LED
_enabled = ((surface.mcp().device_info().has_separate_meters() || transport_is_rolling) && metering_active);
msg << (_enabled ? 0x07 : 0x00);
// sysex trailer
msg << MIDI::eox;
surface.write (msg);
}
void
Meter::send_update (Surface& surface, float dB)
{
float def = 0.0f; /* Meter deflection %age */
// DEBUG_TRACE (DEBUG::US2400, string_compose ("Meter ID %1 dB %2\n", id(), dB));
if (dB < -70.0f) {
def = 0.0f;
} else if (dB < -60.0f) {
def = (dB + 70.0f) * 0.25f;
} else if (dB < -50.0f) {
def = (dB + 60.0f) * 0.5f + 2.5f;
} else if (dB < -40.0f) {
def = (dB + 50.0f) * 0.75f + 7.5f;
} else if (dB < -30.0f) {
def = (dB + 40.0f) * 1.5f + 15.0f;
} else if (dB < -20.0f) {
def = (dB + 30.0f) * 2.0f + 30.0f;
} else if (dB < 6.0f) {
def = (dB + 20.0f) * 2.5f + 50.0f;
} else {
def = 115.0f;
}
/* 115 is the deflection %age that would be
when dB=6.0. this is an arbitrary
endpoint for our scaling.
*/
MidiByteArray msg;
if (def > 100.0f) {
if (!overload_on) {
overload_on = true;
surface.write (MidiByteArray (2, 0xd0, (id() << 4) | 0xe));
}
} else {
if (overload_on) {
overload_on = false;
surface.write (MidiByteArray (2, 0xd0, (id() << 4) | 0xf));
}
}
/* we can use up to 13 segments */
int segment = lrintf ((def/115.0) * 13.0);
//only send an update 'twice'
if (segment == last_update_segment) {
if (segment == llast_update_segment) {
return;
}
}
llast_update_segment = last_update_segment;
last_update_segment = segment;
surface.write (MidiByteArray (2, 0xd0, (id()<<4) | segment));
}
MidiByteArray
Meter::zero ()
{
return MidiByteArray (2, 0xD0, (id()<<4 | 0));
}

View File

@@ -0,0 +1,66 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
Copyright (C) 2017 Ben Loftis
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.
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 __ardour_us2400_control_protocol_meter_h__
#define __ardour_us2400_control_protocol_meter_h__
#include "controls.h"
#include "midi_byte_array.h"
namespace ArdourSurface {
namespace US2400 {
class SurfacePort;
class Meter : public Control
{
public:
Meter (int id, std::string name, Group & group)
: Control (id, name, group)
, _enabled (false)
, overload_on (false),
last_update_segment (-1),
llast_update_segment (-1)
{}
void send_update (Surface&, float dB);
bool enabled () const { return _enabled; }
MidiByteArray zero();
static Control* factory (Surface&, int id, const char*, Group&);
void notify_metering_state_changed(Surface& surface, bool transport_is_rolling, bool metering_active);
private:
bool _enabled;
bool overload_on;
int last_update_segment;
int llast_update_segment;
};
} // US2400 namespace
} // ArdourSurface namespace
#endif /* __ardour_us2400_control_protocol_meter_h__ */

View File

@@ -0,0 +1,96 @@
/*
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 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.
*/
#include "midi_byte_array.h"
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
#include <cstdarg>
#include <iomanip>
#include <stdexcept>
using namespace std;
MidiByteArray::MidiByteArray (size_t size, MIDI::byte array[])
: std::vector<MIDI::byte>()
{
for (size_t i = 0; i < size; ++i)
{
push_back (array[i]);
}
}
MidiByteArray::MidiByteArray (size_t count, MIDI::byte first, ...)
: vector<MIDI::byte>()
{
push_back (first);
va_list var_args;
va_start (var_args, first);
for (size_t i = 1; i < count; ++i)
{
MIDI::byte b = va_arg (var_args, int);
push_back (b);
}
va_end (var_args);
}
void MidiByteArray::copy (size_t count, MIDI::byte * arr)
{
for (size_t i = 0; i < count; ++i) {
push_back (arr[i]);
}
}
MidiByteArray & operator << (MidiByteArray & mba, const MIDI::byte & b)
{
mba.push_back (b);
return mba;
}
MidiByteArray & operator << (MidiByteArray & mba, const MidiByteArray & barr)
{
back_insert_iterator<MidiByteArray> bit (mba);
copy (barr.begin(), barr.end(), bit);
return mba;
}
ostream & operator << (ostream & os, const MidiByteArray & mba)
{
os << "[";
char fill = os.fill('0');
for (MidiByteArray::const_iterator it = mba.begin(); it != mba.end(); ++it) {
if (it != mba.begin()) os << " ";
os << hex << setw(2) << (int)*it;
}
os.fill (fill);
os << dec;
os << "]";
return os;
}
MidiByteArray & operator << (MidiByteArray & mba, const std::string & st)
{
/* note that this assumes that "st" is ASCII encoded
*/
mba.insert (mba.end(), st.begin(), st.end());
return mba;
}

View File

@@ -0,0 +1,76 @@
/*
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 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.
*/
#ifndef midi_byte_array_h
#define midi_byte_array_h
#include <iostream>
#include <vector>
#include <boost/shared_array.hpp>
//#include <midi++/types.h>
namespace MIDI {
typedef unsigned char byte;
}
/**
To make building arrays of bytes easier. Thusly:
MidiByteArray mba;
mba << 0xf0 << 0x00 << 0xf7;
MidiByteArray buf;
buf << mba;
MidiByteArray direct( 3, 0xf0, 0x00, 0xf7 );
cout << mba << endl;
cout << buf << endl;
cout << direct << endl;
will all result in "f0 00 f7" being output to stdout
*/
class MidiByteArray : public std::vector<MIDI::byte>
{
public:
MidiByteArray() : std::vector<MIDI::byte>() {}
MidiByteArray( size_t count, MIDI::byte array[] );
/**
Accepts a preceding count, and then a list of bytes
*/
MidiByteArray( size_t count, MIDI::byte first, ... );
/// copy the given number of bytes from the given array
void copy( size_t count, MIDI::byte arr[] );
};
/// append the given byte to the end of the array
MidiByteArray & operator << ( MidiByteArray & mba, const MIDI::byte & b );
/// append the given string to the end of the array
MidiByteArray & operator << ( MidiByteArray & mba, const std::string & );
/// append the given array to the end of this array
MidiByteArray & operator << ( MidiByteArray & mba, const MidiByteArray & barr );
/// output the bytes as hex to the given stream
std::ostream & operator << ( std::ostream & os, const MidiByteArray & mba );
#endif

View File

@@ -0,0 +1,86 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
Copyright (C) 2017 Ben Loftis
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.
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.
*/
#include <cmath>
#include "pot.h"
#include "surface.h"
#include "control_group.h"
using namespace ArdourSurface;
using namespace US2400;
int const Pot::External = 0x2e; /* specific ID for "vpot" representing external control */
int const Pot::ID = 0x10; /* base value for v-pot IDs */
Control*
Pot::factory (Surface& surface, int id, const char* name, Group& group)
{
Pot* p = new Pot (id, name, group);
surface.pots[id] = p;
surface.controls.push_back (p);
group.add (*p);
return p;
}
MidiByteArray
Pot::set (float val, bool onoff)
{
int posi = lrintf (128.0 * val);
if (posi == last_update_position) {
if (posi == llast_update_position) {
return MidiByteArray();
}
}
llast_update_position = last_update_position;
last_update_position = posi;
// TODO do an exact calc for 0.50? To allow manually re-centering the port.
// center on if val is "very close" to 0.50
MIDI::byte msg = (val > 0.48 && val < 0.58 ? 1 : 0) << 6;
// Pot/LED mode
msg |= (_mode << 4);
/*
* Even though a width value may be negative, there is
* technically still width there, it is just reversed,
* so make sure to show it on the LED ring appropriately.
*/
if (val < 0){
val = val * -1;
}
// val, but only if off hasn't explicitly been set
if (onoff) {
if (_mode == spread) {
msg |= (lrintf (val * 6)) & 0x0f; // 0b00001111
} else {
msg |= (lrintf (val * 10.0) + 1) & 0x0f; // 0b00001111
}
}
/* outbound LED message requires 0x20 to be added to the LED's id
*/
return MidiByteArray (3, 0xb0, 0x20 + id(), msg);
}

View File

@@ -0,0 +1,64 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
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.
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 __ardour_us2400_control_protocol_pot_h__
#define __ardour_us2400_control_protocol_pot_h__
#include "controls.h"
namespace ArdourSurface {
namespace US2400 {
class Pot : public Control
{
public:
static int const External;
static int const ID;
enum Mode {
dot = 0,
boost_cut = 1,
wrap = 2,
spread = 3
};
Pot (int id, std::string name, Group & group)
: Control (id, name, group),
last_update_position (-1),
llast_update_position (-1) {}
void set_mode(Mode m) {_mode = m; last_update_position = -1; }
MidiByteArray set (float, bool);
MidiByteArray zero() { return set (0.0, false); }
static Control* factory (Surface&, int id, const char*, Group&);
int last_update_position;
int llast_update_position;
Mode _mode;
};
}
}
#endif /* __ardour_us2400_control_protocol_pot_h__ */

View File

@@ -0,0 +1,970 @@
/*
Copyright (C) 2006,2007 John Anderson
Copyright (C) 2012 Paul Davis
Copyright (C) 2017 Ben Loftis
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.
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.
*/
#include <sstream>
#include <vector>
#include <climits>
#include <stdint.h>
#include <sys/time.h>
#include <glibmm/convert.h>
#include "midi++/port.h"
#include "pbd/compose.h"
#include "pbd/convert.h"
#include "ardour/amp.h"
#include "ardour/bundle.h"
#include "ardour/debug.h"
#include "ardour/midi_ui.h"
#include "ardour/meter.h"
#include "ardour/monitor_control.h"
#include "ardour/plugin_insert.h"
#include "ardour/pannable.h"
#include "ardour/panner.h"
#include "ardour/panner_shell.h"
#include "ardour/phase_control.h"
#include "ardour/rc_configuration.h"
#include "ardour/record_enable_control.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "ardour/send.h"
#include "ardour/solo_isolate_control.h"
#include "ardour/track.h"
#include "ardour/midi_track.h"
#include "ardour/user_bundle.h"
#include "ardour/profile.h"
#include "ardour/value_as_string.h"
#include "us2400_control_protocol.h"
#include "surface_port.h"
#include "surface.h"
#include "strip.h"
#include "button.h"
#include "led.h"
#include "pot.h"
#include "fader.h"
#include "jog.h"
#include "meter.h"
using namespace std;
using namespace ARDOUR;
using namespace PBD;
using namespace ArdourSurface;
using namespace US2400;
#ifndef timeradd /// only avail with __USE_BSD
#define timeradd(a,b,result) \
do { \
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
if ((result)->tv_usec >= 1000000) \
{ \
++(result)->tv_sec; \
(result)->tv_usec -= 1000000; \
} \
} while (0)
#endif
#define ui_context() US2400Protocol::instance() /* a UICallback-derived object that specifies the event loop for signal handling */
Strip::Strip (Surface& s, const std::string& name, int index, const map<Button::ID,StripButtonInfo>& strip_buttons)
: Group (name)
, _solo (0)
, _mute (0)
, _select (0)
, _fader_touch (0)
, _vpot (0)
, _fader (0)
, _meter (0)
, _index (index)
, _surface (&s)
, _controls_locked (false)
, _transport_is_rolling (false)
, _metering_active (true)
, _pan_mode (PanAzimuthAutomation)
{
_fader = dynamic_cast<Fader*> (Fader::factory (*_surface, index, "fader", *this));
_vpot = dynamic_cast<Pot*> (Pot::factory (*_surface, Pot::ID + index, "vpot", *this));
if (s.mcp().device_info().has_meters()) {
_meter = dynamic_cast<Meter*> (Meter::factory (*_surface, index, "meter", *this));
}
for (map<Button::ID,StripButtonInfo>::const_iterator b = strip_buttons.begin(); b != strip_buttons.end(); ++b) {
Button* bb = dynamic_cast<Button*> (Button::factory (*_surface, b->first, b->second.base_id + index, b->second.name, *this));
DEBUG_TRACE (DEBUG::US2400, string_compose ("surface %1 strip %2 new button BID %3 id %4 from base %5\n",
_surface->number(), index, Button::id_to_name (bb->bid()),
bb->id(), b->second.base_id));
}
_trickle_counter = 0;
}
Strip::~Strip ()
{
/* surface is responsible for deleting all controls */
}
void
Strip::add (Control & control)
{
Button* button;
Group::add (control);
/* fader, vpot, meter were all set explicitly */
if ((button = dynamic_cast<Button*>(&control)) != 0) {
switch (button->bid()) {
case Button::Mute:
_mute = button;
break;
case Button::Solo:
_solo = button;
break;
case Button::Select:
_select = button;
break;
case Button::FaderTouch:
_fader_touch = button;
break;
default:
break;
}
}
}
void
Strip::set_stripable (boost::shared_ptr<Stripable> r, bool /*with_messages*/)
{
if (_controls_locked) {
return;
}
stripable_connections.drop_connections ();
_solo->set_control (boost::shared_ptr<AutomationControl>());
_mute->set_control (boost::shared_ptr<AutomationControl>());
_select->set_control (boost::shared_ptr<AutomationControl>());
_fader->set_control (boost::shared_ptr<AutomationControl>());
_vpot->set_control (boost::shared_ptr<AutomationControl>());
_stripable = r;
reset_saved_values ();
if (!r) {
DEBUG_TRACE (DEBUG::US2400, string_compose ("Surface %1 Strip %2 mapped to null route\n", _surface->number(), _index));
zero ();
return;
}
DEBUG_TRACE (DEBUG::US2400, string_compose ("Surface %1 strip %2 now mapping stripable %3\n",
_surface->number(), _index, _stripable->name()));
_solo->set_control (_stripable->solo_control());
_mute->set_control (_stripable->mute_control());
_stripable->solo_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_solo_changed, this), ui_context());
_stripable->mute_control()->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_mute_changed, this), ui_context());
boost::shared_ptr<AutomationControl> pan_control = _stripable->pan_azimuth_control();
if (pan_control) {
pan_control->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_azi_changed, this, false), ui_context());
}
pan_control = _stripable->pan_width_control();
if (pan_control) {
pan_control->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_panner_width_changed, this, false), ui_context());
}
_stripable->gain_control()->Changed.connect(stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_gain_changed, this, false), ui_context());
_stripable->PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
_stripable->presentation_info().PropertyChanged.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_property_changed, this, _1), ui_context());
// TODO this works when a currently-banked stripable is made inactive, but not
// when a stripable is activated which should be currently banked.
_stripable->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_stripable_deleted, this), ui_context());
/* setup legal VPot modes for this stripable */
possible_pot_parameters.clear();
if (_stripable->pan_azimuth_control()) {
possible_pot_parameters.push_back (PanAzimuthAutomation);
}
if (_stripable->pan_width_control()) {
possible_pot_parameters.push_back (PanWidthAutomation);
}
if (_stripable->pan_elevation_control()) {
possible_pot_parameters.push_back (PanElevationAutomation);
}
if (_stripable->pan_frontback_control()) {
possible_pot_parameters.push_back (PanFrontBackAutomation);
}
if (_stripable->pan_lfe_control()) {
possible_pot_parameters.push_back (PanLFEAutomation);
}
_pan_mode = PanAzimuthAutomation;
if (_surface->mcp().subview_mode() == US2400Protocol::None) {
set_vpot_parameter (_pan_mode);
}
_fader->set_control (_stripable->gain_control());
notify_all ();
}
void
Strip::reset_stripable ()
{
stripable_connections.drop_connections ();
_solo->set_control (boost::shared_ptr<AutomationControl>());
_mute->set_control (boost::shared_ptr<AutomationControl>());
_select->set_control (boost::shared_ptr<AutomationControl>());
_fader->reset_control ();
_vpot->reset_control ();
_stripable.reset();
reset_saved_values ();
notify_all ();
}
void
Strip::notify_all()
{
// if (!_stripable) {
// zero ();
// return;
// }
// The active V-pot control may not be active for this strip
// But if we zero it in the controls function it may erase
// the one we do want
// _surface->write (_vpot->zero());
notify_solo_changed ();
notify_mute_changed ();
notify_gain_changed ();
notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::selected));
notify_panner_azi_changed ();
notify_vpot_change ();
notify_panner_width_changed ();
notify_record_enable_changed ();
// notify_processor_changed ();
}
void
Strip::notify_solo_changed ()
{
// if (_stripable && _solo) {
// _surface->write (_solo->set_state (_stripable->solo_control()->soloed() ? on : off));
// }
_trickle_counter = 0;
}
void
Strip::notify_mute_changed ()
{
DEBUG_TRACE (DEBUG::US2400, string_compose ("Strip %1 mute changed\n", _index));
// if (_stripable && _mute) {
// DEBUG_TRACE (DEBUG::US2400, string_compose ("\tstripable muted ? %1\n", _stripable->mute_control()->muted()));
// DEBUG_TRACE (DEBUG::US2400, string_compose ("mute message: %1\n", _mute->set_state (_stripable->mute_control()->muted() ? on : off)));
//
// _surface->write (_mute->set_state (_stripable->mute_control()->muted() ? on : off));
// } else {
// _surface->write (_mute->zero());
// }
_trickle_counter = 0;
}
void
Strip::notify_record_enable_changed ()
{
}
void
Strip::notify_stripable_deleted ()
{
_surface->mcp().notify_stripable_removed ();
_surface->mcp().refresh_current_bank();
}
void
Strip::notify_gain_changed (bool force_update)
{
_trickle_counter = 0;
}
void
Strip::notify_processor_changed (bool force_update)
{
}
void
Strip::notify_property_changed (const PropertyChange& what_changed)
{
}
void
Strip::update_selection_state ()
{
_trickle_counter = 0;
// if(_stripable) {
// _surface->write (_select->set_state (_stripable->is_selected()));
// }
}
void
Strip::show_stripable_name ()
{
}
void
Strip::notify_vpot_change ()
{
_trickle_counter = 0;
}
void
Strip::notify_panner_azi_changed (bool force_update)
{
_trickle_counter = 0;
}
void
Strip::notify_panner_width_changed (bool force_update)
{
_trickle_counter = 0;
}
void
Strip::select_event (Button&, ButtonState bs)
{
DEBUG_TRACE (DEBUG::US2400, "select button\n");
if (bs == press) {
int ms = _surface->mcp().main_modifier_state();
if (ms & US2400Protocol::MODIFIER_CMDALT) {
_controls_locked = !_controls_locked;
return;
}
DEBUG_TRACE (DEBUG::US2400, "add select button on press\n");
_surface->mcp().add_down_select_button (_surface->number(), _index);
_surface->mcp().select_range (_surface->mcp().global_index (*this));
} else {
DEBUG_TRACE (DEBUG::US2400, "remove select button on release\n");
_surface->mcp().remove_down_select_button (_surface->number(), _index);
}
}
void
Strip::vselect_event (Button&, ButtonState bs)
{
}
void
Strip::fader_touch_event (Button&, ButtonState bs)
{
DEBUG_TRACE (DEBUG::US2400, string_compose ("fader touch, press ? %1\n", (bs == press)));
if (bs == press) {
boost::shared_ptr<AutomationControl> ac = _fader->control ();
_fader->set_in_use (true);
_fader->start_touch (_surface->mcp().transport_frame());
} else {
_fader->set_in_use (false);
_fader->stop_touch (_surface->mcp().transport_frame());
}
}
void
Strip::handle_button (Button& button, ButtonState bs)
{
boost::shared_ptr<AutomationControl> control;
if (bs == press) {
button.set_in_use (true);
} else {
button.set_in_use (false);
}
DEBUG_TRACE (DEBUG::US2400, string_compose ("strip %1 handling button %2 press ? %3\n", _index, button.bid(), (bs == press)));
switch (button.bid()) {
case Button::Select:
select_event (button, bs);
break;
case Button::FaderTouch:
fader_touch_event (button, bs);
break;
default:
if ((control = button.control ())) {
if (bs == press) {
DEBUG_TRACE (DEBUG::US2400, "add button on press\n");
_surface->mcp().add_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
float new_value = control->get_value() ? 0.0 : 1.0;
/* get all controls that either have their
* button down or are within a range of
* several down buttons
*/
US2400Protocol::ControlList controls = _surface->mcp().down_controls ((AutomationType) control->parameter().type(),
_surface->mcp().global_index(*this));
DEBUG_TRACE (DEBUG::US2400, string_compose ("there are %1 buttons down for control type %2, new value = %3\n",
controls.size(), control->parameter().type(), new_value));
/* apply change, with potential modifier semantics */
Controllable::GroupControlDisposition gcd;
if (_surface->mcp().main_modifier_state() & US2400Protocol::MODIFIER_SHIFT) {
gcd = Controllable::InverseGroup;
} else {
gcd = Controllable::UseGroup;
}
for (US2400Protocol::ControlList::iterator c = controls.begin(); c != controls.end(); ++c) {
(*c)->set_value (new_value, gcd);
}
} else {
DEBUG_TRACE (DEBUG::US2400, "remove button on release\n");
_surface->mcp().remove_down_button ((AutomationType) control->parameter().type(), _surface->number(), _index);
}
}
break;
}
}
void
Strip::handle_fader_touch (Fader& fader, bool touch_on)
{
if (touch_on) {
fader.start_touch (_surface->mcp().transport_frame());
} else {
fader.stop_touch (_surface->mcp().transport_frame());
}
}
void
Strip::handle_fader (Fader& fader, float position)
{
DEBUG_TRACE (DEBUG::US2400, string_compose ("fader to %1\n", position));
boost::shared_ptr<AutomationControl> ac = fader.control();
if (!ac) {
return;
}
Controllable::GroupControlDisposition gcd = Controllable::UseGroup;
if (_surface->mcp().main_modifier_state() & US2400Protocol::MODIFIER_SHIFT) {
gcd = Controllable::InverseGroup;
}
fader.set_value (position, gcd);
/* From the Mackie Control MIDI implementation docs:
In order to ensure absolute synchronization with the host software,
Mackie Control uses a closed-loop servo system for the faders,
meaning the faders will always move to their last received position.
When a host receives a Fader Position Message, it must then
re-transmit that message to the Mackie Control or else the faders
will return to their last position.
*/
_surface->write (fader.set_position (position));
}
void
Strip::handle_pot (Pot& pot, float delta)
{
/* Pots only emit events when they move, not when they
stop moving. So to get a stop event, we need to use a timeout.
*/
boost::shared_ptr<AutomationControl> ac = pot.control();
if (!ac) {
return;
}
Controllable::GroupControlDisposition gcd;
if (_surface->mcp().main_modifier_state() & US2400Protocol::MODIFIER_SHIFT) {
gcd = Controllable::InverseGroup;
} else {
gcd = Controllable::UseGroup;
}
if (ac->toggled()) {
/* make it like a single-step, directional switch */
if (delta > 0) {
ac->set_value (1.0, gcd);
} else {
ac->set_value (0.0, gcd);
}
} else if (ac->desc().enumeration || ac->desc().integer_step) {
/* use Controllable::get_value() to avoid the
* "scaling-to-interface" that takes place in
* Control::get_value() via the pot member.
*
* an enumeration with 4 values will have interface values of
* 0.0, 0.25, 0.5 and 0.75 or some similar oddness. Lets not
* deal with that.
*/
if (delta > 0) {
ac->set_value (min (ac->upper(), ac->get_value() + 1.0), gcd);
} else {
ac->set_value (max (ac->lower(), ac->get_value() - 1.0), gcd);
}
} else {
double p = ac->get_interface();
p += delta;
p = max (0.0, p);
p = min (1.0, p);
ac->set_value ( ac->interface_to_internal(p), gcd);
}
}
void
Strip::periodic (ARDOUR::microseconds_t now)
{
update_meter ();
if ( _trickle_counter %5 == 0 ) {
if ( _fader->control() ) {
_surface->write (_fader->set_position (_fader->control()->internal_to_interface (_fader->control()->get_value ())));
} else {
_surface->write (_fader->set_position(0.0));
}
if ( _vpot->control() ) {
_surface->write (_vpot->set (_vpot->control()->internal_to_interface (_vpot->control()->get_value ()), true));
} else {
_surface->write (_vpot->set(0.0, false));
}
if (_stripable) {
_surface->write (_solo->set_state (_stripable->solo_control()->soloed() ? on : off));
_surface->write (_mute->set_state (_stripable->mute_control()->muted() ? on : off));
_surface->write (_select->set_state (_stripable->is_selected()));
} else {
_surface->write (_solo->set_state (off));
_surface->write (_mute->set_state (off));
_surface->write (_select->set_state (off));
}
}
_trickle_counter++;
}
void
Strip::redisplay (ARDOUR::microseconds_t now, bool force)
{
}
void
Strip::update_automation ()
{
}
void
Strip::update_meter ()
{
if (!_stripable) {
return;
}
if (_meter && _transport_is_rolling && _metering_active && _stripable->peak_meter()) {
float dB = _stripable->peak_meter()->meter_level (0, MeterMCP);
_meter->send_update (*_surface, dB);
return;
}
}
void
Strip::zero ()
{
_trickle_counter = 0;
}
void
Strip::lock_controls ()
{
_controls_locked = true;
}
void
Strip::unlock_controls ()
{
_controls_locked = false;
}
string
Strip::vpot_mode_string ()
{
return "???";
}
void
Strip::next_pot_mode ()
{
vector<AutomationType>::iterator i;
boost::shared_ptr<AutomationControl> ac = _vpot->control();
if (!ac) {
return;
}
if (_surface->mcp().subview_mode() != US2400Protocol::None) {
return;
}
if (possible_pot_parameters.empty() || (possible_pot_parameters.size() == 1 && possible_pot_parameters.front() == ac->parameter().type())) {
return;
}
for (i = possible_pot_parameters.begin(); i != possible_pot_parameters.end(); ++i) {
if ((*i) == ac->parameter().type()) {
break;
}
}
/* move to the next mode in the list, or back to the start (which will
also happen if the current mode is not in the current pot mode list)
*/
if (i != possible_pot_parameters.end()) {
++i;
}
if (i == possible_pot_parameters.end()) {
i = possible_pot_parameters.begin();
}
set_vpot_parameter (*i);
}
void
/*
*
* name: Strip::subview_mode_changed
* @param
* @return
*
*/
Strip::subview_mode_changed ()
{
switch (_surface->mcp().subview_mode()) {
case US2400Protocol::None:
set_vpot_parameter (_pan_mode);
notify_metering_state_changed ();
break;
case US2400Protocol::TrackView:
boost::shared_ptr<Stripable> r = _surface->mcp().subview_stripable();
if (r) {
DEBUG_TRACE (DEBUG::US2400, string_compose("subview_mode_changed strip %1:%2- assigning trackview pot\n", _surface->number(), _index));
setup_trackview_vpot (r);
} else {
DEBUG_TRACE (DEBUG::US2400, string_compose("subview_mode_changed strip %1:%2 - no stripable\n", _surface->number(), _index));
}
break;
}
_trickle_counter = 0;
}
void
Strip::setup_dyn_vpot (boost::shared_ptr<Stripable> r)
{
}
void
Strip::setup_eq_vpot (boost::shared_ptr<Stripable> r)
{
}
void
Strip::setup_sends_vpot (boost::shared_ptr<Stripable> r)
{
}
void
Strip::setup_trackview_vpot (boost::shared_ptr<Stripable> r)
{
subview_connections.drop_connections ();
if (!r) {
return;
}
const uint32_t global_pos = _surface->mcp().global_index (*this);
boost::shared_ptr<AutomationControl> pc;
boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (r);
string label;
_vpot->set_mode(Pot::wrap);
#ifdef MIXBUS
int eq_band = -1;
if (r->is_input_strip ()) {
#ifdef MIXBUS32C
switch (global_pos) {
case 6:
pc = r->filter_freq_controllable(true);
break;
case 7:
pc = r->filter_freq_controllable(false);
break;
case 8:
case 10:
case 12:
case 14: {
eq_band = (global_pos-8) / 2;
pc = r->eq_freq_controllable (eq_band);
} break;
case 9:
case 11:
case 13:
case 15: {
eq_band = (global_pos-8) / 2;
pc = r->eq_gain_controllable (eq_band);
_vpot->set_mode(Pot::boost_cut);
} break;
}
#else //regular Mixbus channel EQ
switch (global_pos) {
case 7:
pc = r->filter_freq_controllable(true);
break;
case 8:
case 10:
case 12:
eq_band = (global_pos-8) / 2;
pc = r->eq_gain_controllable (eq_band);
_vpot->set_mode(Pot::boost_cut);
break;
case 9:
case 11:
case 13:
eq_band = (global_pos-8) / 2;
pc = r->eq_freq_controllable (eq_band);
break;
}
#endif
//trim & dynamics
switch (global_pos) {
case 0:
pc = r->trim_control ();
_vpot->set_mode(Pot::boost_cut);
break;
case 1:
pc = r->pan_azimuth_control ();
_vpot->set_mode(Pot::dot);
break;
case 2:
pc = r->comp_threshold_controllable();
break;
case 3:
pc = r->comp_speed_controllable();
break;
case 4:
pc = r->comp_mode_controllable();
_vpot->set_mode(Pot::wrap);
break;
case 5:
pc = r->comp_makeup_controllable();
break;
} //trim & dynamics
//mixbus sends
switch (global_pos) {
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
pc = r->send_level_controllable ( global_pos - 16 );
break;
} //global_pos switch
} //if input_strip
#endif //ifdef MIXBUS
if (pc) { //control found; set our knob to watch for changes in it
_vpot->set_control (pc);
pc->Changed.connect (subview_connections, MISSING_INVALIDATOR, boost::bind (&Strip::notify_vpot_change, this), ui_context());
} else { //no control, just set the knob to "empty"
_vpot->reset_control ();
}
notify_vpot_change ();
}
void
Strip::set_vpot_parameter (AutomationType p)
{
if (!_stripable || (p == NullAutomation)) {
_vpot->set_control (boost::shared_ptr<AutomationControl>());
return;
}
boost::shared_ptr<AutomationControl> pan_control;
DEBUG_TRACE (DEBUG::US2400, string_compose ("switch to vpot mode %1\n", p));
reset_saved_values ();
switch (p) {
case PanAzimuthAutomation:
pan_control = _stripable->pan_azimuth_control ();
break;
case PanWidthAutomation:
pan_control = _stripable->pan_width_control ();
break;
case PanElevationAutomation:
break;
case PanFrontBackAutomation:
break;
case PanLFEAutomation:
break;
default:
return;
}
if (pan_control) {
_pan_mode = p;
_vpot->set_mode (Pot::dot);
_vpot->set_control (pan_control);
}
notify_panner_azi_changed (true);
}
bool
Strip::is_midi_track () const
{
return boost::dynamic_pointer_cast<MidiTrack>(_stripable) != 0;
}
void
Strip::reset_saved_values ()
{
}
void
Strip::notify_metering_state_changed()
{
if (_surface->mcp().subview_mode() != US2400Protocol::None) {
return;
}
if (!_stripable || !_meter) {
return;
}
bool transport_is_rolling = (_surface->mcp().get_transport_speed () != 0.0f);
bool metering_active = _surface->mcp().metering_active ();
if ((_transport_is_rolling == transport_is_rolling) && (_metering_active == metering_active)) {
return;
}
_meter->notify_metering_state_changed (*_surface, transport_is_rolling, metering_active);
if (!transport_is_rolling || !metering_active) {
notify_property_changed (PBD::PropertyChange (ARDOUR::Properties::name));
notify_panner_azi_changed (true);
}
_transport_is_rolling = transport_is_rolling;
_metering_active = metering_active;
}

View File

@@ -0,0 +1,158 @@
#ifndef __ardour_us2400_control_protocol_strip_h__
#define __ardour_us2400_control_protocol_strip_h__
#include <string>
#include <iostream>
#include "evoral/Parameter.hpp"
#include "pbd/property_basics.h"
#include "pbd/ringbuffer.h"
#include "pbd/signals.h"
#include "ardour/types.h"
#include "control_protocol/types.h"
#include "control_group.h"
#include "types.h"
#include "us2400_control_protocol.h"
#include "midi_byte_array.h"
#include "device_info.h"
namespace ARDOUR {
class Stripable;
class Bundle;
class ChannelCount;
}
namespace ArdourSurface {
namespace US2400 {
class Control;
class Surface;
class Button;
class Pot;
class Fader;
class Meter;
class SurfacePort;
struct GlobalControlDefinition {
const char* name;
int id;
Control* (*factory)(Surface&, int index, const char* name, Group&);
const char* group_name;
};
/**
This is the set of controls that make up a strip.
*/
class Strip : public Group
{
public:
Strip (Surface&, const std::string & name, int index, const std::map<Button::ID,StripButtonInfo>&);
~Strip();
boost::shared_ptr<ARDOUR::Stripable> stripable() const { return _stripable; }
void add (Control & control);
int index() const { return _index; } // zero based
Surface* surface() const { return _surface; }
void set_stripable (boost::shared_ptr<ARDOUR::Stripable>, bool with_messages = true);
void reset_stripable ();
// call all signal handlers manually
void notify_all ();
void handle_button (Button&, ButtonState bs);
void handle_fader (Fader&, float position);
void handle_fader_touch (Fader&, bool touch_on);
void handle_pot (Pot&, float delta);
void periodic (ARDOUR::microseconds_t now_usecs);
void redisplay (ARDOUR::microseconds_t now_usecs, bool force = true);
void zero ();
void subview_mode_changed ();
void lock_controls ();
void unlock_controls ();
bool locked() const { return _controls_locked; }
void notify_metering_state_changed();
void update_selection_state ();
int global_index() { return _surface->mcp().global_index (*this); }
private:
enum VPotDisplayMode {
Name,
Value
};
Button* _solo;
Button* _mute;
Button* _select;
Button* _fader_touch;
Pot* _vpot;
Fader* _fader;
Meter* _meter;
int _index;
Surface* _surface;
bool _controls_locked;
bool _transport_is_rolling;
bool _metering_active;
boost::shared_ptr<ARDOUR::Stripable> _stripable;
PBD::ScopedConnectionList stripable_connections;
PBD::ScopedConnectionList subview_connections;
PBD::ScopedConnectionList send_connections;
int eq_band;
int _trickle_counter;
ARDOUR::AutomationType _pan_mode;
void notify_solo_changed ();
void notify_mute_changed ();
void notify_record_enable_changed ();
void notify_gain_changed (bool force_update = true);
void notify_property_changed (const PBD::PropertyChange&);
void notify_panner_azi_changed (bool force_update = true);
void notify_panner_width_changed (bool force_update = true);
void notify_stripable_deleted ();
void notify_processor_changed (bool force_update = true);
void update_automation ();
void update_meter ();
std::string vpot_mode_string ();
void next_pot_mode ();
void select_event (Button&, ButtonState);
void vselect_event (Button&, ButtonState);
void fader_touch_event (Button&, ButtonState);
std::vector<ARDOUR::AutomationType> possible_pot_parameters;
std::vector<ARDOUR::AutomationType> possible_trim_parameters;
void set_vpot_parameter (ARDOUR::AutomationType);
void show_stripable_name ();
void reset_saved_values ();
bool is_midi_track () const;
void notify_vpot_change ();
void setup_eq_vpot (boost::shared_ptr<ARDOUR::Stripable>);//
void setup_dyn_vpot (boost::shared_ptr<ARDOUR::Stripable>);//
void setup_sends_vpot (boost::shared_ptr<ARDOUR::Stripable>);//
void setup_trackview_vpot (boost::shared_ptr<ARDOUR::Stripable>);
};
}
}
#endif /* __ardour_us2400_control_protocol_strip_h__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,204 @@
#ifndef mackie_surface_h
#define mackie_surface_h
#include <stdint.h>
#include <sigc++/trackable.h>
#include "pbd/signals.h"
#include "pbd/xml++.h"
#include "midi++/types.h"
#include "ardour/types.h"
#include "control_protocol/types.h"
#include "controls.h"
#include "types.h"
#include "jog_wheel.h"
namespace MIDI {
class Parser;
}
namespace ARDOUR {
class Stripable;
class Port;
}
class MidiByteArray;
namespace ArdourSurface {
class US2400Protocol;
namespace US2400
{
class MackieButtonHandler;
class SurfacePort;
class MackieMidiBuilder;
class Button;
class Meter;
class Fader;
class Jog;
class Pot;
class Led;
class Surface : public PBD::ScopedConnectionList, public sigc::trackable
{
public:
Surface (US2400Protocol&, 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; }
const std::string& name() { return _name; }
void connected ();
bool active() const { return _active; }
typedef std::vector<Control*> Controls;
Controls controls;
std::map<int,Fader*> faders;
std::map<int,Pot*> pots;
std::map<int,Button*> buttons; // index is device-DEPENDENT
std::map<int,Led*> leds;
std::map<int,Meter*> meters;
std::map<int,Control*> controls_by_device_independent_id;
US2400::JogWheel* jog_wheel() const { return _jog_wheel; }
Fader* master_fader() const { return _master_fader; }
/// The collection of all numbered strips.
typedef std::vector<Strip*> Strips;
Strips strips;
uint32_t n_strips (bool with_locked_strips = true) const;
Strip* nth_strip (uint32_t n) const;
bool stripable_is_locked_to_strip (boost::shared_ptr<ARDOUR::Stripable>) const;
bool stripable_is_mapped (boost::shared_ptr<ARDOUR::Stripable>) const;
/// This collection owns the groups
typedef std::map<std::string,Group*> Groups;
Groups groups;
SurfacePort& port() const { return *_port; }
void map_stripables (const std::vector<boost::shared_ptr<ARDOUR::Stripable> >&);
void update_strip_selection ();
const MidiByteArray& sysex_hdr() const;
void periodic (ARDOUR::microseconds_t now_usecs);
void redisplay (ARDOUR::microseconds_t now_usecs, bool force);
void hui_heartbeat ();
void handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t, uint32_t channel_id);
void handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes*);
void handle_midi_note_on_message (MIDI::Parser&, MIDI::EventTwoBytes*);
/// Connect the any signal from the parser to handle_midi_any
/// unless it's already connected
void connect_to_signals ();
/// write a sysex message
void write_sysex (const MidiByteArray& mba);
void write_sysex (MIDI::byte msg);
/// proxy write for port
void write (const MidiByteArray&);
/// display an indicator of the first switched-in Route. Do nothing by default.
void display_bank_start (uint32_t /*current_bank*/);
/// called from US2400Protocol::zero_all to turn things off
void zero_all ();
void zero_controls ();
/// turn off leds around the jog wheel. This is for surfaces that use a pot
/// pretending to be a jog wheel.
void blank_jog_ring ();
/// sends MCP "reset" message to surface
void reset ();
void recalibrate_faders ();
void toggle_backlight ();
void set_touch_sensitivity (int);
/**
This is used to calculate the clicks per second that define
a transport speed of 1.0 for the jog wheel. 100.0 is 10 clicks
per second, 50.5 is 5 clicks per second.
*/
float scrub_scaling_factor() const;
/**
The scaling factor function for speed increase and decrease. At
low transport speeds this should return a small value, for high transport
speeds, this should return an exponentially larger value. This provides
high definition control at low speeds and quick speed changes to/from
higher speeds.
*/
float scaled_delta (float delta, float current_speed);
void subview_mode_changed ();
US2400Protocol& mcp() const { return _mcp; }
void next_jog_mode ();
void set_jog_mode (US2400::JogWheel::Mode);
void notify_metering_state_changed();
void turn_it_on ();
bool connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool);
void master_monitor_may_have_changed ();
XMLNode& get_state ();
int set_state (const XMLNode&, int version);
private:
US2400Protocol& _mcp;
SurfacePort* _port;
surface_type_t _stype;
uint32_t _number;
std::string _name;
bool _active;
bool _connected;
US2400::JogWheel* _jog_wheel;
Fader* _master_fader;
float _last_master_gain_written;
PBD::ScopedConnection master_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 ();
enum ConnectionState {
InputConnected = 0x1,
OutputConnected = 0x2
};
int connection_state;
public:
/* IP MIDI devices need to keep a handle on this and destroy it */
GSource* input_source;
};
}
}
#endif

View File

@@ -0,0 +1,201 @@
/*
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 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.
*/
#include <sstream>
#include <cstring>
#include <cerrno>
#include <sigc++/sigc++.h>
#include <boost/shared_array.hpp>
#include "pbd/failed_constructor.h"
#include "midi++/types.h"
#include "ardour/async_midi_port.h"
#include "ardour/debug.h"
#include "ardour/rc_configuration.h"
#include "ardour/session.h"
#include "ardour/audioengine.h"
#include "controls.h"
#include "us2400_control_protocol.h"
#include "surface.h"
#include "surface_port.h"
#include "pbd/i18n.h"
using namespace std;
using namespace PBD;
using namespace ARDOUR;
using namespace ArdourSurface;
using namespace US2400;
SurfacePort::SurfacePort (Surface& s)
: _surface (&s)
{
string in_name;
string out_name;
in_name = string_compose (X_("US-2400 In #%1"), (_surface->number() + 1));
out_name = string_compose (X_("US-2400 Out #%1"), _surface->number() + 1);
_async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, in_name, true);
_async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, out_name, true);
if (_async_in == 0 || _async_out == 0) {
throw failed_constructor();
}
_input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
_output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
}
SurfacePort::~SurfacePort()
{
if (_async_in) {
DEBUG_TRACE (DEBUG::US2400, string_compose ("unregistering input port %1\n", _async_in->name()));
Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
AudioEngine::instance()->unregister_port (_async_in);
_async_in.reset ((ARDOUR::Port*) 0);
}
if (_async_out) {
_output_port->drain (10000, 250000);
DEBUG_TRACE (DEBUG::US2400, string_compose ("unregistering output port %1\n", _async_out->name()));
Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
AudioEngine::instance()->unregister_port (_async_out);
_async_out.reset ((ARDOUR::Port*) 0);
}
}
XMLNode&
SurfacePort::get_state ()
{
XMLNode* node = new XMLNode (X_("Port"));
XMLNode* child;
child = new XMLNode (X_("Input"));
child->add_child_nocopy (_async_in->get_state());
node->add_child_nocopy (*child);
child = new XMLNode (X_("Output"));
child->add_child_nocopy (_async_out->get_state());
node->add_child_nocopy (*child);
return *node;
}
int
SurfacePort::set_state (const XMLNode& node, int version)
{
XMLNode* child;
if ((child = node.child (X_("Input"))) != 0) {
XMLNode* portnode = child->child (Port::state_node_name.c_str());
if (portnode) {
_async_in->set_state (*portnode, version);
}
}
if ((child = node.child (X_("Output"))) != 0) {
XMLNode* portnode = child->child (Port::state_node_name.c_str());
if (portnode) {
_async_out->set_state (*portnode, version);
}
}
return 0;
}
void
SurfacePort::reconnect ()
{
_async_out->reconnect ();
_async_in->reconnect ();
}
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)
{
char * msg = strerror (error_number);
return msg;
}
int
SurfacePort::write (const MidiByteArray & mba)
{
if (mba.empty()) {
DEBUG_TRACE (DEBUG::US2400, string_compose ("port %1 asked to write an empty MBA\n", output_port().name()));
return 0;
}
DEBUG_TRACE (DEBUG::US2400, string_compose ("port %1 write %2\n", output_port().name(), mba));
if (mba[0] != 0xf0 && mba.size() > 3) {
std::cerr << "TOO LONG WRITE: " << mba << std::endl;
}
/* this call relies on std::vector<T> using contiguous storage. not
* actually guaranteed by the standard, but way, way beyond likely.
*/
int count = output_port().write (&mba[0], 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;
}
return -1;
}
return 0;
}
ostream &
US2400::operator << (ostream & os, const SurfacePort & port)
{
os << "{ ";
os << "name: " << port.input_port().name() << " " << port.output_port().name();
os << "; ";
os << " }";
return os;
}

View File

@@ -0,0 +1,91 @@
/*
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 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.
*/
#ifndef surface_port_h
#define surface_port_h
#include <midi++/types.h>
#include "pbd/signals.h"
#include "midi_byte_array.h"
#include "types.h"
namespace MIDI {
class Parser;
class Port;
}
namespace ARDOUR {
class AsyncMIDIPort;
class Port;
}
namespace ArdourSurface {
class US2400Protocol;
namespace US2400
{
class Surface;
/**
Make a relationship between a midi port and a Mackie device.
*/
class SurfacePort
{
public:
SurfacePort (US2400::Surface&);
virtual ~SurfacePort();
/// 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; }
ARDOUR::Port& input() const { return *_async_in; }
ARDOUR::Port& output() const { return *_async_out; }
std::string input_name() const;
std::string output_name() const;
void reconnect ();
XMLNode& get_state ();
int set_state (const XMLNode&, int version);
protected:
private:
US2400::Surface* _surface;
MIDI::Port* _input_port;
MIDI::Port* _output_port;
boost::shared_ptr<ARDOUR::Port> _async_in;
boost::shared_ptr<ARDOUR::Port> _async_out;
};
std::ostream& operator << (std::ostream& , const SurfacePort& port);
}
}
#endif

View File

@@ -0,0 +1,25 @@
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
#include <cstdarg>
#include <iomanip>
#include "midi_byte_array.h"
using namespace std;
namespace MIDI {
typedef unsigned char byte;
byte sysex = 0xf0;
byte eox = 0xf7;
}
int main()
{
MidiByteArray bytes( 4, 0xf0, 0x01, 0x03, 0x7f );
cout << bytes << endl;
return 0;
}

View File

@@ -0,0 +1,104 @@
/*
Copyright (C) 1998, 1999, 2000, 2007 John Anderson
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Library 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.
You should have received a copy of the GNU Library General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef timer_h
#define timer_h
#ifdef _WIN32
#include "windows.h"
#else
#include <sys/time.h>
#endif
namespace ArdourSurface {
namespace US2400
{
/**
millisecond timer class.
*/
class Timer
{
public:
/**
start the timer running if true, or just create the
object if false.
*/
Timer( bool shouldStart = true )
{
if ( shouldStart )
start();
}
/**
Start the timer running. Return the current timestamp, in milliseconds
*/
unsigned long start()
{
_start = g_get_monotonic_time();
return _start / 1000;
}
/**
returns the number of milliseconds since start
also stops the timer running
*/
unsigned long stop()
{
_stop = g_get_monotonic_time();
return elapsed();
}
/**
returns the number of milliseconds since start
*/
unsigned long elapsed() const
{
if ( running )
{
uint64_t now = g_get_monotonic_time();
return (now - _start) / 1000;
}
else
{
return (_stop - _start) / 1000;
}
}
/**
Call stop and then start. Return the value from stop.
*/
unsigned long restart()
{
unsigned long retval = stop();
start();
return retval;
}
private:
uint64_t _start;
uint64_t _stop;
bool running;
};
} // US2400 namespace
} // ArdourSurface namespace
#endif

View File

@@ -0,0 +1,51 @@
/*
Copyright (C) 2012 Paul Davis
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.
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.
*/
#include "types.h"
namespace ArdourSurface {
namespace US2400 {
LedState on( LedState::on );
LedState off( LedState::off );
LedState flashing( LedState::flashing );
LedState none( LedState::none );
}
}
std::ostream & operator << ( std::ostream & os, const ArdourSurface::US2400::ControlState & cs )
{
os << "ControlState { ";
os << "pos: " << cs.pos;
os << ", ";
os << "sign: " << cs.sign;
os << ", ";
os << "delta: " << cs.delta;
os << ", ";
os << "ticks: " << cs.ticks;
os << ", ";
os << "led_state: " << cs.led_state.state();
os << ", ";
os << "button_state: " << cs.button_state;
os << " }";
return os;
}

View File

@@ -0,0 +1,115 @@
/*
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 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.
*/
#ifndef mackie_types_h
#define mackie_types_h
#include <iostream>
namespace ArdourSurface {
namespace US2400 {
enum surface_type_t {
st_mcu,
st_ext,
st_joy,
st_knb,
};
/**
This started off as an enum, but it got really annoying
typing ? on : off
*/
class LedState
{
public:
enum state_t { none, off, flashing, on };
LedState() : _state (none) {}
LedState (bool yn): _state (yn ? on : off) {}
LedState (state_t state): _state (state) {}
LedState& operator= (state_t s) { _state = s; return *this; }
bool operator == (const LedState & other) const
{
return state() == other.state();
}
bool operator != (const LedState & other) const
{
return state() != other.state();
}
state_t state() const { return _state; }
private:
state_t _state;
};
extern LedState on;
extern LedState off;
extern LedState flashing;
extern LedState none;
enum ButtonState { neither = -1, release = 0, press = 1 };
/**
Contains the state for a control, with some convenience
constructors
*/
struct ControlState
{
ControlState(): pos(0.0), sign(0), delta(0.0), ticks(0), led_state(off), button_state(neither) {}
ControlState (LedState ls): pos(0.0), delta(0.0), led_state(ls), button_state(neither) {}
// Note that this sets both pos and delta to the flt value
ControlState (LedState ls, float flt): pos(flt), delta(flt), ticks(0), led_state(ls), button_state(neither) {}
ControlState (float flt): pos(flt), delta(flt), ticks(0), led_state(none), button_state(neither) {}
ControlState (float flt, unsigned int tcks): pos(flt), delta(flt), ticks(tcks), led_state(none), button_state(neither) {}
ControlState (ButtonState bs): pos(0.0), delta(0.0), ticks(0), led_state(none), button_state(bs) {}
/// For faders. Between 0 and 1.
float pos;
/// For pots. Sign. Either -1 or 1;
int sign;
/// For pots. Signed value of total movement. Between 0 and 1
float delta;
/// For pots. Unsigned number of ticks. Usually between 1 and 16.
unsigned int ticks;
LedState led_state;
ButtonState button_state;
};
std::ostream & operator << (std::ostream &, const ControlState &);
class Control;
class Fader;
class Button;
class Strip;
class Group;
class Pot;
class Led;
}
}
#endif

View File

@@ -0,0 +1,48 @@
/*
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 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.
*/
#ifndef us2400_control_exception_h
#define us2400_control_exception_h
#include <stdexcept>
namespace ArdourSurface {
namespace US2400 {
class MackieControlException : public std::exception
{
public:
MackieControlException( const std::string & msg )
: _msg( msg )
{
}
virtual ~MackieControlException() throw () {}
const char * what() const throw ()
{
return _msg.c_str();
}
private:
std::string _msg;
};
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,480 @@
/*
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 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.
*/
#ifndef ardour_us2400_control_protocol_h
#define ardour_us2400_control_protocol_h
#include <vector>
#include <map>
#include <list>
#include <set>
#include <sys/time.h>
#include <pthread.h>
#include <boost/smart_ptr.hpp>
#define ABSTRACT_UI_EXPORTS
#include "pbd/abstract_ui.h"
#include "midi++/types.h"
#include "ardour/types.h"
#include "control_protocol/control_protocol.h"
#include "types.h"
#include "midi_byte_array.h"
#include "controls.h"
#include "jog_wheel.h"
#include "timer.h"
#include "device_info.h"
#include "device_profile.h"
namespace ARDOUR {
class AutomationControl;
class Port;
}
namespace MIDI {
class Port;
}
namespace ArdourSurface {
namespace US2400 {
class Surface;
class Control;
class SurfacePort;
class Button;
class Strip;
}
struct US2400ControlUIRequest : public BaseUI::BaseRequestObject {
public:
US2400ControlUIRequest () {}
~US2400ControlUIRequest () {}
};
class US2400Protocol
: public ARDOUR::ControlProtocol
, public AbstractUI<US2400ControlUIRequest>
{
public:
static const int MODIFIER_OPTION;
static const int MODIFIER_CONTROL;
static const int MODIFIER_SHIFT;
static const int MODIFIER_CMDALT;
static const int MODIFIER_ZOOM;
static const int MODIFIER_SCRUB;
static const int MODIFIER_MARKER;
static const int MODIFIER_DROP; //US2400 replaces MODIFIER_NUDGE which is unused
static const int MAIN_MODIFIER_MASK;
enum ViewMode {
Mixer,
Busses,
};
enum SubViewMode {
None,
TrackView,
};
US2400Protocol(ARDOUR::Session &);
virtual ~US2400Protocol();
static US2400Protocol* instance() { return _instance; }
const US2400::DeviceInfo& device_info() const { return _device_info; }
US2400::DeviceProfile& device_profile() { return _device_profile; }
PBD::Signal0<void> DeviceChanged;
PBD::Signal1<void,boost::shared_ptr<US2400::Surface> > ConnectionChange;
void device_ready ();
int set_active (bool yn);
int set_device (const std::string&, bool force);
void set_profile (const std::string&);
ViewMode view_mode () const { return _view_mode; }
SubViewMode subview_mode () const { return _subview_mode; }
static bool subview_mode_would_be_ok (SubViewMode, boost::shared_ptr<ARDOUR::Stripable>);
boost::shared_ptr<ARDOUR::Stripable> subview_stripable() const;
bool zoom_mode () const { return modifier_state() & MODIFIER_ZOOM; }
bool metering_active () const { return _metering_active; }
bool is_track (boost::shared_ptr<ARDOUR::Stripable>) const;
bool is_audio_track (boost::shared_ptr<ARDOUR::Stripable>) const;
bool is_midi_track (boost::shared_ptr<ARDOUR::Stripable>) const;
bool is_mapped (boost::shared_ptr<ARDOUR::Stripable>) const;
boost::shared_ptr<ARDOUR::Stripable> first_selected_stripable () const;
void check_fader_automation_state ();
void update_fader_automation_state ();
void set_automation_state (ARDOUR::AutoState);
void set_view_mode (ViewMode);
int set_subview_mode (SubViewMode, boost::shared_ptr<ARDOUR::Stripable>);
void display_view_mode ();
XMLNode& get_state ();
int set_state (const XMLNode&, int version);
/* Note: because Mackie control is inherently a duplex protocol,
we do not implement get/set_feedback() since this aspect of
support for the protocol is not optional.
*/
static bool probe();
static void* request_factory (uint32_t);
mutable Glib::Threads::Mutex surfaces_lock;
typedef std::list<boost::shared_ptr<US2400::Surface> > Surfaces;
Surfaces surfaces;
boost::shared_ptr<US2400::Surface> get_surface_by_raw_pointer (void*) const;
boost::shared_ptr<US2400::Surface> nth_surface (uint32_t) const;
uint32_t global_index (US2400::Strip&);
uint32_t global_index_locked (US2400::Strip&);
std::list<boost::shared_ptr<ARDOUR::Bundle> > bundles ();
void set_master_on_surface_strip (uint32_t surface, uint32_t strip);
void set_monitor_on_surface_strip (uint32_t surface, uint32_t strip);
uint32_t n_strips (bool with_locked_strips = true) const;
bool has_editor () const { return true; }
void* get_gui () const;
void tear_down_gui ();
void handle_button_event (US2400::Surface&, US2400::Button& button, US2400::ButtonState);
void notify_subview_stripable_deleted ();
void notify_stripable_removed ();
void notify_routes_added (ARDOUR::RouteList &);
void notify_vca_added (ARDOUR::VCAList &);
void notify_presentation_info_changed(PBD::PropertyChange const &);
void recalibrate_faders ();
void toggle_backlight ();
void set_touch_sensitivity (int);
/// rebuild the current bank. Called on route or vca added/removed and
/// presentation info changed.
void refresh_current_bank();
// button-related signals
void notify_record_state_changed();
void notify_transport_state_changed();
void notify_loop_state_changed();
void notify_metering_state_changed();
// mainly to pick up punch-in and punch-out
void notify_parameter_changed(std::string const &);
void notify_solo_active_changed(bool);
/// Turn timecode on and beats off, or vice versa, depending
/// on state of _timecode_type
void update_timecode_beats_led();
/// this is called to generate the midi to send in response to a button press.
void update_led(US2400::Surface&, US2400::Button & button, US2400::LedState);
void update_global_button (int id, US2400::LedState);
void update_global_led (int id, US2400::LedState);
ARDOUR::Session & get_session() { return *session; }
samplepos_t transport_frame() const;
int modifier_state() const { return _modifier_state; }
int main_modifier_state() const { return _modifier_state & MAIN_MODIFIER_MASK; }
typedef std::list<boost::shared_ptr<ARDOUR::AutomationControl> > ControlList;
void add_down_button (ARDOUR::AutomationType, int surface, int strip);
void remove_down_button (ARDOUR::AutomationType, int surface, int strip);
ControlList down_controls (ARDOUR::AutomationType, uint32_t pressed);
void add_down_select_button (int surface, int strip);
void remove_down_select_button (int surface, int strip);
void select_range (uint32_t pressed);
protected:
// shut down the surface
void close();
// This sets up the notifications and sets the
// controls to the correct values
void update_surfaces();
// connects global (not strip) signals from the Session to here
// so the surface can be notified of changes from the other UIs.
void connect_session_signals();
// set all controls to their zero position
void zero_all();
/**
Fetch the set of Stripables to be considered for control by the
surface. Excluding master, hidden and control routes, and inactive routes
*/
typedef std::vector<boost::shared_ptr<ARDOUR::Stripable> > Sorted;
Sorted get_sorted_stripables();
// bank switching
int switch_banks (uint32_t first_remote_id, bool force = false);
void prev_track ();
void next_track ();
void do_request (US2400ControlUIRequest*);
int stop ();
void thread_init ();
bool stripable_is_locked_to_strip (boost::shared_ptr<ARDOUR::Stripable>) const;
private:
struct ButtonHandlers {
US2400::LedState (US2400Protocol::*press) (US2400::Button&);
US2400::LedState (US2400Protocol::*release) (US2400::Button&);
ButtonHandlers (US2400::LedState (US2400Protocol::*p) (US2400::Button&),
US2400::LedState (US2400Protocol::*r) (US2400::Button&))
: press (p)
, release (r) {}
};
typedef std::map<US2400::Button::ID,ButtonHandlers> ButtonMap;
static US2400Protocol* _instance;
bool profile_exists (std::string const&) const;
US2400::DeviceInfo _device_info;
US2400::DeviceProfile _device_profile;
sigc::connection periodic_connection;
sigc::connection redisplay_connection;
sigc::connection hui_connection;
uint32_t _current_initial_bank;
PBD::ScopedConnectionList audio_engine_connections;
PBD::ScopedConnectionList session_connections;
PBD::ScopedConnectionList stripable_connections;
PBD::ScopedConnectionList subview_stripable_connections;
PBD::ScopedConnectionList gui_connections;
// timer for two quick marker left presses
US2400::Timer _frm_left_last;
// last written timecode string
std::string _timecode_last;
samplepos_t _frame_last;
// Which timecode are we displaying? BBT or Timecode
ARDOUR::AnyTime::Type _timecode_type;
// Bundle to represent our input ports
boost::shared_ptr<ARDOUR::Bundle> _input_bundle;
// Bundle to represent our output ports
boost::shared_ptr<ARDOUR::Bundle> _output_bundle;
void* _gui;
bool _scrub_mode;
ViewMode _view_mode;
SubViewMode _subview_mode;
boost::shared_ptr<ARDOUR::Stripable> _subview_stripable;
int _current_selected_track;
int _modifier_state;
ButtonMap button_map;
bool _metering_active;
bool _initialized;
XMLNode* configuration_state;
int state_version;
int _last_bank[9];
bool marker_modifier_consumed_by_button;
bool nudge_modifier_consumed_by_button;
boost::shared_ptr<ArdourSurface::US2400::Surface> _master_surface;
int create_surfaces ();
bool periodic();
bool redisplay();
bool redisplay_subview_mode ();
bool hui_heartbeat ();
void build_gui ();
bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port);
void clear_ports ();
void clear_surfaces ();
void force_special_stripable_to_strip (boost::shared_ptr<ARDOUR::Stripable> r, uint32_t surface, uint32_t strip_number);
void build_button_map ();
void stripable_selection_changed ();
void initialize ();
int set_device_info (const std::string& device_name);
void update_configuration_state ();
/* MIDI port connection management */
PBD::ScopedConnection port_connection;
void connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool);
/* BUTTON HANDLING */
typedef std::set<uint32_t> DownButtonList;
typedef std::map<ARDOUR::AutomationType,DownButtonList> DownButtonMap;
DownButtonMap _down_buttons;
DownButtonList _down_select_buttons;
void pull_stripable_range (DownButtonList&, ARDOUR::StripableList&, uint32_t pressed);
/* implemented button handlers */
US2400::LedState stop_press(US2400::Button &);
US2400::LedState stop_release(US2400::Button &);
US2400::LedState play_press(US2400::Button &);
US2400::LedState play_release(US2400::Button &);
US2400::LedState record_press(US2400::Button &);
US2400::LedState record_release(US2400::Button &);
US2400::LedState loop_press(US2400::Button &);
US2400::LedState loop_release(US2400::Button &);
US2400::LedState rewind_press(US2400::Button & button);
US2400::LedState rewind_release(US2400::Button & button);
US2400::LedState ffwd_press(US2400::Button & button);
US2400::LedState ffwd_release(US2400::Button & button);
US2400::LedState cursor_up_press (US2400::Button &);
US2400::LedState cursor_up_release (US2400::Button &);
US2400::LedState cursor_down_press (US2400::Button &);
US2400::LedState cursor_down_release (US2400::Button &);
US2400::LedState cursor_left_press (US2400::Button &);
US2400::LedState cursor_left_release (US2400::Button &);
US2400::LedState cursor_right_press (US2400::Button &);
US2400::LedState cursor_right_release (US2400::Button &);
US2400::LedState left_press(US2400::Button &);
US2400::LedState left_release(US2400::Button &);
US2400::LedState right_press(US2400::Button &);
US2400::LedState right_release(US2400::Button &);
US2400::LedState channel_left_press(US2400::Button &);
US2400::LedState channel_left_release(US2400::Button &);
US2400::LedState channel_right_press(US2400::Button &);
US2400::LedState channel_right_release(US2400::Button &);
US2400::LedState marker_press(US2400::Button &);
US2400::LedState marker_release(US2400::Button &);
US2400::LedState save_press(US2400::Button &);
US2400::LedState save_release(US2400::Button &);
US2400::LedState timecode_beats_press(US2400::Button &);
US2400::LedState timecode_beats_release(US2400::Button &);
US2400::LedState zoom_press(US2400::Button &);
US2400::LedState zoom_release(US2400::Button &);
US2400::LedState scrub_press(US2400::Button &);
US2400::LedState scrub_release(US2400::Button &);
US2400::LedState undo_press (US2400::Button &);
US2400::LedState undo_release (US2400::Button &);
US2400::LedState shift_press (US2400::Button &);
US2400::LedState shift_release (US2400::Button &);
US2400::LedState option_press (US2400::Button &);
US2400::LedState option_release (US2400::Button &);
US2400::LedState control_press (US2400::Button &);
US2400::LedState control_release (US2400::Button &);
US2400::LedState cmd_alt_press (US2400::Button &);
US2400::LedState cmd_alt_release (US2400::Button &);
US2400::LedState pan_press (US2400::Button &);
US2400::LedState pan_release (US2400::Button &);
US2400::LedState plugin_press (US2400::Button &);
US2400::LedState plugin_release (US2400::Button &);
US2400::LedState eq_press (US2400::Button &);
US2400::LedState eq_release (US2400::Button &);
US2400::LedState dyn_press (US2400::Button &);
US2400::LedState dyn_release (US2400::Button &);
US2400::LedState flip_press (US2400::Button &);
US2400::LedState flip_release (US2400::Button &);
US2400::LedState name_value_press (US2400::Button &);
US2400::LedState name_value_release (US2400::Button &);
// US2400::LedState F1_press (US2400::Button &);
// US2400::LedState F1_release (US2400::Button &);
// US2400::LedState F2_press (US2400::Button &);
// US2400::LedState F2_release (US2400::Button &);
// US2400::LedState F3_press (US2400::Button &);
// US2400::LedState F3_release (US2400::Button &);
// US2400::LedState F4_press (US2400::Button &);
// US2400::LedState F4_release (US2400::Button &);
// US2400::LedState F5_press (US2400::Button &);
// US2400::LedState F5_release (US2400::Button &);
// US2400::LedState F6_press (US2400::Button &);
// US2400::LedState F6_release (US2400::Button &);
// US2400::LedState F7_press (US2400::Button &);
// US2400::LedState F7_release (US2400::Button &);
// US2400::LedState F8_press (US2400::Button &);
// US2400::LedState F8_release (US2400::Button &);
US2400::LedState touch_press (US2400::Button &);
US2400::LedState touch_release (US2400::Button &);
US2400::LedState enter_press (US2400::Button &);
US2400::LedState enter_release (US2400::Button &);
US2400::LedState cancel_press (US2400::Button &);
US2400::LedState cancel_release (US2400::Button &);
US2400::LedState user_a_press (US2400::Button &);
US2400::LedState user_a_release (US2400::Button &);
US2400::LedState user_b_press (US2400::Button &);
US2400::LedState user_b_release (US2400::Button &);
US2400::LedState fader_touch_press (US2400::Button &);
US2400::LedState fader_touch_release (US2400::Button &);
US2400::LedState master_fader_touch_press (US2400::Button &);
US2400::LedState master_fader_touch_release (US2400::Button &);
US2400::LedState read_press (US2400::Button&);
US2400::LedState read_release (US2400::Button&);
US2400::LedState write_press (US2400::Button&);
US2400::LedState write_release (US2400::Button&);
US2400::LedState clearsolo_press (US2400::Button&);
US2400::LedState clearsolo_release (US2400::Button&);
US2400::LedState track_press (US2400::Button&);
US2400::LedState track_release (US2400::Button&);
US2400::LedState send_press (US2400::Button&);
US2400::LedState send_release (US2400::Button&);
US2400::LedState miditracks_press (US2400::Button&);
US2400::LedState miditracks_release (US2400::Button&);
US2400::LedState inputs_press (US2400::Button&);
US2400::LedState inputs_release (US2400::Button&);
US2400::LedState audiotracks_press (US2400::Button&);
US2400::LedState audiotracks_release (US2400::Button&);
US2400::LedState audioinstruments_press (US2400::Button&);
US2400::LedState audioinstruments_release (US2400::Button&);
US2400::LedState aux_press (US2400::Button&);
US2400::LedState aux_release (US2400::Button&);
US2400::LedState busses_press (US2400::Button&);
US2400::LedState busses_release (US2400::Button&);
US2400::LedState outputs_press (US2400::Button&);
US2400::LedState outputs_release (US2400::Button&);
US2400::LedState user_press (US2400::Button&);
US2400::LedState user_release (US2400::Button&);
US2400::LedState trim_press (US2400::Button&);
US2400::LedState trim_release (US2400::Button&);
US2400::LedState latch_press (US2400::Button&);
US2400::LedState latch_release (US2400::Button&);
US2400::LedState grp_press (US2400::Button&);
US2400::LedState grp_release (US2400::Button&);
US2400::LedState nudge_press (US2400::Button&);
US2400::LedState nudge_release (US2400::Button&);
US2400::LedState drop_press (US2400::Button&);
US2400::LedState drop_release (US2400::Button&);
US2400::LedState replace_press (US2400::Button&);
US2400::LedState replace_release (US2400::Button&);
US2400::LedState click_press (US2400::Button&);
US2400::LedState click_release (US2400::Button&);
US2400::LedState view_press (US2400::Button&);
US2400::LedState view_release (US2400::Button&);
US2400::LedState bank_release (US2400::Button&, uint32_t bank_num);
};
} // namespace
#endif // ardour_us2400_control_protocol_h

View File

@@ -0,0 +1,26 @@
#include "us2400_control_protocol.h"
#include "midi_byte_array.h"
#include "surface_port.h"
#include "pbd/pthread_utils.h"
#include "pbd/error.h"
#include "midi++/types.h"
#include "midi++/port.h"
#include "midi++/manager.h"
#include "pbd/i18n.h"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
using namespace US2400;
using namespace PBD;

View File

@@ -0,0 +1,49 @@
#!/usr/bin/env python
from waflib.extras import autowaf as autowaf
import os
# Mandatory variables
top = '.'
out = 'build'
def options(opt):
autowaf.set_options(opt)
def configure(conf):
autowaf.configure(conf)
def build(bld):
obj = bld(features = 'cxx cxxshlib')
obj.source = '''
button.cc
controls.cc
device_info.cc
device_profile.cc
fader.cc
gui.cc
interface.cc
jog.cc
jog_wheel.cc
led.cc
us2400_control_protocol.cc
mcp_buttons.cc
meter.cc
midi_byte_array.cc
pot.cc
strip.cc
surface.cc
surface_port.cc
types.cc
'''
obj.export_includes = ['./us2400']
obj.defines = [ 'PACKAGE="ardour_us2400"' ]
obj.defines += [ 'ARDOURSURFACE_DLL_EXPORTS' ]
obj.includes = [ '.' ]
obj.name = 'libardour_us2400'
obj.target = 'ardour_us2400'
obj.uselib = 'GTKMM XML'
obj.use = 'libardour libardour_cp libgtkmm2ext'
obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces')
def shutdown():
autowaf.shutdown()