summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2007-06-28 19:23:18 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2007-06-28 19:23:18 +0000
commit1cf9ed976abb2aa0c85175d91893990ff298d428 (patch)
tree628c4e3f5bd3b5f772513fe4190bb69a631964f9
parent4ecfc19541ed05c5e51c367556e9902c7f83b705 (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--SConstruct7
-rw-r--r--libs/surfaces/powermate/README21
-rw-r--r--libs/surfaces/powermate/SConscript57
-rw-r--r--libs/surfaces/powermate/interface.cc57
-rw-r--r--libs/surfaces/powermate/powermate.cc235
-rw-r--r--libs/surfaces/powermate/powermate.h34
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