diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2007-06-28 19:23:18 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2007-06-28 19:23:18 +0000 |
commit | 1cf9ed976abb2aa0c85175d91893990ff298d428 (patch) | |
tree | 628c4e3f5bd3b5f772513fe4190bb69a631964f9 | |
parent | 4ecfc19541ed05c5e51c367556e9902c7f83b705 (diff) |
add basic support for the griffin powermate (a control surface) (from ben loftis)
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2077 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r-- | SConstruct | 7 | ||||
-rw-r--r-- | libs/surfaces/powermate/README | 21 | ||||
-rw-r--r-- | libs/surfaces/powermate/SConscript | 57 | ||||
-rw-r--r-- | libs/surfaces/powermate/interface.cc | 57 | ||||
-rw-r--r-- | libs/surfaces/powermate/powermate.cc | 235 | ||||
-rw-r--r-- | libs/surfaces/powermate/powermate.h | 34 |
6 files changed, 410 insertions, 1 deletions
diff --git a/SConstruct b/SConstruct index 4d0b6ef677..8ea69bf902 100644 --- a/SConstruct +++ b/SConstruct @@ -979,7 +979,12 @@ else: # its included in the tarball # -surface_subdirs = [ 'libs/surfaces/control_protocol', 'libs/surfaces/generic_midi', 'libs/surfaces/tranzport', 'libs/surfaces/mackie' ] +surface_subdirs = [ 'libs/surfaces/control_protocol', + 'libs/surfaces/generic_midi', + 'libs/surfaces/tranzport', + 'libs/surfaces/mackie', + 'libs/surfaces/powermate' + ] if env['SURFACES']: if have_libusb: diff --git a/libs/surfaces/powermate/README b/libs/surfaces/powermate/README new file mode 100644 index 0000000000..d786b1d898 --- /dev/null +++ b/libs/surfaces/powermate/README @@ -0,0 +1,21 @@ + +This module works with the Griffin Powermate and allows some basic transport control. + +It autodetects the Powermate on any input device of the form "/dev/input/event*". This means you must have the powermate module in your kernel. It works out-of-the-box with 64Studio and presumably lots of other modern distributions. + +Turning the wheel left and right will act as a "Shuttle" wheel, adjusting playback speed up and down +Pushing the knob will switch between play and stop +Pushing the knob while turning will jump to the next or previous markers + + +In order for the powermate to work, you have to have permission to open the input device for reading. +In debian, I changed /etc/udev/rules.d/0_permissions.rules to have the line: +KERNEL=="event[0-9]*", MODE="0666" +but there are other ways to achieve this + + +Feedback, tweaks, bug fixes and feature ideas are encouraged + +-Ben Loftis, ben@benloftis.com + +
\ No newline at end of file diff --git a/libs/surfaces/powermate/SConscript b/libs/surfaces/powermate/SConscript new file mode 100644 index 0000000000..e6f7e251df --- /dev/null +++ b/libs/surfaces/powermate/SConscript @@ -0,0 +1,57 @@ +# -*- python -*- + +import os +import os.path +import glob + +Import('env final_prefix install_prefix final_config_prefix libraries i18n') + +powermate = env.Copy() + +# +# this defines the version number of powermate +# + +domain = 'ardour_powermate' + +powermate.Append(DOMAIN = domain, MAJOR = 1, MINOR = 0, MICRO = 0) +powermate.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"") +powermate.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED") +powermate.Append(PACKAGE = domain) +powermate.Append(POTFILE = domain + '.pot') + +powermate_files=Split(""" +interface.cc +powermate.cc +""") + +powermate.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE") +powermate.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"") +powermate.Append(CXXFLAGS="-DCONFIG_DIR=\\\""+final_config_prefix+"\\\"") +powermate.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"") + +powermate.Merge ([ + libraries['ardour'], + libraries['ardour_cp'], + libraries['sigc2'], + libraries['pbd'], + libraries['midi++2'], + libraries['xml'], + libraries['usb'], + libraries['glib2'], + libraries['glibmm2'] + ]) + +libardour_powermate = powermate.SharedLibrary('ardour_powermate', powermate_files) + +Default(libardour_powermate) + +if env['NLS']: + i18n (powermate, powermate_files, env) + +env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2/surfaces'), libardour_powermate)) + +env.Alias('tarball', env.Distribute (env['DISTTREE'], + [ 'SConscript' ] + + powermate_files + + glob.glob('po/*.po') + glob.glob('*.h'))) diff --git a/libs/surfaces/powermate/interface.cc b/libs/surfaces/powermate/interface.cc new file mode 100644 index 0000000000..6012c064d8 --- /dev/null +++ b/libs/surfaces/powermate/interface.cc @@ -0,0 +1,57 @@ +/* + Ardour9pin interface file + Ben Loftis + Created: 05/18/06 11:07:56 + Copyright Harrison Audio, LLC, 2007 +*/ + +#include "powermate.h" + +using namespace ARDOUR; + +ControlProtocol* +new_powermate_protocol (ControlProtocolDescriptor* descriptor, Session* s) +{ + PowermateControlProtocol* pcp = new PowermateControlProtocol (*s); + + if (pcp->set_active (true)) { + delete pcp; + return 0; + } + + return pcp; + +} + +void +delete_powermate_protocol (ControlProtocolDescriptor* descriptor, ControlProtocol* cp) +{ + delete cp; +} + +bool +probe_powermate_protocol (ControlProtocolDescriptor* descriptor) +{ + return PowermateControlProtocol::probe (); +} + +static ControlProtocolDescriptor powermate_descriptor = { + name : "powermate", + id : "uri://ardour.org/ardour/powermate:0", + ptr : 0, + module : 0, + mandatory : 0, + supports_feedback : false, + probe : probe_powermate_protocol, + initialize : new_powermate_protocol, + destroy : delete_powermate_protocol +}; + + +extern "C" { +ControlProtocolDescriptor* +protocol_descriptor () { + return &powermate_descriptor; +} +} + diff --git a/libs/surfaces/powermate/powermate.cc b/libs/surfaces/powermate/powermate.cc new file mode 100644 index 0000000000..139313f3f8 --- /dev/null +++ b/libs/surfaces/powermate/powermate.cc @@ -0,0 +1,235 @@ +/* + powermate.cc + Ben Loftis + Created: 03/26/07 20:07:56 +*/ + + +#include <linux/input.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> + +#include <i18n.h> +#include <pbd/xml++.h> + +#include "powermate.h" + +using namespace ARDOUR; +using namespace std; +using namespace sigc; + +#define NUM_VALID_PREFIXES 2 + +static const char *valid_prefix[NUM_VALID_PREFIXES] = { + "Griffin PowerMate", + "Griffin SoundKnob" +}; + +#define NUM_EVENT_DEVICES 16 + +int open_powermate(const char *dev, int mode) +{ + int fd = open(dev, mode); + int i; + char name[255]; + + if(fd < 0){ + fprintf(stderr, "Unable to open \"%s\": %s\n", dev, strerror(errno)); + return -1; + } + + if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0){ + fprintf(stderr, "\"%s\": EVIOCGNAME failed: %s\n", dev, strerror(errno)); + close(fd); + return -1; + } + + // it's the correct device if the prefix matches what we expect it to be: + for(i=0; i<NUM_VALID_PREFIXES; i++) + if(!strncasecmp(name, valid_prefix[i], strlen(valid_prefix[i]))) + return fd; + + close(fd); + return -1; +} + +int find_powermate(int mode) +{ + char devname[256]; + int i, r; + + for(i=0; i<NUM_EVENT_DEVICES; i++){ + sprintf(devname, "/dev/input/event%d", i); + r = open_powermate(devname, mode); + if(r >= 0) + return r; + } + + return -1; +} + +PowermateControlProtocol::PowermateControlProtocol (Session& s) + : ControlProtocol (s, "powermate") +{ +} + +PowermateControlProtocol::~PowermateControlProtocol () +{ + set_active (false); +} + +bool +PowermateControlProtocol::probe () +{ + int port = find_powermate( O_RDONLY ); + + if (port < 0) { + printf ("powermate: Opening of powermate failed - %s\n", strerror(errno)); + close (port); + return false; + } + + close (port); + return true; +} + +int +PowermateControlProtocol::set_active (bool inActivate) +{ + if (inActivate != _active) { + + if (inActivate) { + + mPort = find_powermate(O_RDONLY); + + if ( mPort < 0 ) { + return -1; + } + + if (pthread_create (&mThread, 0, SerialThreadEntry, this) == 0) { + _active = true; + } else { + return -1; + } + + printf("Powermate Control Protocol activated\n"); + + } else { + pthread_cancel (mThread); + close (mPort); + _active = false; + printf("Powermate Control Protocol deactivated\n"); + } + } + + return 0; +} + +XMLNode& +PowermateControlProtocol::get_state () +{ + XMLNode* node = new XMLNode (X_("Protocol")); + node->add_property (X_("name"), _name); + return *node; +} + +int +PowermateControlProtocol::set_state (const XMLNode& node) +{ + return 0; +} + + +void* +PowermateControlProtocol::SerialThreadEntry (void* arg) +{ + return static_cast<PowermateControlProtocol*>(arg)->SerialThread (); +} + +#define BUFFER_SIZE 32 + +bool held = false; +bool skippingMarkers = false; + +void +PowermateControlProtocol::ProcessEvent(struct input_event *ev) +{ +#ifdef VERBOSE + fprintf(stderr, "type=0x%04x, code=0x%04x, value=%d\n", + ev->type, ev->code, (int)ev->value); +#endif + + switch(ev->type){ + case EV_MSC: + printf("The LED pulse settings were changed; code=0x%04x, value=0x%08x\n", ev->code, ev->value); + break; + case EV_REL: + if(ev->code != REL_DIAL) + fprintf(stderr, "Warning: unexpected rotation event; ev->code = 0x%04x\n", ev->code); + else{ + if (held) { + //click and hold to skip forward and back by markers + skippingMarkers = true;; + if (ev->value > 0) + next_marker(); + else + prev_marker(); + } else { + //scale the range so that we can go from +/-8x within 180 degrees, with less precision at the higher speeds + float speed = get_transport_speed(); + speed += (float)ev->value * 0.05; + if (speed > 1.5 || speed < -1.5 ) + speed += ev->value; + set_transport_speed( speed ); + } + } + break; + case EV_KEY: + if(ev->code != BTN_0) + fprintf(stderr, "Warning: unexpected key event; ev->code = 0x%04x\n", ev->code); + else + if (ev->value) + held = true; + else { + held = false; + if (skippingMarkers) { + skippingMarkers = false; + } else { + if (get_transport_speed() == 0.0) { + set_transport_speed(1.0); + } else { + set_transport_speed(0.0); + } + } + } + break; + } + + fflush(stdout); +} + +void* +PowermateControlProtocol::SerialThread () +{ + struct input_event ibuffer[BUFFER_SIZE]; + int r, events, i; + + while(1){ + r = read(mPort, ibuffer, sizeof(struct input_event) * BUFFER_SIZE); + if( r > 0 ){ + events = r / sizeof(struct input_event); + for(i=0; i<events; i++) + ProcessEvent(&ibuffer[i]); + }else{ + fprintf(stderr, "read() failed: %s\n", strerror(errno)); + return (void*) 0; + } + } + + return (void*) 0; +} + + diff --git a/libs/surfaces/powermate/powermate.h b/libs/surfaces/powermate/powermate.h new file mode 100644 index 0000000000..6b0a2fbbc3 --- /dev/null +++ b/libs/surfaces/powermate/powermate.h @@ -0,0 +1,34 @@ +#ifndef ardour_powermate_h +#define ardour_powermate_h + +#include <sys/time.h> +#include <pthread.h> + +#include "control_protocol/control_protocol.h" + +class PowermateControlProtocol : public ARDOUR::ControlProtocol +{ + public: + PowermateControlProtocol (ARDOUR::Session&); + virtual ~PowermateControlProtocol(); + + int set_active (bool yn); + static bool probe (); + + XMLNode& get_state (); + int set_state (const XMLNode&); + + private: + + static void* SerialThreadEntry (void* arg); + void* SerialThread (); + + void ProcessEvent(struct input_event *ev); + + int mPort; + pthread_t mThread; + +}; + + +#endif |