mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 23:05:04 +01:00
Add support for contourdesign ShuttlePRO v2 and ShuttleXpress
This commit is contained in:
parent
89f39d14f2
commit
b8349069f1
14 changed files with 1777 additions and 2 deletions
|
|
@ -13,7 +13,7 @@ export GTK2_RC_FILES=/nonexistent
|
||||||
# can find all the components.
|
# 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_PANNER_PATH=$libs/panners
|
||||||
export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:.
|
export ARDOUR_DATA_PATH=$TOP:$TOP/build:$TOP/gtk2_ardour:$TOP/build/gtk2_ardour:.
|
||||||
export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
|
export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ namespace PBD {
|
||||||
LIBARDOUR_API extern DebugBits OrderKeys;
|
LIBARDOUR_API extern DebugBits OrderKeys;
|
||||||
LIBARDOUR_API extern DebugBits Automation;
|
LIBARDOUR_API extern DebugBits Automation;
|
||||||
LIBARDOUR_API extern DebugBits WiimoteControl;
|
LIBARDOUR_API extern DebugBits WiimoteControl;
|
||||||
|
LIBARDOUR_API extern DebugBits ContourDesignControl;
|
||||||
LIBARDOUR_API extern DebugBits Ports;
|
LIBARDOUR_API extern DebugBits Ports;
|
||||||
LIBARDOUR_API extern DebugBits AudioEngine;
|
LIBARDOUR_API extern DebugBits AudioEngine;
|
||||||
LIBARDOUR_API extern DebugBits Soundcloud;
|
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::OrderKeys = PBD::new_debug_bit ("orderkeys");
|
||||||
PBD::DebugBits PBD::DEBUG::Automation = PBD::new_debug_bit ("automation");
|
PBD::DebugBits PBD::DEBUG::Automation = PBD::new_debug_bit ("automation");
|
||||||
PBD::DebugBits PBD::DEBUG::WiimoteControl = PBD::new_debug_bit ("wiimotecontrol");
|
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::Ports = PBD::new_debug_bit ("Ports");
|
||||||
PBD::DebugBits PBD::DEBUG::AudioEngine = PBD::new_debug_bit ("AudioEngine");
|
PBD::DebugBits PBD::DEBUG::AudioEngine = PBD::new_debug_bit ("AudioEngine");
|
||||||
PBD::DebugBits PBD::DEBUG::Soundcloud = PBD::new_debug_bit ("Soundcloud");
|
PBD::DebugBits PBD::DEBUG::Soundcloud = PBD::new_debug_bit ("Soundcloud");
|
||||||
|
|
|
||||||
272
libs/surfaces/contourdesign/button_config_widget.cc
Normal file
272
libs/surfaces/contourdesign/button_config_widget.cc
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
66
libs/surfaces/contourdesign/button_config_widget.h
Normal file
66
libs/surfaces/contourdesign/button_config_widget.h
Normal 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 */
|
||||||
640
libs/surfaces/contourdesign/contourdesign.cc
Normal file
640
libs/surfaces/contourdesign/contourdesign.cc
Normal 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();
|
||||||
|
}
|
||||||
217
libs/surfaces/contourdesign/contourdesign.h
Normal file
217
libs/surfaces/contourdesign/contourdesign.h
Normal 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 */
|
||||||
334
libs/surfaces/contourdesign/contourdesign_gui.cc
Normal file
334
libs/surfaces/contourdesign/contourdesign_gui.cc
Normal 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;
|
||||||
|
}
|
||||||
65
libs/surfaces/contourdesign/interface.cc
Normal file
65
libs/surfaces/contourdesign/interface.cc
Normal 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; }
|
||||||
76
libs/surfaces/contourdesign/jump_distance_widget.cc
Normal file
76
libs/surfaces/contourdesign/jump_distance_widget.cc
Normal 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 */
|
||||||
|
}
|
||||||
58
libs/surfaces/contourdesign/jump_distance_widget.h
Normal file
58
libs/surfaces/contourdesign/jump_distance_widget.h
Normal 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 */
|
||||||
35
libs/surfaces/contourdesign/wscript
Normal file
35
libs/surfaces/contourdesign/wscript
Normal 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()
|
||||||
|
|
@ -49,8 +49,10 @@ def configure(conf):
|
||||||
|
|
||||||
if conf.is_defined('HAVE_USB'):
|
if conf.is_defined('HAVE_USB'):
|
||||||
children += [ 'push2' ]
|
children += [ 'push2' ]
|
||||||
|
children += [ 'contourdesign' ]
|
||||||
|
|
||||||
else:
|
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:
|
if conf.is_defined('HAVE_HIDAPI') and Options.options.maschine:
|
||||||
children += [ 'maschine2' ]
|
children += [ 'maschine2' ]
|
||||||
|
|
@ -96,6 +98,7 @@ def build(bld):
|
||||||
bld.recurse('tranzport')
|
bld.recurse('tranzport')
|
||||||
if bld.is_defined('HAVE_USB'):
|
if bld.is_defined('HAVE_USB'):
|
||||||
bld.recurse('push2')
|
bld.recurse('push2')
|
||||||
|
bld.recurse('contourdesign')
|
||||||
if bld.is_defined('BUILD_MASCHINE'):
|
if bld.is_defined('BUILD_MASCHINE'):
|
||||||
bld.recurse('maschine2')
|
bld.recurse('maschine2')
|
||||||
|
|
||||||
|
|
|
||||||
7
tools/udev/99-counterdesign.rules
Normal file
7
tools/udev/99-counterdesign.rules
Normal 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"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue