add basic support for the griffin powermate (a control surface) (from ben loftis)

git-svn-id: svn://localhost/ardour2/trunk@2078 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2007-06-28 19:35:48 +00:00
parent 39f5c0ad56
commit f95aa76f4a
15 changed files with 438 additions and 16 deletions

View file

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

View file

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

View file

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

Binary file not shown.

Binary file not shown.

View file

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

View file

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

Binary file not shown.