diff --git a/gtk2_ardour/ardev_common.sh.in b/gtk2_ardour/ardev_common.sh.in index 89dd925420..96a7ab8cf5 100644 --- a/gtk2_ardour/ardev_common.sh.in +++ b/gtk2_ardour/ardev_common.sh.in @@ -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:. diff --git a/libs/ardour/ardour/debug.h b/libs/ardour/ardour/debug.h index 069840a149..ba4b97dc1b 100644 --- a/libs/ardour/ardour/debug.h +++ b/libs/ardour/ardour/debug.h @@ -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; diff --git a/libs/ardour/debug.cc b/libs/ardour/debug.cc index 3eced3063b..1c23aa0703 100644 --- a/libs/ardour/debug.cc +++ b/libs/ardour/debug.cc @@ -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"); diff --git a/libs/surfaces/contourdesign/button_config_widget.cc b/libs/surfaces/contourdesign/button_config_widget.cc new file mode 100644 index 0000000000..17fadc7c9f --- /dev/null +++ b/libs/surfaces/contourdesign/button_config_widget.cc @@ -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 +#include + +#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 model () const { return _available_action_model; } + + const TreeModelColumn& name () const { return _action_columns.name; } + const TreeModelColumn& path () const { return _action_columns.path; } + +private: + ActionModel (); + struct ActionColumns : public TreeModel::ColumnRecord { + ActionColumns() { + add (name); + add (path); + } + TreeModelColumn name; + TreeModelColumn path; + }; + + const ActionColumns _action_columns; + Glib::RefPtr _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 btn_cnf) +{ + const ButtonAction* ba = dynamic_cast (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 (btn_cnf.get()); + set_jump_distance (bj->get_jump_distance()); + _action_cb.set_sensitive (false); + _jump_distance.set_sensitive (true); + } +} + +boost::shared_ptr +ButtonConfigWidget::get_current_config (ContourDesignControlProtocol& ccp) const +{ + if (_choice_jump.get_active ()) { + return boost::shared_ptr (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 (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 NodeMap; + NodeMap nodes; + NodeMap::iterator r; + + TreeIter rowp; + TreeModel::Row parent; + + rowp = _available_action_model->append (); + parent = *(rowp); + parent[_action_columns.name] = _("Disabled"); + + vector paths; + vector labels; + vector tooltips; + vector keys; + vector > actions; + + ActionManager::get_all_actions (paths, labels, tooltips, keys, actions); + + vector::iterator k; + vector::iterator p; + vector::iterator t; + vector::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 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; + } +} diff --git a/libs/surfaces/contourdesign/button_config_widget.h b/libs/surfaces/contourdesign/button_config_widget.h new file mode 100644 index 0000000000..c1f8f52b78 --- /dev/null +++ b/libs/surfaces/contourdesign/button_config_widget.h @@ -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 +#include +#include +#include + +#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 btn_cnf); + boost::shared_ptr get_current_config (ContourDesignControlProtocol& ccp) const; + + sigc::signal 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 */ diff --git a/libs/surfaces/contourdesign/contourdesign.cc b/libs/surfaces/contourdesign/contourdesign.cc new file mode 100644 index 0000000000..b0976f0ddf --- /dev/null +++ b/libs/surfaces/contourdesign/contourdesign.cc @@ -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 + +#include + +#include + +#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 ("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::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::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 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 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 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< _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 +ContourDesignControlProtocol::make_button_action (string action_string) +{ + return boost::shared_ptr (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 (new ButtonJump (JumpDistance (-4.0, BARS), *this))); + _button_actions.push_back (boost::shared_ptr (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 (transfer->user_data); + spc->handle_event(); +} diff --git a/libs/surfaces/contourdesign/contourdesign.h b/libs/surfaces/contourdesign/contourdesign.h new file mode 100644 index 0000000000..730e89ddaf --- /dev/null +++ b/libs/surfaces/contourdesign/contourdesign.h @@ -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 +#include +#include +#include + +#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 +{ + 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 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 ButtonPress; + PBD::Signal1 ButtonRelease; + + // Config stuff + + bool _keep_rolling; + std::vector _shuttle_speeds; + JumpDistance _jog_distance; + + std::vector > _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 */ diff --git a/libs/surfaces/contourdesign/contourdesign_gui.cc b/libs/surfaces/contourdesign/contourdesign_gui.cc new file mode 100644 index 0000000000..88c05234df --- /dev/null +++ b/libs/surfaces/contourdesign/contourdesign_gui.cc @@ -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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 > _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 > _btn_leds; + + void init_on_show (); + bool reset_test_state (GdkEventAny* = 0); + bool update_device_state (); + + Gtk::Label _device_state_lbl; + + sigc::signal ProButtonsSensitive; + sigc::signal 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 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 >::const_iterator it; + unsigned int btn_idx = 0; + for (it = _ccp._button_actions.begin(); it != _ccp._button_actions.end(); ++it) { + boost::shared_ptr 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 >::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 ("Found ShuttlePRO v2"); + XpressButtonsSensitive (true); + ProButtonsSensitive (true); + break; + case ContourDesignControlProtocol::ShuttleXpress: + _device_state_lbl.set_markup ("Found shuttleXpress"); + XpressButtonsSensitive (true); + ProButtonsSensitive (false); + break; + default: + XpressButtonsSensitive (false); + ProButtonsSensitive (false); + _device_state_lbl.set_markup (string_compose ("Device not working: %1", + libusb_strerror ((libusb_error)_ccp._error))); + } + + return false; +} + +void* +ContourDesignControlProtocol::get_gui () const +{ + if (!_gui) { + const_cast(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; +} diff --git a/libs/surfaces/contourdesign/interface.cc b/libs/surfaces/contourdesign/interface.cc new file mode 100644 index 0000000000..b1eb379a4d --- /dev/null +++ b/libs/surfaces/contourdesign/interface.cc @@ -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; } diff --git a/libs/surfaces/contourdesign/jump_distance_widget.cc b/libs/surfaces/contourdesign/jump_distance_widget.cc new file mode 100644 index 0000000000..1e7955fd05 --- /dev/null +++ b/libs/surfaces/contourdesign/jump_distance_widget.cc @@ -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 + +#include + +#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 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 */ +} diff --git a/libs/surfaces/contourdesign/jump_distance_widget.h b/libs/surfaces/contourdesign/jump_distance_widget.h new file mode 100644 index 0000000000..6bc4156bda --- /dev/null +++ b/libs/surfaces/contourdesign/jump_distance_widget.h @@ -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 +#include +#include + +#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 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 */ diff --git a/libs/surfaces/contourdesign/wscript b/libs/surfaces/contourdesign/wscript new file mode 100644 index 0000000000..da67c6a66f --- /dev/null +++ b/libs/surfaces/contourdesign/wscript @@ -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() diff --git a/libs/surfaces/wscript b/libs/surfaces/wscript index e3a32d05bf..502a2bd500 100644 --- a/libs/surfaces/wscript +++ b/libs/surfaces/wscript @@ -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') diff --git a/tools/udev/99-counterdesign.rules b/tools/udev/99-counterdesign.rules new file mode 100644 index 0000000000..90e191f6b5 --- /dev/null +++ b/tools/udev/99-counterdesign.rules @@ -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"