Add support for contourdesign ShuttlePRO v2 and ShuttleXpress

This commit is contained in:
Johannes Mueller 2019-05-18 13:31:24 +02:00
parent 89f39d14f2
commit b8349069f1
14 changed files with 1777 additions and 2 deletions

View File

@ -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:.

View File

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

View File

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

View File

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

View File

@ -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 */

View File

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

View File

@ -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 */

View File

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

View File

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

View File

@ -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 */
}

View File

@ -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 */

View File

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

View File

@ -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')

View File

@ -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"