Add support for contourdesign ShuttlePRO v2 and ShuttleXpress
This commit is contained in:
parent
89f39d14f2
commit
b8349069f1
|
@ -13,7 +13,7 @@ export GTK2_RC_FILES=/nonexistent
|
|||
# can find all the components.
|
||||
#
|
||||
|
||||
export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/faderport8:$libs/surfaces/faderport:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie:$libs/surfaces/us2400:$libs/surfaces/wiimote:$libs/surfaces/push2:$libs/surfaces/maschine2:$libs/surfaces/cc121:$libs/surfaces/launch_control_xl
|
||||
export ARDOUR_SURFACES_PATH=$libs/surfaces/osc:$libs/surfaces/faderport8:$libs/surfaces/faderport:$libs/surfaces/generic_midi:$libs/surfaces/tranzport:$libs/surfaces/powermate:$libs/surfaces/mackie:$libs/surfaces/us2400:$libs/surfaces/wiimote:$libs/surfaces/push2:$libs/surfaces/maschine2:$libs/surfaces/cc121:$libs/surfaces/launch_control_xl:$libs/surfaces/contourdesign
|
||||
export ARDOUR_PANNER_PATH=$libs/panners
|
||||
export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:.
|
||||
export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
|
||||
|
|
|
@ -69,6 +69,7 @@ namespace PBD {
|
|||
LIBARDOUR_API extern DebugBits OrderKeys;
|
||||
LIBARDOUR_API extern DebugBits Automation;
|
||||
LIBARDOUR_API extern DebugBits WiimoteControl;
|
||||
LIBARDOUR_API extern DebugBits ContourDesignControl;
|
||||
LIBARDOUR_API extern DebugBits Ports;
|
||||
LIBARDOUR_API extern DebugBits AudioEngine;
|
||||
LIBARDOUR_API extern DebugBits Soundcloud;
|
||||
|
|
|
@ -65,6 +65,7 @@ PBD::DebugBits PBD::DEBUG::TempoMap = PBD::new_debug_bit ("tempomap");
|
|||
PBD::DebugBits PBD::DEBUG::OrderKeys = PBD::new_debug_bit ("orderkeys");
|
||||
PBD::DebugBits PBD::DEBUG::Automation = PBD::new_debug_bit ("automation");
|
||||
PBD::DebugBits PBD::DEBUG::WiimoteControl = PBD::new_debug_bit ("wiimotecontrol");
|
||||
PBD::DebugBits PBD::DEBUG::ContourDesignControl = PBD::new_debug_bit ("contourdesigncontrol");
|
||||
PBD::DebugBits PBD::DEBUG::Ports = PBD::new_debug_bit ("Ports");
|
||||
PBD::DebugBits PBD::DEBUG::AudioEngine = PBD::new_debug_bit ("AudioEngine");
|
||||
PBD::DebugBits PBD::DEBUG::Soundcloud = PBD::new_debug_bit ("Soundcloud");
|
||||
|
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
Copyright (C) 2019 Paul Davis
|
||||
Author: Johannes Mueller
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include <gtkmm/label.h>
|
||||
#include <gtkmm/treemodelsort.h>
|
||||
|
||||
#include "gtkmm2ext/bindings.h"
|
||||
#include "gtkmm2ext/gui_thread.h"
|
||||
#include "gtkmm2ext/actions.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
#include "pbd/strsplit.h"
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "button_config_widget.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace Gtk;
|
||||
using namespace ArdourSurface;
|
||||
|
||||
class ActionModel
|
||||
{
|
||||
public:
|
||||
static const ActionModel& instance ();
|
||||
|
||||
const Glib::RefPtr<TreeStore> model () const { return _available_action_model; }
|
||||
|
||||
const TreeModelColumn<string>& name () const { return _action_columns.name; }
|
||||
const TreeModelColumn<string>& path () const { return _action_columns.path; }
|
||||
|
||||
private:
|
||||
ActionModel ();
|
||||
struct ActionColumns : public TreeModel::ColumnRecord {
|
||||
ActionColumns() {
|
||||
add (name);
|
||||
add (path);
|
||||
}
|
||||
TreeModelColumn<string> name;
|
||||
TreeModelColumn<string> path;
|
||||
};
|
||||
|
||||
const ActionColumns _action_columns;
|
||||
Glib::RefPtr<TreeStore> _available_action_model;
|
||||
};
|
||||
|
||||
|
||||
ButtonConfigWidget::ButtonConfigWidget ()
|
||||
: HBox ()
|
||||
, _choice_jump (_("Jump: "))
|
||||
, _choice_action (_("Other action: "))
|
||||
, _jump_distance (JumpDistance ())
|
||||
, _action_model (ActionModel::instance ())
|
||||
{
|
||||
RadioButtonGroup cbg = _choice_jump.get_group ();
|
||||
_choice_action.set_group (cbg);
|
||||
_choice_jump.signal_toggled().connect (sigc::mem_fun (*this, &ButtonConfigWidget::update_choice));
|
||||
|
||||
_jump_distance.Changed.connect (sigc::mem_fun (*this, &ButtonConfigWidget::update_config));
|
||||
|
||||
_action_cb.set_model (_action_model.model());
|
||||
_action_cb.signal_changed().connect (sigc::mem_fun (*this, &ButtonConfigWidget::update_config));
|
||||
_action_cb.pack_start (_action_model.name (), true);
|
||||
|
||||
HBox* jump_box = manage (new HBox);
|
||||
jump_box->pack_start (_choice_jump, false, true);
|
||||
jump_box->pack_start (_jump_distance, false, true);
|
||||
|
||||
HBox* action_box = manage (new HBox);
|
||||
action_box->pack_start (_choice_action, false, true);
|
||||
action_box->pack_start (_action_cb, false, true);
|
||||
|
||||
set_spacing (25);
|
||||
pack_start (*jump_box, false, true);
|
||||
pack_start (*action_box, false, true);
|
||||
}
|
||||
|
||||
bool
|
||||
ButtonConfigWidget::find_action_in_model (const TreeModel::iterator& iter, string const& action_path, TreeModel::iterator* found)
|
||||
{
|
||||
TreeModel::Row row = *iter;
|
||||
|
||||
if (action_path == string(row[_action_model.path ()])) {
|
||||
*found = iter;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ButtonConfigWidget::set_current_config (boost::shared_ptr<ButtonBase> btn_cnf)
|
||||
{
|
||||
const ButtonAction* ba = dynamic_cast<const ButtonAction*> (btn_cnf.get());
|
||||
if (ba) {
|
||||
set_current_action (ba->get_path ());
|
||||
_action_cb.set_sensitive (true);
|
||||
_jump_distance.set_sensitive (false);
|
||||
} else {
|
||||
const ButtonJump* bj = static_cast<const ButtonJump*> (btn_cnf.get());
|
||||
set_jump_distance (bj->get_jump_distance());
|
||||
_action_cb.set_sensitive (false);
|
||||
_jump_distance.set_sensitive (true);
|
||||
}
|
||||
}
|
||||
|
||||
boost::shared_ptr<ButtonBase>
|
||||
ButtonConfigWidget::get_current_config (ContourDesignControlProtocol& ccp) const
|
||||
{
|
||||
if (_choice_jump.get_active ()) {
|
||||
return boost::shared_ptr<ButtonBase> (new ButtonJump (JumpDistance (_jump_distance.get_distance ()), ccp));
|
||||
}
|
||||
|
||||
TreeModel::const_iterator row = _action_cb.get_active ();
|
||||
string action_path = (*row)[_action_model.path ()];
|
||||
|
||||
return boost::shared_ptr<ButtonBase> (new ButtonAction (action_path, ccp));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ButtonConfigWidget::set_current_action (std::string action_string)
|
||||
{
|
||||
_choice_action.set_active (true);
|
||||
_choice_jump.set_active (false);
|
||||
if (action_string.empty()) {
|
||||
_action_cb.set_active (0);
|
||||
return;
|
||||
}
|
||||
|
||||
TreeModel::iterator iter = _action_model.model()->children().end();
|
||||
|
||||
_action_model.model()->foreach_iter (sigc::bind (sigc::mem_fun (*this, &ButtonConfigWidget::find_action_in_model), action_string, &iter));
|
||||
|
||||
if (iter != _action_model.model()->children().end()) {
|
||||
_action_cb.set_active (iter);
|
||||
} else {
|
||||
_action_cb.set_active (0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ButtonConfigWidget::set_jump_distance (JumpDistance dist)
|
||||
{
|
||||
_choice_jump.set_active (true);
|
||||
_choice_action.set_active (false);
|
||||
_jump_distance.set_distance (dist);
|
||||
|
||||
Changed (); /* emit signal */
|
||||
}
|
||||
|
||||
void
|
||||
ButtonConfigWidget::update_choice ()
|
||||
{
|
||||
_jump_distance.set_sensitive (_choice_jump.get_active ());
|
||||
_action_cb.set_sensitive (_choice_action.get_active ());
|
||||
|
||||
Changed (); /* emit signal */
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ButtonConfigWidget::update_config ()
|
||||
{
|
||||
Changed (); /* emit signal */
|
||||
}
|
||||
|
||||
|
||||
|
||||
const ActionModel&
|
||||
ActionModel::instance ()
|
||||
{
|
||||
static ActionModel am;
|
||||
return am;
|
||||
}
|
||||
|
||||
ActionModel::ActionModel ()
|
||||
{
|
||||
_available_action_model = TreeStore::create (_action_columns);
|
||||
_available_action_model->clear ();
|
||||
|
||||
typedef std::map<string,TreeIter> NodeMap;
|
||||
NodeMap nodes;
|
||||
NodeMap::iterator r;
|
||||
|
||||
TreeIter rowp;
|
||||
TreeModel::Row parent;
|
||||
|
||||
rowp = _available_action_model->append ();
|
||||
parent = *(rowp);
|
||||
parent[_action_columns.name] = _("Disabled");
|
||||
|
||||
vector<string> paths;
|
||||
vector<string> labels;
|
||||
vector<string> tooltips;
|
||||
vector<string> keys;
|
||||
vector<Glib::RefPtr<Gtk::Action> > actions;
|
||||
|
||||
ActionManager::get_all_actions (paths, labels, tooltips, keys, actions);
|
||||
|
||||
vector<string>::iterator k;
|
||||
vector<string>::iterator p;
|
||||
vector<string>::iterator t;
|
||||
vector<string>::iterator l;
|
||||
|
||||
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[0] == _("Main_menu") )
|
||||
continue;
|
||||
if ( parts[0] == _("JACK") )
|
||||
continue;
|
||||
if ( parts[0] == _("redirectmenu") )
|
||||
continue;
|
||||
if ( parts[0] == _("Editor_menus") )
|
||||
continue;
|
||||
if ( parts[0] == _("RegionList") )
|
||||
continue;
|
||||
if ( parts[0] == _("ProcessorMenu") )
|
||||
continue;
|
||||
|
||||
if ((r = nodes.find (parts[0])) == nodes.end()) {
|
||||
/* top level is missing */
|
||||
|
||||
TreeIter rowp;
|
||||
TreeModel::Row parent;
|
||||
rowp = _available_action_model->append();
|
||||
nodes[parts[0]] = rowp;
|
||||
parent = *(rowp);
|
||||
parent[_action_columns.name] = parts[0];
|
||||
|
||||
row = *(_available_action_model->append (parent.children()));
|
||||
} else {
|
||||
row = *(_available_action_model->append ((*r->second)->children()));
|
||||
}
|
||||
|
||||
/* add this action */
|
||||
|
||||
if (l->empty ()) {
|
||||
row[_action_columns.name] = *t;
|
||||
} else {
|
||||
row[_action_columns.name] = *l;
|
||||
}
|
||||
|
||||
row[_action_columns.path] = *p;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
Copyright (C) 2019 Paul Davis
|
||||
Author: Johannes Mueller
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ardour_contourdesign_button_config_widget_h
|
||||
#define ardour_contourdesign_button_config_widget_h
|
||||
|
||||
#include <gtkmm/box.h>
|
||||
#include <gtkmm/radiobutton.h>
|
||||
#include <gtkmm/combobox.h>
|
||||
#include <gtkmm/treestore.h>
|
||||
|
||||
#include "contourdesign.h"
|
||||
#include "jump_distance_widget.h"
|
||||
|
||||
class ActionModel;
|
||||
|
||||
namespace ArdourSurface
|
||||
{
|
||||
class ButtonConfigWidget : public Gtk::HBox
|
||||
{
|
||||
public:
|
||||
ButtonConfigWidget ();
|
||||
~ButtonConfigWidget () {};
|
||||
|
||||
void set_current_config (boost::shared_ptr<ButtonBase> btn_cnf);
|
||||
boost::shared_ptr<ButtonBase> get_current_config (ContourDesignControlProtocol& ccp) const;
|
||||
|
||||
sigc::signal<void> Changed;
|
||||
|
||||
private:
|
||||
void set_current_action (std::string action_string);
|
||||
void set_jump_distance (JumpDistance dist);
|
||||
|
||||
Gtk::RadioButton _choice_jump;
|
||||
Gtk::RadioButton _choice_action;
|
||||
|
||||
void update_choice ();
|
||||
void update_config ();
|
||||
|
||||
bool find_action_in_model (const Gtk::TreeModel::iterator& iter, std::string const& action_path, Gtk::TreeModel::iterator* found);
|
||||
|
||||
JumpDistanceWidget _jump_distance;
|
||||
Gtk::ComboBox _action_cb;
|
||||
|
||||
const ActionModel& _action_model;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* ardour_contourdesign_button_config_widget_h */
|
|
@ -0,0 +1,640 @@
|
|||
/*
|
||||
Copyright (C) 2019 Paul Davis
|
||||
Author: Johannes Mueller
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
#include <glibmm.h>
|
||||
|
||||
#include "pbd/compose.h"
|
||||
#include "pbd/error.h"
|
||||
#include "ardour/debug.h"
|
||||
#include "ardour/session.h"
|
||||
#include "ardour/tempo.h"
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
#include "contourdesign.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
using namespace Glib;
|
||||
using namespace std;
|
||||
using namespace ArdourSurface;
|
||||
|
||||
#include "pbd/abstract_ui.cc" // instantiate template
|
||||
|
||||
static const uint16_t ContourDesign = 0x0b33;
|
||||
static const uint16_t ShuttlePRO_id = 0x0030;
|
||||
static const uint16_t ShuttleXpress_id = 0x0020;
|
||||
|
||||
static void LIBUSB_CALL event_callback (struct libusb_transfer* transfer);
|
||||
|
||||
|
||||
ContourDesignControlProtocol::ContourDesignControlProtocol (Session& session)
|
||||
: ControlProtocol (session, X_("ContourDesign"))
|
||||
, AbstractUI<ContourDesignControlUIRequest> ("contourdesign")
|
||||
, _io_source (0)
|
||||
, _dev_handle (0)
|
||||
, _usb_transfer (0)
|
||||
, _supposed_to_quit (false)
|
||||
, _device_type (None)
|
||||
, _shuttle_was_zero (true)
|
||||
, _was_rolling_before_shuttle (false)
|
||||
, _test_mode (false)
|
||||
, _keep_rolling (true)
|
||||
, _shuttle_speeds ()
|
||||
, _jog_distance ()
|
||||
, _gui (0)
|
||||
{
|
||||
libusb_init (0);
|
||||
// libusb_set_debug(0, LIBUSB_LOG_LEVEL_WARNING);
|
||||
|
||||
_shuttle_speeds.push_back (0.50);
|
||||
_shuttle_speeds.push_back (0.75);
|
||||
_shuttle_speeds.push_back (1.0);
|
||||
_shuttle_speeds.push_back (1.5);
|
||||
_shuttle_speeds.push_back (2.0);
|
||||
_shuttle_speeds.push_back (5.0);
|
||||
_shuttle_speeds.push_back (10.0);
|
||||
|
||||
setup_default_button_actions ();
|
||||
BaseUI::run();
|
||||
}
|
||||
|
||||
ContourDesignControlProtocol::~ContourDesignControlProtocol ()
|
||||
{
|
||||
stop ();
|
||||
libusb_exit (0);
|
||||
BaseUI::quit();
|
||||
tear_down_gui ();
|
||||
}
|
||||
|
||||
bool
|
||||
ContourDesignControlProtocol::probe ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
ContourDesignControlProtocol::set_active (bool yn)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, string_compose ("set_active() init with yn: '%1'\n", yn));
|
||||
|
||||
if (yn == active()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (yn) {
|
||||
start ();
|
||||
} else {
|
||||
stop ();
|
||||
}
|
||||
|
||||
ControlProtocol::set_active (yn);
|
||||
|
||||
return _error;
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
ContourDesignControlProtocol::get_state ()
|
||||
{
|
||||
XMLNode& node (ControlProtocol::get_state());
|
||||
node.set_property (X_("keep-rolling"), _keep_rolling);
|
||||
|
||||
ostringstream os;
|
||||
vector<double>::const_iterator it = _shuttle_speeds.begin ();
|
||||
os << *(it++);
|
||||
for (; it != _shuttle_speeds.end (); ++it) {
|
||||
os << ' ' << *it;
|
||||
}
|
||||
string s = os.str ();
|
||||
node.set_property (X_("shuttle-speeds"), s);
|
||||
|
||||
node.set_property (X_("jog-distance"), _jog_distance.value);
|
||||
switch (_jog_distance.unit) {
|
||||
case SECONDS: s = X_("seconds"); break;
|
||||
case BARS: s = X_("bars"); break;
|
||||
case BEATS:
|
||||
default: s = X_("beats");
|
||||
}
|
||||
node.set_property (X_("jog-unit"), s);
|
||||
|
||||
for (unsigned int i=0; i<_button_actions.size(); ++i) {
|
||||
XMLNode* child = new XMLNode (string_compose (X_("button-%1"), i+1));
|
||||
node.add_child_nocopy (_button_actions[i]->get_state (*child));
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
int
|
||||
ContourDesignControlProtocol::set_state (const XMLNode& node, int version)
|
||||
{
|
||||
if (ControlProtocol::set_state (node, version)) {
|
||||
return -1;
|
||||
}
|
||||
node.get_property (X_("keep-rolling"), _keep_rolling);
|
||||
|
||||
string s;
|
||||
node.get_property (X_("shuttle-speeds"), s);
|
||||
istringstream is (s);
|
||||
for (vector<double>::iterator it = _shuttle_speeds.begin (); it != _shuttle_speeds.end (); ++it) {
|
||||
is >> *it;
|
||||
}
|
||||
|
||||
node.get_property (X_("jog-distance"), _jog_distance.value);
|
||||
node.get_property (X_("jog-unit"), s);
|
||||
if (s == "seconds") {
|
||||
_jog_distance.unit = SECONDS;
|
||||
} else if (s == "bars") {
|
||||
_jog_distance.unit = BARS;
|
||||
} else {
|
||||
_jog_distance.unit = BEATS;
|
||||
}
|
||||
|
||||
XMLNode* child;
|
||||
for (unsigned int i=0; i<_button_actions.size(); ++i) {
|
||||
if ((child = node.child (string_compose(X_("button-%1"), i+1).c_str())) == 0) {
|
||||
continue;
|
||||
}
|
||||
string type;
|
||||
child->get_property (X_("type"), type);
|
||||
if (type == X_("action")) {
|
||||
string path ("");
|
||||
child->get_property (X_("path"), path);
|
||||
boost::shared_ptr<ButtonBase> b (new ButtonAction (path, *this));
|
||||
_button_actions[i] = b;
|
||||
} else {
|
||||
double value;
|
||||
child->get_property(X_("value"), value);
|
||||
|
||||
string s;
|
||||
child->get_property(X_("unit"), s);
|
||||
JumpUnit unit;
|
||||
if (s == X_("seconds")) {
|
||||
unit = SECONDS;
|
||||
} else if (s == X_("bars")) {
|
||||
unit = BARS;
|
||||
} else {
|
||||
unit = BEATS;
|
||||
}
|
||||
|
||||
boost::shared_ptr<ButtonBase> b (new ButtonJump (JumpDistance (value, unit), *this));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::do_request (ContourDesignControlUIRequest* req)
|
||||
{
|
||||
if (req->type == CallSlot) {
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "do_request type CallSlot\n");
|
||||
call_slot (MISSING_INVALIDATOR, req->the_slot);
|
||||
} else if (req->type == Quit) {
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "do_request type Quit\n");
|
||||
stop ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::thread_init ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "thread_init()\n");
|
||||
|
||||
pthread_set_name (X_("contourdesign"));
|
||||
PBD::notify_event_loops_about_thread_creation (pthread_self (), X_("contourdesign"), 2048);
|
||||
ARDOUR::SessionEvent::create_per_thread_pool (X_("contourdesign"), 128);
|
||||
|
||||
set_thread_priority ();
|
||||
}
|
||||
|
||||
bool
|
||||
ContourDesignControlProtocol::wait_for_event ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "wait_for_event\n");
|
||||
if (!_supposed_to_quit) {
|
||||
libusb_handle_events (0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
get_usb_device (uint16_t vendor_id, uint16_t product_id, libusb_device** device)
|
||||
{
|
||||
struct libusb_device **devs;
|
||||
struct libusb_device *dev;
|
||||
size_t i = 0;
|
||||
int r;
|
||||
|
||||
*device = 0;
|
||||
|
||||
if (libusb_get_device_list (0, &devs) < 0) {
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
while ((dev = devs[i++])) {
|
||||
struct libusb_device_descriptor desc;
|
||||
r = libusb_get_device_descriptor (dev, &desc);
|
||||
if (r < 0) {
|
||||
goto out;
|
||||
}
|
||||
if (desc.idVendor == vendor_id && desc.idProduct == product_id) {
|
||||
*device = dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
libusb_free_device_list(devs, 1);
|
||||
if (!dev && !r) {
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
ContourDesignControlProtocol::acquire_device ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "acquire_device()\n");
|
||||
|
||||
int err;
|
||||
|
||||
if (_dev_handle) {
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "already have a device handle\n");
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
libusb_device* dev;
|
||||
|
||||
|
||||
if ((err = get_usb_device (ContourDesign, ShuttleXpress_id, &dev)) == 0) {
|
||||
_device_type = ShuttleXpress;
|
||||
}
|
||||
else if ((err = get_usb_device (ContourDesign, ShuttlePRO_id, &dev)) == 0) {
|
||||
_device_type = ShuttlePRO;
|
||||
} else {
|
||||
_device_type = None;
|
||||
return err;
|
||||
}
|
||||
|
||||
err = libusb_open (dev, &_dev_handle);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
libusb_set_auto_detach_kernel_driver (_dev_handle, true);
|
||||
|
||||
if ((err = libusb_claim_interface (_dev_handle, 0x00))) {
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "failed to claim USB device\n");
|
||||
goto usb_close;
|
||||
}
|
||||
|
||||
_usb_transfer = libusb_alloc_transfer (0);
|
||||
if (!_usb_transfer) {
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "failed to alloc usb transfer\n");
|
||||
err = LIBUSB_ERROR_NO_MEM;
|
||||
goto usb_close;
|
||||
}
|
||||
|
||||
libusb_fill_interrupt_transfer (_usb_transfer, _dev_handle, 1 | LIBUSB_ENDPOINT_IN, _buf, sizeof(_buf),
|
||||
event_callback, this, 0);
|
||||
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "callback installed\n");
|
||||
|
||||
if ((err = libusb_submit_transfer (_usb_transfer))) {
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, string_compose ("failed to submit tansfer: %1\n", err));
|
||||
goto free_transfer;
|
||||
}
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
|
||||
free_transfer:
|
||||
libusb_free_transfer (_usb_transfer);
|
||||
|
||||
usb_close:
|
||||
libusb_close (_dev_handle);
|
||||
_dev_handle = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::release_device ()
|
||||
{
|
||||
if (!_dev_handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
libusb_close (_dev_handle);
|
||||
libusb_free_transfer (_usb_transfer);
|
||||
libusb_release_interface (_dev_handle, 0);
|
||||
_usb_transfer = 0;
|
||||
_dev_handle = 0;
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::start ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "start()\n");
|
||||
|
||||
_supposed_to_quit = false;
|
||||
|
||||
_error = acquire_device();
|
||||
if (_error) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_dev_handle) { // can this actually happen?
|
||||
_error = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
_state.shuttle = 0;
|
||||
_state.jog = 0;
|
||||
_state.buttons = 0;
|
||||
|
||||
Glib::RefPtr<Glib::IdleSource> source = Glib::IdleSource::create ();
|
||||
source->connect (sigc::mem_fun (*this, &ContourDesignControlProtocol::wait_for_event));
|
||||
source->attach (_main_loop->get_context ());
|
||||
|
||||
_io_source = source->gobj ();
|
||||
g_source_ref (_io_source);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::stop ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "stop()\n");
|
||||
|
||||
_supposed_to_quit = true;
|
||||
|
||||
if (_io_source) {
|
||||
g_source_destroy (_io_source);
|
||||
g_source_unref (_io_source);
|
||||
_io_source = 0;
|
||||
}
|
||||
|
||||
if (_dev_handle) {
|
||||
release_device ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::handle_event () {
|
||||
if (_usb_transfer->status == LIBUSB_TRANSFER_TIMED_OUT) {
|
||||
goto resubmit;
|
||||
}
|
||||
if (_usb_transfer->status != LIBUSB_TRANSFER_COMPLETED) {
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, string_compose("libusb_transfer not completed: %1\n", _usb_transfer->status));
|
||||
_error = LIBUSB_ERROR_NO_DEVICE;
|
||||
return;
|
||||
}
|
||||
|
||||
State new_state;
|
||||
new_state.shuttle = _buf[0];
|
||||
new_state.jog = _buf[1];
|
||||
new_state.buttons = (_buf[4] << 8) + _buf[3];
|
||||
|
||||
// cout << "event " << (int)new_state.shuttle << " " << (int)new_state.jog << " " << (int)new_state.buttons << endl;;
|
||||
|
||||
for (uint8_t btn=0; btn<16; btn++) {
|
||||
if ( (new_state.buttons & (1<<btn)) && !(_state.buttons & (1<<btn)) ) {
|
||||
handle_button_press (btn);
|
||||
} else if ( !(new_state.buttons & (1<<btn)) && (_state.buttons & (1<<btn)) ) {
|
||||
handle_button_release (btn);
|
||||
}
|
||||
}
|
||||
|
||||
if (new_state.jog == 255 && _state.jog == 0) {
|
||||
jog_event_backward ();
|
||||
} else if (new_state.jog == 0 && _state.jog == 255) {
|
||||
jog_event_forward();
|
||||
} else if (new_state.jog < _state.jog) {
|
||||
jog_event_backward();
|
||||
} else if (new_state.jog > _state.jog) {
|
||||
jog_event_forward();
|
||||
}
|
||||
|
||||
if (new_state.shuttle != _state.shuttle) {
|
||||
shuttle_event(new_state.shuttle);
|
||||
}
|
||||
|
||||
_state = new_state;
|
||||
|
||||
resubmit:
|
||||
if (libusb_submit_transfer (_usb_transfer)) {
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "failed to resubmit usb transfer after callback\n");
|
||||
stop ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<ButtonBase>
|
||||
ContourDesignControlProtocol::make_button_action (string action_string)
|
||||
{
|
||||
return boost::shared_ptr<ButtonBase> (new ButtonAction (action_string, *this));
|
||||
}
|
||||
|
||||
/* The buttons have the following layout
|
||||
*
|
||||
* 00 01 02 03
|
||||
* 04 05 06 07 08
|
||||
*
|
||||
* 13 Jog 14
|
||||
*
|
||||
* 09 10
|
||||
* 11 12
|
||||
*/
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::setup_default_button_actions ()
|
||||
{
|
||||
_button_actions.push_back (make_button_action ("MIDI/panic"));
|
||||
_button_actions.push_back (make_button_action ("Editor/remove-last-capture"));
|
||||
_button_actions.push_back (make_button_action ("Editor/undo"));
|
||||
_button_actions.push_back (make_button_action ("Editor/redo"));
|
||||
_button_actions.push_back (make_button_action ("Common/jump-backward-to-mark"));
|
||||
_button_actions.push_back (make_button_action ("Transport/Record"));
|
||||
_button_actions.push_back (make_button_action ("Transport/Stop"));
|
||||
_button_actions.push_back (make_button_action ("Transport/Roll"));
|
||||
_button_actions.push_back (make_button_action ("Common/jump-forward-to-mark"));
|
||||
_button_actions.push_back (boost::shared_ptr<ButtonBase> (new ButtonJump (JumpDistance (-4.0, BARS), *this)));
|
||||
_button_actions.push_back (boost::shared_ptr<ButtonBase> (new ButtonJump (JumpDistance (+4.0, BARS), *this)));
|
||||
_button_actions.push_back (make_button_action (""));
|
||||
_button_actions.push_back (make_button_action ("Common/add-location-from-playhead"));
|
||||
_button_actions.push_back (make_button_action ("Transport/GotoStart"));
|
||||
_button_actions.push_back (make_button_action ("Transport/GotoEnd"));
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::handle_button_press (unsigned short btn)
|
||||
{
|
||||
if (_test_mode) {
|
||||
ButtonPress (btn); /* emit signal */
|
||||
return;
|
||||
}
|
||||
if (btn >= _button_actions.size ()) {
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl,
|
||||
string_compose ("ContourDesign button number out of bounds %1, max is %2\n",
|
||||
btn, _button_actions.size ()));
|
||||
return;
|
||||
}
|
||||
|
||||
_button_actions[btn]->execute ();
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::handle_button_release (unsigned short btn)
|
||||
{
|
||||
if (_test_mode) {
|
||||
ButtonRelease (btn); /* emit signal */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::prev_marker_keep_rolling ()
|
||||
{
|
||||
samplepos_t pos = session->locations()->first_mark_before (session->transport_sample());
|
||||
|
||||
if (pos >= 0) {
|
||||
session->request_locate (pos, _keep_rolling && session->transport_rolling());
|
||||
} else {
|
||||
session->goto_start ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::next_marker_keep_rolling ()
|
||||
{
|
||||
samplepos_t pos = session->locations()->first_mark_after (session->transport_sample());
|
||||
|
||||
if (pos >= 0) {
|
||||
session->request_locate (pos, _keep_rolling && session->transport_rolling());
|
||||
} else {
|
||||
session->goto_end();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::jog_event_backward ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "jog event backward\n");
|
||||
jump_backward (_jog_distance);
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::jog_event_forward ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, "jog event forward\n");
|
||||
jump_forward (_jog_distance);
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::jump_forward (JumpDistance dist)
|
||||
{
|
||||
bool kr = _keep_rolling && session->transport_rolling ();
|
||||
switch (dist.unit) {
|
||||
case SECONDS: jump_by_seconds (dist.value, kr); break;
|
||||
case BEATS: jump_by_beats (dist.value, kr); break;
|
||||
case BARS: jump_by_bars (dist.value, kr); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void ContourDesignControlProtocol::jump_backward (JumpDistance dist)
|
||||
{
|
||||
JumpDistance bw = dist;
|
||||
bw.value = -bw.value;
|
||||
jump_forward(bw);
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::shuttle_event(int position)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, string_compose ("shuttle event %1\n", position));
|
||||
if (position != 0) {
|
||||
if (_shuttle_was_zero) {
|
||||
_was_rolling_before_shuttle = session->transport_rolling ();
|
||||
}
|
||||
double speed = position > 0 ? _shuttle_speeds[position-1] : -_shuttle_speeds[-position-1];
|
||||
set_transport_speed (speed);
|
||||
_shuttle_was_zero = false;
|
||||
} else {
|
||||
if (_keep_rolling && _was_rolling_before_shuttle) {
|
||||
set_transport_speed (1.0);
|
||||
} else {
|
||||
transport_stop ();
|
||||
}
|
||||
_shuttle_was_zero = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ButtonJump::execute ()
|
||||
{
|
||||
_spc.jump_forward (_dist);
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
ButtonJump::get_state (XMLNode& node) const
|
||||
{
|
||||
string ts (X_("jump"));
|
||||
node.set_property (X_("type"), ts);
|
||||
node.set_property (X_("distance"), _dist.value);
|
||||
|
||||
string s;
|
||||
switch (_dist.unit) {
|
||||
case SECONDS: s = X_("seconds"); break;
|
||||
case BARS: s = X_("bars"); break;
|
||||
case BEATS:
|
||||
default: s = X_("beats");
|
||||
}
|
||||
node.set_property (X_("unit"), s);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void
|
||||
ButtonAction::execute ()
|
||||
{
|
||||
_spc.access_action (_action_string);
|
||||
}
|
||||
|
||||
|
||||
XMLNode&
|
||||
ButtonAction::get_state (XMLNode& node) const
|
||||
{
|
||||
string ts (X_("action"));
|
||||
node.set_property (X_("type"), ts);
|
||||
node.set_property (X_("path"), _action_string);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static void LIBUSB_CALL event_callback (libusb_transfer* transfer)
|
||||
{
|
||||
ContourDesignControlProtocol* spc = static_cast<ContourDesignControlProtocol*> (transfer->user_data);
|
||||
spc->handle_event();
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
Copyright (C) 2019 Paul Davis
|
||||
Author: Johannes Mueller
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ardour_contourdesign_control_protocol_h
|
||||
#define ardour_contourdesign_control_protocol_h
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <glibmm/main.h>
|
||||
|
||||
#define ABSTRACT_UI_EXPORTS
|
||||
#include "pbd/abstract_ui.h"
|
||||
#include "ardour/types.h"
|
||||
#include "control_protocol/control_protocol.h"
|
||||
|
||||
struct libusb_device_handle;
|
||||
struct libusb_transfer;
|
||||
|
||||
class ContourDesignGUI;
|
||||
|
||||
namespace ArdourSurface {
|
||||
|
||||
struct ContourDesignControlUIRequest : public BaseUI::BaseRequestObject {
|
||||
public:
|
||||
ContourDesignControlUIRequest () {}
|
||||
~ContourDesignControlUIRequest () {}
|
||||
};
|
||||
|
||||
enum JumpUnit {
|
||||
SECONDS = 0,
|
||||
BEATS = 1,
|
||||
BARS = 2
|
||||
};
|
||||
|
||||
struct JumpDistance {
|
||||
JumpDistance () : value (1.0), unit (BEATS) {}
|
||||
JumpDistance (double v, JumpUnit u) : value (v), unit (u) {}
|
||||
JumpDistance (const JumpDistance& o) : value (o.value), unit (o.unit) {}
|
||||
double value;
|
||||
JumpUnit unit;
|
||||
};
|
||||
|
||||
class ButtonBase;
|
||||
|
||||
|
||||
class ContourDesignControlProtocol
|
||||
: public ARDOUR::ControlProtocol
|
||||
, public AbstractUI<ContourDesignControlUIRequest>
|
||||
{
|
||||
friend ContourDesignGUI;
|
||||
public:
|
||||
ContourDesignControlProtocol (ARDOUR::Session &);
|
||||
virtual ~ContourDesignControlProtocol ();
|
||||
|
||||
enum DeviceType {
|
||||
None = 0,
|
||||
ShuttlePRO,
|
||||
ShuttleXpress
|
||||
};
|
||||
|
||||
DeviceType device_type() const { return _device_type; }
|
||||
|
||||
static bool probe ();
|
||||
|
||||
int set_active (bool yn);
|
||||
|
||||
XMLNode& get_state ();
|
||||
int set_state (const XMLNode&, int version);
|
||||
|
||||
void stripable_selection_changed () {}
|
||||
|
||||
void handle_event ();
|
||||
|
||||
static const int num_shuttle_speeds = 7;
|
||||
|
||||
void prev_marker_keep_rolling ();
|
||||
void next_marker_keep_rolling ();
|
||||
|
||||
void jump_forward (JumpDistance dist);
|
||||
void jump_backward (JumpDistance dist);
|
||||
|
||||
boost::shared_ptr<ButtonBase> make_button_action (std::string action_string);
|
||||
|
||||
private:
|
||||
void do_request (ContourDesignControlUIRequest*);
|
||||
void start ();
|
||||
void stop ();
|
||||
|
||||
bool has_editor () const { return true; }
|
||||
void* get_gui () const;
|
||||
void tear_down_gui ();
|
||||
|
||||
void thread_init ();
|
||||
|
||||
int acquire_device ();
|
||||
void release_device ();
|
||||
|
||||
void handle_button_press (unsigned short btn);
|
||||
void handle_button_release (unsigned short btn);
|
||||
|
||||
void jog_event_backward ();
|
||||
void jog_event_forward ();
|
||||
|
||||
void shuttle_event (int position);
|
||||
|
||||
bool wait_for_event ();
|
||||
GSource* _io_source;
|
||||
libusb_device_handle* _dev_handle;
|
||||
libusb_transfer* _usb_transfer;
|
||||
bool _supposed_to_quit;
|
||||
|
||||
unsigned char _buf[5];
|
||||
|
||||
DeviceType _device_type;
|
||||
|
||||
bool _shuttle_was_zero, _was_rolling_before_shuttle;
|
||||
|
||||
struct State {
|
||||
int8_t shuttle;
|
||||
uint8_t jog;
|
||||
uint16_t buttons;
|
||||
};
|
||||
State _state;
|
||||
|
||||
bool _test_mode;
|
||||
PBD::Signal1<void, unsigned short> ButtonPress;
|
||||
PBD::Signal1<void, unsigned short> ButtonRelease;
|
||||
|
||||
// Config stuff
|
||||
|
||||
bool _keep_rolling;
|
||||
std::vector<double> _shuttle_speeds;
|
||||
JumpDistance _jog_distance;
|
||||
|
||||
std::vector<boost::shared_ptr<ButtonBase> > _button_actions;
|
||||
void setup_default_button_actions ();
|
||||
|
||||
mutable ContourDesignGUI* _gui;
|
||||
void build_gui ();
|
||||
|
||||
int _error;
|
||||
bool _needs_reattach;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ButtonBase
|
||||
{
|
||||
public:
|
||||
ButtonBase (ContourDesignControlProtocol& spc) : _spc (spc) {}
|
||||
virtual ~ButtonBase () {}
|
||||
virtual void execute () = 0;
|
||||
|
||||
virtual XMLNode& get_state (XMLNode& node) const = 0;
|
||||
|
||||
protected:
|
||||
ContourDesignControlProtocol& _spc;
|
||||
};
|
||||
|
||||
|
||||
class ButtonJump : public ButtonBase
|
||||
{
|
||||
public:
|
||||
ButtonJump (JumpDistance dist, ContourDesignControlProtocol& ccp)
|
||||
: ButtonBase (ccp)
|
||||
, _dist (dist) {}
|
||||
~ButtonJump () {}
|
||||
|
||||
void execute ();
|
||||
JumpDistance get_jump_distance () const { return _dist; };
|
||||
|
||||
XMLNode& get_state (XMLNode& node) const;
|
||||
|
||||
private:
|
||||
JumpDistance _dist;
|
||||
};
|
||||
|
||||
class ButtonAction : public ButtonBase
|
||||
{
|
||||
public:
|
||||
ButtonAction (const std::string as, ContourDesignControlProtocol& ccp)
|
||||
: ButtonBase (ccp)
|
||||
, _action_string (as) {}
|
||||
~ButtonAction () {}
|
||||
|
||||
void execute ();
|
||||
std::string get_path () const { return _action_string; }
|
||||
|
||||
XMLNode& get_state (XMLNode& node) const;
|
||||
|
||||
private:
|
||||
const std::string _action_string;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif /* ardour_contourdesign_control_protocol_h */
|
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
Copyright (C) 2019 Paul Davis
|
||||
Author: Johannes Mueller
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include <libusb.h>
|
||||
|
||||
#include <gtkmm/adjustment.h>
|
||||
#include <gtkmm/box.h>
|
||||
#include <gtkmm/comboboxtext.h>
|
||||
#include <gtkmm/frame.h>
|
||||
#include <gtkmm/label.h>
|
||||
#include <gtkmm/liststore.h>
|
||||
#include <gtkmm/spinbutton.h>
|
||||
#include <gtkmm/table.h>
|
||||
|
||||
#include "pbd/unwind.h"
|
||||
|
||||
#include "ardour/debug.h"
|
||||
|
||||
#include "gtkmm2ext/gtk_ui.h"
|
||||
#include "gtkmm2ext/gui_thread.h"
|
||||
#include "gtkmm2ext/utils.h"
|
||||
|
||||
#include "widgets/ardour_button.h"
|
||||
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
#include "contourdesign.h"
|
||||
#include "jump_distance_widget.h"
|
||||
#include "button_config_widget.h"
|
||||
|
||||
using namespace ArdourSurface;
|
||||
|
||||
class ContourDesignGUI : public Gtk::VBox, public PBD::ScopedConnectionList
|
||||
{
|
||||
public:
|
||||
ContourDesignGUI (ContourDesignControlProtocol& ccp);
|
||||
~ContourDesignGUI () {}
|
||||
|
||||
private:
|
||||
ContourDesignControlProtocol& _ccp;
|
||||
|
||||
ArdourWidgets::ArdourButton _test_button;
|
||||
|
||||
Gtk::CheckButton _keep_rolling;
|
||||
void toggle_keep_rolling ();
|
||||
|
||||
std::vector<boost::shared_ptr<Gtk::Adjustment> > _shuttle_speed_adjustments;
|
||||
void set_shuttle_speed (int index);
|
||||
|
||||
JumpDistanceWidget _jog_distance;
|
||||
void update_jog_distance ();
|
||||
|
||||
void update_action(unsigned int index, ButtonConfigWidget* sender);
|
||||
|
||||
void toggle_test_mode ();
|
||||
|
||||
void test_button_press (unsigned short btn);
|
||||
void test_button_release (unsigned short btn);
|
||||
|
||||
std::vector<boost::shared_ptr<ArdourWidgets::ArdourButton> > _btn_leds;
|
||||
|
||||
void init_on_show ();
|
||||
bool reset_test_state (GdkEventAny* = 0);
|
||||
bool update_device_state ();
|
||||
|
||||
Gtk::Label _device_state_lbl;
|
||||
|
||||
sigc::signal<void, bool> ProButtonsSensitive;
|
||||
sigc::signal<void, bool> XpressButtonsSensitive;
|
||||
};
|
||||
|
||||
|
||||
using namespace PBD;
|
||||
using namespace ARDOUR;
|
||||
using namespace std;
|
||||
using namespace Gtk;
|
||||
using namespace Gtkmm2ext;
|
||||
using namespace Glib;
|
||||
using namespace ArdourWidgets;
|
||||
|
||||
|
||||
ContourDesignGUI::ContourDesignGUI (ContourDesignControlProtocol& ccp)
|
||||
: _ccp (ccp)
|
||||
, _test_button (_("Button Test"), ArdourButton::led_default_elements)
|
||||
, _keep_rolling (_("Keep rolling after jumps"))
|
||||
, _jog_distance (ccp._jog_distance)
|
||||
, _device_state_lbl ()
|
||||
{
|
||||
Frame* dg_sample = manage (new Frame (_("Device")));
|
||||
dg_sample->set_size_request (300, -1);
|
||||
VBox* dg_box = manage (new VBox);
|
||||
dg_sample->add (*dg_box);
|
||||
dg_box->set_border_width (6);
|
||||
dg_box->pack_start (_device_state_lbl);
|
||||
|
||||
_device_state_lbl.set_line_wrap (true);
|
||||
|
||||
Frame* sj_sample = manage (new Frame (_("Shuttle speeds and jog jump distances")));
|
||||
Table* sj_table = manage (new Table);
|
||||
sj_sample->set_border_width (6);
|
||||
sj_table->set_border_width (12);
|
||||
sj_sample->add (*sj_table);
|
||||
|
||||
Label* speed_label = manage (new Label (_("Transport speeds for the shuttle positions:"), ALIGN_START));
|
||||
sj_table->attach (*speed_label, 0,1, 0,1, FILL|EXPAND, FILL|EXPAND, /* xpadding = */ 12);
|
||||
|
||||
HBox* speed_box = manage (new HBox);
|
||||
for (int i=0; i != ContourDesignControlProtocol::num_shuttle_speeds; ++i) {
|
||||
double speed = ccp._shuttle_speeds[i];
|
||||
boost::shared_ptr<Gtk::Adjustment> adj (new Gtk::Adjustment (speed, 0.0, 100.0, 0.25));
|
||||
_shuttle_speed_adjustments.push_back (adj);
|
||||
SpinButton* sb = manage (new SpinButton (*adj, 0.25, 2));
|
||||
speed_box->pack_start (*sb);
|
||||
sb->signal_value_changed().connect (sigc::bind (sigc::mem_fun(*this, &ContourDesignGUI::set_shuttle_speed), i));
|
||||
}
|
||||
sj_table->attach (*speed_box, 1,2, 0,1);
|
||||
|
||||
Label* jog_label = manage (new Label (_("Jump distance for jog wheel:"), ALIGN_START));
|
||||
_jog_distance.Changed.connect (sigc::mem_fun (*this, &ContourDesignGUI::update_jog_distance));
|
||||
|
||||
sj_table->attach (*jog_label, 0,1, 1,2, FILL|EXPAND, FILL|EXPAND, /* xpadding = */ 12);
|
||||
sj_table->attach (_jog_distance, 1,2, 1,2);
|
||||
|
||||
_keep_rolling.set_tooltip_text (_("If checked Ardour keeps rolling after jog or shuttle events. If unchecked it stops."));
|
||||
_keep_rolling.signal_toggled().connect (sigc::mem_fun (*this, &ContourDesignGUI::toggle_keep_rolling));
|
||||
_keep_rolling.set_active (_ccp._keep_rolling);
|
||||
|
||||
sj_table->attach (_keep_rolling, 0,1, 2,3);
|
||||
|
||||
|
||||
Frame* btn_action_sample = manage (new Frame (_("Actions or jumps for buttons")));
|
||||
HBox* btn_action_box = manage (new HBox);
|
||||
btn_action_sample->set_border_width (6);
|
||||
btn_action_box->set_border_width (12);
|
||||
btn_action_sample->add (*btn_action_box);
|
||||
|
||||
VBox* tbb = manage (new VBox);
|
||||
_test_button.set_tooltip_text (_("If the button is active, all the button presses are not handled, "
|
||||
"but in the corresponding line in the button table the LED will light up."));
|
||||
_test_button.signal_clicked.connect (sigc::mem_fun (*this, &ContourDesignGUI::toggle_test_mode));
|
||||
_test_button.set_size_request (-1, 64);
|
||||
tbb->pack_start(_test_button, true, false);
|
||||
btn_action_box->pack_start (*tbb, true, false, 12);
|
||||
|
||||
|
||||
Table* table = manage (new Table);
|
||||
table->set_row_spacings (6);
|
||||
table->set_col_spacings (6);;
|
||||
|
||||
vector<boost::shared_ptr<ButtonBase> >::const_iterator it;
|
||||
unsigned int btn_idx = 0;
|
||||
for (it = _ccp._button_actions.begin(); it != _ccp._button_actions.end(); ++it) {
|
||||
boost::shared_ptr<ArdourButton> b (new ArdourButton (string_compose (_("Setting for button %1"), btn_idx+1),
|
||||
ArdourButton::Element(ArdourButton::Indicator|ArdourButton::Text|ArdourButton::Inactive)));
|
||||
table->attach (*b, 0, 2, btn_idx, btn_idx+1);
|
||||
_btn_leds.push_back (b);
|
||||
|
||||
ButtonConfigWidget* bcw = manage (new ButtonConfigWidget);
|
||||
|
||||
bcw->set_current_config (*it);
|
||||
bcw->Changed.connect (sigc::bind (sigc::mem_fun (*this, &ContourDesignGUI::update_action), btn_idx, bcw));
|
||||
table->attach (*bcw, 3, 5, btn_idx, btn_idx+1);
|
||||
|
||||
if (btn_idx > 3 && btn_idx < 9) {
|
||||
this->XpressButtonsSensitive.connect (sigc::mem_fun (*b, &ArdourButton::set_sensitive));
|
||||
this->XpressButtonsSensitive.connect (sigc::mem_fun (*bcw, &ButtonConfigWidget::set_sensitive));
|
||||
} else {
|
||||
this->ProButtonsSensitive.connect (sigc::mem_fun (*b, &ArdourButton::set_sensitive));
|
||||
this->ProButtonsSensitive.connect (sigc::mem_fun (*bcw, &ButtonConfigWidget::set_sensitive));
|
||||
}
|
||||
++btn_idx;
|
||||
}
|
||||
|
||||
set_spacing (6);
|
||||
btn_action_box->pack_start (*table, false, false);
|
||||
|
||||
HBox* top_box = manage (new HBox);
|
||||
top_box->pack_start (*dg_sample);
|
||||
top_box->pack_start (*sj_sample);
|
||||
pack_start (*top_box);
|
||||
pack_start (*btn_action_sample);
|
||||
|
||||
_ccp.ButtonPress.connect (*this, invalidator (*this), boost::bind (&ContourDesignGUI::test_button_press, this, _1), gui_context ());
|
||||
_ccp.ButtonRelease.connect (*this, invalidator (*this), boost::bind (&ContourDesignGUI::test_button_release, this, _1), gui_context ());
|
||||
|
||||
signal_map().connect (sigc::mem_fun (*this, &ContourDesignGUI::init_on_show));
|
||||
update_device_state ();
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignGUI::toggle_keep_rolling ()
|
||||
{
|
||||
_ccp._keep_rolling = _keep_rolling.get_active();
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignGUI::set_shuttle_speed (int index)
|
||||
{
|
||||
double speed = _shuttle_speed_adjustments[index]->get_value ();
|
||||
_ccp._shuttle_speeds[index] = speed;
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignGUI::update_jog_distance ()
|
||||
{
|
||||
_ccp._jog_distance = _jog_distance.get_distance ();
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignGUI::update_action (unsigned int index, ButtonConfigWidget* sender)
|
||||
{
|
||||
if (index >= _ccp._button_actions.size()) {
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, string_compose ("ContourDesignGUI::update_action() index out of bounds %1 / %2\n", index, _ccp._button_actions.size()));
|
||||
return;
|
||||
}
|
||||
_ccp._button_actions[index] = sender->get_current_config (_ccp);
|
||||
DEBUG_TRACE (DEBUG::ContourDesignControl, string_compose ("update_action () %1\n", index));
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignGUI::toggle_test_mode ()
|
||||
{
|
||||
_ccp._test_mode = !_ccp._test_mode;
|
||||
if (_ccp._test_mode) {
|
||||
_test_button.set_active_state (ActiveState::ExplicitActive);
|
||||
} else {
|
||||
reset_test_state ();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignGUI::init_on_show ()
|
||||
{
|
||||
Gtk::Widget* p = get_parent();
|
||||
if (p) {
|
||||
p->signal_delete_event().connect (sigc::mem_fun (*this, &ContourDesignGUI::reset_test_state));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ContourDesignGUI::reset_test_state (GdkEventAny*)
|
||||
{
|
||||
_ccp._test_mode = false;
|
||||
_test_button.set_active (ActiveState::Off);
|
||||
vector<boost::shared_ptr<ArdourButton> >::const_iterator it;
|
||||
for (it = _btn_leds.begin(); it != _btn_leds.end(); ++it) {
|
||||
(*it)->set_active_state (ActiveState::Off);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignGUI::test_button_press (unsigned short btn)
|
||||
{
|
||||
_btn_leds[btn]->set_active_state (ActiveState::ExplicitActive);
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignGUI::test_button_release (unsigned short btn)
|
||||
{
|
||||
_btn_leds[btn]->set_active_state (ActiveState::Off);
|
||||
}
|
||||
|
||||
bool
|
||||
ContourDesignGUI::update_device_state ()
|
||||
{
|
||||
switch (_ccp.device_type ()) {
|
||||
case ContourDesignControlProtocol::ShuttlePRO:
|
||||
_device_state_lbl.set_markup ("<span weight=\"bold\" foreground=\"green\">Found ShuttlePRO v2</span>");
|
||||
XpressButtonsSensitive (true);
|
||||
ProButtonsSensitive (true);
|
||||
break;
|
||||
case ContourDesignControlProtocol::ShuttleXpress:
|
||||
_device_state_lbl.set_markup ("<span weight=\"bold\" foreground=\"green\">Found shuttleXpress</span>");
|
||||
XpressButtonsSensitive (true);
|
||||
ProButtonsSensitive (false);
|
||||
break;
|
||||
default:
|
||||
XpressButtonsSensitive (false);
|
||||
ProButtonsSensitive (false);
|
||||
_device_state_lbl.set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">Device not working:</span> %1",
|
||||
libusb_strerror ((libusb_error)_ccp._error)));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void*
|
||||
ContourDesignControlProtocol::get_gui () const
|
||||
{
|
||||
if (!_gui) {
|
||||
const_cast<ContourDesignControlProtocol*>(this)->build_gui ();
|
||||
}
|
||||
_gui->show_all();
|
||||
return (void*) _gui;
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::build_gui ()
|
||||
{
|
||||
_gui = new ContourDesignGUI (*this);
|
||||
}
|
||||
|
||||
void
|
||||
ContourDesignControlProtocol::tear_down_gui ()
|
||||
{
|
||||
if (_gui) {
|
||||
Gtk::Widget *w = _gui->get_parent();
|
||||
if (w) {
|
||||
w->hide();
|
||||
delete w;
|
||||
}
|
||||
}
|
||||
delete _gui;
|
||||
_gui = 0;
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
Copyright (C) 2019 Paul Davis
|
||||
Author: Johannes Mueller
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "pbd/failed_constructor.h"
|
||||
#include "pbd/error.h"
|
||||
|
||||
#include "ardour/session.h"
|
||||
#include "control_protocol/control_protocol.h"
|
||||
|
||||
#include "contourdesign.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
using namespace ArdourSurface;
|
||||
|
||||
static ControlProtocol*
|
||||
new_contourdesign_protocol (ControlProtocolDescriptor*, Session* s)
|
||||
{
|
||||
ContourDesignControlProtocol* wmcp = new ContourDesignControlProtocol (*s);
|
||||
wmcp->set_active (true);
|
||||
return wmcp;
|
||||
}
|
||||
|
||||
static void
|
||||
delete_contourdesign_protocol (ControlProtocolDescriptor* /*descriptor*/, ControlProtocol* cp)
|
||||
{
|
||||
delete cp;
|
||||
}
|
||||
|
||||
static bool
|
||||
probe_contourdesign_protocol (ControlProtocolDescriptor*)
|
||||
{
|
||||
return ContourDesignControlProtocol::probe ();
|
||||
}
|
||||
|
||||
static ControlProtocolDescriptor contourdesign_descriptor = {
|
||||
name : "ContourDesign",
|
||||
id : "uri://ardour.org/surfaces/contourdesign:0",
|
||||
ptr : 0,
|
||||
module : 0,
|
||||
mandatory : 0,
|
||||
supports_feedback : false,
|
||||
probe : probe_contourdesign_protocol,
|
||||
initialize : new_contourdesign_protocol,
|
||||
destroy : delete_contourdesign_protocol
|
||||
};
|
||||
|
||||
extern "C" ARDOURSURFACE_API ControlProtocolDescriptor* protocol_descriptor () { return &contourdesign_descriptor; }
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
Copyright (C) 2019 Paul Davis
|
||||
Author: Johannes Mueller
|
||||
|
||||
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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <gtkmm/spinbutton.h>
|
||||
|
||||
#include "gtkmm2ext/utils.h"
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
#include "jump_distance_widget.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace Gtk;
|
||||
using namespace Gtkmm2ext;
|
||||
using namespace ArdourSurface;
|
||||
|
||||
JumpDistanceWidget::JumpDistanceWidget (JumpDistance dist)
|
||||
: HBox ()
|
||||
, _distance (dist)
|
||||
, _value_adj (dist.value, -100, 100, 0.25)
|
||||
{
|
||||
SpinButton* sb = manage (new SpinButton (_value_adj, 0.25, 2));
|
||||
sb->signal_value_changed().connect (sigc::mem_fun (*this, &JumpDistanceWidget::update_value));
|
||||
pack_start (*sb);
|
||||
|
||||
vector<string> jog_units_strings;
|
||||
jog_units_strings.push_back (_("seconds"));
|
||||
jog_units_strings.push_back (_("beats"));
|
||||
jog_units_strings.push_back (_("bars"));
|
||||
|
||||
set_popdown_strings (_unit_cb, jog_units_strings);
|
||||
_unit_cb.set_active (_distance.unit);
|
||||
_unit_cb.signal_changed().connect (sigc::mem_fun (*this, &JumpDistanceWidget::update_unit));
|
||||
pack_start (_unit_cb);
|
||||
}
|
||||
|
||||
void
|
||||
JumpDistanceWidget::set_distance (JumpDistance dist)
|
||||
{
|
||||
_distance = dist;
|
||||
_value_adj.set_value (dist.value);
|
||||
_unit_cb.set_active (dist.unit);
|
||||
}
|
||||
|
||||
void
|
||||
JumpDistanceWidget::update_unit ()
|
||||
{
|
||||
_distance.unit = JumpUnit (_unit_cb.get_active_row_number ());
|
||||
Changed (); /* emit signal */
|
||||
}
|
||||
|
||||
void
|
||||
JumpDistanceWidget::update_value ()
|
||||
{
|
||||
_distance.value = _value_adj.get_value ();
|
||||
Changed (); /* emit signal */
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Copyright (C) 2019 Paul Davis
|
||||
Author: Johannes Mueller
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ardour_contourdesign_jump_distance_widget_h
|
||||
#define ardour_contourdesign_jump_distance_widget_h
|
||||
|
||||
#include <gtkmm/comboboxtext.h>
|
||||
#include <gtkmm/box.h>
|
||||
#include <gtkmm/adjustment.h>
|
||||
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "contourdesign.h"
|
||||
|
||||
namespace ArdourSurface
|
||||
{
|
||||
|
||||
class JumpDistanceWidget : public Gtk::HBox
|
||||
{
|
||||
public:
|
||||
JumpDistanceWidget (JumpDistance dist);
|
||||
~JumpDistanceWidget () {}
|
||||
|
||||
JumpDistance get_distance () const { return _distance; }
|
||||
void set_distance (JumpDistance dist);
|
||||
|
||||
sigc::signal<void> Changed;
|
||||
|
||||
private:
|
||||
JumpDistance _distance;
|
||||
|
||||
void update_value ();
|
||||
void update_unit ();
|
||||
|
||||
Gtk::Adjustment _value_adj;
|
||||
Gtk::ComboBoxText _unit_cb;
|
||||
};
|
||||
|
||||
} /* namespace */
|
||||
|
||||
#endif /* ardour_contourdesign_jump_distance_widget_h */
|
|
@ -0,0 +1,35 @@
|
|||
#!/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 = '''
|
||||
contourdesign.cc
|
||||
contourdesign_gui.cc
|
||||
jump_distance_widget.cc
|
||||
button_config_widget.cc
|
||||
interface.cc
|
||||
'''
|
||||
obj.export_includes = ['./contourdesign']
|
||||
obj.defines = [ 'PACKAGE="ardour_contourdesign"' ]
|
||||
obj.defines += [ 'ARDOURSURFACE_DLL_EXPORTS' ]
|
||||
obj.includes = ['.', '../libs', '../../widgets']
|
||||
obj.name = 'libardour_contourdesign'
|
||||
obj.target = 'ardour_contourdesign'
|
||||
obj.uselib = 'GTKMM USB'
|
||||
obj.use = 'libardour libardour_cp libgtkmm2ext'
|
||||
obj.install_path = os.path.join(bld.env['LIBDIR'], 'surfaces')
|
||||
|
||||
def shutdown():
|
||||
autowaf.shutdown()
|
|
@ -49,8 +49,10 @@ def configure(conf):
|
|||
|
||||
if conf.is_defined('HAVE_USB'):
|
||||
children += [ 'push2' ]
|
||||
children += [ 'contourdesign' ]
|
||||
|
||||
else:
|
||||
print ('You are missing the libusb-1.0 development package needed to compile Push2 support')
|
||||
print ('You are missing the libusb-1.0 development package needed to compile Push2 and ContourDesign support')
|
||||
|
||||
if conf.is_defined('HAVE_HIDAPI') and Options.options.maschine:
|
||||
children += [ 'maschine2' ]
|
||||
|
@ -96,6 +98,7 @@ def build(bld):
|
|||
bld.recurse('tranzport')
|
||||
if bld.is_defined('HAVE_USB'):
|
||||
bld.recurse('push2')
|
||||
bld.recurse('contourdesign')
|
||||
if bld.is_defined('BUILD_MASCHINE'):
|
||||
bld.recurse('maschine2')
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# This is a sample udev file to set "ContourDesign ShuttlePRO v2 and
|
||||
# shuttleXpress" device permissions on GNU/Linux systems.
|
||||
#
|
||||
# copy this file to /etc/udev/rules.d/
|
||||
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="0b33", ATTRS{idProduct}=="0030", MODE="660", GROUP="audio"
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="0b33", ATTRS{idProduct}=="0020", MODE="660", GROUP="audio"
|
Loading…
Reference in New Issue