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

@ -989,7 +989,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:

View file

@ -56,8 +56,8 @@ using namespace Gtkmm2ext;
using namespace Gtk;
using namespace sigc;
LadspaPluginUI::LadspaPluginUI (boost::shared_ptr<PluginInsert> pi, bool scrollable)
: PlugUIBase (pi),
LadspaPluginUI::LadspaPluginUI (boost::shared_ptr<PluginInsert> pi, nframes64_t sample_rate, nframes64_t period_size, bool scrollable)
: PlugUIBase (pi, sample_rate, period_size),
button_table (initial_button_rows, initial_button_cols),
output_table (initial_output_rows, initial_output_cols),
hAdjustment(0.0, 0.0, 0.0),
@ -77,7 +77,11 @@ LadspaPluginUI::LadspaPluginUI (boost::shared_ptr<PluginInsert> pi, bool scrolla
Label* combo_label = manage (new Label (_("<span size=\"large\">Presets</span>")));
combo_label->set_use_markup (true);
smaller_hbox->pack_start (*combo_label, false, false, 10);
Label* latency_label = manage (new Label (_("<span size=\"large\">Latency</span>")));
latency_label->set_use_markup (true);
smaller_hbox->pack_start (*latency_label, false, false, 10);
smaller_hbox->pack_start (latency_gui, false, false, 10);
smaller_hbox->pack_start (combo, false, false);
smaller_hbox->pack_start (save_button, false, false);

View file

@ -1,3 +1,6 @@
#ifndef __gtk2_ardour_latency_gui_h__
#define __gtk2_ardour_latency_gui_h__
#include <vector>
#include <string>
@ -59,3 +62,5 @@ class LatencyDialog : public ArdourDialog
private:
LatencyGUI lwidget;
};
#endif /* __gtk2_ardour_latency_gui_h__ */

View file

@ -61,7 +61,7 @@ using namespace Gtkmm2ext;
using namespace Gtk;
using namespace sigc;
PluginUIWindow::PluginUIWindow (boost::shared_ptr<PluginInsert> insert, bool scrollable)
PluginUIWindow::PluginUIWindow (boost::shared_ptr<PluginInsert> insert, nframes64_t sr, nframes64_t period, bool scrollable)
: ArdourDialog ("plugin ui")
{
if (insert->plugin()->has_editor()) {
@ -73,7 +73,7 @@ PluginUIWindow::PluginUIWindow (boost::shared_ptr<PluginInsert> insert, bool scr
if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (insert->plugin())) != 0) {
VSTPluginUI* vpu = new VSTPluginUI (insert, vp);
VSTPluginUI* vpu = new VSTPluginUI (insert, vp, session.frame_rate(), session.engine().frames_per_cycle());
_pluginui = vpu;
get_vbox()->add (*vpu);
@ -90,7 +90,7 @@ PluginUIWindow::PluginUIWindow (boost::shared_ptr<PluginInsert> insert, bool scr
} else {
LadspaPluginUI* pu = new LadspaPluginUI (insert, scrollable);
LadspaPluginUI* pu = new LadspaPluginUI (insert, sr, period, scrollable);
_pluginui = pu;
get_vbox()->add (*pu);
@ -145,11 +145,12 @@ PluginUIWindow::plugin_going_away ()
delete_when_idle (this);
}
PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi)
PlugUIBase::PlugUIBase (boost::shared_ptr<PluginInsert> pi, nframes64_t sr, nframes64_t period)
: insert (pi),
plugin (insert->plugin()),
save_button(_("Add")),
bypass_button (_("Bypass"))
bypass_button (_("Bypass")),
latency_gui (*pi, sr, period)
{
//combo.set_use_arrows_always(true);
set_popdown_strings (combo, plugin->get_presets());

View file

@ -43,6 +43,8 @@
#include <ardour_dialog.h>
#include <ardour/types.h>
#include "latency_gui.h"
namespace ARDOUR {
class PluginInsert;
class Plugin;
@ -66,7 +68,7 @@ namespace Gtkmm2ext {
class PlugUIBase : public virtual sigc::trackable
{
public:
PlugUIBase (boost::shared_ptr<ARDOUR::PluginInsert>);
PlugUIBase (boost::shared_ptr<ARDOUR::PluginInsert>, nframes64_t sample_rate, nframes64_t period_size);
virtual ~PlugUIBase() {}
virtual gint get_preferred_height () = 0;
@ -79,6 +81,7 @@ class PlugUIBase : public virtual sigc::trackable
Gtk::ComboBoxText combo;
Gtk::Button save_button;
Gtk::ToggleButton bypass_button;
LatencyGUI latency_gui;
void setting_selected();
void save_plugin_setting (void);
@ -88,7 +91,7 @@ class PlugUIBase : public virtual sigc::trackable
class LadspaPluginUI : public PlugUIBase, public Gtk::VBox
{
public:
LadspaPluginUI (boost::shared_ptr<ARDOUR::PluginInsert> plug, bool scrollable=false);
LadspaPluginUI (boost::shared_ptr<ARDOUR::PluginInsert> plug, nframes64_t sample_rate, nframes64_t period_size, bool scrollable = false);
~LadspaPluginUI ();
gint get_preferred_height () { return prefheight; }
@ -198,7 +201,7 @@ class LadspaPluginUI : public PlugUIBase, public Gtk::VBox
class PluginUIWindow : public ArdourDialog
{
public:
PluginUIWindow (boost::shared_ptr<ARDOUR::PluginInsert> insert, bool scrollable=false);
PluginUIWindow (boost::shared_ptr<ARDOUR::PluginInsert> insert, nframes64_t sample_rate, nframes64_t period_size, bool scrollable = false);
~PluginUIWindow ();
PlugUIBase& pluginui() { return *_pluginui; }
@ -217,7 +220,7 @@ class PluginUIWindow : public ArdourDialog
class VSTPluginUI : public PlugUIBase, public Gtk::VBox
{
public:
VSTPluginUI (boost::shared_ptr<ARDOUR::PluginInsert>, boost::shared_ptr<ARDOUR::VSTPlugin>);
VSTPluginUI (boost::shared_ptr<ARDOUR::PluginInsert>, boost::shared_ptr<ARDOUR::VSTPlugin>, nframes64_t sample_rate, nframes64_t period_size);
~VSTPluginUI ();
gint get_preferred_height ();

View file

@ -1088,7 +1088,7 @@ ProcessorBox::edit_processor (boost::shared_ptr<Processor> processor)
if (plugin_processor->get_gui() == 0) {
plugin_ui = new PluginUIWindow (plugin_processor);
plugin_ui = new PluginUIWindow (plugin_processor, _session.frame_rate(), _session.engine().frames_per_cycle());
if (_owner_is_mixer) {
ARDOUR_UI::instance()->the_mixer()->ensure_float (*plugin_ui);
@ -1190,7 +1190,7 @@ ProcessorBox::register_actions ()
/* new stuff */
ActionManager::register_action (popup_act_grp, X_("newplugin"), _("New Plugin ..."), sigc::ptr_fun (ProcessorBox::rb_choose_plugin));
act = ActionManager::register_action (popup_act_grp, X_("newprocessor"), _("New Insert"), sigc::ptr_fun (ProcessorBox::rb_choose_processor));
act = ActionManager::register_action (popup_act_grp, X_("newinsert"), _("New Insert"), sigc::ptr_fun (ProcessorBox::rb_choose_processor));
ActionManager::jack_sensitive_actions.push_back (act);
act = ActionManager::register_action (popup_act_grp, X_("newsend"), _("New Send ..."), sigc::ptr_fun (ProcessorBox::rb_choose_send));
ActionManager::jack_sensitive_actions.push_back (act);

View file

@ -581,7 +581,7 @@ RouteParams_UI::redirect_selected (boost::shared_ptr<ARDOUR::Processor> insert,
}
} else if ((plugin_insert = boost::dynamic_pointer_cast<PluginInsert> (insert)) != 0) {
LadspaPluginUI *plugin_ui = new LadspaPluginUI (plugin_insert, true);
LadspaPluginUI *plugin_ui = new LadspaPluginUI (plugin_insert, session->frame_rate(), session->engine().frames_per_cycle(), true);
if (place == PreFader) {
cleanup_pre_view();

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.